init: web_hgdjc_server
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
package srv_playlet
|
||||
|
||||
import "videoweb/pub/common/playletcache"
|
||||
|
||||
// clearAppPlayletHomeCache 清除app首页短剧缓存
|
||||
func clearAppPlayletHomeCache() {
|
||||
playletcache.ClearHomeCache()
|
||||
}
|
||||
@@ -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
@@ -0,0 +1,371 @@
|
||||
package srv_playlet
|
||||
|
||||
import (
|
||||
"context"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
"videoweb/pub/common/srvdef"
|
||||
"videoweb/pub/common/utils"
|
||||
"videoweb/pub/models/playletchapterunlockmod"
|
||||
"videoweb/pub/models/playletcollectmod"
|
||||
"videoweb/pub/models/playletlikemod"
|
||||
"videoweb/pub/models/playletmod"
|
||||
"videoweb/pub/pubdef"
|
||||
"videoweb/web/api/proto"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
)
|
||||
|
||||
const playletSalesLimit = 50
|
||||
|
||||
type playletSalesAggRow struct {
|
||||
PlayletId int64 `bson:"_id"`
|
||||
Count int64 `bson:"count"`
|
||||
Coins int64 `bson:"coins"`
|
||||
}
|
||||
|
||||
type playletSalesRow struct {
|
||||
Id int64
|
||||
UnlockCount int64
|
||||
UnlockCoins int64
|
||||
LikeCount int64
|
||||
CollectCount int64
|
||||
}
|
||||
|
||||
// ListPlayletSales 按时间段汇总短剧金币解锁、点赞、收藏,最多返回 50 条。
|
||||
func ListPlayletSales(ctx context.Context, req *proto.PlayletSalesReq) (*proto.PlayletSalesResp, *srvdef.SrvErr) {
|
||||
if req == nil {
|
||||
return nil, srvdef.New(srvdef.ErrParamError, "", "")
|
||||
}
|
||||
start, end, serr := resolvePlayletSalesTimeRange(req.StartTime, req.EndTime)
|
||||
if serr != nil {
|
||||
return nil, serr
|
||||
}
|
||||
resp := &proto.PlayletSalesResp{
|
||||
StartTime: start.In(utils.LocBJ).Format(pubdef.TimeISO),
|
||||
EndTime: end.In(utils.LocBJ).Format(pubdef.TimeISO),
|
||||
List: make([]*proto.PlayletSalesItem, 0),
|
||||
}
|
||||
filterIDs, playlets, serr := loadPlayletSalesFilter(ctx, req)
|
||||
if serr != nil {
|
||||
return nil, serr
|
||||
}
|
||||
if filterIDs != nil && len(filterIDs) == 0 {
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
unlocks, serr := aggregatePlayletUnlocks(ctx, start, end, filterIDs)
|
||||
if serr != nil {
|
||||
return nil, serr
|
||||
}
|
||||
likes, serr := aggregatePlayletMetric(ctx, start, end, filterIDs, playletlikemod.FN.PlayletLike.CreatedAt, playletlikemod.FN.PlayletLike.PlayletId, playletlikemod.AggregatePlayletLike)
|
||||
if serr != nil {
|
||||
return nil, serr
|
||||
}
|
||||
collects, serr := aggregatePlayletMetric(ctx, start, end, filterIDs, playletcollectmod.FN.PlayletCollect.CreatedAt, playletcollectmod.FN.PlayletCollect.PlayletId, playletcollectmod.AggregatePlayletCollect)
|
||||
if serr != nil {
|
||||
return nil, serr
|
||||
}
|
||||
|
||||
rows := mergePlayletSalesRows(filterIDs, unlocks, likes, collects)
|
||||
sortPlayletSalesRows(rows, req.SortType)
|
||||
rows = takePlayletSalesRows(rows, playletSalesLimit)
|
||||
if len(rows) == 0 {
|
||||
return resp, nil
|
||||
}
|
||||
if playlets == nil {
|
||||
playlets, serr = loadPlayletsByIDs(ctx, playletSalesIDs(rows))
|
||||
if serr != nil {
|
||||
return nil, serr
|
||||
}
|
||||
}
|
||||
for _, row := range rows {
|
||||
item := &proto.PlayletSalesItem{
|
||||
Id: row.Id,
|
||||
UnlockCount: row.UnlockCount,
|
||||
UnlockCoins: row.UnlockCoins,
|
||||
LikeCount: row.LikeCount,
|
||||
CollectCount: row.CollectCount,
|
||||
}
|
||||
if playlet := playlets[row.Id]; playlet != nil {
|
||||
item.Title = playlet.Title
|
||||
item.PlayName = playlet.PlayName
|
||||
item.Cover = playlet.Cover
|
||||
item.Slug = playlet.Slug
|
||||
item.Status = playlet.Status
|
||||
}
|
||||
resp.List = append(resp.List, item)
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func resolvePlayletSalesTimeRange(startRaw, endRaw string) (time.Time, time.Time, *srvdef.SrvErr) {
|
||||
startRaw = strings.TrimSpace(startRaw)
|
||||
endRaw = strings.TrimSpace(endRaw)
|
||||
if startRaw == "" && endRaw == "" {
|
||||
day := time.Now().In(utils.LocBJ).AddDate(0, 0, -1)
|
||||
return utils.BeginOfTime(day), utils.EndOfTime(day), nil
|
||||
}
|
||||
if startRaw == "" || endRaw == "" {
|
||||
return time.Time{}, time.Time{}, srvdef.New(srvdef.ErrParamError, "开始时间和结束时间需同时传入", "开始时间和结束时间需同时传入")
|
||||
}
|
||||
return parsePlayletSalesTimeRange(startRaw, endRaw)
|
||||
}
|
||||
|
||||
func parsePlayletSalesTimeRange(startRaw, endRaw string) (time.Time, time.Time, *srvdef.SrvErr) {
|
||||
start, ok := parsePlayletSalesTime(startRaw, true)
|
||||
if !ok {
|
||||
return time.Time{}, time.Time{}, srvdef.New(srvdef.ErrParamError, "开始时间无效", "开始时间无效")
|
||||
}
|
||||
end, ok := parsePlayletSalesTime(endRaw, false)
|
||||
if !ok {
|
||||
return time.Time{}, time.Time{}, srvdef.New(srvdef.ErrParamError, "结束时间无效", "结束时间无效")
|
||||
}
|
||||
if end.Before(start) {
|
||||
return time.Time{}, time.Time{}, srvdef.New(srvdef.ErrParamError, "结束时间不能早于开始时间", "结束时间不能早于开始时间")
|
||||
}
|
||||
return start, end, nil
|
||||
}
|
||||
|
||||
func parsePlayletSalesTime(raw string, isStart bool) (time.Time, bool) {
|
||||
raw = strings.TrimSpace(raw)
|
||||
if raw == "" {
|
||||
return time.Time{}, false
|
||||
}
|
||||
layouts := []string{
|
||||
pubdef.TimeISO,
|
||||
time.RFC3339,
|
||||
time.RFC3339Nano,
|
||||
"2006-01-02 15:04:05 -0700 MST",
|
||||
"2006-01-02 15:04:05.999999999 -0700 MST",
|
||||
}
|
||||
for _, layout := range layouts {
|
||||
if t, err := time.ParseInLocation(layout, raw, utils.LocBJ); err == nil {
|
||||
return t, true
|
||||
}
|
||||
}
|
||||
if t, err := time.ParseInLocation(pubdef.DateISO, raw, utils.LocBJ); err == nil {
|
||||
if isStart {
|
||||
return utils.BeginOfTime(t), true
|
||||
}
|
||||
return utils.EndOfTime(t), true
|
||||
}
|
||||
return time.Time{}, false
|
||||
}
|
||||
|
||||
func loadPlayletSalesFilter(ctx context.Context, req *proto.PlayletSalesReq) ([]int64, map[int64]*playletmod.Playlet, *srvdef.SrvErr) {
|
||||
id := int64(0)
|
||||
if req.Id != nil {
|
||||
id = *req.Id
|
||||
}
|
||||
title := strings.TrimSpace(req.Title)
|
||||
if id <= 0 && title == "" {
|
||||
return nil, nil, nil
|
||||
}
|
||||
filter := bson.M{}
|
||||
if id > 0 {
|
||||
filter[playletmod.FN.Playlet.Id] = id
|
||||
}
|
||||
if title != "" {
|
||||
pattern := regexp.QuoteMeta(title)
|
||||
filter["$or"] = []bson.M{
|
||||
{playletmod.FN.Playlet.Title: bson.M{"$regex": pattern}},
|
||||
{playletmod.FN.Playlet.PlayName: bson.M{"$regex": pattern}},
|
||||
}
|
||||
}
|
||||
list, serr := playletmod.FindManyPlaylet(ctx, filter, options.Find().SetProjection(playletSalesProjection()))
|
||||
if serr != nil {
|
||||
return nil, nil, serr
|
||||
}
|
||||
ids := make([]int64, 0, len(list))
|
||||
playlets := make(map[int64]*playletmod.Playlet, len(list))
|
||||
for _, item := range list {
|
||||
if item == nil || item.Id <= 0 {
|
||||
continue
|
||||
}
|
||||
ids = append(ids, item.Id)
|
||||
playlets[item.Id] = item
|
||||
}
|
||||
return ids, playlets, nil
|
||||
}
|
||||
|
||||
func playletSalesProjection() bson.D {
|
||||
return bson.D{
|
||||
{Key: playletmod.FN.Playlet.Id, Value: 1},
|
||||
{Key: playletmod.FN.Playlet.Title, Value: 1},
|
||||
{Key: playletmod.FN.Playlet.PlayName, Value: 1},
|
||||
{Key: playletmod.FN.Playlet.Cover, Value: 1},
|
||||
{Key: playletmod.FN.Playlet.Slug, Value: 1},
|
||||
{Key: playletmod.FN.Playlet.Status, Value: 1},
|
||||
}
|
||||
}
|
||||
|
||||
func loadPlayletsByIDs(ctx context.Context, ids []int64) (map[int64]*playletmod.Playlet, *srvdef.SrvErr) {
|
||||
out := make(map[int64]*playletmod.Playlet, len(ids))
|
||||
if len(ids) == 0 {
|
||||
return out, nil
|
||||
}
|
||||
list, serr := playletmod.FindManyPlaylet(ctx, bson.M{
|
||||
playletmod.FN.Playlet.Id: bson.M{"$in": ids},
|
||||
}, options.Find().SetProjection(playletSalesProjection()))
|
||||
if serr != nil {
|
||||
return nil, serr
|
||||
}
|
||||
for _, item := range list {
|
||||
if item == nil || item.Id <= 0 {
|
||||
continue
|
||||
}
|
||||
out[item.Id] = item
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func aggregatePlayletUnlocks(ctx context.Context, start, end time.Time, playletIDs []int64) (map[int64]playletSalesAggRow, *srvdef.SrvErr) {
|
||||
match := bson.M{
|
||||
playletchapterunlockmod.FN.PlayletChapterUnlock.CreatedAt: bson.M{
|
||||
"$gte": start,
|
||||
"$lte": end,
|
||||
},
|
||||
}
|
||||
if len(playletIDs) > 0 {
|
||||
match[playletchapterunlockmod.FN.PlayletChapterUnlock.PlayletId] = bson.M{"$in": playletIDs}
|
||||
}
|
||||
pipeline := []bson.M{
|
||||
{"$match": match},
|
||||
{"$group": bson.M{
|
||||
"_id": "$" + playletchapterunlockmod.FN.PlayletChapterUnlock.PlayletId,
|
||||
"count": bson.M{"$sum": 1},
|
||||
"coins": bson.M{"$sum": "$" + playletchapterunlockmod.FN.PlayletChapterUnlock.Coins},
|
||||
}},
|
||||
}
|
||||
rows := make([]playletSalesAggRow, 0)
|
||||
if serr := playletchapterunlockmod.AggregatePlayletChapterUnlock(ctx, &rows, pipeline); serr != nil {
|
||||
return nil, serr
|
||||
}
|
||||
return playletSalesAggMap(rows), nil
|
||||
}
|
||||
|
||||
func aggregatePlayletMetric(ctx context.Context, start, end time.Time, playletIDs []int64, createdAtField, playletIDField string, agg func(context.Context, interface{}, []bson.M) *srvdef.SrvErr) (map[int64]playletSalesAggRow, *srvdef.SrvErr) {
|
||||
match := bson.M{
|
||||
createdAtField: bson.M{
|
||||
"$gte": start,
|
||||
"$lte": end,
|
||||
},
|
||||
}
|
||||
if len(playletIDs) > 0 {
|
||||
match[playletIDField] = bson.M{"$in": playletIDs}
|
||||
}
|
||||
pipeline := []bson.M{
|
||||
{"$match": match},
|
||||
{"$group": bson.M{
|
||||
"_id": "$" + playletIDField,
|
||||
"count": bson.M{"$sum": 1},
|
||||
}},
|
||||
}
|
||||
rows := make([]playletSalesAggRow, 0)
|
||||
if serr := agg(ctx, &rows, pipeline); serr != nil {
|
||||
return nil, serr
|
||||
}
|
||||
return playletSalesAggMap(rows), nil
|
||||
}
|
||||
|
||||
func playletSalesAggMap(rows []playletSalesAggRow) map[int64]playletSalesAggRow {
|
||||
out := make(map[int64]playletSalesAggRow, len(rows))
|
||||
for _, row := range rows {
|
||||
if row.PlayletId <= 0 {
|
||||
continue
|
||||
}
|
||||
out[row.PlayletId] = row
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func mergePlayletSalesRows(filterIDs []int64, unlocks, likes, collects map[int64]playletSalesAggRow) []playletSalesRow {
|
||||
seen := make(map[int64]struct{})
|
||||
if filterIDs != nil {
|
||||
for _, id := range filterIDs {
|
||||
if id > 0 {
|
||||
seen[id] = struct{}{}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for id := range unlocks {
|
||||
seen[id] = struct{}{}
|
||||
}
|
||||
for id := range likes {
|
||||
seen[id] = struct{}{}
|
||||
}
|
||||
for id := range collects {
|
||||
seen[id] = struct{}{}
|
||||
}
|
||||
}
|
||||
rows := make([]playletSalesRow, 0, len(seen))
|
||||
for id := range seen {
|
||||
row := playletSalesRow{Id: id}
|
||||
if item, ok := unlocks[id]; ok {
|
||||
row.UnlockCount = item.Count
|
||||
row.UnlockCoins = item.Coins
|
||||
}
|
||||
if item, ok := likes[id]; ok {
|
||||
row.LikeCount = item.Count
|
||||
}
|
||||
if item, ok := collects[id]; ok {
|
||||
row.CollectCount = item.Count
|
||||
}
|
||||
rows = append(rows, row)
|
||||
}
|
||||
return rows
|
||||
}
|
||||
|
||||
func sortPlayletSalesRows(rows []playletSalesRow, sortType proto.PlayletSalesSortType) {
|
||||
sort.SliceStable(rows, func(i, j int) bool {
|
||||
left, right := rows[i], rows[j]
|
||||
switch sortType {
|
||||
case proto.PlayletSalesSortLike:
|
||||
if left.LikeCount != right.LikeCount {
|
||||
return left.LikeCount > right.LikeCount
|
||||
}
|
||||
case proto.PlayletSalesSortCollect:
|
||||
if left.CollectCount != right.CollectCount {
|
||||
return left.CollectCount > right.CollectCount
|
||||
}
|
||||
default:
|
||||
if left.UnlockCount != right.UnlockCount {
|
||||
return left.UnlockCount > right.UnlockCount
|
||||
}
|
||||
if left.UnlockCoins != right.UnlockCoins {
|
||||
return left.UnlockCoins > right.UnlockCoins
|
||||
}
|
||||
}
|
||||
if left.UnlockCount != right.UnlockCount {
|
||||
return left.UnlockCount > right.UnlockCount
|
||||
}
|
||||
if left.LikeCount != right.LikeCount {
|
||||
return left.LikeCount > right.LikeCount
|
||||
}
|
||||
if left.CollectCount != right.CollectCount {
|
||||
return left.CollectCount > right.CollectCount
|
||||
}
|
||||
return left.Id > right.Id
|
||||
})
|
||||
}
|
||||
|
||||
func takePlayletSalesRows(rows []playletSalesRow, limit int) []playletSalesRow {
|
||||
if limit <= 0 || len(rows) <= limit {
|
||||
return rows
|
||||
}
|
||||
return rows[:limit]
|
||||
}
|
||||
|
||||
func playletSalesIDs(rows []playletSalesRow) []int64 {
|
||||
ids := make([]int64, 0, len(rows))
|
||||
for _, row := range rows {
|
||||
if row.Id > 0 {
|
||||
ids = append(ids, row.Id)
|
||||
}
|
||||
}
|
||||
return ids
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package srv_playlet
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
"videoweb/pub/common/log"
|
||||
"videoweb/pub/models/playletmod"
|
||||
"videoweb/pub/seo/sitemapdirty"
|
||||
"videoweb/web/service/srv_seopush"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
)
|
||||
|
||||
func markSitemapDirty(ctx context.Context) {
|
||||
sitemapdirty.Mark(ctx)
|
||||
}
|
||||
|
||||
// notifyPlayletSeoAfterWrite 短剧写入后:刷新 sitemap 标记。
|
||||
// 搜索引擎只在新上架(含新增即上架)或从已上架变为下架时推送,普通改内容不推。
|
||||
// prevStatus 为变更前状态;nil 表示新增。
|
||||
func notifyPlayletSeoAfterWrite(ctx context.Context, playletId int64, prevStatus *playletmod.Status) {
|
||||
markSitemapDirty(ctx)
|
||||
item, err := playletmod.FindOnePlaylet(ctx, bson.M{playletmod.FN.Playlet.Id: playletId},
|
||||
options.FindOne().SetProjection(bson.D{
|
||||
{Key: playletmod.FN.Playlet.Id, Value: 1},
|
||||
{Key: playletmod.FN.Playlet.Status, Value: 1},
|
||||
{Key: playletmod.FN.Playlet.OnlineAt, Value: 1},
|
||||
}))
|
||||
if err != nil || item == nil {
|
||||
if err != nil {
|
||||
log.Warn("seopush: load playlet after write failed", log.Any("playletId", playletId), log.E(err))
|
||||
}
|
||||
return
|
||||
}
|
||||
wasUp := prevStatus != nil && *prevStatus == playletmod.StatusUp
|
||||
if item.Status == playletmod.StatusUp {
|
||||
if !wasUp {
|
||||
srv_seopush.EnqueuePlayletUpdated(ctx, item.Id, item.Status, item.OnlineAt)
|
||||
}
|
||||
return
|
||||
}
|
||||
if wasUp {
|
||||
srv_seopush.EnqueuePlayletDeleted(ctx, item.Id)
|
||||
}
|
||||
}
|
||||
|
||||
// notifyPlayletSeoAfterStatus 批量上下架后的 SEO 通知
|
||||
func notifyPlayletSeoAfterStatus(ctx context.Context, ids []int64, status playletmod.Status, onlineAt time.Time) {
|
||||
markSitemapDirty(ctx)
|
||||
if status == playletmod.StatusUp {
|
||||
for _, id := range ids {
|
||||
srv_seopush.EnqueuePlayletUpdated(ctx, id, status, onlineAt)
|
||||
}
|
||||
return
|
||||
}
|
||||
for _, id := range ids {
|
||||
srv_seopush.EnqueuePlayletDeleted(ctx, id)
|
||||
}
|
||||
}
|
||||
|
||||
// notifyPlayletSeoAfterDelete 物理删除后刷新 sitemap 标记,并对原上架短剧推送删除通知。
|
||||
func notifyPlayletSeoAfterDelete(ctx context.Context, onlineIds []int64) {
|
||||
markSitemapDirty(ctx)
|
||||
for _, id := range onlineIds {
|
||||
srv_seopush.EnqueuePlayletDeleted(ctx, id)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,416 @@
|
||||
package srv_playlet
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"time"
|
||||
"videoweb/pub/common/playletcache"
|
||||
"videoweb/pub/common/srvdef"
|
||||
"videoweb/pub/common/utils"
|
||||
"videoweb/pub/models/playletmod"
|
||||
"videoweb/pub/models/playlettagmod"
|
||||
"videoweb/pub/models/playletthememod"
|
||||
"videoweb/web/api/proto"
|
||||
"videoweb/web/service/srv_homeconfig"
|
||||
|
||||
"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, "playlet") || srv_homeconfig.HasEmptySlug(ctx)
|
||||
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, "playlet") || srv_homeconfig.HasEmptySlug(ctx)
|
||||
playletcache.ClearHomeCache()
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
playletUpdated, playletMore, err := syncPlayletEmptySlugs(ctx, remain)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp.PlayletUpdated = playletUpdated
|
||||
remain -= playletUpdated
|
||||
if remain <= 0 {
|
||||
resp.HasMore = playletMore || srv_homeconfig.HasEmptySlug(ctx)
|
||||
playletcache.ClearHomeCache()
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
homeUpdated, homeMore, err := srv_homeconfig.SyncEmptySlugs(ctx, remain)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp.HomeConfigUpdated = homeUpdated
|
||||
resp.HasMore = playletMore || homeMore
|
||||
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 "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 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
|
||||
}
|
||||
@@ -0,0 +1,170 @@
|
||||
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()
|
||||
markSitemapDirty(ctx)
|
||||
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()
|
||||
markSitemapDirty(ctx)
|
||||
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()
|
||||
markSitemapDirty(ctx)
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,207 @@
|
||||
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()
|
||||
markSitemapDirty(ctx)
|
||||
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()
|
||||
markSitemapDirty(ctx)
|
||||
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()
|
||||
markSitemapDirty(ctx)
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
package srv_playlet
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"videoweb/pub/common/srvdef"
|
||||
"videoweb/pub/models/playletthememod"
|
||||
"videoweb/pub/models/playlettopicgroupmod"
|
||||
"videoweb/web/api/proto"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
)
|
||||
|
||||
// ListPlayletTopicGroups 查询后台专题区块
|
||||
func ListPlayletTopicGroups(ctx context.Context, req *proto.PlayletTopicGroupListReq) (*proto.PlayletTopicGroupListResp, *srvdef.SrvErr) {
|
||||
if req == nil {
|
||||
return nil, srvdef.New(srvdef.ErrParamError, "", "")
|
||||
}
|
||||
pageNum, pageSize := normalizePage(req.PageNum, req.PageSize)
|
||||
filter := bson.M{}
|
||||
if req.Status != nil {
|
||||
filter["status"] = *req.Status
|
||||
}
|
||||
totalCount, err := playlettopicgroupmod.Count(ctx, filter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp := &proto.PlayletTopicGroupListResp{
|
||||
TotalCount: totalCount, PageNum: int(pageNum), PageSize: int(pageSize),
|
||||
List: make(playlettopicgroupmod.PlayletTopicGroupList, 0),
|
||||
}
|
||||
if totalCount == 0 || (pageNum-1)*pageSize >= totalCount {
|
||||
return resp, nil
|
||||
}
|
||||
list, err := playlettopicgroupmod.FindMany(ctx, filter, options.Find().SetSkip((pageNum-1)*pageSize).SetLimit(pageSize))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp.List = list
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// AddPlayletTopicGroup 新增专题区块
|
||||
func AddPlayletTopicGroup(ctx context.Context, req *proto.PlayletTopicGroupAddReq) (*proto.PlayletTopicGroupAddResp, *srvdef.SrvErr) {
|
||||
if req == nil || strings.TrimSpace(req.Name) == "" || req.Status == nil {
|
||||
return nil, srvdef.New(srvdef.ErrParamError, "专题区块参数无效", "")
|
||||
}
|
||||
ids, err := validateTopicGroupIDs(ctx, req.TopicIds)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
doc := &playlettopicgroupmod.PlayletTopicGroup{
|
||||
Name: strings.TrimSpace(req.Name), Desc: strings.TrimSpace(req.Desc),
|
||||
TopicIds: ids, Rank: req.Rank, Status: *req.Status,
|
||||
}
|
||||
if err := playlettopicgroupmod.Insert(ctx, doc); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &proto.PlayletTopicGroupAddResp{Id: doc.Id}, nil
|
||||
}
|
||||
|
||||
// UpdatePlayletTopicGroup 更新专题区块
|
||||
func UpdatePlayletTopicGroup(ctx context.Context, req *proto.PlayletTopicGroupUpdateReq) *srvdef.SrvErr {
|
||||
if req == nil || req.Id <= 0 {
|
||||
return srvdef.New(srvdef.ErrParamError, "专题区块ID无效", "")
|
||||
}
|
||||
set := bson.M{}
|
||||
if req.Name != nil {
|
||||
name := strings.TrimSpace(*req.Name)
|
||||
if name == "" {
|
||||
return srvdef.New(srvdef.ErrParamError, "专题区块名称不能为空", "")
|
||||
}
|
||||
set["name"] = name
|
||||
}
|
||||
if req.Desc != nil {
|
||||
set["desc"] = strings.TrimSpace(*req.Desc)
|
||||
}
|
||||
if req.TopicIds != nil {
|
||||
ids, err := validateTopicGroupIDs(ctx, *req.TopicIds)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
set["topicIds"] = ids
|
||||
}
|
||||
if req.Rank != nil {
|
||||
set["rank"] = *req.Rank
|
||||
}
|
||||
if req.Status != nil {
|
||||
set["status"] = *req.Status
|
||||
}
|
||||
if len(set) == 0 {
|
||||
return srvdef.New(srvdef.ErrParamError, "没有可更新字段", "")
|
||||
}
|
||||
return playlettopicgroupmod.Update(ctx, req.Id, set)
|
||||
}
|
||||
|
||||
// DeletePlayletTopicGroup 删除专题区块
|
||||
func DeletePlayletTopicGroup(ctx context.Context, id int64) *srvdef.SrvErr {
|
||||
if id <= 0 {
|
||||
return srvdef.New(srvdef.ErrParamError, "专题区块ID无效", "")
|
||||
}
|
||||
return playlettopicgroupmod.Delete(ctx, id)
|
||||
}
|
||||
|
||||
func validateTopicGroupIDs(ctx context.Context, ids []int64) ([]int64, *srvdef.SrvErr) {
|
||||
if len(ids) == 0 {
|
||||
return []int64{}, nil
|
||||
}
|
||||
seen := make(map[int64]bool, len(ids))
|
||||
for _, id := range ids {
|
||||
if id <= 0 || seen[id] {
|
||||
return nil, srvdef.New(srvdef.ErrParamError, "专题ID无效或重复", "")
|
||||
}
|
||||
seen[id] = true
|
||||
}
|
||||
count, err := playletthememod.CountPlayletTheme(ctx, bson.M{playletthememod.FN.PlayletTheme.Id: bson.M{"$in": ids}})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if count != int64(len(ids)) {
|
||||
return nil, srvdef.New(srvdef.ErrParamError, "专题不存在", "")
|
||||
}
|
||||
return ids, nil
|
||||
}
|
||||
Reference in New Issue
Block a user