按意图重做在 huakui 代码底: - 删除数据埋点逻辑:移除 pub/dataReport、reportser、srv_report、 eventmod、上报定时任务及注册/登录/点赞/收藏/评论/解锁/订单处的 上报调用和 reportSdkConfig 配置下发 - app 认证改为按设备ID识别用户:去掉 token 校验、Register/Login/ ResetPassword/GuestLogin 接口,新增 ResolveDeviceUser 和设备缓存 - 广告改为按 AppId 从媒资中心拉取:pub/storage 新增 QueryAdvitise, UpdateAdvitiseData 替代 UpdateAdvitiseDataFromJt - 后台登录取消谷歌二次验证,密码正确即登录 - 短剧/专题 slug 改为 10 位大小写字母+数字混合 - 本地 app.yaml/web.yaml 保留 HKDJNET_* 模板(不提交生产凭据) Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
397 lines
12 KiB
Go
397 lines
12 KiB
Go
package srv_playlet
|
|
|
|
import (
|
|
"context"
|
|
"hkdj-web-server/pub/common/playletcache"
|
|
"hkdj-web-server/pub/common/srvdef"
|
|
"hkdj-web-server/pub/common/utils"
|
|
"hkdj-web-server/pub/models/playletmod"
|
|
"hkdj-web-server/pub/models/playlettagmod"
|
|
"hkdj-web-server/pub/models/playletthememod"
|
|
"hkdj-web-server/web/api/proto"
|
|
"hkdj-web-server/web/service/srv_homeconfig"
|
|
"strings"
|
|
"time"
|
|
|
|
"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
|
|
}
|
|
ac.Slug = utils.MakeRandomSlug(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
|
|
}
|
|
newSlug := utils.MakeRandomSlug(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
|
|
}
|
|
ac.Slug = utils.MakeRandomSlug(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
|
|
}
|
|
newSlug := utils.MakeRandomSlug(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
|
|
}
|