go-common/app/job/main/favorite/service/fav.go
2019-04-22 18:49:16 +08:00

961 lines
28 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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
}