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
|
|||
|
}
|