You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
426 lines
12 KiB
426 lines
12 KiB
package mcndao |
|
|
|
import ( |
|
"context" |
|
"fmt" |
|
"sort" |
|
"time" |
|
|
|
"go-common/app/admin/main/up/util" |
|
"go-common/app/admin/main/up/util/mathutil" |
|
"go-common/app/interface/main/mcn/conf" |
|
"go-common/app/interface/main/mcn/dao/cache" |
|
"go-common/app/interface/main/mcn/dao/global" |
|
"go-common/app/interface/main/mcn/model/mcnmodel" |
|
arcgrpc "go-common/app/service/main/archive/api" |
|
"go-common/library/log" |
|
|
|
"github.com/bluele/gcache" |
|
) |
|
|
|
// RankByTid . |
|
// 存储rank的缓存 |
|
// 对每种排行榜的分类进行缓存 |
|
// 对排序进行实时计算 |
|
// 先去localcache中取,取不到的话,去db中取 |
|
type RankByTid struct { |
|
// [tid][datatype] rank list |
|
TidMap map[int16]map[mcnmodel.DataType][]mcnmodel.RankDataInterface |
|
TidTypeListMap map[mcnmodel.DataType][]*mcnmodel.TidnameInfo |
|
} |
|
|
|
//RankFunc rank func |
|
type RankFunc func(signId int64) (result *RankByTid, err error) |
|
|
|
type tidnameUnique struct { |
|
tidInfoMap map[mcnmodel.DataType]map[int16]*mcnmodel.TidnameInfo |
|
} |
|
|
|
func newTidNameUnique() *tidnameUnique { |
|
return &tidnameUnique{tidInfoMap: make(map[mcnmodel.DataType]map[int16]*mcnmodel.TidnameInfo)} |
|
} |
|
|
|
func (t *tidnameUnique) addTid(tid int16, name string, datatype mcnmodel.DataType) { |
|
var dmap map[int16]*mcnmodel.TidnameInfo |
|
var ok bool |
|
if dmap, ok = t.tidInfoMap[datatype]; !ok { |
|
dmap = make(map[int16]*mcnmodel.TidnameInfo) |
|
t.tidInfoMap[datatype] = dmap |
|
} |
|
dmap[tid] = &mcnmodel.TidnameInfo{Tid: tid, Name: name} |
|
} |
|
|
|
func (t *tidnameUnique) getList(datatype mcnmodel.DataType) (typeList []*mcnmodel.TidnameInfo) { |
|
for _, v := range t.tidInfoMap[datatype] { |
|
typeList = append(typeList, v) |
|
} |
|
return |
|
} |
|
|
|
func (t *tidnameUnique) export(dmap map[mcnmodel.DataType][]*mcnmodel.TidnameInfo) { |
|
for dataType, tidMap := range t.tidInfoMap { |
|
var typeList []*mcnmodel.TidnameInfo |
|
for k, v := range tidMap { |
|
if k == 0 { |
|
continue |
|
} |
|
typeList = append(typeList, v) |
|
} |
|
dmap[dataType] = typeList |
|
} |
|
} |
|
|
|
// // 排序,根据increase数量做倒序 |
|
// type rankByFansIncreaseDesc []*mcnmodel.RankUpFansInfo |
|
|
|
// func (s rankByFansIncreaseDesc) Len() int { |
|
// return len(s) |
|
// } |
|
// func (s rankByFansIncreaseDesc) Swap(i, j int) { |
|
// s[i], s[j] = s[j], s[i] |
|
// } |
|
// func (s rankByFansIncreaseDesc) Less(i, j int) bool { |
|
// return s[i].FansIncrease > s[j].FansIncrease |
|
// } |
|
|
|
type sortRankFunc func(p1, p2 mcnmodel.RankDataInterface) bool |
|
|
|
type rankDataSorter struct { |
|
datas []mcnmodel.RankDataInterface |
|
by sortRankFunc // Closure used in the Less method. |
|
} |
|
|
|
// Len is part of sort.Interface. |
|
func (s *rankDataSorter) Len() int { |
|
return len(s.datas) |
|
} |
|
|
|
// Swap is part of sort.Interface. |
|
func (s *rankDataSorter) Swap(i, j int) { |
|
s.datas[i], s.datas[j] = s.datas[j], s.datas[i] |
|
} |
|
|
|
// Less is part of sort.Interface. It is implemented by calling the "by" closure in the sorter. |
|
func (s *rankDataSorter) Less(i, j int) bool { |
|
return s.by(s.datas[i], s.datas[j]) |
|
} |
|
|
|
func byValueDesc(p1, p2 mcnmodel.RankDataInterface) bool { |
|
return p1.GetValue() > p2.GetValue() |
|
} |
|
|
|
//GetList get list |
|
func (s *RankByTid) GetList(tid int16, dataType mcnmodel.DataType) (res []mcnmodel.RankDataInterface) { |
|
if s.TidMap == nil { |
|
return |
|
} |
|
tMap, ok := s.TidMap[tid] |
|
if !ok || tMap == nil { |
|
return |
|
} |
|
if res, ok = tMap[dataType]; !ok { |
|
res = nil |
|
} |
|
return |
|
} |
|
|
|
var nulltidlist = make([]*mcnmodel.TidnameInfo, 0) |
|
|
|
//GetTypeList get type's list |
|
func (s *RankByTid) GetTypeList(dataType mcnmodel.DataType) (res []*mcnmodel.TidnameInfo) { |
|
res = s.TidTypeListMap[dataType] |
|
if res == nil { |
|
res = nulltidlist |
|
} |
|
return |
|
} |
|
|
|
func (s *RankByTid) addRank(v mcnmodel.RankDataInterface) { |
|
var dmap, allMap map[mcnmodel.DataType][]mcnmodel.RankDataInterface |
|
if s.TidMap == nil { |
|
s.TidMap = make(map[int16]map[mcnmodel.DataType][]mcnmodel.RankDataInterface) |
|
} |
|
var ok bool |
|
if v.GetTid() != 0 { |
|
if dmap, ok = s.TidMap[v.GetTid()]; !ok { |
|
dmap = make(map[mcnmodel.DataType][]mcnmodel.RankDataInterface) |
|
s.TidMap[v.GetTid()] = dmap |
|
} |
|
dmap[v.GetDataType()] = append(dmap[v.GetDataType()], v) |
|
} |
|
|
|
if allMap, ok = s.TidMap[0]; !ok { |
|
allMap = make(map[mcnmodel.DataType][]mcnmodel.RankDataInterface) |
|
s.TidMap[0] = allMap |
|
} |
|
|
|
allMap[v.GetDataType()] = append(allMap[v.GetDataType()], v) |
|
} |
|
|
|
func (s *RankByTid) addTidMap(v *tidnameUnique) { |
|
if s.TidTypeListMap == nil { |
|
s.TidTypeListMap = make(map[mcnmodel.DataType][]*mcnmodel.TidnameInfo) |
|
} |
|
|
|
v.export(s.TidTypeListMap) |
|
} |
|
|
|
//Truncate truncate all the sorted list by max item number |
|
func (s *RankByTid) Truncate(max int) { |
|
for _, v := range s.TidMap { |
|
for k2, v2 := range v { |
|
var length = len(v2) |
|
if length == 0 { |
|
continue |
|
} |
|
var m = mathutil.Min(max, length) |
|
v[k2] = v2[0:m] |
|
} |
|
} |
|
} |
|
|
|
// Sort sort by sorting function |
|
func (s *RankByTid) Sort(sortFunc sortRankFunc) { |
|
for _, v := range s.TidMap { |
|
for k2, v2 := range v { |
|
var sorter = &rankDataSorter{ |
|
datas: v2, |
|
by: sortFunc, |
|
} |
|
sort.Sort(sorter) |
|
v[k2] = v2 |
|
} |
|
} |
|
} |
|
|
|
type keyFunc func(int64) string |
|
type loadRankFunc func(signID int64, date time.Time) (result *RankByTid, err error) |
|
|
|
// GetRankUpFans get fans |
|
func (d *Dao) GetRankUpFans(signID int64) (result *RankByTid, err error) { |
|
return d.getRankCache(signID, cacheKeyRankFans, d.loadRankUpFansCache) |
|
} |
|
|
|
// GetRankArchiveLikes get fans |
|
func (d *Dao) GetRankArchiveLikes(signID int64) (result *RankByTid, err error) { |
|
return d.getRankCache(signID, cacheKeyRankArchiveLikes, d.loadRankArchiveLikesCache) |
|
} |
|
|
|
func (d *Dao) getRankCache(signID int64, keyCalc keyFunc, load loadRankFunc) (result *RankByTid, err error) { |
|
var key = keyCalc(signID) |
|
v, err := d.localcache.Get(key) |
|
if err != nil { |
|
if err == gcache.KeyNotFoundError { |
|
// load cache |
|
v, err = load(signID, time.Now()) |
|
if err != nil { |
|
log.Error("load cache error, signID=%d, err=%s", signID, err) |
|
return |
|
} |
|
|
|
d.localcache.SetWithExpire(key, v, time.Duration(conf.Conf.RankCache.ExpireTime)) |
|
} else { |
|
log.Error("get from gcache err, signID=%d, err=%s", signID, err) |
|
return |
|
} |
|
} |
|
if v == nil { |
|
return |
|
} |
|
result, _ = v.(*RankByTid) |
|
return |
|
} |
|
|
|
func cacheKeyRankFans(signID int64) string { |
|
return fmt.Sprintf("rank_fans_%d", signID) |
|
} |
|
|
|
func cacheKeyRankArchiveLikes(signID int64) string { |
|
return fmt.Sprintf("rank_likes_%d", signID) |
|
} |
|
|
|
var dataTypes = []mcnmodel.DataType{ |
|
mcnmodel.DataTypeAccumulate, |
|
mcnmodel.DataTypeDay, |
|
mcnmodel.DataTypeWeek, |
|
mcnmodel.DataTypeMonth, |
|
mcnmodel.DataTypeActiveFans, |
|
} |
|
|
|
// --------------------------------------- load rank up fans ------------------------------- |
|
func (d *Dao) loadRankUpFansCache(signID int64, date time.Time) (result *RankByTid, err error) { |
|
rawRanks, err := d.RawRankUpFans(signID, date) |
|
if err != nil { |
|
log.Error("fail to get raw rank up fans, signid=%d, err=%s", signID, err) |
|
return |
|
} |
|
result = new(RankByTid) |
|
if len(rawRanks) == 0 { |
|
log.Info("up fans rank data is empty, sign id=%d", signID) |
|
return |
|
} |
|
var midMap = make(map[int64]struct{}) |
|
//var accumulateMap = make(map[int64]*mcnmodel.McnRankUpFan) |
|
// 获取mid列表 |
|
for _, v := range rawRanks { |
|
midMap[v.UpMid] = struct{}{} |
|
//if v.DataType == mcnmodel.DataTypeAccumulate { |
|
// accumulateMap[v.UpMid] = v |
|
//} |
|
} |
|
|
|
var mids []int64 |
|
for k := range midMap { |
|
mids = append(mids, k) |
|
} |
|
|
|
// 获取账号信息,头像 |
|
accInfos, err := global.GetInfos(context.Background(), mids) |
|
if err != nil || accInfos == nil { |
|
log.Warn("get infos fail, err=%s", err) |
|
} |
|
|
|
var tidUnique = newTidNameUnique() |
|
// 组装信息 |
|
for _, v := range rawRanks { |
|
var info mcnmodel.RankUpFansInfo |
|
info.Copy(v) |
|
if account, ok := accInfos[v.UpMid]; ok { |
|
info.Name = account.Name |
|
info.UpFaceLink = account.Face |
|
info.TidName = cache.GetTidName(int64(info.Tid)) |
|
if info.TidName == "" { |
|
info.TidName = "其他" |
|
} |
|
tidUnique.addTid(info.Tid, info.TidName, info.DataType) |
|
} |
|
//if accumulateData, ok := accumulateMap[v.UpMid]; ok { |
|
// info.FansAccumulate = accumulateData.Value1 |
|
//} |
|
result.addRank(&info) |
|
} |
|
|
|
result.addTidMap(tidUnique) |
|
|
|
// 排序 |
|
result.Sort(byValueDesc) |
|
// 截断到10个,截断需要在排序之后 |
|
result.Truncate(10) |
|
return |
|
} |
|
|
|
//RawRankUpFans get from db |
|
func (d *Dao) RawRankUpFans(signID int64, date time.Time) (result []*mcnmodel.McnRankUpFan, err error) { |
|
// 有X种类型, |
|
// 昨日、上周、上月、累计 |
|
// 每种类型取最近日期的数据 |
|
for _, typ := range dataTypes { |
|
var tmpResult []*mcnmodel.McnRankUpFan |
|
e := d.mcndb.Raw(`select * from mcn_rank_up_fans where data_type=? and sign_id=? |
|
and generate_date=(select generate_date from mcn_rank_up_fans where data_type=? and sign_id=? and generate_date <= ? order by generate_date desc limit 1)`, |
|
typ, signID, typ, signID, date). |
|
Find(&tmpResult).Error |
|
if e != nil { |
|
log.Error("fail to get rank, type=%d, sign id=%d, err=%s", typ, signID, e) |
|
continue |
|
} |
|
result = append(result, tmpResult...) |
|
} |
|
log.Info("get rank from db, sign id=%d, len=%d, date=%s", signID, len(result), date.Format(dateFmt)) |
|
return |
|
} |
|
|
|
//ReloadRank reload rank from db |
|
func (d *Dao) ReloadRank(signID int64) (err error) { |
|
// load cache |
|
v, err := d.loadRankUpFansCache(signID, time.Now()) |
|
|
|
if err != nil { |
|
log.Error("load cache error, signID=%d, err=%s", signID, err) |
|
return |
|
} |
|
|
|
var key = cacheKeyRankFans(signID) |
|
d.localcache.SetWithExpire(key, v, time.Hour) |
|
log.Info("reload rank cache, sign id=%d", signID) |
|
return |
|
} |
|
|
|
// ----------------------------------------- load rank archive likes ------------------------------------ |
|
func (d *Dao) loadRankArchiveLikesCache(signID int64, date time.Time) (result *RankByTid, err error) { |
|
rawArchiveLike, err := d.RawRankArchiveLikes(signID, date) |
|
if err != nil { |
|
log.Error("fail to get raw rank up fans, signid=%d, err=%s", signID, err) |
|
return |
|
} |
|
|
|
result = new(RankByTid) |
|
if len(rawArchiveLike) == 0 { |
|
log.Info("archive likes rank data is empty, sign id=%d", signID) |
|
return |
|
} |
|
// 获取 aid列表 |
|
var aids []int64 |
|
//var accumulateMap = make(map[int64]*mcnmodel.McnRankArchiveLike) |
|
for _, v := range rawArchiveLike { |
|
aids = append(aids, v.ArchiveID) |
|
//if v.DataType == mcnmodel.DataTypeAccumulate { |
|
// accumulateMap[v.ArchiveID] = v |
|
//} |
|
} |
|
|
|
aids = util.Unique(aids) |
|
// 获取archive信息 |
|
|
|
arcsReply, err := global.GetArcGRPC().Arcs(context.Background(), &arcgrpc.ArcsRequest{Aids: aids}) |
|
if err != nil { |
|
log.Error("fail to get archive info, sign_id=%d err=%s", signID, err) |
|
return |
|
} |
|
archiveDataMap := arcsReply.Arcs |
|
var tidUnique = newTidNameUnique() |
|
// 组装archive信息 |
|
for _, v := range rawArchiveLike { |
|
var info = mcnmodel.RankArchiveLikeInfo{} |
|
info.CopyFromDB(v) |
|
var archive, ok = archiveDataMap[v.ArchiveID] |
|
if !ok { |
|
continue |
|
} |
|
info.CopyFromArchive(archive) |
|
|
|
tidUnique.addTid(info.Tid, info.TidName, info.DataType) |
|
|
|
//if accumulateData, ok := accumulateMap[v.ArchiveID]; ok { |
|
// info.LikesAccumulate = accumulateData.LikeCount |
|
//} |
|
result.addRank(&info) |
|
} |
|
|
|
result.addTidMap(tidUnique) |
|
// 排序 |
|
result.Sort(byValueDesc) |
|
result.Truncate(10) |
|
return |
|
} |
|
|
|
//RawRankArchiveLikes get from db |
|
func (d *Dao) RawRankArchiveLikes(signID int64, date time.Time) (result []*mcnmodel.McnRankArchiveLike, err error) { |
|
// 有X种类型, |
|
// 昨日、上周、上月、累计 |
|
// 每种类型取最近日期的数据 |
|
for _, typ := range dataTypes { |
|
var tmpResult []*mcnmodel.McnRankArchiveLike |
|
e := d.mcndb.Raw(`select * from mcn_rank_archive_likes where data_type=? and sign_id=? |
|
and generate_date=(select generate_date from mcn_rank_archive_likes where data_type=? and sign_id=? and generate_date <= ? order by generate_date desc limit 1)`, |
|
typ, signID, typ, signID, date). |
|
Find(&tmpResult).Error |
|
if e != nil { |
|
log.Error("fail to get rank, type=%d, sign id=%d, err=%s", typ, signID, e) |
|
continue |
|
} |
|
result = append(result, tmpResult...) |
|
} |
|
log.Info("get rank from db, sign id=%d, len=%d, date=%s", signID, len(result), date.Format(dateFmt)) |
|
return |
|
}
|
|
|