Initial commit

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-23 11:07:42 +08:00
co-authored by Claude Opus 5.5
commit 7974a42301
633 changed files with 120248 additions and 0 deletions
+8
View File
@@ -0,0 +1,8 @@
package srv_playlet
import "videoweb/pub/common/playletcache"
// clearAppPlayletHomeCache 清除app首页短剧缓存
func clearAppPlayletHomeCache() {
playletcache.ClearHomeCache()
}
+57
View File
@@ -0,0 +1,57 @@
package srv_playlet
import (
"context"
"videoweb/pub/models/mwacc"
"videoweb/pub/models/playletmod"
"videoweb/web/middleware/auth"
"github.com/gin-gonic/gin"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo/options"
)
// ResolveOperator 解析当前后台登录操作人,供短剧写操作与媒资入库共用。
func ResolveOperator(ctx context.Context) (int64, string) {
return resolvePlayletOperator(ctx)
}
// resolvePlayletOperator 从后台登录态解析当前操作人。
func resolvePlayletOperator(ctx context.Context) (int64, string) {
c, ok := ctx.(*gin.Context)
if !ok || c == nil {
return 0, ""
}
accId := auth.GetAccount(c)
if accId <= 0 {
return 0, ""
}
acc, err := mwacc.FindOneAccByFilter(c, bson.M{
mwacc.FN.MwAcc.Id: accId,
}, options.FindOne().SetProjection(bson.D{
{Key: mwacc.FN.MwAcc.Id, Value: 1},
{Key: mwacc.FN.MwAcc.UserName, Value: 1},
}))
if err != nil || acc == nil {
return accId, ""
}
return acc.Id, acc.UserName
}
// applyPlayletOperatorToSetDoc 将当前操作人写入更新文档。
func applyPlayletOperatorToSetDoc(ctx context.Context, setDoc bson.M) {
if setDoc == nil {
return
}
operatorId, operatorName := resolvePlayletOperator(ctx)
setDoc[playletmod.FN.Playlet.OperatorId] = operatorId
setDoc[playletmod.FN.Playlet.OperatorName] = operatorName
}
// applyPlayletOperatorToDoc 将当前操作人写入新增文档。
func applyPlayletOperatorToDoc(ctx context.Context, doc *playletmod.Playlet) {
if doc == nil {
return
}
doc.OperatorId, doc.OperatorName = resolvePlayletOperator(ctx)
}
File diff suppressed because it is too large Load Diff
+454
View File
@@ -0,0 +1,454 @@
package srv_playlet
import (
"context"
"strings"
"time"
"videoweb/pub/common/playletcache"
"videoweb/pub/common/srvdef"
"videoweb/pub/common/utils"
"videoweb/pub/models/playletcategorymod"
"videoweb/pub/models/playletmod"
"videoweb/pub/models/playlettagmod"
"videoweb/pub/models/playletthememod"
"videoweb/web/api/proto"
"videoweb/web/service/srv_playletcategory"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo/options"
)
const (
syncSlugDefaultSize = int64(500)
syncSlugMaxSize = int64(2000)
)
func emptySlugFilter() bson.M {
return bson.M{
"$or": []bson.M{
{"slug": ""},
{"slug": bson.M{"$exists": false}},
},
}
}
func ensureThemeSlug(ctx context.Context, ac *playletthememod.PlayletTheme, excludeId int64) *srvdef.SrvErr {
ac.Slug = utils.SanitizeSlug(ac.Slug)
if ac.Slug != "" {
if utils.IsSlugAllDigits(ac.Slug) {
return srvdef.New(srvdef.ErrParamError, "Slug不能全是数字", "Slug不能全是数字")
}
filter := bson.M{playletthememod.FN.PlayletTheme.Slug: ac.Slug}
if excludeId > 0 {
filter[playletthememod.FN.PlayletTheme.Id] = bson.M{"$ne": excludeId}
}
exist, _ := playletthememod.FindOnePlayletTheme(ctx, filter)
if exist != nil {
return srvdef.New(srvdef.ErrDbInputExist, "Slug已存在", "Slug已存在")
}
return nil
}
source := utils.SlugSourceFromKeywordsOrTitle(ac.SeoInfo.Keyworld, ac.Name)
ac.Slug = utils.MakeUniqueSlug(source, func(slug string) bool {
filter := bson.M{playletthememod.FN.PlayletTheme.Slug: slug}
if excludeId > 0 {
filter[playletthememod.FN.PlayletTheme.Id] = bson.M{"$ne": excludeId}
}
exist, _ := playletthememod.FindOnePlayletTheme(ctx, filter)
return exist != nil
})
return nil
}
func prepareThemeSlugOnUpdate(ctx context.Context, id int64, info *playletthememod.PlayletTheme, req *proto.PlayletThemeUpdateReq, setDoc bson.M) *srvdef.SrvErr {
slug := info.Slug
if req.Slug != nil {
slug = utils.SanitizeSlug(*req.Slug)
}
if slug != "" {
if req.Slug != nil {
if utils.IsSlugAllDigits(slug) {
return srvdef.New(srvdef.ErrParamError, "Slug不能全是数字", "Slug不能全是数字")
}
exist, _ := playletthememod.FindOnePlayletTheme(ctx, bson.M{
playletthememod.FN.PlayletTheme.Slug: slug,
playletthememod.FN.PlayletTheme.Id: bson.M{"$ne": id},
})
if exist != nil {
return srvdef.New(srvdef.ErrDbInputExist, "Slug已存在", "Slug已存在")
}
setDoc[playletthememod.FN.PlayletTheme.Slug] = slug
}
return nil
}
name := info.Name
if req.Name != nil {
name = strings.TrimSpace(*req.Name)
}
keywords := info.SeoInfo.Keyworld
if req.SeoInfo != nil {
keywords = normalizeSeoValue(*req.SeoInfo).Keyworld
}
source := utils.SlugSourceFromKeywordsOrTitle(keywords, name)
newSlug := utils.MakeUniqueSlug(source, func(s string) bool {
exist, _ := playletthememod.FindOnePlayletTheme(ctx, bson.M{
playletthememod.FN.PlayletTheme.Slug: s,
playletthememod.FN.PlayletTheme.Id: bson.M{"$ne": id},
})
return exist != nil
})
setDoc[playletthememod.FN.PlayletTheme.Slug] = newSlug
return nil
}
func ensureTagSlug(ctx context.Context, ac *playlettagmod.PlayletTag, excludeId int64) *srvdef.SrvErr {
ac.Slug = utils.SanitizeSlug(ac.Slug)
if ac.Slug != "" {
if utils.IsSlugAllDigits(ac.Slug) {
return srvdef.New(srvdef.ErrParamError, "Slug不能全是数字", "Slug不能全是数字")
}
filter := bson.M{playlettagmod.FN.PlayletTag.Slug: ac.Slug}
if excludeId > 0 {
filter[playlettagmod.FN.PlayletTag.Id] = bson.M{"$ne": excludeId}
}
exist, _ := playlettagmod.FindOnePlayletTag(ctx, filter)
if exist != nil {
return srvdef.New(srvdef.ErrDbInputExist, "Slug已存在", "Slug已存在")
}
return nil
}
source := utils.SlugSourceFromKeywordsOrTitle(ac.SeoInfo.Keyworld, ac.Name)
ac.Slug = utils.MakeUniqueSlug(source, func(slug string) bool {
filter := bson.M{playlettagmod.FN.PlayletTag.Slug: slug}
if excludeId > 0 {
filter[playlettagmod.FN.PlayletTag.Id] = bson.M{"$ne": excludeId}
}
exist, _ := playlettagmod.FindOnePlayletTag(ctx, filter)
return exist != nil
})
return nil
}
func prepareTagSlugOnUpdate(ctx context.Context, id int64, info *playlettagmod.PlayletTag, req *proto.PlayletTagUpdateReq, setDoc bson.M) *srvdef.SrvErr {
slug := info.Slug
if req.Slug != nil {
slug = utils.SanitizeSlug(*req.Slug)
}
if slug != "" {
if req.Slug != nil {
if utils.IsSlugAllDigits(slug) {
return srvdef.New(srvdef.ErrParamError, "Slug不能全是数字", "Slug不能全是数字")
}
exist, _ := playlettagmod.FindOnePlayletTag(ctx, bson.M{
playlettagmod.FN.PlayletTag.Slug: slug,
playlettagmod.FN.PlayletTag.Id: bson.M{"$ne": id},
})
if exist != nil {
return srvdef.New(srvdef.ErrDbInputExist, "Slug已存在", "Slug已存在")
}
setDoc[playlettagmod.FN.PlayletTag.Slug] = slug
}
return nil
}
name := info.Name
if req.Name != nil {
name = strings.TrimSpace(*req.Name)
}
keywords := info.SeoInfo.Keyworld
if req.SeoInfo != nil {
keywords = normalizeSeoValue(*req.SeoInfo).Keyworld
}
source := utils.SlugSourceFromKeywordsOrTitle(keywords, name)
newSlug := utils.MakeUniqueSlug(source, func(s string) bool {
exist, _ := playlettagmod.FindOnePlayletTag(ctx, bson.M{
playlettagmod.FN.PlayletTag.Slug: s,
playlettagmod.FN.PlayletTag.Id: bson.M{"$ne": id},
})
return exist != nil
})
setDoc[playlettagmod.FN.PlayletTag.Slug] = newSlug
return nil
}
func ensurePlayletSlug(ctx context.Context, ac *playletmod.Playlet, excludeId int64) *srvdef.SrvErr {
ac.Slug = utils.SanitizeSlug(ac.Slug)
if ac.Slug != "" {
if utils.IsSlugAllDigits(ac.Slug) {
return srvdef.New(srvdef.ErrParamError, "Slug不能全是数字", "Slug不能全是数字")
}
filter := bson.M{playletmod.FN.Playlet.Slug: ac.Slug}
if excludeId > 0 {
filter[playletmod.FN.Playlet.Id] = bson.M{"$ne": excludeId}
}
exist, _ := playletmod.FindOnePlaylet(ctx, filter)
if exist != nil {
return srvdef.New(srvdef.ErrDbInputExist, "Slug已存在", "Slug已存在")
}
return nil
}
source := utils.SlugSourceFromKeywordsOrTitle(ac.SeoInfo.Keyworld, ac.Title)
ac.Slug = utils.MakeUniqueSlug(source, func(slug string) bool {
filter := bson.M{playletmod.FN.Playlet.Slug: slug}
if excludeId > 0 {
filter[playletmod.FN.Playlet.Id] = bson.M{"$ne": excludeId}
}
exist, _ := playletmod.FindOnePlaylet(ctx, filter)
return exist != nil
})
return nil
}
func preparePlayletSlugOnUpdate(ctx context.Context, id int64, info *playletmod.Playlet, req *proto.PlayletUpdateReq, setDoc bson.M) *srvdef.SrvErr {
slug := info.Slug
if req.Slug != nil {
slug = utils.SanitizeSlug(*req.Slug)
}
if slug != "" {
if req.Slug != nil {
if utils.IsSlugAllDigits(slug) {
return srvdef.New(srvdef.ErrParamError, "Slug不能全是数字", "Slug不能全是数字")
}
exist, _ := playletmod.FindOnePlaylet(ctx, bson.M{
playletmod.FN.Playlet.Slug: slug,
playletmod.FN.Playlet.Id: bson.M{"$ne": id},
})
if exist != nil {
return srvdef.New(srvdef.ErrDbInputExist, "Slug已存在", "Slug已存在")
}
setDoc[playletmod.FN.Playlet.Slug] = slug
}
return nil
}
title := info.Title
if req.Title != nil {
title = strings.TrimSpace(*req.Title)
}
keywords := info.SeoInfo.Keyworld
if req.SeoInfo != nil {
keywords = normalizeSeoValue(*req.SeoInfo).Keyworld
}
source := utils.SlugSourceFromKeywordsOrTitle(keywords, title)
newSlug := utils.MakeUniqueSlug(source, func(s string) bool {
exist, _ := playletmod.FindOnePlaylet(ctx, bson.M{
playletmod.FN.Playlet.Slug: s,
playletmod.FN.Playlet.Id: bson.M{"$ne": id},
})
return exist != nil
})
setDoc[playletmod.FN.Playlet.Slug] = newSlug
return nil
}
// SyncEmptySlugs 为题材、标签、分类、短剧中空 slug 自动生成。可重复调用。
func SyncEmptySlugs(ctx context.Context, req *proto.PlayletSlugSyncReq) (*proto.PlayletSlugSyncResp, *srvdef.SrvErr) {
maxSize := syncSlugDefaultSize
if req != nil && req.MaxSize > 0 {
maxSize = req.MaxSize
}
if maxSize > syncSlugMaxSize {
maxSize = syncSlugMaxSize
}
resp := &proto.PlayletSlugSyncResp{}
remain := maxSize
themeUpdated, themeMore, err := syncThemeEmptySlugs(ctx, remain)
if err != nil {
return nil, err
}
resp.ThemeUpdated = themeUpdated
remain -= themeUpdated
if remain <= 0 {
resp.HasMore = themeMore || hasEmptySlug(ctx, "tag") || hasEmptySlug(ctx, "category") || hasEmptySlug(ctx, "playlet")
playletcache.ClearHomeCache()
return resp, nil
}
tagUpdated, tagMore, err := syncTagEmptySlugs(ctx, remain)
if err != nil {
return nil, err
}
resp.TagUpdated = tagUpdated
remain -= tagUpdated
if remain <= 0 {
resp.HasMore = tagMore || hasEmptySlug(ctx, "category") || hasEmptySlug(ctx, "playlet")
playletcache.ClearHomeCache()
return resp, nil
}
categoryUpdated, categoryMore, err := syncCategoryEmptySlugs(ctx, remain)
if err != nil {
return nil, err
}
resp.CategoryUpdated = categoryUpdated
remain -= categoryUpdated
if remain <= 0 {
resp.HasMore = categoryMore || hasEmptySlug(ctx, "playlet")
playletcache.ClearHomeCache()
return resp, nil
}
playletUpdated, playletMore, err := syncPlayletEmptySlugs(ctx, remain)
if err != nil {
return nil, err
}
resp.PlayletUpdated = playletUpdated
resp.HasMore = playletMore
playletcache.ClearHomeCache()
return resp, nil
}
func hasEmptySlug(ctx context.Context, kind string) bool {
filter := emptySlugFilter()
switch kind {
case "theme":
count, _ := playletthememod.CountPlayletTheme(ctx, filter)
return count > 0
case "tag":
count, _ := playlettagmod.CountPlayletTag(ctx, filter)
return count > 0
case "category":
count, _ := playletcategorymod.CountPlayletCategory(ctx, filter)
return count > 0
case "playlet":
count, _ := playletmod.CountPlaylet(ctx, filter)
return count > 0
default:
return false
}
}
func syncThemeEmptySlugs(ctx context.Context, maxSize int64) (int64, bool, *srvdef.SrvErr) {
list, err := playletthememod.FindManyPlayletTheme(ctx, emptySlugFilter(), options.Find().
SetSort(bson.D{{Key: playletthememod.FN.PlayletTheme.Id, Value: 1}}).
SetLimit(maxSize+1))
if err != nil {
return 0, false, err
}
hasMore := int64(len(list)) > maxSize
if hasMore {
list = list[:maxSize]
}
var updated int64
now := time.Now()
for _, item := range list {
if item == nil {
continue
}
if serr := ensureThemeSlug(ctx, item, item.Id); serr != nil {
return updated, hasMore, serr
}
if err := playletthememod.UpdateOnePlayletTheme(ctx,
bson.M{playletthememod.FN.PlayletTheme.Id: item.Id},
bson.M{"$set": bson.M{
playletthememod.FN.PlayletTheme.Slug: item.Slug,
playletthememod.FN.PlayletTheme.UpdatedAt: now,
}},
); err != nil {
return updated, hasMore, err
}
updated++
}
return updated, hasMore, nil
}
func syncTagEmptySlugs(ctx context.Context, maxSize int64) (int64, bool, *srvdef.SrvErr) {
list, err := playlettagmod.FindManyPlayletTag(ctx, emptySlugFilter(), options.Find().
SetSort(bson.D{{Key: playlettagmod.FN.PlayletTag.Id, Value: 1}}).
SetLimit(maxSize+1))
if err != nil {
return 0, false, err
}
hasMore := int64(len(list)) > maxSize
if hasMore {
list = list[:maxSize]
}
var updated int64
now := time.Now()
for _, item := range list {
if item == nil {
continue
}
if serr := ensureTagSlug(ctx, item, item.Id); serr != nil {
return updated, hasMore, serr
}
if err := playlettagmod.UpdateOnePlayletTag(ctx,
bson.M{playlettagmod.FN.PlayletTag.Id: item.Id},
bson.M{"$set": bson.M{
playlettagmod.FN.PlayletTag.Slug: item.Slug,
playlettagmod.FN.PlayletTag.UpdatedAt: now,
}},
); err != nil {
return updated, hasMore, err
}
updated++
}
return updated, hasMore, nil
}
func syncCategoryEmptySlugs(ctx context.Context, maxSize int64) (int64, bool, *srvdef.SrvErr) {
list, err := playletcategorymod.FindManyPlayletCategory(ctx, emptySlugFilter(), options.Find().
SetSort(bson.D{{Key: playletcategorymod.FN.PlayletCategory.Id, Value: 1}}).
SetLimit(maxSize+1))
if err != nil {
return 0, false, err
}
hasMore := int64(len(list)) > maxSize
if hasMore {
list = list[:maxSize]
}
var updated int64
now := time.Now()
for _, item := range list {
if item == nil {
continue
}
if serr := srv_playletcategory.EnsureCategorySlug(ctx, item, item.Id); serr != nil {
return updated, hasMore, serr
}
if err := playletcategorymod.UpdateOnePlayletCategory(ctx,
bson.M{playletcategorymod.FN.PlayletCategory.Id: item.Id},
bson.M{"$set": bson.M{
playletcategorymod.FN.PlayletCategory.Slug: item.Slug,
playletcategorymod.FN.PlayletCategory.UpdatedAt: now,
}},
); err != nil {
return updated, hasMore, err
}
updated++
}
return updated, hasMore, nil
}
func syncPlayletEmptySlugs(ctx context.Context, maxSize int64) (int64, bool, *srvdef.SrvErr) {
list, err := playletmod.FindManyPlaylet(ctx, emptySlugFilter(), options.Find().
SetSort(bson.D{{Key: playletmod.FN.Playlet.Id, Value: 1}}).
SetLimit(maxSize+1))
if err != nil {
return 0, false, err
}
hasMore := int64(len(list)) > maxSize
if hasMore {
list = list[:maxSize]
}
var updated int64
now := time.Now()
for _, item := range list {
if item == nil {
continue
}
if serr := ensurePlayletSlug(ctx, item, item.Id); serr != nil {
return updated, hasMore, serr
}
if err := playletmod.UpdateOnePlaylet(ctx,
bson.M{playletmod.FN.Playlet.Id: item.Id},
bson.M{"$set": bson.M{
playletmod.FN.Playlet.Slug: item.Slug,
playletmod.FN.Playlet.UpdatedAt: now,
}},
); err != nil {
return updated, hasMore, err
}
updated++
}
return updated, hasMore, nil
}
+167
View File
@@ -0,0 +1,167 @@
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)}
}
if slug := strings.TrimSpace(req.Slug); slug != "" {
filter[playlettagmod.FN.PlayletTag.Slug] = slug
}
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,
Slug: item.Slug,
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),
Slug: strings.TrimSpace(req.Slug),
Status: req.Status,
Rank: req.Rank,
SeoInfo: normalizeSeoValue(req.SeoInfo),
}
if err := ensureTagSlug(ctx, doc, 0); err != nil {
return nil, err
}
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无效", "")
}
info, err := playlettagmod.FindOnePlayletTag(ctx, bson.M{playlettagmod.FN.PlayletTag.Id: req.Id})
if err != nil {
return err
}
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 err := prepareTagSlugOnUpdate(ctx, req.Id, info, req, setDoc); err != nil {
return err
}
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
}
+204
View File
@@ -0,0 +1,204 @@
package srv_playlet
import (
"context"
"regexp"
"strings"
"time"
"videoweb/pub/common/response"
"videoweb/pub/common/srvdef"
"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"
)
// 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 req.IsRecommend != nil {
filter[playletthememod.FN.PlayletTheme.IsRecommend] = *req.IsRecommend
}
if name := strings.TrimSpace(req.Name); name != "" {
filter[playletthememod.FN.PlayletTheme.Name] = bson.M{"$regex": regexp.QuoteMeta(name)}
}
if slug := strings.TrimSpace(req.Slug); slug != "" {
filter[playletthememod.FN.PlayletTheme.Slug] = slug
}
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().
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,
IsRecommend: item.IsRecommend,
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: strings.TrimSpace(req.Slug),
Icon: req.Icon,
Status: req.Status,
IsRecommend: req.IsRecommend,
Rank: req.Rank,
SeoInfo: normalizeSeoValue(req.SeoInfo),
}
if err := ensureThemeSlug(ctx, doc, 0); err != nil {
return nil, err
}
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无效", "")
}
info, 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] = *req.Icon
}
if req.Status != nil {
setDoc[playletthememod.FN.PlayletTheme.Status] = *req.Status
}
if req.IsRecommend != nil {
setDoc[playletthememod.FN.PlayletTheme.IsRecommend] = *req.IsRecommend
}
if req.Rank != nil {
setDoc[playletthememod.FN.PlayletTheme.Rank] = *req.Rank
}
if req.SeoInfo != nil {
setDoc["seoInfo"] = normalizeSeoValue(*req.SeoInfo)
}
if err := prepareThemeSlugOnUpdate(ctx, req.Id, info, req, setDoc); err != nil {
return err
}
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
}
// SetPlayletThemeRecommend 单/批量设置题材是否推荐
func SetPlayletThemeRecommend(ctx context.Context, req *proto.PlayletThemeRecommendSetReq) *srvdef.SrvErr {
if req == nil {
return srvdef.New(srvdef.ErrParamError, "参数不能为空", "")
}
ids := dedupePositiveInt64Ids(req.Ids)
if len(ids) == 0 {
return srvdef.New(srvdef.ErrParamError, "题材ID不能为空", "")
}
if err := playletthememod.UpdateManyPlayletTheme(ctx,
bson.M{playletthememod.FN.PlayletTheme.Id: bson.M{"$in": ids}},
bson.M{"$set": bson.M{
playletthememod.FN.PlayletTheme.IsRecommend: req.IsRecommend,
playletthememod.FN.PlayletTheme.UpdatedAt: time.Now(),
}},
); 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
}