go-common/app/interface/main/mcn/dao/mcndao/rank.go

427 lines
12 KiB
Go
Raw Normal View History

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