Files
2026-10-26 16:28:14 +08:00

145 lines
3.2 KiB
Go

package configser
import (
"sync"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo/options"
)
const rankLimit = 48
// NormalizeRankPage 榜单分页:页码从 1 起,pageSize 不是 rankLimit 时强制改为 rankLimit。
func NormalizeRankPage(pageNum, pageSize int64) (int64, int64) {
if pageNum <= 0 {
pageNum = 1
}
if pageSize != int64(rankLimit) {
pageSize = int64(rankLimit)
}
return pageNum, pageSize
}
type periodRankCache[T any] struct {
Lock sync.RWMutex
Total []T
Week []T
Month []T
Year []T
}
func (c *periodRankCache[T]) hasAny() bool {
c.Lock.RLock()
defer c.Lock.RUnlock()
return len(c.Total) > 0 || len(c.Week) > 0 || len(c.Month) > 0 || len(c.Year) > 0
}
func (c *periodRankCache[T]) pick(p RankPeriod) []T {
switch p {
case RankPeriodWeek:
return c.Week
case RankPeriodMonth:
return c.Month
case RankPeriodYear:
return c.Year
default:
return c.Total
}
}
func (c *periodRankCache[T]) replace(total, week, month, year []T) {
c.Lock.Lock()
c.Total, c.Week, c.Month, c.Year = total, week, month, year
c.Lock.Unlock()
}
func (c *periodRankCache[T]) get(p RankPeriod, limit int, refreshIfEmpty func()) []T {
c.Lock.RLock()
list := c.pick(p)
empty := len(list) == 0
c.Lock.RUnlock()
if empty {
refreshIfEmpty()
c.Lock.RLock()
list = c.pick(p)
c.Lock.RUnlock()
}
return sliceLimit(list, limit)
}
func sliceLimit[T any](s []T, limit int) []T {
if limit <= 0 || limit >= len(s) {
return s
}
return s[:limit]
}
// SliceRankPage 对榜单缓存做分页切片
func SliceRankPage[T any](all []T, pageNum, pageSize int64) []T {
if pageNum <= 0 {
pageNum = 1
}
if pageSize <= 0 {
return []T{}
}
start := (pageNum - 1) * pageSize
if start >= int64(len(all)) {
return []T{}
}
end := start + pageSize
if end > int64(len(all)) {
end = int64(len(all))
}
return append([]T{}, all[start:end]...)
}
func contentLikeRankPipeline(statusField string, statusVal interface{}, realLikes, fakeLikes, updatedAt, idField string, limit int64) []bson.M {
return []bson.M{
{"$match": bson.M{statusField: statusVal}},
{"$addFields": bson.M{"rankScore": bson.M{"$add": []interface{}{
bson.M{"$ifNull": []interface{}{"$" + realLikes, 0}},
bson.M{"$ifNull": []interface{}{"$" + fakeLikes, 0}},
}}}},
{"$match": bson.M{"rankScore": bson.M{"$gt": 0}}},
{"$sort": bson.D{
{Key: "rankScore", Value: -1},
{Key: updatedAt, Value: -1},
{Key: idField, Value: -1},
}},
{"$limit": limit},
}
}
func contentWatchRankOpts(watchField, updatedAt, idField string, limit int64) *options.FindOptions {
return options.Find().
SetSort(bson.D{
{Key: watchField, Value: -1},
{Key: updatedAt, Value: -1},
{Key: idField, Value: -1},
}).
SetLimit(limit)
}
func contentRankFillPipeline(idField, statusField string, statusVal interface{}, excludeIds []int64, needFill int64) []bson.M {
return []bson.M{
{"$match": bson.M{
idField: bson.M{"$nin": excludeIds},
statusField: statusVal,
}},
{"$sample": bson.M{"size": needFill}},
}
}
func periodWatchNum(p RankPeriod, week, month, year, total int64) int64 {
switch p {
case RankPeriodWeek:
return week
case RankPeriodMonth:
return month
case RankPeriodYear:
return year
default:
return total
}
}