package service import ( "context" "fmt" "sort" "time" "go-common/app/job/main/favorite/model" favmdl "go-common/app/service/main/favorite/model" "go-common/library/ecode" "go-common/library/log" xtime "go-common/library/time" ) const gap uint64 = 1 << 47 const maxSquence uint64 = 1 + gap*64000 const initSquence uint64 = 1 + gap*2000 func (s *Service) upResource(c context.Context, msg *favmdl.Message) (err error) { if msg.Otype == 0 { //为了上线的时候兼容老的service逻辑 msg.Otype = msg.Type } if msg.Type == favmdl.TypeMusicNew { msg.Type = favmdl.TypeVideo } switch msg.Action { case favmdl.ActionAdd: if err = s.addFav(c, msg.Otype, msg.Mid, msg.Fid, msg.Oid, msg.FTime, msg.Type); err != nil { return } s.delRecentOidsMc(c, msg.Type, msg.Mid) s.upFolderCnt(c, msg.Type, msg.Mid, msg.Fid, msg.FTime) s.upFavStat(c, msg.Otype, msg.Mid, msg.Oid, msg.FTime, true) case favmdl.ActionDel: if err = s.delFav(c, msg.Otype, msg.Mid, msg.Fid, msg.Oid, msg.FTime); err != nil { return } s.delRecentOidsMc(c, msg.Type, msg.Mid) s.upFolderCnt(c, msg.Type, msg.Mid, msg.Fid, msg.FTime) s.upFavStat(c, msg.Otype, msg.Mid, msg.Oid, msg.FTime, false) case favmdl.ActionMultiAdd: s.batchUpdateFavSeqsByAadd(c, msg.Mid, msg.Fid, msg.Type, msg.Oids) s.upFolderCnt(c, msg.Type, msg.Mid, msg.Fid, msg.FTime) s.upFavStats(c, msg.Otype, msg.Mid, msg.Oids, msg.FTime, true) s.delRecentOidsMc(c, msg.Type, msg.Mid) case favmdl.ActionMultiDel: s.upFolderCnt(c, msg.Type, msg.Mid, msg.Fid, msg.FTime) s.upFavStats(c, msg.Otype, msg.Mid, msg.Oids, msg.FTime, false) s.delRecentOidsMc(c, msg.Type, msg.Mid) s.initFolderRelations(c, msg.Type, msg.Mid, msg.Fid) s.initAllRelations(c, msg.Mid, msg.Fid) case model.ActionCopy: s.batchUpdateFavSeqsByAadd(c, msg.Mid, msg.NewFid, msg.Type, msg.Oids) s.upFolderCnt(c, msg.Type, msg.Mid, msg.NewFid, msg.FTime) s.upFavStats(c, msg.Otype, msg.Mid, msg.Oids, msg.FTime, true) s.delRecentOidsMc(c, msg.Type, msg.Mid) s.initFolderRelations(c, msg.Type, msg.Mid, msg.NewFid) s.initAllRelations(c, msg.Mid, msg.NewFid) case model.ActionMove: s.batchUpdateFavSeqsByAadd(c, msg.Mid, msg.NewFid, msg.Type, msg.Oids) s.upFolderCnt(c, msg.Type, msg.Mid, msg.NewFid, msg.FTime) s.upFolderCnt(c, msg.Type, msg.Mid, msg.OldFid, msg.FTime) s.delRecentOidsMc(c, msg.Type, msg.Mid) s.initFolderRelations(c, msg.Type, msg.Mid, msg.NewFid) s.initAllRelations(c, msg.Mid, msg.NewFid) case model.ActionClean: s.cleanInvalidFavs(c, msg.Type, msg.Mid, msg.Fid, msg.FTime) s.upFolderCnt(c, msg.Type, msg.Mid, msg.Fid, msg.FTime) s.delRecentOidsMc(c, msg.Type, msg.Mid) s.initFolderRelations(c, msg.Type, msg.Mid, msg.Fid) s.initAllRelations(c, msg.Mid, msg.Fid) case favmdl.ActionFolderDel: if err = s.delRelationsByFid(c, msg.Type, msg.Mid, msg.Fid, msg.FTime); err != nil { log.Error("s.delRelationsByFid(%d,%d,%d) error(%v)", msg.Type, msg.Mid, msg.Fid, err) } if err = s.favDao.DelRelationsCache(c, msg.Mid, msg.Fid); err != nil { log.Error("s.favDao.DelRelationsCache(%d,%d) error(%v)", msg.Mid, msg.Fid, err) } if err = s.favDao.DelAllRelationsCache(c, msg.Mid, msg.Fid); err != nil { log.Error("s.favDao.DelAllRelationsCache(%d,%d) error(%v)", msg.Mid, msg.Fid, err) } s.delRecentOidsMc(c, msg.Type, msg.Mid) case favmdl.ActionInitFolderRelations: s.initFolderRelations(c, msg.Type, msg.Mid, msg.Fid) case favmdl.ActionInitAllFolderRelations: s.initAllRelations(c, msg.Mid, msg.Fid) case favmdl.ActionInitRelationFids: var ok bool if ok, err = s.favDao.ExpireRelationOids(c, msg.Otype, msg.Mid); err != nil || ok { return } var rfmap map[int64][]int64 if rfmap, err = s.favDao.RelationFids(c, msg.Otype, msg.Mid); err != nil { log.Error("favDao.FavedFids(%d,%d) error(%v)", msg.Otype, msg.Mid, err) return } if len(rfmap) == 0 { if err = s.favDao.SetUnFavedBit(c, msg.Otype, msg.Mid); err != nil { log.Error("s,favDao.SetUnFavedBit(type:%d,mid:%d) error(%v)", msg.Otype, msg.Mid, err) } } else { var oids []int64 for oid := range rfmap { oids = append(oids, oid) } if err = s.favDao.SetRelationOidsCache(c, msg.Otype, msg.Mid, oids); err != nil { log.Error("s.favDao.SetRelationOidsCache(%d,%d,%v) error(%v)", msg.Otype, msg.Mid, oids, err) } } case favmdl.ActionSortFavs: s.sortFavs(c, msg) } return } func (s *Service) batchUpdateFavSeqsByAadd(c context.Context, mid, fid int64, typ int8, oids []int64) { log.Info("begin batchUpdateFavSeqsByAadd(%d,%d,%d,%+v)", mid, fid, typ, oids) favs, err := s.favDao.RelationsByOids(c, typ, mid, fid, oids) if err != nil { log.Error("s.RelationsByOids(%d,%d,%d,%v) failed!err:=%v", typ, mid, fid, oids, err) return } next, err := s.nextSquence(c, mid, fid) if err != nil { log.Error("s.nextSquence(%d,%d) failed!err:=%v", mid, fid, err) return } for _, fav := range favs { fav.Sequence = next if next >= maxSquence { next++ } else { next += gap } } if err = s.batchUpdateSeqs(c, mid, fid, favs); err != nil { log.Error("s.batchUpdateSeqs (%d,%+v) err:=%v", mid, favs, err) } if err = s.favDao.DelAllRelationsCache(c, mid, fid); err != nil { err = nil log.Error("s.favDao.DelAllRelationsCache(%d,%d) err(%v)", mid, fid, err) } } func (s *Service) sortFavs(c context.Context, msg *favmdl.Message) { favs, err := s.favDao.AllRelations(c, msg.Mid, msg.Fid, 0, 1024) if err != nil { log.Error("s.favDao.AllRelations(%d,%d,%d,%d) error(%v)", msg.Mid, msg.Fid, 0, 2000, err) return } if len(favs) > 1000 || len(favs) <= 1 { log.Warn("sortFavs invalid fav(%d,%d) length:%d", msg.Mid, msg.Fid, len(favs)) return } favsM := make(map[int64]*favmdl.Favorite) for i := range favs { favsM[favs[i].ResourceID()] = favs[i] } changed := make([]*favmdl.Favorite, 0) var reCount = 0 sortFavsDesc(favs) if favs[len(favs)-1].Sequence == 0 { reSequence(favs) reCount++ } for _, req := range msg.SortFavs { if req.Insert == nil { return } target, ok := favsM[req.Insert.ResourceID()] if !ok { return } if req.Pre == nil { max := favs[0].Sequence if max >= maxSquence { reCount++ reSequence(favs) target.Sequence = favs[len(favs)-1].Sequence + gap } else if max >= maxSquence-gap { target.Sequence = (max + maxSquence + 1) / 2 } else { target.Sequence = max + gap } } else { pre, ok := favsM[req.Pre.ResourceID()] if !ok { return } idx := searchIdx(favs, pre.Sequence) if idx == -1 { return } if idx < len(favs)-1 { next := favs[idx+1] if next.Oid == req.Insert.Oid && next.Type == int8(req.Insert.Typ) { // already sorted continue } if next.Sequence-pre.Sequence <= 1 { reCount++ reSequence(favs) } target.Sequence = (pre.Sequence + next.Sequence) / 2 } else { min := pre.Sequence if min <= 1 { // no space , need to reidx reCount++ reSequence(favs) target.Sequence = favs[0].Sequence - gap } else if min <= 1+gap { // insert into the gap target.Sequence = min / 2 } else { target.Sequence = min - gap } } } changed = append(changed, target) sortFavsDesc(favs) } if reCount > 0 { s.batchUpdateSeqs(c, msg.Mid, msg.Fid, favs) } else { s.batchUpdateSeqs(c, msg.Mid, msg.Fid, changed) } for i := range favs { favs[len(favs)-1-i].Sequence = uint64(i) } if err = s.favDao.AddAllRelationsCache(c, msg.Mid, msg.Fid, favs); err != nil { err = nil log.Error("s.favDao.AddAllRelationsCache(%d,%d,%v) err(%v)", msg.Mid, msg.Fid, favs, err) } } func (s *Service) batchUpdateSeqs(c context.Context, mid int64, fid int64, favs []*favmdl.Favorite) (err error) { for i := 0; i < len(favs); i += 10000 { end := i + 10000 if end > len(favs) { end = len(favs) } _, err = s.favDao.BatchUpdateSeq(c, mid, favs[i:end]) if err != nil { errStr := err.Error() if len(errStr) > 200 { errStr = errStr[:200] } log.Error("s.favDao.BatchUpdateSeq(%d,%v) err(%v)", mid, favs, errStr) _, err = s.favDao.BatchUpdateSeq(c, mid, favs[i:end]) if err != nil { errStr := err.Error() if len(errStr) > 200 { errStr = errStr[:200] } log.Error("s.favDao.BatchUpdateSeq(%d,%v) err(%v)", mid, favs, errStr) return err } } } return } func searchIdx(favs []*favmdl.Favorite, sequence uint64) int { i := sort.Search(len(favs), func(i int) bool { return favs[i].Sequence <= sequence }) if i < len(favs) && favs[i].Sequence == sequence { return i } return -1 } func sortFavsDesc(favs []*favmdl.Favorite) { sort.Slice(favs, func(i, j int) bool { if favs[i].Sequence == favs[j].Sequence { return favs[i].MTime > favs[j].MTime } return favs[i].Sequence > favs[j].Sequence }) } // 重新计算所有数据的Sequence func reSequence(favs []*favmdl.Favorite) { seq := initSquence last := len(favs) - 1 for i := range favs { favs[last-i].Sequence = seq seq += gap } } func (s *Service) nextSquence(c context.Context, mid, fid int64) (uint64, error) { max, err := s.favDao.MaxRelation(c, mid, fid) if err != nil { log.Error("s.favDao.MaxRelation(%d,%d) error(%v)", mid, fid, err) return 0, err } var seq uint64 if max == nil { seq = initSquence } else if max.Sequence == 0 { var cnt int cnt, err = s.favDao.RelationCnt(c, mid, fid) if err != nil { log.Error("s.favDao.RelationCnt(%d,%d) error(%v)", mid, fid, err) return 0, err } if cnt <= 50000 { seq = initSquence + uint64(cnt+10)*gap } else { log.Error("nextSquence: can't add res over 50000") err = ecode.FavMaxVideoCount return 0, err } } else if max.Sequence+gap <= maxSquence { seq = max.Sequence + gap } else { seq = max.Sequence + 1 } return seq, nil } func (s *Service) addFav(c context.Context, typ int8, mid, fid, oid, ftime int64, ftype int8) (err error) { v := &favmdl.Favorite{ Type: typ, Mid: mid, Fid: fid, Oid: oid, CTime: xtime.Time(ftime), MTime: xtime.Time(ftime), } v.Sequence, err = s.nextSquence(c, mid, fid) if err != nil { return } rows, err := s.favDao.AddRelation(c, v) if err != nil { log.Error("s.favDao.AddRelation(%v) error(%v)", v, err) return } if rows < 1 { log.Warn("type(%d) oid(%d) already favoured", typ, oid) err = model.ErrFavResourceExist return } v.ID = rows err = s.cache.Do(c, func(c context.Context) { if err = s.favDao.SetFavedBit(c, typ, mid); err != nil { log.Error("s.favDao.SetFavedBit(%d, %d) error(%v)", typ, mid, err) } var ok bool if ftype == typ { if ok, err = s.favDao.ExpireRelations(c, mid, fid); err != nil { log.Error("s.favDao.ExpireRelations(%d,%d) error(%v)", mid, fid, err) } if ok { if err = s.favDao.AddRelationCache(c, v); err != nil { log.Error("s.favDao.AddRelationCache(%d, %d,%d) error(%v)", typ, mid, fid, err) } } } if ok, err = s.favDao.ExpireAllRelations(c, mid, fid); err != nil { log.Error("s.favDao.ExpireAllRelations(%d,%d) error(%v)", mid, fid, err) } if ok { if err = s.favDao.AddAllRelationCache(c, v); err != nil { log.Error("s.favDao.AddAllRelationCache(%d, %d,%d) error(%v)", typ, mid, fid, err) } } if ok, err = s.favDao.ExpireRelationOids(c, typ, mid); err != nil { log.Error("s.favDao.ExpireRelationOids(%d,%d) error(%v)", typ, mid, err) } if ok { if err = s.favDao.AddRelationOidCache(c, typ, mid, oid); err != nil { log.Error("s.favDao.AddRelationOidCache(%d,%d,%d) error(%v)", typ, mid, oid, err) } } if err = s.favDao.DelRelationFidsMc(c, typ, mid, oid); err != nil { log.Error("s.favDao.DelRelationFidsMc(%d,%d,%d) error(%v)", typ, mid, oid, err) } }) if err != nil { log.Error("s.cache.Do error(%v)", err) } return } // DelFav delete a favorite. func (s *Service) delFav(c context.Context, typ int8, mid, fid, oid, ftime int64) (err error) { rows, err := s.favDao.DelRelation(c, typ, mid, fid, oid, xtime.Time(ftime)) if err != nil { log.Error("s.favDao.DelRelation(%d,%d,%d) error(%v)", typ, oid, fid, err) return } if rows < 1 { log.Warn("s.favDao.DelRelation(%d,%d,%d,%d) have no del", typ, mid, fid, oid) err = ecode.FavResourceAlreadyDel return } err = s.cache.Do(c, func(c context.Context) { if err1 := s.favDao.DelRelationCache(c, mid, fid, oid); err1 != nil { log.Error("s.favDao.DelRelationCache(%d,%d,%d) error(%v)", mid, fid, oid, err1) } if err1 := s.favDao.DelAllRelationCache(c, mid, fid, oid, typ); err1 != nil { log.Error("s.favDao.DelAllRelationCache(%d,%d,%d) error(%v)", mid, fid, oid, err1) } if err1 := s.favDao.DelRelationFidsMc(c, typ, mid, oid); err1 != nil { log.Error("s.favDao.DelRelationFidsMc(%d,%d,%d) error(%v)", typ, mid, oid, err1) } }) if err != nil { log.Error("s.cache.Do error(%v)", err) } return } func (s *Service) setAllRelationCache(c context.Context, mid, fid int64) (err error) { var ( mtime = xtime.Time(0) pageSize = 8000 favss []*favmdl.Favorite ) for { favs, err1 := s.favDao.AllRelations(c, mid, fid, mtime, pageSize) if err1 != nil { if err = s.favDao.DelAllRelationsCache(c, mid, fid); err != nil { log.Error("s.favDao.DelAllRelationsCache(%d,%d) error(%v)", mid, fid, err) } return err1 } if len(favs) == 0 { break } mtime = favs[len(favs)-1].MTime if mtime == favs[0].MTime { mtime++ } favss = append(favss, favs...) } if len(favss) <= 0 { return } sortFavsDesc(favss) var needUpdateSeq bool if favss[len(favss)-1].Sequence == 0 { needUpdateSeq = true } for i := range favss { favss[len(favss)-1-i].Sequence = uint64(i) } if err = s.favDao.AddAllRelationsCache(c, mid, fid, favss); err != nil { log.Error("s.favDao.AddAllRelationsCache(%d,%d,%d,%v) error(%v)", mid, fid, favss, err) } if needUpdateSeq { reSequence(favss) s.batchUpdateSeqs(c, mid, fid, favss) } return } func (s *Service) setRelationCache(c context.Context, tp int8, mid, fid int64) (err error) { var ( mtime = xtime.Time(0) pageSize = 8000 favss []*favmdl.Favorite ) for { favs, err1 := s.favDao.Relations(c, tp, mid, fid, mtime, pageSize) if err1 != nil { log.Error("s.favDao.Relations(%d,%d,%d,%d,%d) error(%v)", tp, mid, fid, mtime, pageSize, err) if err = s.favDao.DelRelationsCache(c, mid, fid); err != nil { log.Error("s.favDao.DelRelationsCache(%d,%d) error(%v)", mid, fid, err) } return err1 } if len(favs) == 0 { break } mtime = favs[len(favs)-1].MTime if mtime == favs[0].MTime { mtime++ } favss = append(favss, favs...) } if err = s.favDao.AddRelationsCache(c, tp, mid, fid, favss); err != nil { log.Error("s.favDao.AddRelationsCache(%d,%d,%d,%v) error(%v)", tp, mid, fid, favss, err) } return } func (s *Service) upFolderCnt(c context.Context, tp int8, mid, fid, ftime int64) (err error) { cnt, err := s.favDao.RelationCnt(c, mid, fid) if err != nil { log.Error("s.favDao.CntRelations(%d,%d,%d) error(%v)", tp, mid, fid, err) return } folder, err := s.folder(c, tp, mid, fid) if err != nil { return } if _, err = s.favDao.UpFolderCnt(c, mid, fid, cnt, xtime.Time(ftime)); err != nil { log.Error("s.favDao.UpFolderCnt(%d,%d,%d,%d) error(%v)", mid, fid, ftime, cnt, err) return } folder.Count = cnt folder.MTime = xtime.Time(ftime) var recent []*favmdl.Resource if recent, err = s.favDao.RecentRes(c, mid, fid); err != nil { log.Error(" s.favDao.RecentRes(%d,%d) error(%v) or folder is nil", mid, fid, err) err = nil } folder.RecentOids = []int64{} folder.RecentRes = []*favmdl.Resource{} if len(recent) > 0 { folder.RecentRes = recent for _, res := range recent { if res.Typ == int32(tp) { folder.RecentOids = append(folder.RecentOids, res.Oid) } } } err = s.cache.Do(c, func(c context.Context) { if err1 := s.favDao.SetFoldersMc(c, folder); err1 != nil { log.Error("s.favDao.SetFolderMc(%v) error(%v)", folder, err1) } if err1 := s.favDao.DelNewCoverCache(c, folder.Mid, folder.ID); err1 != nil { log.Error("s.favDao.DelNewCoverCache(%v) error(%v)", folder, err1) } }) if err != nil { log.Error("s.cache.Do error(%v)", err) } return } func (s *Service) folder(c context.Context, typ int8, mid, fid int64) (folder *favmdl.Folder, err error) { if folder, err = s.favDao.FolderMc(c, typ, mid, fid); err != nil { log.Error("s.favDao.FolderMc(%d,%d) error(%v)", mid, fid, err) err = nil } if folder == nil { if folder, err = s.favDao.Folder(c, typ, mid, fid); err != nil { log.Error("favDao.Folder(%d,%d) error(%v) or folder is nil", mid, fid, err) return } } if folder == nil { err = ecode.FavFolderNotExist } return } // upFavStats update upFavStat. func (s *Service) upFavStats(c context.Context, typ int8, mid int64, oids []int64, now int64, isAdd bool) error { for _, oid := range oids { if err := s.upFavStat(c, typ, mid, oid, now, isAdd); err != nil { log.Error("s.upFavStat(%d,%d,%d,%d,%d) error(%v)", typ, mid, oid, now, isAdd, err) return err } } return nil } // upFavStat update update resource fav count. func (s *Service) upFavStat(c context.Context, tp int8, mid, oid, now int64, isAdd bool) error { fids, err := s.favDao.RelationFidsByOid(c, tp, mid, oid) if err != nil { log.Error("s.favDao.RelationFidsByOid(%d,%d,%d) error(%v)", tp, mid, oid, err) return err } if len(fids) != 0 { if err1 := s.favDao.SetRelaitonFidsMc(c, tp, mid, oid, fids); err != nil { log.Error("s.favDao.SetRelaitonFidsMc(%d,%d,%d) error(%v)", tp, mid, oid, err1) } } length := len(fids) var incr int if isAdd && length == 1 { incr = 1 err = s.addFavOperations(c, tp, mid, oid, now) if err != nil { return err } } else if !isAdd && length == 0 { incr = -1 err = s.delFavOperations(c, tp, mid, oid, now) if err != nil { return err } } if incr != 0 { cnt, err := s.favDao.StatCnt(c, tp, oid) if err != nil { return err } if (cnt + incr) < 0 { return nil } rows, err := s.favDao.UpStatCnt(c, tp, oid, incr, xtime.Time(now)) if err != nil { log.Error("s.favDao.UpStatCnt(%d,%d,%d) error(%v)", tp, oid, incr, err) return err } if rows < 1 { log.Warn("s.favDao.UpStatCnt(%d,%d,%d) rows(%d)", tp, oid, incr, rows) return nil } err = s.cache.Do(c, func(c context.Context) { if err = s.favDao.SetOidCountMc(c, tp, oid, int64(cnt+incr)); err != nil { log.Error("s.favDao.SetOidCountMc(%d,%d,%d) error(%v)", tp, oid, int64(cnt+incr), err) } if err = s.favDao.DelBatchOidsMc(c, tp, mid); err != nil { log.Error("s.favDao.SetOidCountMc(%d,%d) error(%v)", tp, mid, err) } }) if err != nil { log.Error("s.cache.Do error(%v)", err) } s.addCoin(c, isAdd, cnt+incr, tp, oid) s.pubDao.PubStats(c, tp, oid, int64(cnt+incr)) // bnj merge stat if err1 := s.bnjStatMerge(c, tp, oid, incr); err1 != nil { log.Error("s.bnjMergeStat(%d,%d,%d) error(%v)", tp, oid, incr, err1) } } return nil } func (s *Service) bnjStatMerge(c context.Context, typ int8, oid int64, incr int) (err error) { target := s.mergeTarget(int(typ), oid) if target <= 0 { return } cnt, err := s.favDao.StatCnt(c, typ, target) if err != nil { return } rows, err := s.favDao.UpStatCnt(c, typ, target, incr, xtime.Time(time.Now().Unix())) if err != nil || rows < 1 { log.Error("s.favDao.UpStatCnt(%d,%d,%d,%d) error(%v)", typ, target, incr, rows, err) return } s.pubDao.PubStats(c, typ, target, int64(cnt+incr)) return } func (s *Service) addCoin(c context.Context, isAdd bool, count int, tp int8, oid int64) (err error) { var ( mid int64 msgAdd, msgDel string ) mod := count % 200 if mod != 0 && mod != 199 { return } switch tp { case favmdl.Article: article, err := s.articleRPC(c, oid) if err != nil { log.Error("s.favDao.ArticleRPC error(%v)", oid, err) return err } meta, ok := article[oid] if !ok || meta == nil { log.Error("article martmdl.Meta(%v) error(%v)", article, err) return err } mid = meta.Author.Mid msgAdd = "专栏CV%d新增200人收藏,总收藏%d" msgDel = "专栏CV%d有200人取消收藏,总收藏%d" case favmdl.TypeVideo: archive, err := s.archiveRPC(c, oid) if err != nil { log.Error("s.favDao.archiveRPC error(%v)", oid, err) return err } mid = archive.Author.Mid msgAdd = "稿件AV%d新增200人收藏,总收藏%d" msgDel = "稿件AV%d有200人取消收藏,总收藏%d" default: log.Warn("this type(%d) need not to add coin", tp) return } // add money to upper if isAdd && mod == 0 { if err := s.addCoinRPC(c, mid, 1, fmt.Sprintf(msgAdd, oid, count)); err != nil { log.Error("s.addCoinRPC(%d,%s) error(%v)", mid, fmt.Sprintf(msgAdd, oid, count), err) return err } } if !isAdd && mod == 199 { if err := s.addCoinRPC(c, mid, -1, fmt.Sprintf(msgDel, oid, count)); err != nil { log.Error("s.addCoinRPC(%d,%s) error(%v)", mid, fmt.Sprintf(msgAdd, oid, count), err) return err } } return } func (s *Service) delRelationsByFid(c context.Context, typ int8, mid, fid, ftime int64) (err error) { var ( offset int count = s.c.Fav.MaxPageSize ) typs := []int8{typ} if typ == 2 { // 收藏夹type=2是混合类型的收藏夹,需要删除多个type的稿件关系,现在只有music所以只要append 12 typs = append(typs, 12) } for _, tp := range typs { for { var ( rows int64 oids []int64 ) if oids, err = s.favDao.OidsByFid(c, tp, mid, fid, offset, count); err != nil { log.Error("s.favDao.OidsByFid(%d,%d,%d,%d,%d) error(%v)", tp, mid, fid, offset, count, err) time.Sleep(time.Millisecond * 500) // avoid endless loop continue } if len(oids) == 0 { break } if rows, err = s.favDao.DelRelationsByOids(c, tp, mid, fid, oids, xtime.Time(ftime)); err != nil { log.Error("s.favDao.DelRelationsByOids(%d,%d,%d,%v) error(%v)", tp, mid, fid, oids, err) time.Sleep(time.Millisecond * 500) // avoid endless loop continue } offset += count if rows != int64(len(oids)) { log.Error("rows!=int64(len(oids)) rows:%d,len(aids):%d", rows, len(oids)) } if rows > 0 { s.upFavStats(c, tp, mid, oids, ftime, false) } time.Sleep(time.Duration(s.c.Fav.SleepTime)) // for binlog cunsumers } } return } func (s *Service) addFavOperations(c context.Context, typ int8, mid, oid, now int64) (err error) { ok, err := s.favDao.ExpireRelationOids(c, typ, mid) if err != nil { log.Error("s.favDao.ExpireRelationFids(%d,%d) error(%v)", typ, mid, err) } else if ok { if err = s.favDao.AddRelationOidCache(c, typ, mid, oid); err != nil { log.Error("s.favDao.AddRelationOidCache(%d,%d,%d) error(%v)", typ, mid, oid, err) } } if typ < favmdl.TypeBangumi { err = nil return } u := &favmdl.User{ Type: typ, Oid: oid, Mid: mid, CTime: xtime.Time(now), MTime: xtime.Time(now), } rows, err := s.favDao.AddUser(c, u) if err != nil { log.Error("s.favDao.AddUser(%+v) error(%v)", u, err) return } if rows == 0 { log.Warn("s.favDao.DelUser(%+v) rows(%v)", u, rows) } return } func (s *Service) delFavOperations(c context.Context, typ int8, mid, oid, now int64) (err error) { if err = s.favDao.RemRelationOidCache(c, typ, mid, oid); err != nil { log.Error("s.favDao.RemRelationOidCache(%d,%d,%d) error(%v)", typ, mid, oid, err) err = nil } if typ < favmdl.TypeBangumi { return } u := &favmdl.User{ Type: typ, Oid: oid, Mid: mid, State: favmdl.StateIsDel, CTime: xtime.Time(now), MTime: xtime.Time(now), } rows, err := s.favDao.DelUser(c, u) if err != nil { log.Error("s.favDao.DelUser(%+v) error(%v)", u, err) return err } if rows == 0 { log.Warn("s.favDao.DelUser(%+v) rows(%v)", u, rows) } return } func (s *Service) delRecentOidsMc(c context.Context, typ int8, mid int64) { if err := s.favDao.DelRecentOidsMc(c, typ, mid); err != nil { log.Error("s.favDao.DelRecentOidsMc(%d,%d) error(%v)", typ, mid, err) } if err := s.favDao.DelRecentResMc(c, favmdl.TypeVideo, mid); err != nil { log.Error("s.favDao.DelRecentResMc(%d,%d) error(%v)", typ, mid, err) } } func (s *Service) cleanInvalidFavs(c context.Context, typ int8, mid, fid, ftime int64) (err error) { if typ != favmdl.TypeVideo { return } var ( mtime = xtime.Time(0) pageSize = 8000 batchCount = s.c.Fav.MaxPageSize ) var oids = make(map[int64]struct{}) var musicIds = make(map[int64]struct{}) var batchOids []int64 for { favs, err := s.favDao.AllRelations(c, mid, fid, mtime, pageSize) if err != nil { return err } if len(favs) == 0 { break } mtime = favs[len(favs)-1].MTime if mtime == favs[0].MTime { mtime++ } for _, fav := range favs { if fav.Type == favmdl.TypeVideo { oids[fav.Oid] = struct{}{} } else if fav.Type == favmdl.TypeMusicNew { musicIds[fav.Oid] = struct{}{} } } } for oid := range oids { if len(batchOids) >= batchCount { s.cleanVideoFavs(c, mid, fid, ftime, batchOids) batchOids = batchOids[:0] } batchOids = append(batchOids, oid) } if len(batchOids) > 0 { s.cleanVideoFavs(c, mid, fid, ftime, batchOids) } batchOids = batchOids[:0] for oid := range musicIds { if len(batchOids) >= batchCount { s.cleanMuiscFavs(c, mid, fid, ftime, batchOids) batchOids = batchOids[:0] } batchOids = append(batchOids, oid) } if len(batchOids) > 0 { s.cleanMuiscFavs(c, mid, fid, ftime, batchOids) } batchOids = batchOids[:0] err = s.favDao.SetCleanedCache(c, typ, mid, fid, ftime, s.cleanCDTime) s.cache.Do(c, func(c context.Context) { if err1 := s.favDao.DelRecentOidsMc(c, typ, mid); err1 != nil { log.Error("s.favDao.DelRecentOidsMc(%d,%d) error(%v)", typ, mid, err1) } if err := s.favDao.DelRecentResMc(c, favmdl.TypeVideo, mid); err != nil { log.Error("s.favDao.DelRecentResMc(%d,%d) error(%v)", typ, mid, err) } if err1 := s.favDao.DelRelationOidsCache(c, typ, mid); err1 != nil { log.Error("s.favDao.DelRelationOidsCache(%d,%d) error(%v)", typ, mid, err1) } if err1 := s.favDao.DelRelationsCache(c, mid, fid); err1 != nil { log.Error("s.favDao.DelRelationsCache(%d,%d) error(%v)", mid, fid, err1) } if err1 := s.favDao.DelAllRelationsCache(c, mid, fid); err1 != nil { log.Error("s.favDao.DelAllRelationsCache(%d,%d) error(%v)", mid, fid, err1) } }) return } func (s *Service) cleanMuiscFavs(c context.Context, mid, fid, ftime int64, oids []int64) (err error) { var delOids []int64 musics, err := s.musicDao.MusicMap(c, oids) if err != nil { log.Error("s.ArcsRPC(%v) error(%v)", oids, err) return } for _, oid := range oids { if _, ok := musics[oid]; !ok { delOids = append(delOids, oid) } } if len(delOids) > 0 { var rows int64 if rows, err = s.favDao.DelRelationsByOids(c, favmdl.TypeMusicNew, mid, fid, delOids, xtime.Time(ftime)); err != nil { log.Error("s.favDao.DelRelationsByOids(%d,%d,%v) error(%v)", favmdl.TypeMusicNew, mid, fid, delOids, err) } if rows > 0 { s.upFavStats(c, favmdl.TypeMusicNew, mid, delOids, ftime, false) } } return } func (s *Service) cleanVideoFavs(c context.Context, mid, fid, ftime int64, oids []int64) (err error) { var delOids []int64 arcs, err := s.ArcsRPC(c, oids) if err != nil { log.Error("s.ArcsRPC(%v) error(%v)", oids, err) return } for aid, arc := range arcs { if arc.IsNormal() { continue } delOids = append(delOids, aid) } if len(delOids) > 0 { var rows int64 if rows, err = s.favDao.DelRelationsByOids(c, favmdl.TypeVideo, mid, fid, delOids, xtime.Time(ftime)); err != nil { log.Error("s.favDao.DelRelationsByOids(%d,%d,%v) error(%v)", favmdl.TypeVideo, mid, fid, delOids, err) } if rows > 0 { s.upFavStats(c, favmdl.TypeVideo, mid, delOids, ftime, false) } } return } func (s *Service) initFolderRelations(c context.Context, typ int8, mid, fid int64) (err error) { if fid <= 0 { log.Warn("folderID must not be zero!%d %d", mid, fid) return } var ok bool if ok, err = s.favDao.ExpireRelations(c, mid, fid); err != nil || ok { return } // 顺带更新folder的count s.setRelationCache(c, typ, mid, fid) return } func (s *Service) initAllRelations(c context.Context, mid, fid int64) (err error) { if fid <= 0 { log.Warn("folderID must not be zero!%d %d", mid, fid) return } var ok bool if ok, err = s.favDao.ExpireAllRelations(c, mid, fid); err != nil || ok { return } // 顺带更新folder的count s.setAllRelationCache(c, mid, fid) return }