init: web_hgdjc_server

This commit is contained in:
2026-11-07 10:46:18 +08:00
commit b2ff6ac8ff
707 changed files with 147645 additions and 0 deletions
@@ -0,0 +1,49 @@
package playletser
import (
"reflect"
"testing"
"videoweb/pub/models/homeconfigmod"
"videoweb/pub/models/playletmod"
"go.mongodb.org/mongo-driver/bson"
)
func TestHomeConfigDisplayLimit(t *testing.T) {
tests := []struct {
name string
config *homeconfigmod.HomeConfig
want int64
}{
{name: "旧题材配置", config: &homeconfigmod.HomeConfig{Type: homeconfigmod.TypeTheme}, want: 8},
{name: "自定义题材数量", config: &homeconfigmod.HomeConfig{Type: homeconfigmod.TypeTheme, Limit: 5}, want: 5},
{name: "旧短剧配置", config: &homeconfigmod.HomeConfig{Type: homeconfigmod.TypePlaylet}, want: 12},
{name: "自定义短剧数量", config: &homeconfigmod.HomeConfig{Type: homeconfigmod.TypePlaylet, Limit: 20}, want: 20},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := homeConfigDisplayLimit(tt.config); got != tt.want {
t.Fatalf("homeConfigDisplayLimit() = %d, want %d", got, tt.want)
}
})
}
}
func TestHomeConfigSortTypeFourUsesOnlineAt(t *testing.T) {
filter, sort := homeConfigPlayletQuery(homeconfigmod.SortTypeOnlineAt, 0)
if _, ok := filter[playletmod.FN.Playlet.Preview]; ok {
t.Fatal("上架时间排序不应过滤无预告片的短剧")
}
want := bson.D{
{Key: playletmod.FN.Playlet.OnlineAt, Value: -1},
{Key: playletmod.FN.Playlet.Id, Value: -1},
}
if !reflect.DeepEqual(sort, want) {
t.Fatalf("上架时间排序 = %v, want %v", sort, want)
}
_, latestSort := homeConfigPlayletQuery(homeconfigmod.SortTypeLatest, 0)
if len(latestSort) == 0 || latestSort[0].Key != playletmod.FN.Playlet.UpdatedAt {
t.Fatalf("最近更新应按更新时间排序,得到 %v", latestSort)
}
}
+231
View File
@@ -0,0 +1,231 @@
package playletser
import (
"context"
"strings"
"videoweb/app/api/proto"
"videoweb/app/service/configser"
"videoweb/app/service/playletcache"
"videoweb/pub/common/srvdef"
"videoweb/pub/models/homeconfigmod"
"videoweb/pub/models/playletmod"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo/options"
)
func homeConfigToModule(item *proto.HomeConfigData) *proto.HomeModuleData {
moduleType := int32(1)
if item.Type == homeconfigmod.TypeTheme {
moduleType = 2
}
return &proto.HomeModuleData{Id: item.Id, Slug: item.Slug, Title: item.Name, Desc: item.Desc, Keyword: item.Keyword, Label: item.Label,
Style: item.Style, CardBackgroundImage: item.CardBackgroundImage, ModuleType: moduleType, DataType: 1,
RecommendType: item.RecommendType, CategoryId: item.CategoryId, Rank: item.Rank, Limit: item.Limit, SeoInfo: item.SeoInfo,
List: item.List, TopicList: item.ThemeList}
}
// recommendType=0 保留历史 sortType 的含义
func homeConfigPlayletQueryForConfig(config *homeconfigmod.HomeConfig) (bson.M, bson.D, int64) {
categoryID := config.CategoryId
if categoryID == 0 && config.RecommendType == 0 && config.Type == homeconfigmod.TypePlaylet {
categoryID = homeConfigCategoryID(config.SortType, config.Id)
}
if config.RecommendType == 0 {
filter, sort := homeConfigPlayletQuery(config.SortType, categoryID)
return filter, sort, categoryID
}
filter := homeUpPlayletFilter(nil)
field := playletmod.FN.Playlet.UpdatedAt
switch config.RecommendType {
case 2:
field = playletmod.FN.Playlet.CountLike
case 3:
field = playletmod.FN.Playlet.CountBrowse
case 4:
field = playletmod.FN.Playlet.HeatValue
case 5:
if categoryID > 0 {
filter[playletmod.FN.Playlet.CategoryIds] = categoryID
}
field = playletmod.FN.Playlet.RecommendRank
}
return filter, bson.D{{Key: field, Value: -1}, {Key: playletmod.FN.Playlet.Id, Value: -1}}, categoryID
}
func findHomeConfigPlayletListForConfig(ctx context.Context, config *homeconfigmod.HomeConfig, limit int64) (playletmod.PlayletList, *srvdef.SrvErr) {
if config.RecommendType == 0 {
_, _, categoryID := homeConfigPlayletQueryForConfig(config)
return findHomeConfigPlayletList(ctx, config.SortType, categoryID, limit)
}
filter, sort, categoryID := homeConfigPlayletQueryForConfig(config)
if config.RecommendType == 5 {
if categoryID <= 0 {
return make(playletmod.PlayletList, 0), nil
}
return playletmod.FindManyPlayletByCategoryRank(ctx, filter, categoryID, 0, limit, playletmod.CategoryRankSort(), playletHomeProjection)
}
return findHomePlaylets(ctx, filter, sort, limit)
}
func findHomeConfigThemePlayletsForConfig(ctx context.Context, config *homeconfigmod.HomeConfig, skip, limit int64) ([]homeConfigThemePlaylets, playletmod.PlayletList, *srvdef.SrvErr) {
if len(config.TopicIds) == 0 {
if skip == 0 {
return findHomeConfigThemePlaylets(ctx, limit)
}
groups, _, all, err := findHomeConfigThemePlayletsPage(ctx, skip, limit)
return groups, all, err
}
selected := map[int64]bool{}
for _, id := range config.TopicIds {
selected[id] = true
}
result := make([]homeConfigThemePlaylets, 0)
all := make(playletmod.PlayletList, 0)
var offset int64
for _, theme := range playletcache.ListEnabledThemesByPlayletCount(0) {
if theme == nil || !selected[theme.Id] {
continue
}
filter := topicDetailPlayletFilter(theme.Id)
count, err := playletmod.CountPlaylet(ctx, filter)
if err != nil {
return nil, nil, err
}
if count == 0 {
continue
}
if offset < skip {
offset++
continue
}
if int64(len(result)) >= limit {
break
}
list, err := findHomePlaylets(ctx, filter, playletTopicDetailSort(topicDetailSortEdit), topicListPlayletLimit)
if err != nil {
return nil, nil, err
}
result = append(result, homeConfigThemePlaylets{Theme: theme, Playlets: list, TotalCount: count})
all = append(all, list...)
offset++
}
return result, all, nil
}
func countHomeConfigThemes(ctx context.Context, config *homeconfigmod.HomeConfig) (int64, *srvdef.SrvErr) {
if len(config.TopicIds) == 0 {
_, count, _, err := findHomeConfigThemePlayletsPage(ctx, 0, 0)
return count, err
}
selected := map[int64]bool{}
for _, id := range config.TopicIds {
selected[id] = true
}
var count int64
for _, theme := range playletcache.ListEnabledThemesByPlayletCount(0) {
if theme == nil || !selected[theme.Id] {
continue
}
filter := topicDetailPlayletFilter(theme.Id)
total, err := playletmod.CountPlaylet(ctx, filter)
if err != nil {
return 0, err
}
if total > 0 {
count++
}
}
return count, nil
}
func HomeConfigList(ctx context.Context, req *proto.PlayletHomeConfigListReq) (*proto.PlayletHomeConfigListResp, *srvdef.SrvErr) {
if req == nil || (req.Id <= 0 && strings.TrimSpace(req.Slug) == "") {
return nil, srvdef.New(srvdef.ErrParamError, "首页配置ID或slug不能为空", "")
}
filter := bson.M{}
if slug := strings.TrimSpace(req.Slug); slug != "" {
filter[homeconfigmod.FN.HomeConfig.Slug] = slug
} else {
filter[homeconfigmod.FN.HomeConfig.Id] = req.Id
}
config, err := homeconfigmod.FindOneHomeConfig(ctx, filter)
if err != nil {
return nil, err
}
if config.Type != homeconfigmod.TypePlaylet && config.Type != homeconfigmod.TypeTheme {
return nil, srvdef.New(srvdef.ErrNoData, "首页配置不存在", "")
}
pageNum, pageSize := req.PageNum, req.PageSize
if pageNum <= 0 {
pageNum = 1
}
if pageSize <= 0 {
pageSize = 20
}
if pageSize > maxPageSize {
pageSize = maxPageSize
}
module := &proto.HomeModuleData{Id: config.Id, Slug: config.Slug, Title: config.Name, Desc: config.Desc, Keyword: homeconfigmod.NormalizeKeywords(config.Keyword), Label: config.Label,
Style: config.Style, CardBackgroundImage: withImageDomain(ctx, config.CardBackgroundImage), DataType: 1,
RecommendType: config.RecommendType, CategoryId: config.CategoryId, Rank: config.Rank, Limit: homeConfigDisplayLimit(config), SeoInfo: config.SeoInfo,
List: make([]*proto.CommonData, 0), TopicList: make([]*proto.HomeConfigThemeData, 0)}
if config.Type == homeconfigmod.TypeTheme {
module.ModuleType = 2
} else {
module.ModuleType = 1
}
resp := &proto.PlayletHomeConfigListResp{Config: module, PageNum: int(pageNum), PageSize: int(pageSize),
List: make([]*proto.CommonData, 0), TopicList: make([]*proto.HomeConfigThemeData, 0), Advitise: configser.GetAdvitise()}
if !config.Status {
return resp, nil
}
skip := (pageNum - 1) * pageSize
if config.Type == homeconfigmod.TypePlaylet {
playletFilter, sort, categoryID := homeConfigPlayletQueryForConfig(config)
if (config.RecommendType == 5 || (config.RecommendType == 0 && config.SortType == homeconfigmod.SortTypeCategory)) && categoryID <= 0 {
return resp, nil
}
resp.TotalCount, err = playletmod.CountPlaylet(ctx, playletFilter)
if err != nil {
return nil, err
}
var list playletmod.PlayletList
if config.RecommendType == 5 || (config.RecommendType == 0 && config.SortType == homeconfigmod.SortTypeCategory) {
list, err = playletmod.FindManyPlayletByCategoryRank(ctx, playletFilter, categoryID, skip, pageSize, playletmod.CategoryRankSort(), playletHomeProjection)
} else {
list, err = findHomePlaylets(ctx, playletFilter, sort, pageSize, options.Find().SetSkip(skip))
}
if err != nil {
return nil, err
}
themeNames, err := buildHomeThemeNameMap(ctx, list)
if err != nil {
return nil, err
}
tagNames, err := buildHomeTagNameMap(ctx, list)
if err != nil {
return nil, err
}
resp.List = playletsToHomeCommonData(ctx, list, themeNames, tagNames)
return resp, nil
}
resp.TotalCount, err = countHomeConfigThemes(ctx, config)
if err != nil {
return nil, err
}
groups, all, err := findHomeConfigThemePlayletsForConfig(ctx, config, skip, pageSize)
if err != nil {
return nil, err
}
themeNames, err := buildHomeThemeNameMap(ctx, all)
if err != nil {
return nil, err
}
tagNames, err := buildHomeTagNameMap(ctx, all)
if err != nil {
return nil, err
}
resp.TopicList = homeConfigThemesToHomeData(ctx, groups, themeNames, tagNames)
return resp, nil
}
@@ -0,0 +1,38 @@
package playletser
import (
"testing"
"videoweb/pub/models/homeconfigmod"
"videoweb/pub/models/playletmod"
)
func TestHomeConfigPlayletQueryForConfig(t *testing.T) {
tests := []struct {
name string
config homeconfigmod.HomeConfig
field string
category int64
recommended bool
}{
{name: "legacy recommendation", config: homeconfigmod.HomeConfig{SortType: homeconfigmod.SortTypeRecommend}, field: playletmod.FN.Playlet.RecommendRank, recommended: true},
{name: "new likes", config: homeconfigmod.HomeConfig{RecommendType: 2}, field: playletmod.FN.Playlet.CountLike},
{name: "new category", config: homeconfigmod.HomeConfig{RecommendType: 5, CategoryId: 42}, field: playletmod.FN.Playlet.RecommendRank, category: 42},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
filter, sort, category := homeConfigPlayletQueryForConfig(&tt.config)
if category != tt.category {
t.Fatalf("category = %d, want %d", category, tt.category)
}
if len(sort) == 0 || sort[0].Key != tt.field {
t.Fatalf("sort = %v, want %s", sort, tt.field)
}
if _, ok := filter[playletmod.FN.Playlet.IsRecommend]; ok != tt.recommended {
t.Fatalf("recommend filter = %v, want %v", ok, tt.recommended)
}
if tt.category > 0 && filter[playletmod.FN.Playlet.CategoryIds] != tt.category {
t.Fatalf("category filter = %v", filter)
}
})
}
}
+91
View File
@@ -0,0 +1,91 @@
package playletser
import (
"context"
"videoweb/app/api/proto"
"videoweb/pub/common/srvdef"
"videoweb/pub/models/playletlikemod"
"videoweb/pub/models/playletmod"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo/options"
)
// LikeList 分页查询当前用户点赞的短剧
func LikeList(ctx context.Context, userID int64, req *proto.PlayletLikeListReq) (*proto.PlayletLikeListResp, *srvdef.SrvErr) {
if userID <= 0 {
return nil, srvdef.New(srvdef.InvalidToken, "请先登录", "请先登录")
}
if req == nil {
return nil, srvdef.New(srvdef.ErrParamError, "", "")
}
pageNum, pageSize := normalizePlayletPage(req.PageNum, req.PageSize)
filter := bson.M{playletlikemod.FN.PlayletLike.UserId: userID}
totalCount, err := playletlikemod.CountPlayletLike(ctx, filter)
if err != nil {
return nil, err
}
resp := &proto.PlayletLikeListResp{
TotalCount: totalCount, PageNum: int(pageNum), PageSize: int(pageSize),
List: make([]proto.PlayletLikeData, 0),
}
skip := (pageNum - 1) * pageSize
if skip >= totalCount {
return resp, nil
}
likes, err := playletlikemod.FindManyPlayletLike(ctx, filter, options.Find().
SetProjection(bson.D{
{Key: playletlikemod.FN.PlayletLike.Id, Value: 1},
{Key: playletlikemod.FN.PlayletLike.PlayletId, Value: 1},
{Key: playletlikemod.FN.PlayletLike.CreatedAt, Value: 1},
}).
SetSort(bson.D{
{Key: playletlikemod.FN.PlayletLike.CreatedAt, Value: -1},
{Key: playletlikemod.FN.PlayletLike.Id, Value: -1},
}).
SetSkip(skip).
SetLimit(pageSize))
if err != nil {
return nil, err
}
ids := make([]int64, 0, len(likes))
for _, like := range likes {
if like != nil && like.PlayletId > 0 {
ids = append(ids, like.PlayletId)
}
}
if len(ids) == 0 {
return resp, nil
}
playlets, err := playletmod.FindManyPlaylet(ctx, bson.M{
playletmod.FN.Playlet.Id: bson.M{"$in": ids},
}, options.Find().SetProjection(playletHomeProjection))
if err != nil {
return nil, err
}
themeNameMap, err := buildHomeThemeNameMap(ctx, playlets)
if err != nil {
return nil, err
}
tagNameMap, err := buildHomeTagNameMap(ctx, playlets)
if err != nil {
return nil, err
}
dataByID := make(map[int64]*proto.CommonData, len(playlets))
for _, data := range playletsToHomeCommonData(ctx, playlets, themeNameMap, tagNameMap) {
dataByID[data.Id] = data
}
for _, like := range likes {
if like == nil {
continue
}
data := dataByID[like.PlayletId]
if data == nil {
continue
}
resp.List = append(resp.List, proto.PlayletLikeData{
CommonData: *data, IsLike: 1, LikedAt: like.CreatedAt,
})
}
return resp, nil
}
+81
View File
@@ -0,0 +1,81 @@
package playletser
import (
"context"
"time"
"videoweb/pub/common/srvdef"
"videoweb/pub/models/playletmod"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo/options"
)
const operateRandomModulus int64 = 2147483647
func operateListFilter(typ int64) bson.M {
filter := homeUpPlayletFilter(nil)
if typ == operateListOriginal {
filter[playletmod.FN.Playlet.IsRecommend] = true
}
return filter
}
func operateListSort(typ int64) bson.D {
if typ == operateListHot {
return bson.D{{Key: playletmod.FN.Playlet.HeatValue, Value: -1}, {Key: playletmod.FN.Playlet.Id, Value: -1}}
}
return playletRecentlyUpdatedSort()
}
// findRandomOperatePlaylets 按每日固定的伪随机顺序分页查询短剧
func findRandomOperatePlaylets(ctx context.Context, filter bson.M, skip, limit int64, now time.Time) (playletmod.PlayletList, *srvdef.SrvErr) {
var rows []struct {
Id int64 `bson:"_id"`
}
if err := playletmod.AggregatePlaylet(ctx, &rows, operateRandomPipeline(filter, skip, limit, now)); err != nil {
return nil, err
}
ids := make([]int64, 0, len(rows))
for _, row := range rows {
ids = append(ids, row.Id)
}
if len(ids) == 0 {
return make(playletmod.PlayletList, 0), nil
}
queryFilter := bson.M{playletmod.FN.Playlet.Id: bson.M{"$in": ids}}
for key, value := range filter {
queryFilter[key] = value
}
list, err := playletmod.FindManyPlaylet(ctx, queryFilter, options.Find().SetProjection(playletHomeProjection))
if err != nil {
return nil, err
}
byID := make(map[int64]*playletmod.Playlet, len(list))
for _, item := range list {
if item != nil {
byID[item.Id] = item
}
}
ordered := make(playletmod.PlayletList, 0, len(ids))
for _, id := range ids {
if item := byID[id]; item != nil {
ordered = append(ordered, item)
}
}
return ordered, nil
}
func operateRandomPipeline(filter bson.M, skip, limit int64, now time.Time) []bson.M {
day := (now.Unix() + 8*60*60) / (24 * 60 * 60)
factor := int64(48271) + day%997*2767
idMod := bson.M{"$mod": bson.A{"$_id", operateRandomModulus}}
score := bson.M{"$mod": bson.A{bson.M{"$multiply": bson.A{idMod, factor}}, operateRandomModulus}}
return []bson.M{
{"$match": filter},
{"$project": bson.M{"_id": 1}},
{"$addFields": bson.M{"randomScore": score}},
{"$sort": bson.D{{Key: "randomScore", Value: 1}, {Key: "_id", Value: 1}}},
{"$skip": skip},
{"$limit": limit},
}
}
@@ -0,0 +1,46 @@
package playletser
import (
"reflect"
"testing"
"time"
"videoweb/pub/models/playletmod"
"go.mongodb.org/mongo-driver/bson"
)
func TestOperateListFiltersAndSorts(t *testing.T) {
for _, typ := range []int64{operateListLatest, operateListHot, operateListOriginal, operateListRandom} {
filter := operateListFilter(typ)
if filter[playletmod.FN.Playlet.Status] != playletmod.StatusUp {
t.Fatalf("type %d must contain the up status filter", typ)
}
_, recommend := filter[playletmod.FN.Playlet.IsRecommend]
if recommend != (typ == operateListOriginal) {
t.Fatalf("type %d recommend filter = %v", typ, recommend)
}
}
if got := operateListSort(operateListLatest)[0].Key; got != playletmod.FN.Playlet.UpdatedAt {
t.Fatalf("latest sort = %s", got)
}
if got := operateListSort(operateListHot)[0].Key; got != playletmod.FN.Playlet.HeatValue {
t.Fatalf("hot sort = %s", got)
}
}
func TestOperateRandomPipelineIsStableWithinDay(t *testing.T) {
filter := bson.M{playletmod.FN.Playlet.Status: playletmod.StatusUp}
day := time.Date(2026, 10, 24, 1, 0, 0, 0, time.UTC)
first := operateRandomPipeline(filter, 20, 10, day)
second := operateRandomPipeline(filter, 20, 10, day.Add(time.Hour))
if !reflect.DeepEqual(first, second) {
t.Fatal("random order changed within one China Standard Time day")
}
third := operateRandomPipeline(filter, 20, 10, day.Add(24*time.Hour))
if reflect.DeepEqual(first, third) {
t.Fatal("random order did not change on the next China Standard Time day")
}
if first[4]["$skip"] != int64(20) || first[5]["$limit"] != int64(10) {
t.Fatalf("pagination stages = %v", first)
}
}
File diff suppressed because it is too large Load Diff
+77
View File
@@ -0,0 +1,77 @@
package playletser
import (
"context"
"strings"
"videoweb/app/service/playletcache"
"videoweb/pub/common/srvdef"
"videoweb/pub/models/playletmod"
"videoweb/pub/models/playlettagmod"
"videoweb/pub/models/playletthememod"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo/options"
)
// resolvePlayletID 解析短剧ID:slug 非空时先按 slug 查 ID,否则使用传入 ID。
func resolvePlayletID(ctx context.Context, id int64, slug string, required bool) (int64, *srvdef.SrvErr) {
slug = strings.TrimSpace(slug)
if slug != "" {
item, err := playletmod.FindOnePlaylet(ctx, bson.M{
playletmod.FN.Playlet.Slug: slug,
}, options.FindOne().SetProjection(bson.D{
{Key: playletmod.FN.Playlet.Id, Value: 1},
}))
if err != nil {
return 0, err
}
return item.Id, nil
}
if required && id <= 0 {
return 0, srvdef.New(srvdef.ErrParamError, "短剧ID不能为空", "短剧ID不能为空")
}
return id, nil
}
// resolveThemeID 解析题材ID:slug 非空时先按 slug 查 ID,否则使用传入 ID(0 表示不按题材筛选)。
func resolveThemeID(ctx context.Context, id int64, slug string) (int64, *srvdef.SrvErr) {
slug = strings.TrimSpace(slug)
if slug == "" {
return id, nil
}
if item := playletcache.GetThemeBySlug(slug); item != nil {
return item.Id, nil
}
theme, err := playletthememod.FindOnePlayletTheme(ctx, bson.M{
playletthememod.FN.PlayletTheme.Slug: slug,
}, options.FindOne().SetProjection(bson.D{
{Key: playletthememod.FN.PlayletTheme.Id, Value: 1},
}))
if err != nil {
return 0, err
}
return theme.Id, nil
}
// resolveTagID 解析标签ID:slug 非空时先按 slug 查 ID,否则使用传入 ID。
func resolveTagID(ctx context.Context, id int64, slug string) (int64, *srvdef.SrvErr) {
slug = strings.TrimSpace(slug)
if slug != "" {
if item := playletcache.GetTagBySlug(slug); item != nil {
return item.Id, nil
}
tag, err := playlettagmod.FindOnePlayletTag(ctx, bson.M{
playlettagmod.FN.PlayletTag.Slug: slug,
}, options.FindOne().SetProjection(bson.D{
{Key: playlettagmod.FN.PlayletTag.Id, Value: 1},
}))
if err != nil {
return 0, err
}
return tag.Id, nil
}
if id <= 0 {
return 0, srvdef.New(srvdef.ErrParamError, "标签ID不能为空", "标签ID不能为空")
}
return id, nil
}
+131
View File
@@ -0,0 +1,131 @@
package playletser
import (
"context"
"strings"
"videoweb/app/api/proto"
"videoweb/pub/common/srvdef"
"videoweb/pub/models/playletmod"
"videoweb/pub/models/playletthememod"
"videoweb/pub/models/playlettopicgroupmod"
"go.mongodb.org/mongo-driver/bson"
)
func enabledTopicGroups(ctx context.Context) (playlettopicgroupmod.PlayletTopicGroupList, *srvdef.SrvErr) {
return playlettopicgroupmod.FindMany(ctx, bson.M{"status": true})
}
type topicPlayletCount struct {
Id int64 `bson:"_id"`
Count int64 `bson:"count"`
}
// topicGroupPage 分页查询区块下有已上架短剧的专题
func topicGroupPage(ctx context.Context, group *playlettopicgroupmod.PlayletTopicGroup, skip, limit int64) (playletthememod.PlayletThemeList, map[int64]int64, int64, *srvdef.SrvErr) {
counts := make(map[int64]int64)
if len(group.TopicIds) == 0 {
return playletthememod.PlayletThemeList{}, counts, 0, nil
}
themes, err := playletthememod.FindManyPlayletTheme(ctx, bson.M{
playletthememod.FN.PlayletTheme.Id: bson.M{"$in": group.TopicIds},
playletthememod.FN.PlayletTheme.Status: true,
})
if err != nil {
return nil, nil, 0, err
}
byID := make(map[int64]*playletthememod.PlayletTheme, len(themes))
for _, theme := range themes {
if theme != nil {
byID[theme.Id] = theme
}
}
rows := make([]topicPlayletCount, 0)
err = playletmod.AggregatePlaylet(ctx, &rows, []bson.M{
{"$match": bson.M{playletmod.FN.Playlet.Status: playletmod.StatusUp, playletmod.FN.Playlet.ThemeIds: bson.M{"$in": group.TopicIds}}},
{"$unwind": "$" + playletmod.FN.Playlet.ThemeIds},
{"$match": bson.M{playletmod.FN.Playlet.ThemeIds: bson.M{"$in": group.TopicIds}}},
{"$group": bson.M{"_id": "$" + playletmod.FN.Playlet.ThemeIds, "count": bson.M{"$sum": 1}}},
})
if err != nil {
return nil, nil, 0, err
}
for _, row := range rows {
counts[row.Id] = row.Count
}
available := make(playletthememod.PlayletThemeList, 0, len(themes))
seen := make(map[int64]bool, len(group.TopicIds))
for _, id := range group.TopicIds {
if seen[id] {
continue
}
seen[id] = true
if theme := byID[id]; theme != nil && counts[id] > 0 {
available = append(available, theme)
}
}
total := int64(len(available))
if skip >= total {
return playletthememod.PlayletThemeList{}, counts, total, nil
}
end := skip + limit
if end > total {
end = total
}
return available[skip:end], counts, total, nil
}
// topicGroupPageData 组装区块当前页专题及单部短剧预览
func topicGroupPageData(ctx context.Context, group *playlettopicgroupmod.PlayletTopicGroup, skip, limit int64) (*proto.PlayletTopicGroupPageItem, *srvdef.SrvErr) {
themes, counts, totalCount, err := topicGroupPage(ctx, group, skip, limit)
if err != nil {
return nil, err
}
page := &proto.PlayletTopicGroupPageItem{
Id: group.Id, Name: group.Name, Desc: group.Desc, Rank: group.Rank,
TotalCount: totalCount, List: make([]*proto.PlayletTopicPageItem, 0, len(themes)),
}
if len(themes) == 0 {
return page, nil
}
previewByTheme := make(map[int64]playletmod.PlayletList, len(themes))
previews := make(playletmod.PlayletList, 0, len(themes))
for _, theme := range themes {
filter, sort := playletTopicPagePlayletQuery(0, theme.Id)
list, err := findHomePlaylets(ctx, filter, sort, 1)
if err != nil {
return nil, err
}
previewByTheme[theme.Id] = list
previews = append(previews, list...)
}
themeNameMap, err := buildHomeThemeNameMap(ctx, previews)
if err != nil {
return nil, err
}
tagNameMap, err := buildHomeTagNameMap(ctx, previews)
if err != nil {
return nil, err
}
for _, theme := range themes {
preview := playletsToHomeCommonData(ctx, previewByTheme[theme.Id], themeNameMap, tagNameMap)
covers := make([]string, 0, len(preview))
if len(preview) > 0 {
cover := preview[0].HorizontalCover
if cover == "" {
cover = preview[0].Cover
}
if cover != "" {
covers = append(covers, cover)
}
}
page.List = append(page.List, &proto.PlayletTopicPageItem{
Id: theme.Id, Name: theme.Name, Slug: theme.Slug,
Desc: strings.TrimSpace(theme.SeoInfo.Description),
Icon: withImageDomain(ctx, theme.Icon), Rank: theme.Rank,
PlayletCount: counts[theme.Id], Covers: covers, List: preview,
SeoInfo: buildPageSeoInfoFromValue(theme.SeoInfo, theme.Name, "", buildPlayletThemeListCanonicalURL(theme.Slug, theme.Id)),
})
}
return page, nil
}