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

196 lines
5.2 KiB
Go

package appser
import (
"bytes"
"context"
"encoding/json"
"net/url"
"strings"
"sync"
"videoweb/app/api/proto"
"videoweb/app/service/configser"
"videoweb/pub/common/http"
"videoweb/pub/common/log"
"videoweb/pub/common/netutil"
"videoweb/pub/pubdef"
"videoweb/pub/storage/proxy"
"github.com/robfig/cron/v3"
)
const appListAPIPath = "/media/web/appinfo/applist"
var jsonHeader = map[string]string{"Content-Type": "application/json"}
var (
appListCron *cron.Cron
appListCache struct {
lock sync.RWMutex
appList []proto.AppCategoryInfo
categoryMaps map[int64]proto.AppCategoryInfo
}
)
type remoteAppListResp struct {
Code int `json:"code"`
Data struct {
List []proto.AppCategoryInfo `json:"list"`
} `json:"data"`
}
// Start 启动定时刷新应用列表缓存(每10分钟一次)
func Start() {
appListCron = cron.New(cron.WithSeconds())
UpdateAppListCache(context.Background())
_, _ = appListCron.AddFunc("0 */10 * * * ?", func() {
UpdateAppListCache(context.Background())
})
appListCron.Start()
}
// GetAppListCache 返回全部应用分类列表(带各分类下应用)
func GetAppListCache() []proto.AppCategoryInfo {
appListCache.lock.RLock()
defer appListCache.lock.RUnlock()
if len(appListCache.appList) == 0 {
return make([]proto.AppCategoryInfo, 0)
}
result := make([]proto.AppCategoryInfo, len(appListCache.appList))
copy(result, appListCache.appList)
return result
}
// GetCategoryCache 按分类ID返回单个分类(含应用列表);categoryId=0 返回第一个分类
func GetCategoryCache(categoryId int64) proto.AppCategoryInfo {
appListCache.lock.RLock()
defer appListCache.lock.RUnlock()
if categoryId == 0 && len(appListCache.appList) > 0 {
return appListCache.appList[0]
}
if v, ok := appListCache.categoryMaps[categoryId]; ok {
return v
}
return proto.AppCategoryInfo{AppList: make([]proto.AppInfoData, 0)}
}
// UpdateAppListCache 从远端拉取应用列表并刷新内存缓存
func UpdateAppListCache(ctx context.Context) {
domain := proxy.DomainAddressRepair("", proxy.BucketVideoAddressV2)
apiURL := strings.TrimSuffix(domain, "/") + appListAPIPath
reqBody, _ := json.Marshal(map[string]string{"dc": "dc"})
resp, err := http.Post(apiURL, jsonHeader, bytes.NewReader(reqBody))
if err != nil {
log.Warn("UpdateAppListCache http.Post failed", log.Any("url", apiURL), log.E(err))
return
}
var remote remoteAppListResp
if err = json.Unmarshal(resp.Data, &remote); err != nil {
log.Warn("UpdateAppListCache unmarshal failed", log.Any("url", apiURL), log.E(err))
return
}
if remote.Code != 200 {
log.Warn("UpdateAppListCache bad code", log.Any("url", apiURL), log.Any("code", remote.Code))
return
}
config := configser.GetConfigInfo()
advitise := configser.GetAdvitise()
channelCode := config.ChannelCode
categoryMaps := make(map[int64]proto.AppCategoryInfo)
var appList []proto.AppCategoryInfo
for _, category := range remote.Data.List {
var apps []proto.AppInfoData
for _, app := range category.AppList {
// 过滤掉本应用自身
if strings.HasPrefix(app.Name, pubdef.AppName) {
continue
}
apps = append(apps, proto.AppInfoData{
Id: app.Id,
CategoryId: app.CategoryId,
Name: app.Name,
Intro: app.Intro,
Description: app.Description,
Logo: netutil.MergeURL(config.ImgCdn, app.Logo),
Url: appendChannelCode(app.Url, channelCode),
Sort: app.Sort,
})
}
// 广告位 A(code=12)注入图标广告
if category.Code == 12 {
for _, ad := range advitise.AList {
apps = append(apps, proto.AppInfoData{
AdvitiseId: ad.Id,
CategoryId: category.Id,
Name: ad.Title,
Logo: netutil.MergeURL(config.ImgCdn, ad.Images),
Url: ad.Link,
Location: ad.Location,
})
}
}
// 广告位 B(code=13)注入图标文字广告
if category.Code == 13 {
for _, ad := range advitise.BList {
apps = append(apps, proto.AppInfoData{
AdvitiseId: ad.Id,
CategoryId: category.Id,
Name: ad.Title,
Logo: netutil.MergeURL(config.ImgCdn, ad.Images),
Url: ad.Link,
Location: ad.Location,
})
}
}
// 空分类不展示
if len(apps) == 0 {
continue
}
category.AppList = apps
categoryMaps[category.Id] = proto.AppCategoryInfo{
Id: category.Id,
Title: category.Title,
Intro: category.Intro,
Sort: category.Sort,
Code: category.Code,
}
appList = append(appList, category)
}
appListCache.lock.Lock()
appListCache.appList = appList
appListCache.categoryMaps = categoryMaps
appListCache.lock.Unlock()
}
// appendChannelCode 为应用 URL 拼接渠道码参数 dc=xxx;已含 dc 参数则不再拼接
func appendChannelCode(rawURL, channelCode string) string {
if len(channelCode) == 0 {
channelCode = pubdef.DefaultChannelCode
}
rawURL = strings.TrimSpace(rawURL)
channelCode = strings.TrimSpace(channelCode)
if rawURL == "" || channelCode == "" {
return rawURL
}
parsed, err := url.Parse(rawURL)
if err != nil {
return rawURL
}
query := parsed.Query()
if query.Has("dc") {
return parsed.String()
}
query.Set("dc", channelCode)
parsed.RawQuery = query.Encode()
return parsed.String()
}