Create & Init Project...
This commit is contained in:
388
app/interface/main/reply/service/xreply.go
Normal file
388
app/interface/main/reply/service/xreply.go
Normal file
@ -0,0 +1,388 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sort"
|
||||
|
||||
"go-common/app/interface/main/reply/model/reply"
|
||||
xmodel "go-common/app/interface/main/reply/model/xreply"
|
||||
|
||||
"go-common/library/sync/errgroup.v2"
|
||||
)
|
||||
|
||||
const (
|
||||
_secondRepliesPn = 1
|
||||
_secondRepliesPs = 3
|
||||
_hotSize = 5
|
||||
_hotSizeWeb = 3
|
||||
)
|
||||
|
||||
func fillHot(rs []*reply.Reply, sub *reply.Subject, maxSize int) (hots []*reply.Reply) {
|
||||
for _, r := range rs {
|
||||
if r.Like >= 3 && !r.IsTop() && !r.IsDeleted() {
|
||||
hots = append(hots, r)
|
||||
}
|
||||
}
|
||||
if hots == nil {
|
||||
hots = _emptyReplies
|
||||
} else if len(hots) > maxSize {
|
||||
hots = hots[:maxSize]
|
||||
}
|
||||
if sub.RCount <= 20 && len(hots) > 0 {
|
||||
hots = _emptyReplies
|
||||
}
|
||||
return hots
|
||||
}
|
||||
|
||||
// buildResCursor ...
|
||||
func buildResCursor(reqCursor *xmodel.Cursor, replies []*reply.Reply, cursorMode int) (resCursor xmodel.CursorRes) {
|
||||
length := len(replies)
|
||||
switch cursorMode {
|
||||
case xmodel.CursorModePage:
|
||||
// next和prev只可能一个不为0
|
||||
curPage := reqCursor.Next + reqCursor.Prev
|
||||
if reqCursor.Latest() {
|
||||
resCursor.IsBegin = true
|
||||
if length == 0 {
|
||||
resCursor.Next = curPage
|
||||
resCursor.Prev = curPage
|
||||
resCursor.IsEnd = true
|
||||
} else {
|
||||
// 下一页是第二页
|
||||
resCursor.Next = 2
|
||||
resCursor.Prev = 1
|
||||
if length < reqCursor.Ps {
|
||||
resCursor.IsEnd = true
|
||||
}
|
||||
}
|
||||
} else if reqCursor.Forward() {
|
||||
resCursor.Next = curPage + 1
|
||||
resCursor.Prev = curPage
|
||||
if length < reqCursor.Ps {
|
||||
resCursor.IsEnd = true
|
||||
}
|
||||
} else if reqCursor.Backward() {
|
||||
resCursor.Next = curPage
|
||||
resCursor.Prev = curPage - 1
|
||||
if length < reqCursor.Ps {
|
||||
resCursor.IsBegin = true
|
||||
}
|
||||
}
|
||||
case xmodel.CursorModeCursor:
|
||||
if reqCursor.Latest() {
|
||||
resCursor.IsBegin = true
|
||||
// Latest, 点进来默认访问的情况
|
||||
if length == 0 {
|
||||
resCursor.Next = reqCursor.Next
|
||||
resCursor.Prev = reqCursor.Prev
|
||||
resCursor.IsEnd = true
|
||||
} else {
|
||||
resCursor.Next = replies[length-1].Floor
|
||||
resCursor.Prev = replies[0].Floor
|
||||
if length < reqCursor.Ps {
|
||||
resCursor.IsEnd = true
|
||||
}
|
||||
}
|
||||
} else if reqCursor.Forward() {
|
||||
if length == 0 {
|
||||
resCursor.Next = reqCursor.Next
|
||||
resCursor.Prev = reqCursor.Prev
|
||||
resCursor.IsEnd = true
|
||||
} else {
|
||||
resCursor.Next = replies[length-1].Floor
|
||||
resCursor.Prev = replies[0].Floor
|
||||
if length < reqCursor.Ps {
|
||||
resCursor.IsEnd = true
|
||||
}
|
||||
}
|
||||
} else if reqCursor.Backward() {
|
||||
if length == 0 {
|
||||
resCursor.Next = reqCursor.Next
|
||||
resCursor.Prev = reqCursor.Prev
|
||||
resCursor.IsBegin = true
|
||||
} else {
|
||||
resCursor.Next = replies[length-1].Floor
|
||||
resCursor.Prev = replies[0].Floor
|
||||
if length < reqCursor.Ps {
|
||||
resCursor.IsBegin = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// replyMisc ...
|
||||
func (s *Service) replyCommonRes(ctx context.Context, mid, oid int64, tp int8, sub *reply.Subject) (res xmodel.CommonRes) {
|
||||
if sub == nil {
|
||||
return
|
||||
}
|
||||
g := errgroup.WithContext(ctx)
|
||||
g.Go(func(ctx context.Context) error {
|
||||
if s.RelationBlocked(ctx, sub.Mid, mid) {
|
||||
res.Blacklist = 1
|
||||
}
|
||||
return nil
|
||||
})
|
||||
g.Go(func(ctx context.Context) error {
|
||||
if ok, _ := s.CheckAssist(ctx, sub.Mid, mid); ok {
|
||||
res.Assist = 1
|
||||
}
|
||||
return nil
|
||||
})
|
||||
// 默认都是展示
|
||||
res.Config.ShowAdmin, res.Config.ShowEntry, res.Config.ShowFloor = 1, 1, 1
|
||||
g.Go(func(ctx context.Context) error {
|
||||
if cfg, _ := s.GetReplyLogConfig(ctx, sub, 1); cfg != nil {
|
||||
res.Config.ShowAdmin, res.Config.ShowEntry = cfg.ShowAdmin, cfg.ShowEntry
|
||||
}
|
||||
// 特殊稿件不显示楼层
|
||||
if !s.ShowFloor(oid, tp) {
|
||||
res.Config.ShowFloor = 0
|
||||
}
|
||||
return nil
|
||||
})
|
||||
g.Wait()
|
||||
res.Upper.Mid = sub.Mid
|
||||
return
|
||||
}
|
||||
|
||||
// fillXreplyRes ...
|
||||
func (s *Service) fillXreplyRes(ctx context.Context, sub *reply.Subject, req *xmodel.ReplyReq, res *xmodel.ReplyRes) {
|
||||
res.CommonRes = s.replyCommonRes(ctx, req.Mid, req.Oid, req.Type, sub)
|
||||
res.Notice = s.RplyNotice(ctx, req.Plat, req.Build, req.MobiApp, req.Buvid)
|
||||
// 过滤掉可能的脏数据
|
||||
res.Replies = s.FilDelReply(res.Replies)
|
||||
res.Hots = s.FilDelReply(res.Hots)
|
||||
// 过滤掉赞数小于3的
|
||||
res.Hots = fillHot(res.Hots, sub, s.hotNum(req.Oid, req.Type))
|
||||
res.Cursor.AllCount = sub.ACount
|
||||
res.Folder = sub.Folder()
|
||||
}
|
||||
|
||||
// Xreply ...
|
||||
func (s *Service) Xreply(ctx context.Context, req *xmodel.ReplyReq) (res *xmodel.ReplyRes, err error) {
|
||||
var (
|
||||
sub *reply.Subject
|
||||
cursor *reply.Cursor
|
||||
cursorRes *reply.RootReplyList
|
||||
pageRes *reply.PageResult
|
||||
)
|
||||
res = new(xmodel.ReplyRes)
|
||||
mode, supportMode := req.ModeInfo(s.sortByHot, s.sortByTime)
|
||||
switch mode {
|
||||
case xmodel.ModeOrigin, xmodel.ModeTime:
|
||||
// 这里原来用的闭区间,所以这里有坑
|
||||
if req.Cursor.Forward() {
|
||||
if req.Cursor.Next > 1 {
|
||||
req.Cursor.Next--
|
||||
}
|
||||
} else if req.Cursor.Backward() {
|
||||
req.Cursor.Prev++
|
||||
}
|
||||
if cursor, err = reply.NewCursor(int64(req.Cursor.Next), int64(req.Cursor.Prev), req.Cursor.Ps, reply.OrderDESC); err != nil {
|
||||
return
|
||||
}
|
||||
if req.Cursor.Backward() {
|
||||
if cursor, err = reply.NewCursor(int64(req.Cursor.Next), int64(req.Cursor.Prev), req.Cursor.Ps, reply.OrderASC); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
cursorParams := &reply.CursorParams{
|
||||
IP: req.IP,
|
||||
Oid: req.Oid,
|
||||
OTyp: req.Type,
|
||||
Sort: reply.SortByFloor,
|
||||
HTMLEscape: false,
|
||||
Cursor: cursor,
|
||||
HotSize: s.hotNum(req.Oid, req.Type),
|
||||
Mid: req.Mid,
|
||||
}
|
||||
if cursorRes, err = s.GetRootReplyListByCursor(ctx, cursorParams); err != nil {
|
||||
return
|
||||
}
|
||||
sub = cursorRes.Subject
|
||||
res.Replies = cursorRes.Roots
|
||||
if cursorRes.Header != nil {
|
||||
res.Top.Admin = cursorRes.Header.TopAdmin
|
||||
res.Top.Upper = cursorRes.Header.TopUpper
|
||||
res.Hots = cursorRes.Header.Hots
|
||||
// 纯按楼层排序 去掉热评
|
||||
if mode == xmodel.ModeTime {
|
||||
res.Hots = _emptyReplies
|
||||
}
|
||||
}
|
||||
if !sort.SliceIsSorted(res.Replies, func(i, j int) bool { return res.Replies[i].Floor > res.Replies[j].Floor }) {
|
||||
sort.Slice(res.Replies, func(i, j int) bool { return res.Replies[i].Floor > res.Replies[j].Floor })
|
||||
}
|
||||
|
||||
// 这里原来用的闭区间,所以这里有坑
|
||||
if req.Cursor.Next == 1 {
|
||||
res.Replies = _emptyReplies
|
||||
}
|
||||
// 由服务端来控制翻页逻辑
|
||||
res.Cursor = buildResCursor(&xmodel.Cursor{Prev: req.Cursor.Prev, Next: req.Cursor.Next, Ps: req.Cursor.Ps}, res.Replies, xmodel.CursorModeCursor)
|
||||
case xmodel.ModeHot:
|
||||
var curPage = req.Cursor.Prev + req.Cursor.Next
|
||||
if req.Cursor.Latest() {
|
||||
curPage = 1
|
||||
}
|
||||
pageParams := &reply.PageParams{
|
||||
Mid: req.Mid,
|
||||
Oid: req.Oid,
|
||||
Type: req.Type,
|
||||
Sort: reply.SortByLike,
|
||||
PageNum: curPage,
|
||||
PageSize: req.Cursor.Ps,
|
||||
NeedSecond: true,
|
||||
Escape: false,
|
||||
NeedHot: false,
|
||||
}
|
||||
if pageRes, err = s.RootReplies(ctx, pageParams); err != nil {
|
||||
return
|
||||
}
|
||||
sub = pageRes.Subject
|
||||
res.Replies = pageRes.Roots
|
||||
res.Top.Admin = pageRes.TopAdmin
|
||||
res.Top.Upper = pageRes.TopUpper
|
||||
// 按页码翻页控制返回页码
|
||||
res.Cursor = buildResCursor(&xmodel.Cursor{Prev: req.Cursor.Prev, Next: req.Cursor.Next, Ps: req.Cursor.Ps}, res.Replies, xmodel.CursorModePage)
|
||||
}
|
||||
res.Cursor.Mode = mode
|
||||
res.Cursor.SupportMode = supportMode
|
||||
s.fillXreplyRes(ctx, sub, req, res)
|
||||
return
|
||||
}
|
||||
|
||||
// SubFoldedReply ...
|
||||
func (s *Service) SubFoldedReply(ctx context.Context, req *xmodel.SubFolderReq) (res *xmodel.SubFolderRes, err error) {
|
||||
var (
|
||||
rpIDs []int64
|
||||
rootMap map[int64]*reply.Reply
|
||||
childrenMap map[int64][]*reply.Reply
|
||||
rootRps []*reply.Reply
|
||||
childrenRps []*reply.Reply
|
||||
sub *reply.Subject
|
||||
)
|
||||
res = new(xmodel.SubFolderRes)
|
||||
if req.Cursor.Backward() {
|
||||
return
|
||||
}
|
||||
cursor := &xmodel.Cursor{
|
||||
Ps: req.Cursor.Ps,
|
||||
Next: req.Cursor.Next,
|
||||
}
|
||||
if sub, err = s.Subject(ctx, req.Oid, req.Type); err != nil {
|
||||
return
|
||||
}
|
||||
if rpIDs, err = s.foldedReplies(ctx, sub, 0, cursor); err != nil {
|
||||
return
|
||||
}
|
||||
if rootMap, err = s.repliesMap(ctx, req.Oid, req.Type, rpIDs); err != nil {
|
||||
return
|
||||
}
|
||||
if childrenMap, childrenRps, err = s.secondReplies(ctx, sub, rootMap, req.Mid, _secondRepliesPn, _secondRepliesPs); err != nil {
|
||||
return
|
||||
}
|
||||
for _, rpID := range rpIDs {
|
||||
if r, ok := rootMap[rpID]; ok {
|
||||
if children, hasChild := childrenMap[rpID]; hasChild {
|
||||
r.Replies = children
|
||||
childrenRps = append(childrenRps, children...)
|
||||
} else {
|
||||
r.Replies = _emptyReplies
|
||||
}
|
||||
rootRps = append(rootRps, r)
|
||||
}
|
||||
}
|
||||
if rootRps != nil {
|
||||
res.Replies = rootRps
|
||||
} else {
|
||||
res.Replies = _emptyReplies
|
||||
}
|
||||
var rps []*reply.Reply
|
||||
rps = append(rps, rootRps...)
|
||||
rps = append(rps, childrenRps...)
|
||||
if err = s.buildReply(ctx, sub, rps, req.Mid, false); err != nil {
|
||||
return
|
||||
}
|
||||
res.Cursor = buildResCursor(&xmodel.Cursor{Prev: req.Cursor.Prev, Next: req.Cursor.Next, Ps: req.Cursor.Ps}, res.Replies, xmodel.CursorModeCursor)
|
||||
res.CommonRes = s.replyCommonRes(ctx, req.Mid, req.Oid, req.Type, sub)
|
||||
return
|
||||
}
|
||||
|
||||
// RootFoldedReply ...
|
||||
func (s *Service) RootFoldedReply(ctx context.Context, req *xmodel.RootFolderReq) (res *xmodel.RootFolderRes, err error) {
|
||||
var (
|
||||
rpIDs []int64
|
||||
childrenMap map[int64]*reply.Reply
|
||||
childrenRps []*reply.Reply
|
||||
sub *reply.Subject
|
||||
)
|
||||
res = new(xmodel.RootFolderRes)
|
||||
if req.Cursor.Backward() {
|
||||
return
|
||||
}
|
||||
cursor := &xmodel.Cursor{
|
||||
Ps: req.Cursor.Ps,
|
||||
Next: req.Cursor.Next,
|
||||
}
|
||||
if sub, err = s.Subject(ctx, req.Oid, req.Type); err != nil {
|
||||
return
|
||||
}
|
||||
if rpIDs, err = s.foldedReplies(ctx, sub, req.Root, cursor); err != nil {
|
||||
return
|
||||
}
|
||||
if childrenMap, err = s.repliesMap(ctx, req.Oid, req.Type, rpIDs); err != nil {
|
||||
return
|
||||
}
|
||||
for _, rpID := range rpIDs {
|
||||
if r, ok := childrenMap[rpID]; ok {
|
||||
childrenRps = append(childrenRps, r)
|
||||
}
|
||||
}
|
||||
if childrenRps != nil {
|
||||
res.Replies = childrenRps
|
||||
} else {
|
||||
res.Replies = _emptyReplies
|
||||
}
|
||||
if err = s.buildReply(ctx, sub, res.Replies, req.Mid, false); err != nil {
|
||||
return
|
||||
}
|
||||
// 只有往下翻
|
||||
res.Cursor = buildResCursor(&xmodel.Cursor{Prev: req.Cursor.Prev, Next: req.Cursor.Next, Ps: req.Cursor.Ps}, res.Replies, xmodel.CursorModeCursor)
|
||||
res.CommonRes = s.replyCommonRes(ctx, req.Mid, req.Oid, req.Type, sub)
|
||||
return
|
||||
}
|
||||
|
||||
// foldedReplies ...
|
||||
func (s *Service) foldedReplies(ctx context.Context, sub *reply.Subject, root int64, cursor *xmodel.Cursor) (rpIDs []int64, err error) {
|
||||
var (
|
||||
kind string
|
||||
ID int64
|
||||
ok bool
|
||||
)
|
||||
if root == 0 {
|
||||
kind = xmodel.FolderKindSub
|
||||
ID = sub.Oid
|
||||
} else {
|
||||
kind = xmodel.FolderKindRoot
|
||||
ID = root
|
||||
}
|
||||
if ok, err = s.dao.Redis.ExpireFolder(ctx, kind, ID); err != nil {
|
||||
return
|
||||
}
|
||||
if ok {
|
||||
if rpIDs, err = s.dao.Redis.FolderByCursor(ctx, kind, ID, cursor); err != nil {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
if rpIDs, err = s.dao.Reply.FoldedRepliesCursor(ctx, sub.Oid, sub.Type, root, cursor); err != nil {
|
||||
return
|
||||
}
|
||||
s.cache.Do(ctx, func(ctx context.Context) {
|
||||
s.dao.Databus.RecoverFolderIdx(ctx, sub.Oid, sub.Type, root)
|
||||
})
|
||||
}
|
||||
return
|
||||
}
|
Reference in New Issue
Block a user