go-common/app/interface/main/mcn/dao/mcndao/rank.go
2019-04-22 18:49:16 +08:00

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