123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402 |
- package service
- import (
- "context"
- "sort"
- "time"
- "go-common/app/interface/main/space/model"
- arcmdl "go-common/app/service/main/archive/api"
- coinmdl "go-common/app/service/main/coin/api"
- thumbup "go-common/app/service/main/thumbup/model"
- "go-common/library/ecode"
- "go-common/library/log"
- "go-common/library/net/metadata"
- "go-common/library/sync/errgroup"
- )
- const (
- _dyTypeCoin = -1
- _dyTypeLike = -2
- _dyTypeMerge = -3
- _businessLike = "archive"
- _likeVideoCnt = 100
- _dyListCnt = 20
- _dyDefaultQn = 16
- _dyFoldNum = 3
- )
- var dyTypeFoldMap = map[int]struct{}{_dyTypeCoin: {}, _dyTypeLike: {}, _dyTypeMerge: {}}
- // DynamicList get dynamic list.
- func (s Service) DynamicList(c context.Context, arg *model.DyListArg) (dyTotal *model.DyTotal, err error) {
- var (
- list, actList []*model.DyItem
- mergeList []*model.DyActItem
- dyList *model.DyList
- topDy *model.DyCard
- dyListTs, lastCoinTs, lastLikeTs int64
- topErr, dyErr error
- hasCoin, hasDy, hasLike, top bool
- )
- fp := arg.Pn == 1
- repeatDyIDs := make(map[int64]int64, 1)
- group, errCtx := errgroup.WithContext(c)
- group.Go(func() error {
- if topDy, topErr = s.topDynamic(errCtx, arg.Vmid, arg.Qn); topErr == nil && topDy != nil {
- top = fp
- repeatDyIDs[topDy.Desc.DynamicID] = topDy.Desc.DynamicID
- }
- return nil
- })
- group.Go(func() error {
- if dyList, dyErr = s.dao.DynamicList(errCtx, arg.Mid, arg.Vmid, arg.DyID, arg.Qn, arg.Pn); dyErr != nil {
- log.Error("s.dao.DynamicList(mid:%d,vmid:%d,dyID:%d,qn:%d,pn:%d) error(%+v)", arg.Mid, arg.Vmid, arg.DyID, arg.Qn, arg.Pn, dyErr)
- }
- return nil
- })
- group.Go(func() error {
- lastCoinTs, lastLikeTs, mergeList = s.actList(errCtx, arg.Mid, arg.Vmid)
- return nil
- })
- if e := group.Wait(); e != nil {
- log.Error("DynamicList group.Wait mid(%d) error(%v)", arg.Vmid, e)
- }
- // rm repeat data
- if dyErr == nil && dyList != nil && len(dyList.Cards) > 0 {
- for _, v := range dyList.Cards {
- if _, ok := repeatDyIDs[v.Desc.DynamicID]; ok {
- continue
- }
- item := new(model.DyResult)
- item.FromCard(v)
- list = append(list, &model.DyItem{Type: v.Desc.Type, Card: item, Ctime: v.Desc.Timestamp})
- }
- hasDy = dyList.HasMore == 1
- dyListTs = dyList.Cards[len(dyList.Cards)-1].Desc.Timestamp
- }
- if len(mergeList) > 0 {
- hasCoin, hasLike, actList = s.filterActList(c, lastCoinTs, lastLikeTs, arg.LastTime, dyListTs, mergeList, fp)
- list = append(list, actList...)
- }
- sort.Slice(list, func(i, j int) bool { return list[i].Ctime > list[j].Ctime })
- dyTotal = new(model.DyTotal)
- if top {
- topItem := new(model.DyResult)
- topItem.FromCard(topDy)
- dyTotal.List = append(dyTotal.List, &model.DyItem{Type: topDy.Desc.Type, Top: true, Card: topItem, Ctime: topDy.Desc.Timestamp})
- }
- dyTotal.HasMore = hasDy || hasCoin || hasLike
- dyTotal.List = append(dyTotal.List, list...)
- if s.c.Rule.ActFold {
- dyTotal.List = foldDyActItem(dyTotal.List)
- }
- return
- }
- func (s *Service) actList(c context.Context, mid, vmid int64) (lastCoinTs, lastLikeTs int64, mergeList []*model.DyActItem) {
- var (
- coinList, likeList, preList []*model.DyActItem
- coinErr, likeErr error
- coinPcy, likePcy bool
- )
- group, errCtx := errgroup.WithContext(c)
- privacy := s.privacy(c, vmid)
- if value, ok := privacy[model.PcyCoinVideo]; ok && value != _defaultPrivacy {
- coinPcy = true
- }
- if value, ok := privacy[model.PcyLikeVideo]; ok && value != _defaultPrivacy {
- likePcy = true
- }
- // coin video
- if mid == vmid || !coinPcy {
- group.Go(func() error {
- coinList, coinErr = s.coinVideos(errCtx, vmid, coinPcy)
- return nil
- })
- }
- // like video
- if mid == vmid || !likePcy {
- group.Go(func() error {
- likeList, likeErr = s.likeVideos(errCtx, vmid, likePcy)
- return nil
- })
- }
- group.Wait()
- if coinErr == nil {
- if l := len(coinList); l > 0 {
- preList = append(preList, coinList...)
- lastCoinTs = coinList[l-1].ActionTime
- }
- }
- if likeErr == nil {
- if l := len(likeList); l > 0 {
- preList = append(preList, likeList...)
- lastLikeTs = likeList[l-1].ActionTime
- }
- }
- if len(preList) == 0 {
- return
- }
- sort.Slice(preList, func(i, j int) bool { return preList[i].ActionTime > preList[j].ActionTime })
- if s.c.Rule.Merge {
- mergeList = mergeDyActItem(preList)
- } else {
- mergeList = preList
- }
- return
- }
- func (s *Service) filterActList(c context.Context, lastCoinTs, lastLikeTs, lastTime, dyListTs int64, mergeList []*model.DyActItem, fp bool) (hasCoin, hasLike bool, list []*model.DyItem) {
- var (
- actList []*model.DyActItem
- actAids []int64
- coinTs, likeTs int64
- )
- for _, v := range mergeList {
- if dyListTs == 0 && len(actList) >= _dyListCnt {
- lastActTs := actList[len(actList)-1].ActionTime
- penultActTs := actList[len(actList)-2].ActionTime
- y1, m1, d1 := time.Unix(lastActTs, 0).Date()
- y2, m2, d2 := time.Unix(penultActTs, 0).Date()
- if d1 != d2 || m1 != m2 || y1 != y2 {
- actList = actList[:len(actList)-1]
- break
- }
- }
- if fp {
- if dyListTs > 0 {
- if v.ActionTime >= dyListTs {
- actList = append(actList, v)
- actAids = append(actAids, v.Aid)
- }
- } else {
- actList = append(actList, v)
- actAids = append(actAids, v.Aid)
- }
- } else {
- if dyListTs > 0 {
- if v.ActionTime >= dyListTs && v.ActionTime < lastTime {
- actList = append(actList, v)
- actAids = append(actAids, v.Aid)
- }
- } else {
- if v.ActionTime < lastTime {
- actList = append(actList, v)
- actAids = append(actAids, v.Aid)
- }
- }
- }
- switch v.Type {
- case _dyTypeCoin:
- coinTs = v.ActionTime
- case _dyTypeLike:
- likeTs = v.ActionTime
- }
- }
- if coinTs > lastCoinTs {
- hasCoin = true
- }
- if likeTs > lastLikeTs {
- hasLike = true
- }
- if arcsReply, err := s.arcClient.Arcs(c, &arcmdl.ArcsRequest{Aids: actAids}); err != nil {
- log.Error("DynamicList s.arcClient.Arcs(%v) error(%v)", actAids, err)
- } else {
- for _, v := range actList {
- if arc, ok := arcsReply.Arcs[v.Aid]; ok && arc != nil && arc.IsNormal() {
- video := new(model.VideoItem)
- video.FromArchive(arc)
- video.ActionTime = v.ActionTime
- list = append(list, &model.DyItem{Type: v.Type, Archive: video, Ctime: v.ActionTime, Privacy: v.Privacy})
- }
- }
- }
- return
- }
- func mergeDyActItem(preList []*model.DyActItem) (mergeList []*model.DyActItem) {
- type privacy struct {
- Num int
- Coin bool
- Like bool
- }
- aidNumMap := make(map[int64]*privacy, len(preList))
- aidExist := make(map[int64]struct{}, len(preList))
- for _, v := range preList {
- if _, exist := aidNumMap[v.Aid]; !exist {
- aidNumMap[v.Aid] = new(privacy)
- }
- aidNumMap[v.Aid].Num++
- switch v.Type {
- case _dyTypeCoin:
- aidNumMap[v.Aid].Coin = v.Privacy
- case _dyTypeLike:
- aidNumMap[v.Aid].Like = v.Privacy
- }
- }
- for _, v := range preList {
- num := aidNumMap[v.Aid].Num
- if num > 1 {
- if _, ok := aidExist[v.Aid]; !ok {
- v.Type = _dyTypeMerge
- v.Privacy = aidNumMap[v.Aid].Coin && aidNumMap[v.Aid].Like
- mergeList = append(mergeList, v)
- }
- aidExist[v.Aid] = struct{}{}
- } else {
- mergeList = append(mergeList, v)
- }
- }
- return
- }
- func foldDyActItem(list []*model.DyItem) (foldList []*model.DyItem) {
- l := len(list)
- if l == 0 {
- foldList = make([]*model.DyItem, 0)
- return
- }
- if l < _dyFoldNum {
- foldList = list
- return
- }
- var preCk bool
- for index, v := range list {
- if index == 0 {
- foldList = append(foldList, v)
- continue
- }
- last := index == l-1
- if index >= _dyFoldNum-1 {
- _, tpCheck := dyTypeFoldMap[v.Type]
- y1, m1, d1 := time.Unix(v.Ctime, 0).Date()
- _, preTpCheck := dyTypeFoldMap[list[index-1].Type]
- y2, m2, d2 := time.Unix(list[index-1].Ctime, 0).Date()
- _, check := dyTypeFoldMap[list[index-2].Type]
- y3, m3, d3 := time.Unix(list[index-2].Ctime, 0).Date()
- ck := tpCheck && preTpCheck && check && (y1 == y2 && m1 == m2 && d1 == d2) && (y1 == y3 && m1 == m3 && d1 == d3)
- // append pre item to fold if ck or preCk
- if ck || preCk {
- foldList[len(foldList)-1].Fold = append(foldList[len(foldList)-1].Fold, list[index-1])
- if last {
- foldList[len(foldList)-1].Fold = append(foldList[len(foldList)-1].Fold, v)
- }
- } else {
- foldList = append(foldList, list[index-1])
- if last {
- foldList = append(foldList, v)
- }
- }
- preCk = ck
- }
- }
- return foldList
- }
- func (s *Service) coinVideos(c context.Context, vmid int64, pcy bool) (list []*model.DyActItem, err error) {
- var (
- coinReply *coinmdl.ListReply
- aids []int64
- )
- if coinReply, err = s.coinClient.List(c, &coinmdl.ListReq{Mid: vmid, Business: _businessCoin, Ts: time.Now().Unix()}); err != nil {
- log.Error("s.coinClient.List(%d) error(%v)", vmid, err)
- return
- }
- existArcs := make(map[int64]*coinmdl.ModelList, len(coinReply.List))
- for _, v := range coinReply.List {
- if len(aids) > _coinVideoLimit {
- break
- }
- if _, ok := existArcs[v.Aid]; ok {
- continue
- }
- if v.Aid > 0 {
- list = append(list, &model.DyActItem{Aid: v.Aid, Type: _dyTypeCoin, ActionTime: v.Ts, Privacy: pcy})
- existArcs[v.Aid] = v
- }
- }
- return
- }
- func (s *Service) likeVideos(c context.Context, mid int64, pcy bool) (list []*model.DyActItem, err error) {
- var (
- likes *thumbup.UserTotalLike
- ip = metadata.String(c, metadata.RemoteIP)
- )
- arg := &thumbup.ArgUserLikes{Mid: mid, Business: _businessLike, Pn: 1, Ps: _likeVideoCnt, RealIP: ip}
- if likes, err = s.thumbup.UserTotalLike(c, arg); err != nil {
- log.Error("s.thumbup.UserTotalLike(%d) error(%v)", mid, err)
- return
- }
- if likes != nil {
- for _, v := range likes.List {
- if v.MessageID > 0 {
- list = append(list, &model.DyActItem{Aid: v.MessageID, Type: _dyTypeLike, ActionTime: int64(v.Time), Privacy: pcy})
- }
- }
- }
- return
- }
- // topDynamic get top dynamic.
- func (s *Service) topDynamic(c context.Context, mid int64, qn int) (res *model.DyCard, err error) {
- var (
- dyID int64
- )
- if dyID, err = s.dao.TopDynamic(c, mid); err != nil {
- return
- }
- if dyID == 0 {
- err = ecode.NothingFound
- return
- }
- if res, err = s.dao.Dynamic(c, mid, dyID, qn); err != nil || res == nil {
- log.Error("Dynamic s.dao.Dynamic mid(%d) dyID(%d) error(%v)", mid, dyID, err)
- err = ecode.NothingFound
- }
- return
- }
- // SetTopDynamic set top dynamic.
- func (s *Service) SetTopDynamic(c context.Context, mid, dynamicID int64) (err error) {
- var (
- dynamic *model.DyCard
- preDyID int64
- )
- if dynamic, err = s.dao.Dynamic(c, mid, dynamicID, _dyDefaultQn); err != nil || dynamic == nil {
- log.Error("SetTopDynamic s.dao.Dynamic(%d) error(%v)", dynamicID, err)
- return
- }
- if dynamic.Desc.UID != mid {
- err = ecode.RequestErr
- return
- }
- if preDyID, err = s.dao.TopDynamic(c, mid); err != nil {
- return
- }
- if preDyID == dynamicID {
- err = ecode.NotModified
- return
- }
- if err = s.dao.AddTopDynamic(c, mid, dynamicID); err == nil {
- s.dao.AddCacheTopDynamic(c, mid, dynamicID)
- }
- return
- }
- // CancelTopDynamic cancel top dynamic.
- func (s *Service) CancelTopDynamic(c context.Context, mid int64, now time.Time) (err error) {
- var dyID int64
- if dyID, err = s.dao.TopDynamic(c, mid); err != nil {
- return
- }
- if dyID == 0 {
- err = ecode.RequestErr
- return
- }
- if err = s.dao.DelTopDynamic(c, mid, now); err == nil {
- s.dao.AddCacheTopDynamic(c, mid, -1)
- }
- return
- }
|