130 lines
3.3 KiB
Go
130 lines
3.3 KiB
Go
package playletser
|
|
|
|
import (
|
|
"fmt"
|
|
"net/url"
|
|
"path"
|
|
"strings"
|
|
"sync"
|
|
"time"
|
|
"videoweb/app/service/upstream"
|
|
"videoweb/pub/common/log"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
)
|
|
|
|
const homePath = "/api/app/playlet/home"
|
|
const homeCacheTTL = 30 * time.Second
|
|
|
|
var (
|
|
homeMu sync.Mutex
|
|
homeCache *HomePlayletResponse
|
|
homeAt time.Time
|
|
)
|
|
|
|
// FetchHome 拉取首页短剧聚合数据。
|
|
func FetchHome(c *gin.Context) (*HomePlayletResponse, error) {
|
|
var env APIEnvelope[HomePlayletResponse]
|
|
if err := upstream.GetPublicJSON(c, homePath, &env); err != nil {
|
|
return nil, err
|
|
}
|
|
if env.Code != 200 {
|
|
return nil, fmt.Errorf("playlet home code=%d msg=%s", env.Code, env.Msg)
|
|
}
|
|
return &env.Data, nil
|
|
}
|
|
|
|
// FetchHomeOrEmpty 带短缓存;失败时打日志并返回空结构,保证 SSR 页面可渲染。
|
|
func FetchHomeOrEmpty(c *gin.Context) *HomePlayletResponse {
|
|
homeMu.Lock()
|
|
defer homeMu.Unlock()
|
|
if homeCache != nil && time.Since(homeAt) < homeCacheTTL {
|
|
return cloneHome(homeCache)
|
|
}
|
|
|
|
data, err := FetchHome(c)
|
|
if err != nil {
|
|
log.Error("fetch playlet home failed", log.E(err))
|
|
if homeCache != nil {
|
|
return cloneHome(homeCache)
|
|
}
|
|
return &HomePlayletResponse{}
|
|
}
|
|
if data == nil {
|
|
if homeCache != nil {
|
|
return cloneHome(homeCache)
|
|
}
|
|
return &HomePlayletResponse{}
|
|
}
|
|
data.SeoInfo.Normalize()
|
|
homeCache = data
|
|
homeAt = time.Now()
|
|
return cloneHome(homeCache)
|
|
}
|
|
|
|
func cloneHome(src *HomePlayletResponse) *HomePlayletResponse {
|
|
if src == nil {
|
|
return &HomePlayletResponse{}
|
|
}
|
|
out := *src
|
|
if src.Announcements != nil {
|
|
out.Announcements = append([]AnnouncementItem(nil), src.Announcements...)
|
|
}
|
|
if src.HomeBannerData != nil {
|
|
out.HomeBannerData = append([]HomeBannerData(nil), src.HomeBannerData...)
|
|
}
|
|
if src.HomeTodayUpdateData != nil {
|
|
out.HomeTodayUpdateData = append([]CommonData(nil), src.HomeTodayUpdateData...)
|
|
}
|
|
if src.HomeCategoryData != nil {
|
|
out.HomeCategoryData = make([]HomeCategoryData, len(src.HomeCategoryData))
|
|
for i, cat := range src.HomeCategoryData {
|
|
out.HomeCategoryData[i] = cat
|
|
if cat.List != nil {
|
|
out.HomeCategoryData[i].List = append([]CommonData(nil), cat.List...)
|
|
}
|
|
}
|
|
}
|
|
if src.HomeRecommendThemes != nil {
|
|
out.HomeRecommendThemes = append([]HomeRecommendTheme(nil), src.HomeRecommendThemes...)
|
|
}
|
|
if src.SeoInfo.Keywords != nil {
|
|
out.SeoInfo.Keywords = append([]string(nil), src.SeoInfo.Keywords...)
|
|
}
|
|
return &out
|
|
}
|
|
|
|
// ActiveHomeBanners 过滤未启用项,保留接口返回顺序。
|
|
func ActiveHomeBanners(list []HomeBannerData) []HomeBannerData {
|
|
if len(list) == 0 {
|
|
return list
|
|
}
|
|
out := make([]HomeBannerData, 0, len(list))
|
|
for _, item := range list {
|
|
if !item.Status {
|
|
continue
|
|
}
|
|
item.URL = normalizeBannerURL(item.URL)
|
|
out = append(out, item)
|
|
}
|
|
return out
|
|
}
|
|
|
|
// normalizeBannerURL 站内页面链接补尾斜杠,避免后台漏填 "/" 时每次点击都多一次 301。
|
|
// 外链、协议相对地址、带扩展名的文件路径保持原样。
|
|
func normalizeBannerURL(raw string) string {
|
|
raw = strings.TrimSpace(raw)
|
|
if !strings.HasPrefix(raw, "/") || strings.HasPrefix(raw, "//") {
|
|
return raw
|
|
}
|
|
u, err := url.Parse(raw)
|
|
if err != nil || u.Path == "" || strings.HasSuffix(u.Path, "/") {
|
|
return raw
|
|
}
|
|
if strings.Contains(path.Base(u.Path), ".") {
|
|
return raw
|
|
}
|
|
u.Path += "/"
|
|
return u.String()
|
|
}
|