427 lines
12 KiB
Go
427 lines
12 KiB
Go
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
|
||
}
|