281 lines
8.5 KiB
Go
281 lines
8.5 KiB
Go
package configser
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
"sync"
|
|
"sync/atomic"
|
|
"videoweb/app/api/proto"
|
|
"videoweb/pub/common/log"
|
|
"videoweb/pub/common/netutil"
|
|
"videoweb/pub/common/playletcache"
|
|
"videoweb/pub/common/utils"
|
|
"videoweb/pub/models/contenttagmod"
|
|
"videoweb/pub/models/playletmod"
|
|
"videoweb/pub/models/playletthememod"
|
|
"videoweb/pub/pubdef"
|
|
|
|
"go.mongodb.org/mongo-driver/bson"
|
|
"go.mongodb.org/mongo-driver/mongo/options"
|
|
)
|
|
|
|
var (
|
|
PlayletRankMgr periodRankCache[proto.PlayletRankData]
|
|
playletRankRefreshLock sync.Mutex
|
|
playletRankCacheVersion atomic.Int64
|
|
)
|
|
|
|
func UpdatePlayletRankCache() {
|
|
version := playletcache.RankCacheVersion()
|
|
ctx := context.Background()
|
|
imgCdn := GetConfigInfo().ImgCdn
|
|
PlayletRankMgr.replace(
|
|
buildPlayletRankList(ctx, imgCdn, RankPeriodTotal),
|
|
buildPlayletRankList(ctx, imgCdn, RankPeriodWeek),
|
|
buildPlayletRankList(ctx, imgCdn, RankPeriodMonth),
|
|
buildPlayletRankList(ctx, imgCdn, RankPeriodYear),
|
|
)
|
|
playletRankCacheVersion.Store(version)
|
|
}
|
|
|
|
func refreshPlayletRankCacheIfEmpty() {
|
|
if PlayletRankMgr.hasAny() {
|
|
return
|
|
}
|
|
playletRankRefreshLock.Lock()
|
|
defer playletRankRefreshLock.Unlock()
|
|
if PlayletRankMgr.hasAny() {
|
|
return
|
|
}
|
|
UpdatePlayletRankCache()
|
|
}
|
|
|
|
func GetPlayletRankList(p RankPeriod, limit int) []proto.PlayletRankData {
|
|
version := playletcache.RankCacheVersion()
|
|
if version != playletRankCacheVersion.Load() {
|
|
playletRankRefreshLock.Lock()
|
|
if version != playletRankCacheVersion.Load() {
|
|
UpdatePlayletRankCache()
|
|
}
|
|
playletRankRefreshLock.Unlock()
|
|
}
|
|
return PlayletRankMgr.get(p, limit, refreshPlayletRankCacheIfEmpty)
|
|
}
|
|
|
|
func playletPeriodWatchField(p RankPeriod) string {
|
|
switch p {
|
|
case RankPeriodWeek:
|
|
return playletmod.FN.Playlet.WeekWatchs
|
|
case RankPeriodMonth:
|
|
return playletmod.FN.Playlet.MonthWatchs
|
|
case RankPeriodYear:
|
|
return playletmod.FN.Playlet.YearWatchs
|
|
default:
|
|
return ""
|
|
}
|
|
}
|
|
|
|
func buildPlayletRankList(ctx context.Context, imgCdn string, p RankPeriod) []proto.PlayletRankData {
|
|
var ranked playletmod.PlayletList
|
|
if p == RankPeriodTotal {
|
|
if err := playletmod.AggregatePlaylet(ctx, &ranked, contentLikeRankPipeline(
|
|
playletmod.FN.Playlet.Status, playletmod.StatusUp,
|
|
playletmod.FN.Playlet.CountLike, playletmod.FN.Playlet.VirtualCountLike,
|
|
playletmod.FN.Playlet.UpdatedAt, playletmod.FN.Playlet.Id,
|
|
rankLimit,
|
|
)); err != nil {
|
|
log.Warn("buildPlayletRankList like rank query failed", log.E(err))
|
|
ranked = playletmod.PlayletList{}
|
|
}
|
|
} else {
|
|
watchField := playletPeriodWatchField(p)
|
|
list, err := playletmod.FindManyPlaylet(ctx, bson.M{
|
|
playletmod.FN.Playlet.Status: playletmod.StatusUp,
|
|
watchField: bson.M{"$gt": 0},
|
|
}, contentWatchRankOpts(watchField, playletmod.FN.Playlet.UpdatedAt, playletmod.FN.Playlet.Id, rankLimit))
|
|
if err != nil {
|
|
log.Warn("buildPlayletRankList watch rank query failed", log.Any("period", p), log.E(err))
|
|
ranked = playletmod.PlayletList{}
|
|
} else {
|
|
ranked = list
|
|
}
|
|
}
|
|
if int64(len(ranked)) < rankLimit {
|
|
needFill := rankLimit - int64(len(ranked))
|
|
excludeIds := make([]int64, 0, len(ranked))
|
|
for _, v := range ranked {
|
|
if v != nil {
|
|
excludeIds = append(excludeIds, v.Id)
|
|
}
|
|
}
|
|
var filled playletmod.PlayletList
|
|
if err := playletmod.AggregatePlaylet(ctx, &filled, contentRankFillPipeline(
|
|
playletmod.FN.Playlet.Id, playletmod.FN.Playlet.Status,
|
|
playletmod.StatusUp, excludeIds, needFill,
|
|
)); err != nil {
|
|
log.Warn("buildPlayletRankList fill sample failed", log.Any("period", p), log.E(err))
|
|
} else {
|
|
ranked = append(ranked, filled...)
|
|
}
|
|
}
|
|
tagNameMap, themeNameMap := loadPlayletRankNameMaps(ctx, ranked)
|
|
result := make([]proto.PlayletRankData, 0, len(ranked))
|
|
for _, item := range ranked {
|
|
if item == nil {
|
|
continue
|
|
}
|
|
result = append(result, playletToRankData(item, imgCdn, tagNameMap, themeNameMap, p))
|
|
}
|
|
return result
|
|
}
|
|
|
|
func playletToRankData(item *playletmod.Playlet, imgCdn string, tagNameMap contenttagmod.ContentTagMap, themeNameMap map[int64]string, p RankPeriod) proto.PlayletRankData {
|
|
return proto.PlayletRankData{
|
|
PlayletData: proto.PlayletData{
|
|
Id: item.Id,
|
|
Status: item.Status,
|
|
IsRecommend: item.IsRecommend,
|
|
IsNew: !item.OnlineAt.IsZero() && item.OnlineAt.After(utils.BeginOfWeekByTime(utils.Now())),
|
|
IsFeatured: item.IsFeatured,
|
|
IsOriginal: item.IsOriginal || item.MediaCenterId == 0,
|
|
ShowOriginal: item.ShowOriginal,
|
|
OnlineAt: item.OnlineAt,
|
|
MediaCreatedAt: item.MediaCreatedAt,
|
|
MediaUpdatedAt: item.MediaUpdatedAt,
|
|
Title: item.Title,
|
|
Slug: item.Slug,
|
|
Desc: item.Desc,
|
|
Anchor: item.Anchor,
|
|
Cover: netutil.MergeURL(imgCdn, item.DisplayCover()),
|
|
Preview: netutil.MergeURL(imgCdn, strings.TrimSpace(item.Preview)),
|
|
TagIds: item.TagIds,
|
|
TagNames: playletRankTagNamesByIDs(item.TagIds, tagNameMap),
|
|
TagSlugs: playletRankTagSlugsByIDs(item.TagIds, tagNameMap),
|
|
ThemeIds: item.ThemeIds,
|
|
ThemeNames: playletNamesByIDs(item.ThemeIds, themeNameMap),
|
|
ContentType: item.ContentType,
|
|
PayType: item.PayType,
|
|
SingleCoins: item.SingleCoins,
|
|
TotalEpisode: item.TotalEpisode,
|
|
UpdateStatus: item.UpdateStatus,
|
|
CountBrowse: item.DisplayCountBrowse(),
|
|
HeatValue: item.DisplayHeatValue(),
|
|
CountCollect: item.CountCollect,
|
|
CountLike: item.CountLike,
|
|
VirtualCountLike: item.VirtualCountLike,
|
|
DisplayCountLike: item.CountLike + item.VirtualCountLike,
|
|
CreatedAt: item.CreatedAt,
|
|
UpdatedAt: item.UpdatedAt,
|
|
SeoInfo: playletRankSeo(item.SeoInfo),
|
|
},
|
|
RankScore: periodWatchNum(p, item.WeekWatchs, item.MonthWatchs, item.YearWatchs, item.CountLike+item.VirtualCountLike),
|
|
}
|
|
}
|
|
|
|
func playletRankSeo(seo pubdef.SeoValue) pubdef.SeoValue {
|
|
seo.Title = strings.TrimSpace(seo.Title)
|
|
seo.Description = strings.TrimSpace(seo.Description)
|
|
if seo.Keyworld == nil {
|
|
seo.Keyworld = make([]string, 0)
|
|
return seo
|
|
}
|
|
words := make([]string, 0, len(seo.Keyworld))
|
|
for _, word := range seo.Keyworld {
|
|
word = strings.TrimSpace(word)
|
|
if word != "" {
|
|
words = append(words, word)
|
|
}
|
|
}
|
|
seo.Keyworld = words
|
|
return seo
|
|
}
|
|
|
|
func playletNamesByIDs(ids []int64, nameMap map[int64]string) []string {
|
|
result := make([]string, 0, len(ids))
|
|
for _, id := range ids {
|
|
if name := nameMap[id]; name != "" {
|
|
result = append(result, name)
|
|
}
|
|
}
|
|
return result
|
|
}
|
|
|
|
func loadPlayletRankNameMaps(ctx context.Context, list playletmod.PlayletList) (contenttagmod.ContentTagMap, map[int64]string) {
|
|
tagIDs := make([]int64, 0)
|
|
themeIDs := make([]int64, 0)
|
|
seenTag := make(map[int64]struct{})
|
|
seenTheme := make(map[int64]struct{})
|
|
for _, item := range list {
|
|
if item == nil {
|
|
continue
|
|
}
|
|
for _, id := range item.TagIds {
|
|
if id <= 0 {
|
|
continue
|
|
}
|
|
if _, ok := seenTag[id]; ok {
|
|
continue
|
|
}
|
|
seenTag[id] = struct{}{}
|
|
tagIDs = append(tagIDs, id)
|
|
}
|
|
for _, id := range item.ThemeIds {
|
|
if id <= 0 {
|
|
continue
|
|
}
|
|
if _, ok := seenTheme[id]; ok {
|
|
continue
|
|
}
|
|
seenTheme[id] = struct{}{}
|
|
themeIDs = append(themeIDs, id)
|
|
}
|
|
}
|
|
tagNameMap := make(contenttagmod.ContentTagMap, len(tagIDs))
|
|
if len(tagIDs) > 0 {
|
|
if tags, err := contenttagmod.GetMapTags(ctx, tagIDs); err != nil {
|
|
log.Warn("loadPlayletRankNameMaps tags failed", log.E(err))
|
|
} else {
|
|
tagNameMap = tags
|
|
}
|
|
}
|
|
themeNameMap := make(map[int64]string, len(themeIDs))
|
|
if len(themeIDs) > 0 {
|
|
themes, err := playletthememod.FindManyPlayletTheme(ctx, bson.M{
|
|
playletthememod.FN.PlayletTheme.Id: bson.M{"$in": themeIDs},
|
|
}, options.Find().SetProjection(bson.M{
|
|
playletthememod.FN.PlayletTheme.Id: 1,
|
|
playletthememod.FN.PlayletTheme.Name: 1,
|
|
}))
|
|
if err != nil {
|
|
log.Warn("loadPlayletRankNameMaps themes failed", log.E(err))
|
|
} else {
|
|
for _, theme := range themes {
|
|
if theme != nil {
|
|
themeNameMap[theme.Id] = theme.Name
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return tagNameMap, themeNameMap
|
|
}
|
|
|
|
func playletRankTagNamesByIDs(ids []int64, tags contenttagmod.ContentTagMap) []string {
|
|
result := make([]string, 0, len(ids))
|
|
for _, id := range ids {
|
|
if tag := tags[id]; tag != nil && tag.Name != "" {
|
|
result = append(result, tag.Name)
|
|
}
|
|
}
|
|
return result
|
|
}
|
|
|
|
func playletRankTagSlugsByIDs(ids []int64, tags contenttagmod.ContentTagMap) []string {
|
|
result := make([]string, 0, len(ids))
|
|
for _, id := range ids {
|
|
if tag := tags[id]; tag != nil && tag.Name != "" {
|
|
result = append(result, tag.Slug)
|
|
}
|
|
}
|
|
return result
|
|
}
|