261 lines
8.6 KiB
Go
261 lines
8.6 KiB
Go
package translationser
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net/url"
|
|
"strings"
|
|
"sync"
|
|
"videoweb/app/api/proto"
|
|
"videoweb/pub/common/log"
|
|
"videoweb/pub/common/netutil"
|
|
"videoweb/pub/models/cfg"
|
|
"videoweb/pub/models/translationcategorymod"
|
|
"videoweb/pub/models/translationmod"
|
|
"videoweb/pub/models/videocategorymod"
|
|
"videoweb/pub/pubdef"
|
|
|
|
"github.com/robfig/cron/v3"
|
|
"go.mongodb.org/mongo-driver/bson"
|
|
"go.mongodb.org/mongo-driver/mongo/options"
|
|
)
|
|
|
|
var (
|
|
CacheMgr CacheInfo
|
|
skdCron *cron.Cron
|
|
categoryCacheFill sync.Mutex
|
|
)
|
|
|
|
type CacheInfo struct {
|
|
Lock sync.RWMutex // 读写锁
|
|
TranslationData proto.AppTranslationInfo // 顶部导航缓存
|
|
BottomTranslationData proto.AppTranslationInfo // 底部导航缓存
|
|
TranslationMap map[int64]proto.TranslationInfo
|
|
|
|
// 视频分类缓存(按 ID 索引)
|
|
VideoCategoryMap map[int64]*videocategorymod.VideoCategory
|
|
}
|
|
|
|
func init() {
|
|
skdCron = cron.New(cron.WithSeconds())
|
|
CacheMgr.TranslationData = proto.AppTranslationInfo{}
|
|
CacheMgr.BottomTranslationData = proto.AppTranslationInfo{}
|
|
CacheMgr.TranslationMap = make(map[int64]proto.TranslationInfo)
|
|
CacheMgr.VideoCategoryMap = make(map[int64]*videocategorymod.VideoCategory)
|
|
}
|
|
|
|
func Start() {
|
|
ctx := context.Background()
|
|
UpdateTranslationInfo(ctx)
|
|
|
|
_, _ = skdCron.AddFunc("0 */5 * * * ?", func() {
|
|
UpdateTranslationInfo(ctx)
|
|
})
|
|
|
|
skdCron.Start()
|
|
}
|
|
|
|
func UpdateTranslationInfo(ctx context.Context) {
|
|
filter := bson.M{cfg.FN.SysCfg.Id: 1}
|
|
sysCfg, _ := cfg.FindOneSysCfg(context.Background(), filter)
|
|
if sysCfg == nil {
|
|
return
|
|
}
|
|
|
|
translation := proto.AppTranslationInfo{}
|
|
bottomTranslation := proto.AppTranslationInfo{}
|
|
|
|
wg := &sync.WaitGroup{}
|
|
wg.Add(3)
|
|
var translationCategoryList translationcategorymod.TranslationCategoryList
|
|
var translationList translationmod.TranslationInfoList
|
|
var videoCategoryList videocategorymod.VideoCategoryList
|
|
|
|
go func() {
|
|
defer wg.Done()
|
|
translationCategoryList, _ = translationcategorymod.FindManyTranslationCategorys(ctx,
|
|
bson.M{translationcategorymod.FN.TranslationCategory.Status: true},
|
|
options.Find().SetLimit(1000).SetSort(bson.M{translationcategorymod.FN.TranslationCategory.Rank: -1}),
|
|
)
|
|
}()
|
|
go func() {
|
|
defer wg.Done()
|
|
translationList, _ = translationmod.FindManyTranslationInfos(ctx,
|
|
bson.M{translationmod.FN.TranslationInfo.Status: true},
|
|
options.Find().SetLimit(1000).SetSort(bson.M{translationmod.FN.TranslationInfo.Rank: -1}),
|
|
)
|
|
}()
|
|
go func() {
|
|
defer wg.Done()
|
|
videoCategoryList, _ = videocategorymod.FindManyVideoCategorys(ctx,
|
|
bson.M{videocategorymod.FN.VideoCategory.Status: true},
|
|
options.Find().SetLimit(500).SetSort(bson.M{videocategorymod.FN.VideoCategory.Rank: -1}),
|
|
)
|
|
}()
|
|
wg.Wait()
|
|
|
|
// ---- 构建各分类 ID Map(用于缓存 & 导航分组) ----
|
|
newVideoCategoryMap := make(map[int64]*videocategorymod.VideoCategory, len(videoCategoryList))
|
|
videoCategoryByNavId := make(map[int64][]*videocategorymod.VideoCategory)
|
|
for _, c := range videoCategoryList {
|
|
newVideoCategoryMap[c.Id] = c
|
|
if c.TranslationCategoryId > 0 {
|
|
videoCategoryByNavId[c.TranslationCategoryId] = append(videoCategoryByNavId[c.TranslationCategoryId], c)
|
|
}
|
|
}
|
|
|
|
// ---- 构建导航树 ----
|
|
tempTranslationMap := make(map[int64]proto.TranslationInfo)
|
|
|
|
for _, categoryInfo := range translationCategoryList {
|
|
categoryTemp := proto.TranslationCategory{
|
|
ID: categoryInfo.Id,
|
|
Name: categoryInfo.Name,
|
|
Icon: netutil.MergeURL(sysCfg.ImgAppCdn, categoryInfo.Icon),
|
|
TranslationList: make([]proto.TranslationInfo, 0),
|
|
CategoryType: categoryInfo.CategoryType,
|
|
UrlValue: categoryInfo.UrlValue,
|
|
}
|
|
|
|
if categoryInfo.CategoryType == pubdef.TranslationTypeSub {
|
|
// 固定路径条目优先放在最前面(根据导航分类的 UrlValue 决定属于哪个内容域)
|
|
categoryTemp.TranslationList = append(categoryTemp.TranslationList,
|
|
buildFixedNavItems(categoryInfo.UrlValue)...,
|
|
)
|
|
// 手动配置的导航条目
|
|
for _, translationInfo := range translationList {
|
|
if categoryInfo.Id != translationInfo.TransCategoryId {
|
|
continue
|
|
}
|
|
translationTmp := proto.TranslationInfo{
|
|
ID: translationInfo.Id,
|
|
Name: translationInfo.Name,
|
|
TransCategoryId: translationInfo.TransCategoryId,
|
|
}
|
|
if translationInfo.IsFriendAds {
|
|
// 友链广告,顶部暂不支持
|
|
continue
|
|
} else if translationInfo.HtmlId > 0 {
|
|
translationTmp.UrlValue = "/richtext?title=" + url.QueryEscape(strings.TrimSpace(translationInfo.Name))
|
|
} else if len(translationInfo.Link) > 0 {
|
|
translationTmp.UrlValue = translationInfo.Link
|
|
}
|
|
if translationTmp.UrlValue == "" {
|
|
continue
|
|
}
|
|
tempTranslationMap[translationInfo.Id] = translationTmp
|
|
categoryTemp.TranslationList = append(categoryTemp.TranslationList, translationTmp)
|
|
}
|
|
// 挂载到该导航的视频分类
|
|
for _, c := range videoCategoryByNavId[categoryInfo.Id] {
|
|
if c.DisplayPosition == pubdef.CategoryDisplayPositionHomeOnly {
|
|
continue
|
|
}
|
|
categoryTemp.TranslationList = append(categoryTemp.TranslationList, proto.TranslationInfo{
|
|
ID: c.Id,
|
|
Name: c.Name,
|
|
DisplayMode: c.DisplayMode,
|
|
UrlValue: fmt.Sprintf(pubdef.TranslationPath_VideoMore, c.Id),
|
|
})
|
|
}
|
|
} else if categoryInfo.CategoryType == pubdef.TranslationTypeBottom {
|
|
for _, translationInfo := range translationList {
|
|
if categoryInfo.Id != translationInfo.TransCategoryId {
|
|
continue
|
|
}
|
|
translationTmp := proto.TranslationInfo{
|
|
ID: translationInfo.Id,
|
|
Name: translationInfo.Name,
|
|
TransCategoryId: translationInfo.TransCategoryId,
|
|
}
|
|
if translationInfo.IsFriendAds {
|
|
translationTmp.IsFriendAds = true
|
|
} else if translationInfo.HtmlId > 0 {
|
|
translationTmp.UrlValue = "/richtext?title=" + url.QueryEscape(strings.TrimSpace(translationInfo.Name))
|
|
} else if len(translationInfo.Link) > 0 {
|
|
translationTmp.UrlValue = translationInfo.Link
|
|
}
|
|
tempTranslationMap[translationInfo.Id] = translationTmp
|
|
categoryTemp.TranslationList = append(categoryTemp.TranslationList, translationTmp)
|
|
}
|
|
bottomTranslation.CategoryList = append(bottomTranslation.CategoryList, categoryTemp)
|
|
continue
|
|
} else {
|
|
continue
|
|
}
|
|
translation.CategoryList = append(translation.CategoryList, categoryTemp)
|
|
}
|
|
|
|
log.Info("UpdateTranslationInfo", log.Any("categoryList", translation.CategoryList))
|
|
|
|
CacheMgr.Lock.Lock()
|
|
defer CacheMgr.Lock.Unlock()
|
|
CacheMgr.TranslationData = translation
|
|
CacheMgr.BottomTranslationData = bottomTranslation
|
|
CacheMgr.TranslationMap = tempTranslationMap
|
|
CacheMgr.VideoCategoryMap = newVideoCategoryMap
|
|
}
|
|
|
|
// ---- 导航读取 ----
|
|
|
|
func GetTranslationData() proto.AppTranslationInfo {
|
|
CacheMgr.Lock.RLock()
|
|
defer CacheMgr.Lock.RUnlock()
|
|
return CacheMgr.TranslationData
|
|
}
|
|
|
|
func GetTranslationBottomList() proto.AppTranslationInfo {
|
|
CacheMgr.Lock.RLock()
|
|
defer CacheMgr.Lock.RUnlock()
|
|
return CacheMgr.BottomTranslationData
|
|
}
|
|
|
|
// ---- 分类读取 ----
|
|
|
|
func categoryCacheEmpty() bool {
|
|
CacheMgr.Lock.RLock()
|
|
defer CacheMgr.Lock.RUnlock()
|
|
return len(CacheMgr.VideoCategoryMap) == 0
|
|
}
|
|
|
|
// EnsureCategoryCaches 若任一分内容分类缓存为空则立即补全;常规定时 5 分钟刷新。
|
|
func EnsureCategoryCaches(ctx context.Context) {
|
|
if !categoryCacheEmpty() {
|
|
return
|
|
}
|
|
categoryCacheFill.Lock()
|
|
defer categoryCacheFill.Unlock()
|
|
if !categoryCacheEmpty() {
|
|
return
|
|
}
|
|
UpdateTranslationInfo(ctx)
|
|
}
|
|
|
|
// EnsureVideoCategoryCache 兼容旧调用,等同 EnsureCategoryCaches。
|
|
func EnsureVideoCategoryCache(ctx context.Context) {
|
|
EnsureCategoryCaches(ctx)
|
|
}
|
|
|
|
// GetVideoCategory 通过视频分类 ID 获取分类信息,不存在返回 nil
|
|
func GetVideoCategory(id int64) *videocategorymod.VideoCategory {
|
|
EnsureCategoryCaches(context.Background())
|
|
CacheMgr.Lock.RLock()
|
|
defer CacheMgr.Lock.RUnlock()
|
|
return CacheMgr.VideoCategoryMap[id]
|
|
}
|
|
|
|
// buildFixedNavItems 根据导航分类的 UrlValue 返回对应内容域的固定路径条目,
|
|
// 这些条目始终排在动态分类和手动配置条目之前。
|
|
func buildFixedNavItems(urlValue string) []proto.TranslationInfo {
|
|
switch urlValue {
|
|
case pubdef.TranslationPath_Video:
|
|
return []proto.TranslationInfo{
|
|
//{Name: pubdef.TranslationName_Video, UrlValue: pubdef.TranslationPath_Video},
|
|
//{Name: pubdef.TranslationName_VideoAll, UrlValue: pubdef.TranslationPath_VideoAll},
|
|
//{Name: pubdef.TranslationName_VideoRank, UrlValue: pubdef.TranslationPath_VideoRank},
|
|
//{Name: pubdef.TranslationName_ActressRank, UrlValue: pubdef.TranslationPath_ActressRank},
|
|
}
|
|
}
|
|
return nil
|
|
}
|