Initial commit: web_hghl_server

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-10-26 16:28:14 +08:00
co-authored by Claude Opus 4.7
commit 65fca60622
653 changed files with 135901 additions and 0 deletions
+219
View File
@@ -0,0 +1,219 @@
package srv_playlet
import (
"context"
"regexp"
"strings"
"time"
"videoweb/pub/common/response"
"videoweb/pub/common/slugx"
"videoweb/pub/common/srvdef"
"videoweb/pub/common/utils"
"videoweb/pub/models/playletcategorymod"
"videoweb/pub/models/playletmod"
"videoweb/web/api/proto"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo/options"
)
var playletCategoryWebProjection = bson.D{
{Key: playletcategorymod.FN.PlayletCategory.Id, Value: 1},
{Key: playletcategorymod.FN.PlayletCategory.Name, Value: 1},
{Key: playletcategorymod.FN.PlayletCategory.Slug, Value: 1},
{Key: playletcategorymod.FN.PlayletCategory.Icon, Value: 1},
{Key: playletcategorymod.FN.PlayletCategory.Status, Value: 1},
{Key: playletcategorymod.FN.PlayletCategory.Rank, Value: 1},
{Key: playletcategorymod.FN.PlayletCategory.Remark, Value: 1},
{Key: playletcategorymod.FN.PlayletCategory.SeoInfo.Title, Value: 1},
{Key: playletcategorymod.FN.PlayletCategory.SeoInfo.Description, Value: 1},
{Key: playletcategorymod.FN.PlayletCategory.SeoInfo.Keyworld, Value: 1},
{Key: playletcategorymod.FN.PlayletCategory.CreatedAt, Value: 1},
{Key: playletcategorymod.FN.PlayletCategory.UpdatedAt, Value: 1},
}
// ListPlayletCategory 分页查询短剧分类列表
func ListPlayletCategory(ctx context.Context, req *proto.PlayletCategoryListReq) (*proto.PlayletCategoryListPageResp, *srvdef.SrvErr) {
if req == nil {
return nil, srvdef.New(srvdef.ErrParamError, "", "")
}
filter := bson.M{}
if req.Id != nil && *req.Id > 0 {
filter[playletcategorymod.FN.PlayletCategory.Id] = *req.Id
}
if req.Status != nil {
filter[playletcategorymod.FN.PlayletCategory.Status] = *req.Status
}
if name := strings.TrimSpace(req.Name); name != "" {
filter[playletcategorymod.FN.PlayletCategory.Name] = bson.M{"$regex": regexp.QuoteMeta(name)}
}
pageNum, pageSize := normalizePage(req.PageNum, req.PageSize)
totalCount, err := playletcategorymod.CountPlayletCategory(ctx, filter)
if err != nil {
return nil, err
}
resp := &proto.PlayletCategoryListPageResp{
BasePageResp: response.BasePageResp{
TotalCount: totalCount,
PageSize: int(pageSize),
PageNum: int(pageNum),
List: make([]*proto.PlayletCategoryListResp, 0),
},
}
if totalCount == 0 {
return resp, nil
}
list, err := playletcategorymod.FindManyPlayletCategory(ctx, filter, options.Find().
SetProjection(playletCategoryWebProjection).
SetSort(bson.D{
{Key: playletcategorymod.FN.PlayletCategory.Rank, Value: -1},
{Key: playletcategorymod.FN.PlayletCategory.Id, Value: -1},
}).
SetSkip((pageNum-1)*pageSize).
SetLimit(pageSize))
if err != nil {
return nil, err
}
items := make([]*proto.PlayletCategoryListResp, 0, len(list))
for _, item := range list {
if item == nil {
continue
}
items = append(items, &proto.PlayletCategoryListResp{
Id: item.Id,
Name: item.Name,
Slug: item.Slug,
Icon: item.Icon,
Status: item.Status,
Rank: item.Rank,
Remark: item.Remark,
SeoInfo: normalizeSeoValue(item.SeoInfo),
CreatedAt: item.CreatedAt,
UpdatedAt: item.UpdatedAt,
})
}
resp.List = items
return resp, nil
}
// AddPlayletCategory 新增短剧分类
func AddPlayletCategory(ctx context.Context, req *proto.PlayletCategoryAddReq) (*proto.PlayletCategoryAddResp, *srvdef.SrvErr) {
if req == nil || strings.TrimSpace(req.Name) == "" {
return nil, srvdef.New(srvdef.ErrParamError, "分类名称不能为空", "")
}
doc := &playletcategorymod.PlayletCategory{
Name: strings.TrimSpace(req.Name),
Slug: req.Slug,
Icon: strings.TrimSpace(req.Icon),
Status: req.Status,
Rank: req.Rank,
Remark: strings.TrimSpace(req.Remark),
SeoInfo: normalizeSeoValue(req.SeoInfo),
}
exists := func(s string) bool {
item, _ := playletcategorymod.FindOnePlayletCategory(ctx, slugx.Filter(
playletcategorymod.FN.PlayletCategory.Slug, playletcategorymod.FN.PlayletCategory.Id, s, 0))
return item != nil
}
if serr := slugx.Ensure(&doc.Slug, utils.SlugSourceFromKeywordsOrTitle(doc.SeoInfo.Keyworld, doc.Name), exists); serr != nil {
return nil, serr
}
if err := playletcategorymod.InsertOnePlayletCategory(ctx, doc); err != nil {
return nil, err
}
clearAppPlayletHomeCache()
return &proto.PlayletCategoryAddResp{Id: doc.Id}, nil
}
// UpdatePlayletCategory 更新短剧分类
func UpdatePlayletCategory(ctx context.Context, req *proto.PlayletCategoryUpdateReq) *srvdef.SrvErr {
if req == nil || req.Id <= 0 {
return srvdef.New(srvdef.ErrParamError, "分类ID无效", "")
}
current, err := playletcategorymod.FindOnePlayletCategory(ctx, bson.M{
playletcategorymod.FN.PlayletCategory.Id: req.Id,
})
if err != nil {
return err
}
setDoc := bson.M{playletcategorymod.FN.PlayletCategory.UpdatedAt: time.Now()}
if req.Name != nil {
name := strings.TrimSpace(*req.Name)
if name == "" {
return srvdef.New(srvdef.ErrParamError, "分类名称不能为空", "")
}
setDoc[playletcategorymod.FN.PlayletCategory.Name] = name
}
if req.Icon != nil {
setDoc[playletcategorymod.FN.PlayletCategory.Icon] = strings.TrimSpace(*req.Icon)
}
if req.Status != nil {
setDoc[playletcategorymod.FN.PlayletCategory.Status] = *req.Status
}
if req.Rank != nil {
setDoc[playletcategorymod.FN.PlayletCategory.Rank] = *req.Rank
}
if req.Remark != nil {
setDoc[playletcategorymod.FN.PlayletCategory.Remark] = strings.TrimSpace(*req.Remark)
}
if req.SeoInfo != nil {
setDoc["seoInfo"] = normalizeSeoValue(*req.SeoInfo)
}
name := current.Name
if req.Name != nil {
name = strings.TrimSpace(*req.Name)
}
keywords := current.SeoInfo.Keyworld
if req.SeoInfo != nil {
keywords = normalizeSeoValue(*req.SeoInfo).Keyworld
}
exists := func(s string) bool {
item, _ := playletcategorymod.FindOnePlayletCategory(ctx, slugx.Filter(
playletcategorymod.FN.PlayletCategory.Slug, playletcategorymod.FN.PlayletCategory.Id, s, req.Id))
return item != nil
}
slug, changed, serr := slugx.PrepareOnUpdate(current.Slug, req.Slug,
utils.SlugSourceFromKeywordsOrTitle(keywords, name), exists)
if serr != nil {
return serr
}
if changed {
setDoc[playletcategorymod.FN.PlayletCategory.Slug] = slug
}
if len(setDoc) <= 1 {
return srvdef.New(srvdef.ErrParamError, "没有可更新字段", "")
}
if err := playletcategorymod.UpdateOnePlayletCategory(ctx,
bson.M{playletcategorymod.FN.PlayletCategory.Id: req.Id},
bson.M{"$set": setDoc},
); err != nil {
return err
}
clearAppPlayletHomeCache()
return nil
}
// DelPlayletCategory 删除短剧分类,被短剧引用时禁止删除
func DelPlayletCategory(ctx context.Context, id int64) *srvdef.SrvErr {
if id <= 0 {
return srvdef.New(srvdef.ErrParamError, "分类ID无效", "")
}
usedCount, err := playletmod.CountPlaylet(ctx, bson.M{
playletmod.FN.Playlet.CategoryIds: bson.M{"$in": []int64{id}},
})
if err != nil {
return err
}
if usedCount > 0 {
return srvdef.New(srvdef.ErrParamError, "分类已被短剧引用,无法删除", "")
}
count, err := playletcategorymod.DelOnePlayletCategory(ctx, bson.M{playletcategorymod.FN.PlayletCategory.Id: id})
if err != nil {
return err
}
if count == 0 {
return srvdef.New(srvdef.ErrNoData, "", "")
}
clearAppPlayletHomeCache()
return nil
}
+49
View File
@@ -0,0 +1,49 @@
package srv_playlet
import (
"strings"
"videoweb/pub/common/playletcache"
"videoweb/pub/pubdef"
)
const (
defaultPageSize int64 = 10
maxPageSize int64 = 100
)
func normalizePage(pageNum, pageSize int64) (int64, int64) {
if pageNum <= 0 {
pageNum = 1
}
if pageSize <= 0 {
pageSize = defaultPageSize
}
if pageSize > maxPageSize {
pageSize = maxPageSize
}
return pageNum, pageSize
}
// normalizeSeoValue 规范化 SEO 字段,避免 keywords 为 null
func normalizeSeoValue(seo pubdef.SeoValue) pubdef.SeoValue {
seo.Title = strings.TrimSpace(seo.Title)
seo.Description = strings.TrimSpace(seo.Description)
if seo.Keyworld == nil {
seo.Keyworld = make([]string, 0)
return seo
}
words := make([]string, 0, len(seo.Keyworld))
for _, word := range seo.Keyworld {
word = strings.TrimSpace(word)
if word == "" {
continue
}
words = append(words, word)
}
seo.Keyworld = words
return seo
}
func clearAppPlayletHomeCache() {
playletcache.ClearHomeCache()
}
+651
View File
@@ -0,0 +1,651 @@
package srv_playlet
import (
"context"
"regexp"
"strings"
"time"
"videoweb/pub/common/response"
"videoweb/pub/common/slugx"
"videoweb/pub/common/srvdef"
"videoweb/pub/common/utils"
"videoweb/pub/models/playlethomeconfigmod"
"videoweb/pub/models/playletthememod"
"videoweb/pub/models/videocategorymod"
"videoweb/web/api/proto"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo/options"
)
// ListPlayletHomeConfig 分页查询首页配置列表
func ListPlayletHomeConfig(ctx context.Context, req *proto.PlayletHomeConfigListReq) (*proto.PlayletHomeConfigListPageResp, *srvdef.SrvErr) {
if req == nil {
return nil, srvdef.New(srvdef.ErrParamError, "", "")
}
filter, err := buildPlayletHomeConfigFilter(req)
if err != nil {
return nil, err
}
pageNum, pageSize := normalizePage(req.PageNum, req.PageSize)
totalCount, err := playlethomeconfigmod.CountPlayletHomeConfig(ctx, filter)
if err != nil {
return nil, err
}
resp := &proto.PlayletHomeConfigListPageResp{
BasePageResp: response.BasePageResp{
TotalCount: totalCount,
PageSize: int(pageSize),
PageNum: int(pageNum),
List: make([]*proto.PlayletHomeConfigListResp, 0),
},
}
if totalCount == 0 {
return resp, nil
}
list, err := playlethomeconfigmod.FindManyPlayletHomeConfig(ctx, filter, options.Find().
SetSort(bson.D{
{Key: playlethomeconfigmod.FN.PlayletHomeConfig.Rank, Value: -1},
{Key: playlethomeconfigmod.FN.PlayletHomeConfig.Id, Value: -1},
}).
SetSkip((pageNum-1)*pageSize).
SetLimit(pageSize))
if err != nil {
return nil, err
}
items := make([]*proto.PlayletHomeConfigListResp, 0, len(list))
for _, item := range list {
if item == nil {
continue
}
items = append(items, playletHomeConfigToResp(item))
}
resp.List = items
return resp, nil
}
// ListPlayletHomeTopicOptions 分页查询未被其他启用模块占用的题材或视频分类
func ListPlayletHomeTopicOptions(ctx context.Context, req *proto.PlayletHomeTopicOptionsReq) (*proto.PlayletHomeTopicOptionsResp, *srvdef.SrvErr) {
if req == nil || (req.DataType != playlethomeconfigmod.DataTypeAll && req.DataType != playlethomeconfigmod.DataTypeVideo) {
return nil, srvdef.New(srvdef.ErrParamError, "数据类型仅支持1短剧题材、2视频分类", "")
}
if req.ConfigId < 0 {
return nil, srvdef.New(srvdef.ErrParamError, "配置ID无效", "")
}
pageNum, pageSize := normalizePage(req.PageNum, req.PageSize)
resp := &proto.PlayletHomeTopicOptionsResp{
List: make([]*proto.PlayletHomeTopicOption, 0),
SelectedList: make([]*proto.PlayletHomeTopicOption, 0),
PageNum: pageNum,
PageSize: pageSize,
}
var selectedIDs []int64
if req.ConfigId > 0 {
current, err := playlethomeconfigmod.FindOnePlayletHomeConfig(ctx, bson.M{playlethomeconfigmod.FN.PlayletHomeConfig.Id: req.ConfigId})
if err != nil {
return nil, err
}
if current.ModuleType != playlethomeconfigmod.ModuleTypeTopic || current.DataType != req.DataType {
return nil, srvdef.New(srvdef.ErrParamError, "当前配置不是指定数据类型的专题模块", "")
}
selectedIDs = current.TopicIds
}
// 已启用的其他专题模块占用的ID不再出现在可选列表中
configFilter := bson.M{
playlethomeconfigmod.FN.PlayletHomeConfig.Enabled: true,
playlethomeconfigmod.FN.PlayletHomeConfig.ModuleType: playlethomeconfigmod.ModuleTypeTopic,
playlethomeconfigmod.FN.PlayletHomeConfig.DataType: req.DataType,
}
if req.ConfigId > 0 {
configFilter[playlethomeconfigmod.FN.PlayletHomeConfig.Id] = bson.M{"$ne": req.ConfigId}
}
configs, err := playlethomeconfigmod.FindManyPlayletHomeConfig(ctx, configFilter,
options.Find().SetProjection(bson.M{playlethomeconfigmod.FN.PlayletHomeConfig.TopicIds: 1}))
if err != nil {
return nil, err
}
occupied := make(map[int64]struct{})
for _, config := range configs {
if config == nil {
continue
}
for _, id := range config.TopicIds {
occupied[id] = struct{}{}
}
}
skip := (pageNum - 1) * pageSize
if req.DataType == playlethomeconfigmod.DataTypeVideo {
filter := bson.M{videocategorymod.FN.VideoCategory.Status: true}
if len(occupied) > 0 {
ids := make([]int64, 0, len(occupied))
for id := range occupied {
ids = append(ids, id)
}
filter[videocategorymod.FN.VideoCategory.Id] = bson.M{"$nin": ids}
}
resp.TotalCount, err = videocategorymod.CountVideoCategorys(ctx, filter)
if err != nil {
return nil, err
}
list, err := videocategorymod.FindManyVideoCategorys(ctx, filter, options.Find().
SetProjection(bson.M{videocategorymod.FN.VideoCategory.Id: 1, videocategorymod.FN.VideoCategory.Name: 1, videocategorymod.FN.VideoCategory.Status: 1}).
SetSort(bson.D{{Key: videocategorymod.FN.VideoCategory.Rank, Value: -1}, {Key: videocategorymod.FN.VideoCategory.Id, Value: -1}}).
SetSkip(skip).SetLimit(pageSize))
if err != nil {
return nil, err
}
for _, item := range list {
if item != nil {
resp.List = append(resp.List, &proto.PlayletHomeTopicOption{Id: item.Id, Name: item.Name, Status: item.Status})
}
}
if len(selectedIDs) > 0 {
selected, err := videocategorymod.FindManyVideoCategorys(ctx, bson.M{videocategorymod.FN.VideoCategory.Id: bson.M{"$in": selectedIDs}},
options.Find().SetProjection(bson.M{videocategorymod.FN.VideoCategory.Id: 1, videocategorymod.FN.VideoCategory.Name: 1, videocategorymod.FN.VideoCategory.Status: 1}))
if err != nil {
return nil, err
}
byID := make(map[int64]*proto.PlayletHomeTopicOption, len(selected))
for _, item := range selected {
if item != nil {
byID[item.Id] = &proto.PlayletHomeTopicOption{Id: item.Id, Name: item.Name, Status: item.Status}
}
}
for _, id := range selectedIDs {
if item := byID[id]; item != nil {
resp.SelectedList = append(resp.SelectedList, item)
} else {
resp.SelectedList = append(resp.SelectedList, &proto.PlayletHomeTopicOption{Id: id})
}
}
}
return resp, nil
}
filter := bson.M{playletthememod.FN.PlayletTheme.Status: true}
if len(occupied) > 0 {
ids := make([]int64, 0, len(occupied))
for id := range occupied {
ids = append(ids, id)
}
filter[playletthememod.FN.PlayletTheme.Id] = bson.M{"$nin": ids}
}
resp.TotalCount, err = playletthememod.CountPlayletTheme(ctx, filter)
if err != nil {
return nil, err
}
list, err := playletthememod.FindManyPlayletTheme(ctx, filter, options.Find().
SetProjection(bson.M{playletthememod.FN.PlayletTheme.Id: 1, playletthememod.FN.PlayletTheme.Name: 1, playletthememod.FN.PlayletTheme.Status: 1}).
SetSort(bson.D{{Key: playletthememod.FN.PlayletTheme.Rank, Value: -1}, {Key: playletthememod.FN.PlayletTheme.Id, Value: -1}}).
SetSkip(skip).SetLimit(pageSize))
if err != nil {
return nil, err
}
for _, item := range list {
if item != nil {
resp.List = append(resp.List, &proto.PlayletHomeTopicOption{Id: item.Id, Name: item.Name, Status: item.Status})
}
}
if len(selectedIDs) > 0 {
selected, err := playletthememod.FindManyPlayletTheme(ctx, bson.M{playletthememod.FN.PlayletTheme.Id: bson.M{"$in": selectedIDs}},
options.Find().SetProjection(bson.M{playletthememod.FN.PlayletTheme.Id: 1, playletthememod.FN.PlayletTheme.Name: 1, playletthememod.FN.PlayletTheme.Status: 1}))
if err != nil {
return nil, err
}
byID := make(map[int64]*proto.PlayletHomeTopicOption, len(selected))
for _, item := range selected {
if item != nil {
byID[item.Id] = &proto.PlayletHomeTopicOption{Id: item.Id, Name: item.Name, Status: item.Status}
}
}
for _, id := range selectedIDs {
if item := byID[id]; item != nil {
resp.SelectedList = append(resp.SelectedList, item)
} else {
resp.SelectedList = append(resp.SelectedList, &proto.PlayletHomeTopicOption{Id: id})
}
}
}
return resp, nil
}
// AddPlayletHomeConfig 新增首页配置
func AddPlayletHomeConfig(ctx context.Context, req *proto.PlayletHomeConfigAddReq) (*proto.PlayletHomeConfigAddResp, *srvdef.SrvErr) {
if req == nil {
return nil, srvdef.New(srvdef.ErrParamError, "", "")
}
title, style, moduleType, dataType, recommendType, limit, err := normalizePlayletHomeConfig(req.Title, req.Style, req.ModuleType, req.DataType, req.RecommendType, req.Limit)
if err != nil {
return nil, err
}
doc := &playlethomeconfigmod.PlayletHomeConfig{
Title: title,
Slug: req.Slug,
Desc: strings.TrimSpace(req.Desc),
Label: strings.TrimSpace(req.Label),
Style: style,
CardBackgroundImage: strings.TrimSpace(req.CardBackgroundImage),
ModuleType: moduleType,
DataType: dataType,
RecommendType: recommendType,
CategoryId: req.CategoryId,
TopicIds: req.TopicIds,
Rank: req.Rank,
Enabled: req.Enabled,
Limit: limit,
}
if err := validateHomeConfigTopics(ctx, doc); err != nil {
return nil, err
}
exists := func(s string) bool {
item, _ := playlethomeconfigmod.FindOnePlayletHomeConfig(ctx, slugx.Filter(
playlethomeconfigmod.FN.PlayletHomeConfig.Slug, playlethomeconfigmod.FN.PlayletHomeConfig.Id, s, 0))
return item != nil
}
if serr := slugx.Ensure(&doc.Slug, utils.SlugSourceFromKeywordsOrTitle(nil, title), exists); serr != nil {
return nil, serr
}
if err := playlethomeconfigmod.InsertOnePlayletHomeConfig(ctx, doc); err != nil {
return nil, err
}
clearAppPlayletHomeCache()
return &proto.PlayletHomeConfigAddResp{Id: doc.Id}, nil
}
// UpdatePlayletHomeConfig 更新首页配置
func UpdatePlayletHomeConfig(ctx context.Context, req *proto.PlayletHomeConfigUpdateReq) *srvdef.SrvErr {
if req == nil || req.Id <= 0 {
return srvdef.New(srvdef.ErrParamError, "id无效", "")
}
setDoc := bson.M{playlethomeconfigmod.FN.PlayletHomeConfig.UpdatedAt: time.Now()}
current, err := playlethomeconfigmod.FindOnePlayletHomeConfig(ctx, bson.M{playlethomeconfigmod.FN.PlayletHomeConfig.Id: req.Id})
if err != nil {
return err
}
if req.Title != nil {
title := strings.TrimSpace(*req.Title)
if title == "" {
return srvdef.New(srvdef.ErrParamError, "模块标题不能为空", "")
}
setDoc[playlethomeconfigmod.FN.PlayletHomeConfig.Title] = title
}
if req.Desc != nil {
setDoc[playlethomeconfigmod.FN.PlayletHomeConfig.Desc] = strings.TrimSpace(*req.Desc)
}
if req.Label != nil {
setDoc[playlethomeconfigmod.FN.PlayletHomeConfig.Label] = strings.TrimSpace(*req.Label)
}
if req.Style != nil {
style, err := normalizePlayletHomeConfigStyle(*req.Style)
if err != nil {
return err
}
setDoc[playlethomeconfigmod.FN.PlayletHomeConfig.Style] = style
}
if req.CardBackgroundImage != nil {
setDoc[playlethomeconfigmod.FN.PlayletHomeConfig.CardBackgroundImage] = strings.TrimSpace(*req.CardBackgroundImage)
}
if req.ModuleType != nil {
moduleType, err := normalizePlayletHomeModuleType(*req.ModuleType)
if err != nil {
return err
}
setDoc[playlethomeconfigmod.FN.PlayletHomeConfig.ModuleType] = moduleType
}
if req.DataType != nil {
dataType, err := normalizePlayletHomeDataType(*req.DataType)
if err != nil {
return err
}
setDoc[playlethomeconfigmod.FN.PlayletHomeConfig.DataType] = dataType
}
if req.RecommendType != nil {
if err := validatePlayletHomeRecommendType(*req.RecommendType); err != nil {
return err
}
setDoc[playlethomeconfigmod.FN.PlayletHomeConfig.RecommendType] = *req.RecommendType
}
if req.CategoryId != nil {
setDoc[playlethomeconfigmod.FN.PlayletHomeConfig.CategoryId] = *req.CategoryId
}
if req.TopicIds != nil {
current.TopicIds = *req.TopicIds
}
if req.ModuleType != nil {
current.ModuleType = *req.ModuleType
}
if req.DataType != nil {
current.DataType = *req.DataType
}
if req.Enabled != nil {
current.Enabled = *req.Enabled
}
if req.TopicIds != nil || req.ModuleType != nil || req.DataType != nil || req.Enabled != nil {
if err := validateHomeConfigTopics(ctx, current); err != nil {
return err
}
if req.TopicIds != nil {
setDoc[playlethomeconfigmod.FN.PlayletHomeConfig.TopicIds] = current.TopicIds
}
}
if req.Rank != nil {
setDoc[playlethomeconfigmod.FN.PlayletHomeConfig.Rank] = *req.Rank
}
if req.Enabled != nil {
setDoc[playlethomeconfigmod.FN.PlayletHomeConfig.Enabled] = *req.Enabled
}
if req.Limit != nil {
setDoc[playlethomeconfigmod.FN.PlayletHomeConfig.Limit] = normalizePlayletHomeConfigLimit(*req.Limit)
}
title := current.Title
if req.Title != nil {
title = strings.TrimSpace(*req.Title)
}
exists := func(s string) bool {
item, _ := playlethomeconfigmod.FindOnePlayletHomeConfig(ctx, slugx.Filter(
playlethomeconfigmod.FN.PlayletHomeConfig.Slug, playlethomeconfigmod.FN.PlayletHomeConfig.Id, s, req.Id))
return item != nil
}
slug, changed, serr := slugx.PrepareOnUpdate(current.Slug, req.Slug,
utils.SlugSourceFromKeywordsOrTitle(nil, title), exists)
if serr != nil {
return serr
}
if changed {
setDoc[playlethomeconfigmod.FN.PlayletHomeConfig.Slug] = slug
}
if len(setDoc) <= 1 {
return srvdef.New(srvdef.ErrParamError, "没有可更新字段", "")
}
if err := playlethomeconfigmod.UpdateOnePlayletHomeConfig(ctx,
bson.M{playlethomeconfigmod.FN.PlayletHomeConfig.Id: req.Id},
bson.M{"$set": setDoc},
); err != nil {
return err
}
clearAppPlayletHomeCache()
return nil
}
// DelPlayletHomeConfig 删除首页配置
func DelPlayletHomeConfig(ctx context.Context, req *proto.PlayletHomeConfigDelReq) *srvdef.SrvErr {
if req == nil || len(req.Ids) == 0 {
return srvdef.New(srvdef.ErrParamError, "配置ID不能为空", "")
}
ids := make([]int64, 0, len(req.Ids))
seen := make(map[int64]struct{}, len(req.Ids))
for _, id := range req.Ids {
if id <= 0 {
return srvdef.New(srvdef.ErrParamError, "配置ID无效", "")
}
if _, ok := seen[id]; ok {
continue
}
seen[id] = struct{}{}
ids = append(ids, id)
}
count, err := playlethomeconfigmod.DelManyPlayletHomeConfig(ctx, bson.M{
playlethomeconfigmod.FN.PlayletHomeConfig.Id: bson.M{"$in": ids},
})
if err != nil {
return err
}
if count == 0 {
return srvdef.New(srvdef.ErrNoData, "", "")
}
clearAppPlayletHomeCache()
return nil
}
func buildPlayletHomeConfigFilter(req *proto.PlayletHomeConfigListReq) (bson.M, *srvdef.SrvErr) {
filter := bson.M{}
if req.Id != nil && *req.Id > 0 {
filter[playlethomeconfigmod.FN.PlayletHomeConfig.Id] = *req.Id
}
if slug := strings.TrimSpace(req.Slug); slug != "" {
filter[playlethomeconfigmod.FN.PlayletHomeConfig.Slug] = slug
}
if title := strings.TrimSpace(req.Title); title != "" {
filter[playlethomeconfigmod.FN.PlayletHomeConfig.Title] = bson.M{"$regex": regexp.QuoteMeta(title)}
}
if desc := strings.TrimSpace(req.Desc); desc != "" {
filter[playlethomeconfigmod.FN.PlayletHomeConfig.Desc] = bson.M{"$regex": regexp.QuoteMeta(desc)}
}
if label := strings.TrimSpace(req.Label); label != "" {
filter[playlethomeconfigmod.FN.PlayletHomeConfig.Label] = bson.M{"$regex": regexp.QuoteMeta(label)}
}
if style := strings.TrimSpace(req.Style); style != "" {
normalizedStyle, err := normalizePlayletHomeConfigStyle(style)
if err != nil {
return nil, err
}
filter[playlethomeconfigmod.FN.PlayletHomeConfig.Style] = normalizedStyle
}
if req.ModuleType != nil {
moduleType, err := normalizePlayletHomeModuleType(*req.ModuleType)
if err != nil {
return nil, err
}
if moduleType == playlethomeconfigmod.ModuleTypePlaylet {
filter[playlethomeconfigmod.FN.PlayletHomeConfig.ModuleType] = bson.M{"$in": []interface{}{nil, int64(0), playlethomeconfigmod.ModuleTypePlaylet}}
} else {
filter[playlethomeconfigmod.FN.PlayletHomeConfig.ModuleType] = moduleType
}
}
if req.DataType != nil {
dataType, err := normalizePlayletHomeDataType(*req.DataType)
if err != nil {
return nil, err
}
if dataType == playlethomeconfigmod.DataTypeAll {
filter[playlethomeconfigmod.FN.PlayletHomeConfig.DataType] = bson.M{"$in": []interface{}{nil, int64(0), playlethomeconfigmod.DataTypeAll}}
} else {
filter[playlethomeconfigmod.FN.PlayletHomeConfig.DataType] = dataType
}
}
if req.RecommendType != nil {
if err := validatePlayletHomeRecommendType(*req.RecommendType); err != nil {
return nil, err
}
filter[playlethomeconfigmod.FN.PlayletHomeConfig.RecommendType] = *req.RecommendType
}
if req.CategoryId != nil && *req.CategoryId > 0 {
filter[playlethomeconfigmod.FN.PlayletHomeConfig.CategoryId] = *req.CategoryId
}
if req.Enabled != nil {
filter[playlethomeconfigmod.FN.PlayletHomeConfig.Enabled] = *req.Enabled
}
return filter, nil
}
func playletHomeConfigToResp(item *playlethomeconfigmod.PlayletHomeConfig) *proto.PlayletHomeConfigListResp {
if item == nil {
return nil
}
return &proto.PlayletHomeConfigListResp{
Id: item.Id,
Slug: item.Slug,
Title: item.Title,
Desc: item.Desc,
Label: item.Label,
Style: item.Style,
CardBackgroundImage: item.CardBackgroundImage,
ModuleType: normalizePlayletHomeModuleTypeForResp(item.ModuleType),
DataType: normalizePlayletHomeDataTypeForResp(item.DataType),
RecommendType: item.RecommendType,
CategoryId: item.CategoryId,
TopicIds: item.TopicIds,
Rank: item.Rank,
Enabled: item.Enabled,
Limit: item.Limit,
CreatedAt: item.CreatedAt,
UpdatedAt: item.UpdatedAt,
}
}
// validateHomeConfigTopics 校验专题归属和已启用模块间的专题唯一性
func validateHomeConfigTopics(ctx context.Context, doc *playlethomeconfigmod.PlayletHomeConfig) *srvdef.SrvErr {
if doc.ModuleType != playlethomeconfigmod.ModuleTypeTopic {
if len(doc.TopicIds) > 0 {
return srvdef.New(srvdef.ErrParamError, "列表模块不能配置专题ID", "")
}
return nil
}
seen := make(map[int64]struct{}, len(doc.TopicIds))
ids := make([]int64, 0, len(doc.TopicIds))
for _, id := range doc.TopicIds {
if id <= 0 {
return srvdef.New(srvdef.ErrParamError, "专题ID必须大于0", "")
}
if _, ok := seen[id]; ok {
continue
}
seen[id] = struct{}{}
ids = append(ids, id)
}
doc.TopicIds = ids
if !doc.Enabled {
return nil
}
if len(ids) == 0 {
return srvdef.New(srvdef.ErrParamError, "启用的专题模块必须配置专题ID", "")
}
if doc.DataType == playlethomeconfigmod.DataTypeVideo {
categories, err := videocategorymod.FindManyVideoCategorys(ctx, bson.M{
videocategorymod.FN.VideoCategory.Id: bson.M{"$in": ids},
videocategorymod.FN.VideoCategory.Status: true,
})
if err != nil {
return err
}
if len(categories) != len(ids) {
return srvdef.New(srvdef.ErrParamError, "视频分类不存在或未上架", "")
}
} else {
themes, err := playletthememod.FindManyPlayletTheme(ctx, bson.M{
playletthememod.FN.PlayletTheme.Id: bson.M{"$in": ids},
playletthememod.FN.PlayletTheme.Status: true,
})
if err != nil {
return err
}
if len(themes) != len(ids) {
return srvdef.New(srvdef.ErrParamError, "短剧题材不存在或未上架", "")
}
}
configs, err := playlethomeconfigmod.FindManyPlayletHomeConfig(ctx, bson.M{
playlethomeconfigmod.FN.PlayletHomeConfig.Enabled: true,
playlethomeconfigmod.FN.PlayletHomeConfig.ModuleType: playlethomeconfigmod.ModuleTypeTopic,
playlethomeconfigmod.FN.PlayletHomeConfig.DataType: doc.DataType,
playlethomeconfigmod.FN.PlayletHomeConfig.TopicIds: bson.M{"$in": ids},
})
if err != nil {
return err
}
for _, other := range configs {
if other != nil && other.Id != doc.Id {
return srvdef.New(srvdef.ErrParamError, "专题已分配给其他启用的首页模块", "")
}
}
return nil
}
func normalizePlayletHomeConfig(title string, style string, moduleType int64, dataType int64, recommendType int64, limit int64) (string, string, int64, int64, int64, int64, *srvdef.SrvErr) {
title = strings.TrimSpace(title)
if title == "" {
return "", "", 0, 0, 0, 0, srvdef.New(srvdef.ErrParamError, "模块标题不能为空", "")
}
normalizedStyle, err := normalizePlayletHomeConfigStyle(style)
if err != nil {
return "", "", 0, 0, 0, 0, err
}
normalizedModuleType, err := normalizePlayletHomeModuleType(moduleType)
if err != nil {
return "", "", 0, 0, 0, 0, err
}
normalizedDataType, err := normalizePlayletHomeDataType(dataType)
if err != nil {
return "", "", 0, 0, 0, 0, err
}
if err := validatePlayletHomeRecommendType(recommendType); err != nil {
return "", "", 0, 0, 0, 0, err
}
return title, normalizedStyle, normalizedModuleType, normalizedDataType, recommendType, normalizePlayletHomeConfigLimit(limit), nil
}
func normalizePlayletHomeConfigStyle(style string) (string, *srvdef.SrvErr) {
style = strings.ToUpper(strings.TrimSpace(style))
switch style {
case playlethomeconfigmod.StyleA,
playlethomeconfigmod.StyleB,
playlethomeconfigmod.StyleC,
playlethomeconfigmod.StyleD,
playlethomeconfigmod.StyleE,
playlethomeconfigmod.StyleF,
playlethomeconfigmod.StyleG,
playlethomeconfigmod.StyleH:
return style, nil
default:
return "", srvdef.New(srvdef.ErrParamError, "样式无效,仅支持A-H", "")
}
}
func validatePlayletHomeRecommendType(recommendType int64) *srvdef.SrvErr {
switch recommendType {
case playlethomeconfigmod.RecommendTypeUpdated,
playlethomeconfigmod.RecommendTypeLike,
playlethomeconfigmod.RecommendTypeBrowse,
playlethomeconfigmod.RecommendTypeHeat,
playlethomeconfigmod.RecommendTypeCategory:
return nil
default:
return srvdef.New(srvdef.ErrParamError, "推荐类型无效,仅支持1按更新、2按点赞、3按观看、4按热度、5按分类", "")
}
}
func normalizePlayletHomeModuleType(moduleType int64) (int64, *srvdef.SrvErr) {
if moduleType == 0 {
return playlethomeconfigmod.ModuleTypePlaylet, nil
}
switch moduleType {
case playlethomeconfigmod.ModuleTypePlaylet, playlethomeconfigmod.ModuleTypeTopic:
return moduleType, nil
default:
return 0, srvdef.New(srvdef.ErrParamError, "模块类型无效,仅支持1短剧列表、2专题", "")
}
}
func normalizePlayletHomeModuleTypeForResp(moduleType int64) int64 {
if moduleType == playlethomeconfigmod.ModuleTypeTopic {
return playlethomeconfigmod.ModuleTypeTopic
}
return playlethomeconfigmod.ModuleTypePlaylet
}
func normalizePlayletHomeDataType(dataType int64) (int64, *srvdef.SrvErr) {
if dataType == 0 {
return playlethomeconfigmod.DataTypeAll, nil
}
switch dataType {
case playlethomeconfigmod.DataTypeAll, playlethomeconfigmod.DataTypeVideo:
return dataType, nil
default:
return 0, srvdef.New(srvdef.ErrParamError, "数据类型无效,仅支持1全部上架短剧、2全部上架视频", "")
}
}
func normalizePlayletHomeDataTypeForResp(dataType int64) int64 {
if dataType == playlethomeconfigmod.DataTypeVideo {
return playlethomeconfigmod.DataTypeVideo
}
return playlethomeconfigmod.DataTypeAll
}
func normalizePlayletHomeConfigLimit(limit int64) int64 {
if limit <= 0 {
return playlethomeconfigmod.DefaultLimit
}
if limit > playlethomeconfigmod.MaxLimit {
return playlethomeconfigmod.MaxLimit
}
return limit
}
File diff suppressed because it is too large Load Diff
+152
View File
@@ -0,0 +1,152 @@
package srv_playlet
import (
"context"
"regexp"
"strings"
"time"
"videoweb/pub/common/response"
"videoweb/pub/common/srvdef"
"videoweb/pub/models/playletmod"
"videoweb/pub/models/playlettagmod"
"videoweb/web/api/proto"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo/options"
)
// ListPlayletTag 分页查询标签列表
func ListPlayletTag(ctx context.Context, req *proto.PlayletTagListReq) (*proto.PlayletTagListPageResp, *srvdef.SrvErr) {
if req == nil {
return nil, srvdef.New(srvdef.ErrParamError, "", "")
}
filter := bson.M{}
if req.Id != nil && *req.Id > 0 {
filter[playlettagmod.FN.PlayletTag.Id] = *req.Id
}
if req.Status != nil {
filter[playlettagmod.FN.PlayletTag.Status] = *req.Status
}
if name := strings.TrimSpace(req.Name); name != "" {
filter[playlettagmod.FN.PlayletTag.Name] = bson.M{"$regex": regexp.QuoteMeta(name)}
}
pageNum, pageSize := normalizePage(req.PageNum, req.PageSize)
totalCount, err := playlettagmod.CountPlayletTag(ctx, filter)
if err != nil {
return nil, err
}
resp := &proto.PlayletTagListPageResp{
BasePageResp: response.BasePageResp{
TotalCount: totalCount,
PageSize: int(pageSize),
PageNum: int(pageNum),
List: make([]*proto.PlayletTagListResp, 0),
},
}
if totalCount == 0 {
return resp, nil
}
list, err := playlettagmod.FindManyPlayletTag(ctx, filter, options.Find().
SetSort(bson.D{
{Key: playlettagmod.FN.PlayletTag.Rank, Value: -1},
{Key: playlettagmod.FN.PlayletTag.Id, Value: -1},
}).
SetSkip((pageNum-1)*pageSize).
SetLimit(pageSize))
if err != nil {
return nil, err
}
items := make([]*proto.PlayletTagListResp, 0, len(list))
for _, item := range list {
if item == nil {
continue
}
items = append(items, &proto.PlayletTagListResp{
Id: item.Id,
Name: item.Name,
Status: item.Status,
Rank: item.Rank,
SeoInfo: normalizeSeoValue(item.SeoInfo),
CreatedAt: item.CreatedAt,
UpdatedAt: item.UpdatedAt,
})
}
resp.List = items
return resp, nil
}
// AddPlayletTag 新增标签
func AddPlayletTag(ctx context.Context, req *proto.PlayletTagAddReq) (*proto.PlayletTagAddResp, *srvdef.SrvErr) {
if req == nil || strings.TrimSpace(req.Name) == "" {
return nil, srvdef.New(srvdef.ErrParamError, "标签名称不能为空", "")
}
doc := &playlettagmod.PlayletTag{
Name: strings.TrimSpace(req.Name),
Status: req.Status,
Rank: req.Rank,
SeoInfo: normalizeSeoValue(req.SeoInfo),
}
if err := playlettagmod.InsertOnePlayletTag(ctx, doc); err != nil {
return nil, err
}
clearAppPlayletHomeCache()
return &proto.PlayletTagAddResp{Id: doc.Id}, nil
}
// UpdatePlayletTag 更新标签
func UpdatePlayletTag(ctx context.Context, req *proto.PlayletTagUpdateReq) *srvdef.SrvErr {
if req == nil || req.Id <= 0 {
return srvdef.New(srvdef.ErrParamError, "id无效", "")
}
setDoc := bson.M{playlettagmod.FN.PlayletTag.UpdatedAt: time.Now()}
if req.Name != nil {
name := strings.TrimSpace(*req.Name)
if name == "" {
return srvdef.New(srvdef.ErrParamError, "标签名称不能为空", "")
}
setDoc[playlettagmod.FN.PlayletTag.Name] = name
}
if req.Status != nil {
setDoc[playlettagmod.FN.PlayletTag.Status] = *req.Status
}
if req.Rank != nil {
setDoc[playlettagmod.FN.PlayletTag.Rank] = *req.Rank
}
if req.SeoInfo != nil {
setDoc["seoInfo"] = normalizeSeoValue(*req.SeoInfo)
}
if len(setDoc) <= 1 {
return srvdef.New(srvdef.ErrParamError, "没有可更新字段", "")
}
if err := playlettagmod.UpdateOnePlayletTag(ctx,
bson.M{playlettagmod.FN.PlayletTag.Id: req.Id},
bson.M{"$set": setDoc},
); err != nil {
return err
}
clearAppPlayletHomeCache()
return nil
}
// DelPlayletTag 删除标签(被短剧引用时禁止删除)
func DelPlayletTag(ctx context.Context, id int64) *srvdef.SrvErr {
if id <= 0 {
return srvdef.New(srvdef.ErrParamError, "id无效", "")
}
usedCount, err := playletmod.CountPlaylet(ctx, bson.M{playletmod.FN.Playlet.TagIds: id})
if err != nil {
return err
}
if usedCount > 0 {
return srvdef.New(srvdef.ErrParamError, "标签已被短剧引用,无法删除", "")
}
count, err := playlettagmod.DelOnePlayletTag(ctx, bson.M{playlettagmod.FN.PlayletTag.Id: id})
if err != nil {
return err
}
if count == 0 {
return srvdef.New(srvdef.ErrNoData, "", "")
}
clearAppPlayletHomeCache()
return nil
}
+213
View File
@@ -0,0 +1,213 @@
package srv_playlet
import (
"context"
"regexp"
"strings"
"time"
"videoweb/pub/common/response"
"videoweb/pub/common/slugx"
"videoweb/pub/common/srvdef"
"videoweb/pub/common/utils"
"videoweb/pub/models/playletmod"
"videoweb/pub/models/playletthememod"
"videoweb/web/api/proto"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo/options"
)
var playletThemeWebProjection = bson.D{
{Key: playletthememod.FN.PlayletTheme.Id, Value: 1},
{Key: playletthememod.FN.PlayletTheme.Name, Value: 1},
{Key: playletthememod.FN.PlayletTheme.Slug, Value: 1},
{Key: playletthememod.FN.PlayletTheme.Icon, Value: 1},
{Key: playletthememod.FN.PlayletTheme.Status, Value: 1},
{Key: playletthememod.FN.PlayletTheme.Rank, Value: 1},
{Key: playletthememod.FN.PlayletTheme.SeoInfo.Title, Value: 1},
{Key: playletthememod.FN.PlayletTheme.SeoInfo.Description, Value: 1},
{Key: playletthememod.FN.PlayletTheme.SeoInfo.Keyworld, Value: 1},
{Key: playletthememod.FN.PlayletTheme.CreatedAt, Value: 1},
{Key: playletthememod.FN.PlayletTheme.UpdatedAt, Value: 1},
}
// ListPlayletTheme 分页查询题材列表
func ListPlayletTheme(ctx context.Context, req *proto.PlayletThemeListReq) (*proto.PlayletThemeListPageResp, *srvdef.SrvErr) {
if req == nil {
return nil, srvdef.New(srvdef.ErrParamError, "", "")
}
filter := bson.M{}
if req.Id != nil && *req.Id > 0 {
filter[playletthememod.FN.PlayletTheme.Id] = *req.Id
}
if req.Status != nil {
filter[playletthememod.FN.PlayletTheme.Status] = *req.Status
}
if name := strings.TrimSpace(req.Name); name != "" {
filter[playletthememod.FN.PlayletTheme.Name] = bson.M{"$regex": regexp.QuoteMeta(name)}
}
pageNum, pageSize := normalizePage(req.PageNum, req.PageSize)
totalCount, err := playletthememod.CountPlayletTheme(ctx, filter)
if err != nil {
return nil, err
}
resp := &proto.PlayletThemeListPageResp{
BasePageResp: response.BasePageResp{
TotalCount: totalCount,
PageSize: int(pageSize),
PageNum: int(pageNum),
List: make([]*proto.PlayletThemeListResp, 0),
},
}
if totalCount == 0 {
return resp, nil
}
list, err := playletthememod.FindManyPlayletTheme(ctx, filter, options.Find().
SetProjection(playletThemeWebProjection).
SetSort(bson.D{
{Key: playletthememod.FN.PlayletTheme.Rank, Value: -1},
{Key: playletthememod.FN.PlayletTheme.Id, Value: -1},
}).
SetSkip((pageNum-1)*pageSize).
SetLimit(pageSize))
if err != nil {
return nil, err
}
items := make([]*proto.PlayletThemeListResp, 0, len(list))
for _, item := range list {
if item == nil {
continue
}
items = append(items, &proto.PlayletThemeListResp{
Id: item.Id,
Name: item.Name,
Slug: item.Slug,
Icon: item.Icon,
Status: item.Status,
Rank: item.Rank,
SeoInfo: normalizeSeoValue(item.SeoInfo),
CreatedAt: item.CreatedAt,
UpdatedAt: item.UpdatedAt,
})
}
resp.List = items
return resp, nil
}
// AddPlayletTheme 新增题材
func AddPlayletTheme(ctx context.Context, req *proto.PlayletThemeAddReq) (*proto.PlayletThemeAddResp, *srvdef.SrvErr) {
if req == nil || strings.TrimSpace(req.Name) == "" {
return nil, srvdef.New(srvdef.ErrParamError, "题材名称不能为空", "")
}
doc := &playletthememod.PlayletTheme{
Name: strings.TrimSpace(req.Name),
Slug: req.Slug,
Icon: strings.TrimSpace(req.Icon),
Status: req.Status,
Rank: req.Rank,
SeoInfo: normalizeSeoValue(req.SeoInfo),
}
exists := func(s string) bool {
item, _ := playletthememod.FindOnePlayletTheme(ctx, slugx.Filter(
playletthememod.FN.PlayletTheme.Slug, playletthememod.FN.PlayletTheme.Id, s, 0))
return item != nil
}
if serr := slugx.Ensure(&doc.Slug, utils.SlugSourceFromKeywordsOrTitle(doc.SeoInfo.Keyworld, doc.Name), exists); serr != nil {
return nil, serr
}
if err := playletthememod.InsertOnePlayletTheme(ctx, doc); err != nil {
return nil, err
}
clearAppPlayletHomeCache()
return &proto.PlayletThemeAddResp{Id: doc.Id}, nil
}
// UpdatePlayletTheme 更新题材
func UpdatePlayletTheme(ctx context.Context, req *proto.PlayletThemeUpdateReq) *srvdef.SrvErr {
if req == nil || req.Id <= 0 {
return srvdef.New(srvdef.ErrParamError, "题材ID无效", "")
}
current, err := playletthememod.FindOnePlayletTheme(ctx, bson.M{
playletthememod.FN.PlayletTheme.Id: req.Id,
})
if err != nil {
return err
}
setDoc := bson.M{playletthememod.FN.PlayletTheme.UpdatedAt: time.Now()}
if req.Name != nil {
name := strings.TrimSpace(*req.Name)
if name == "" {
return srvdef.New(srvdef.ErrParamError, "题材名称不能为空", "")
}
setDoc[playletthememod.FN.PlayletTheme.Name] = name
}
if req.Icon != nil {
setDoc[playletthememod.FN.PlayletTheme.Icon] = strings.TrimSpace(*req.Icon)
}
if req.Status != nil {
setDoc[playletthememod.FN.PlayletTheme.Status] = *req.Status
}
if req.Rank != nil {
setDoc[playletthememod.FN.PlayletTheme.Rank] = *req.Rank
}
if req.SeoInfo != nil {
setDoc["seoInfo"] = normalizeSeoValue(*req.SeoInfo)
}
name := current.Name
if req.Name != nil {
name = strings.TrimSpace(*req.Name)
}
keywords := current.SeoInfo.Keyworld
if req.SeoInfo != nil {
keywords = normalizeSeoValue(*req.SeoInfo).Keyworld
}
exists := func(s string) bool {
item, _ := playletthememod.FindOnePlayletTheme(ctx, slugx.Filter(
playletthememod.FN.PlayletTheme.Slug, playletthememod.FN.PlayletTheme.Id, s, req.Id))
return item != nil
}
slug, changed, serr := slugx.PrepareOnUpdate(current.Slug, req.Slug,
utils.SlugSourceFromKeywordsOrTitle(keywords, name), exists)
if serr != nil {
return serr
}
if changed {
setDoc[playletthememod.FN.PlayletTheme.Slug] = slug
}
if len(setDoc) <= 1 {
return srvdef.New(srvdef.ErrParamError, "没有可更新字段", "")
}
if err := playletthememod.UpdateOnePlayletTheme(ctx,
bson.M{playletthememod.FN.PlayletTheme.Id: req.Id},
bson.M{"$set": setDoc},
); err != nil {
return err
}
clearAppPlayletHomeCache()
return nil
}
// DelPlayletTheme 删除题材,被短剧引用时禁止删除
func DelPlayletTheme(ctx context.Context, id int64) *srvdef.SrvErr {
if id <= 0 {
return srvdef.New(srvdef.ErrParamError, "题材ID无效", "")
}
usedCount, err := playletmod.CountPlaylet(ctx, bson.M{
playletmod.FN.Playlet.ThemeIds: bson.M{"$in": []int64{id}},
})
if err != nil {
return err
}
if usedCount > 0 {
return srvdef.New(srvdef.ErrParamError, "题材已被短剧引用,无法删除", "")
}
count, err := playletthememod.DelOnePlayletTheme(ctx, bson.M{playletthememod.FN.PlayletTheme.Id: id})
if err != nil {
return err
}
if count == 0 {
return srvdef.New(srvdef.ErrNoData, "", "")
}
clearAppPlayletHomeCache()
return nil
}