go-common/app/job/main/favorite/service/fav.go

961 lines
28 KiB
Go
Raw Normal View History

2019-04-22 10:49:16 +00:00
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
}