来源:hgdjnet-server 9bfbafe10d417deea959bd53117bc88a10e60a83 项目身份 HKDJ / JHA-216,独立官网仓库,Jenkins 守卫键 hkdjnet 测试目录 /server/hkdj_net,App/Web API 30331/30332 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
345 lines
11 KiB
Go
345 lines
11 KiB
Go
package srv_videoslug
|
|
|
|
import (
|
|
"context"
|
|
"hkdj-web-server/pub/common/slugx"
|
|
"hkdj-web-server/pub/common/srvdef"
|
|
"hkdj-web-server/pub/common/utils"
|
|
"hkdj-web-server/pub/models/videocategorymod"
|
|
"hkdj-web-server/pub/models/videocreatermod"
|
|
"hkdj-web-server/pub/models/videoinformationmod"
|
|
"hkdj-web-server/pub/models/videotagmod"
|
|
"hkdj-web-server/pub/models/videotopicmod"
|
|
"hkdj-web-server/web/api/proto"
|
|
"strings"
|
|
|
|
"go.mongodb.org/mongo-driver/bson"
|
|
"go.mongodb.org/mongo-driver/mongo/options"
|
|
)
|
|
|
|
const (
|
|
syncSlugDefaultSize = int64(500)
|
|
syncSlugMaxSize = int64(2000)
|
|
)
|
|
|
|
func videoExists(ctx context.Context, slug string, excludeId int64) bool {
|
|
item, _ := videoinformationmod.FindOneVideoInformation(ctx, slugx.Filter(
|
|
videoinformationmod.FN.VideoInformation.Slug, videoinformationmod.FN.VideoInformation.Id, slug, excludeId))
|
|
return item != nil
|
|
}
|
|
|
|
func tagExists(ctx context.Context, slug string, excludeId int64) bool {
|
|
item, _ := videotagmod.FindOneVideoTagInfo(ctx, slugx.Filter(
|
|
videotagmod.FN.VideoTagInfo.Slug, videotagmod.FN.VideoTagInfo.Id, slug, excludeId))
|
|
return item != nil
|
|
}
|
|
|
|
func topicExists(ctx context.Context, slug string, excludeId int64) bool {
|
|
item, _ := videotopicmod.FindOneVideoTopicModel(ctx, slugx.Filter(
|
|
videotopicmod.FN.VideoTopicModel.Slug, videotopicmod.FN.VideoTopicModel.Id, slug, excludeId))
|
|
return item != nil
|
|
}
|
|
|
|
func createrExists(ctx context.Context, slug string, excludeId int64) bool {
|
|
item, _ := videocreatermod.FindOneVideoCreaterInfo(ctx, slugx.Filter(
|
|
videocreatermod.FN.VideoCreaterInfo.Slug, videocreatermod.FN.VideoCreaterInfo.Id, slug, excludeId))
|
|
return item != nil
|
|
}
|
|
|
|
func categoryExists(ctx context.Context, slug string, excludeId int64) bool {
|
|
item, _ := videocategorymod.FindOneVideoCategory(ctx, slugx.Filter(
|
|
videocategorymod.FN.VideoCategory.Slug, videocategorymod.FN.VideoCategory.Id, slug, excludeId))
|
|
return item != nil
|
|
}
|
|
|
|
func EnsureVideo(ctx context.Context, info *videoinformationmod.VideoInformation, excludeId int64) *srvdef.SrvErr {
|
|
if info == nil {
|
|
return nil
|
|
}
|
|
return slugx.Ensure(&info.Slug, utils.SlugSourceFromKeywordsOrTitle(info.Keyworld, info.Title),
|
|
func(s string) bool { return videoExists(ctx, s, excludeId) })
|
|
}
|
|
|
|
func PrepareVideoOnUpdate(ctx context.Context, info *videoinformationmod.VideoInformation, reqSlug *string, title string, keyworld []string) (string, bool, *srvdef.SrvErr) {
|
|
if info == nil {
|
|
return "", false, nil
|
|
}
|
|
return slugx.PrepareOnUpdate(info.Slug, reqSlug, utils.SlugSourceFromKeywordsOrTitle(keyworld, title),
|
|
func(s string) bool { return videoExists(ctx, s, info.Id) })
|
|
}
|
|
|
|
func EnsureTag(ctx context.Context, info *videotagmod.VideoTagInfo, excludeId int64) *srvdef.SrvErr {
|
|
if info == nil {
|
|
return nil
|
|
}
|
|
return slugx.Ensure(&info.Slug, utils.SlugSourceFromKeywordsOrTitle(info.SeoInfo.Keyworld, info.Title),
|
|
func(s string) bool { return tagExists(ctx, s, excludeId) })
|
|
}
|
|
|
|
func PrepareTagOnUpdate(ctx context.Context, info *videotagmod.VideoTagInfo, reqSlug *string, title string, keywords []string) (string, bool, *srvdef.SrvErr) {
|
|
if info == nil {
|
|
return "", false, nil
|
|
}
|
|
return slugx.PrepareOnUpdate(info.Slug, reqSlug, utils.SlugSourceFromKeywordsOrTitle(keywords, title),
|
|
func(s string) bool { return tagExists(ctx, s, info.Id) })
|
|
}
|
|
|
|
func EnsureTopic(ctx context.Context, info *videotopicmod.VideoTopicModel, excludeId int64) *srvdef.SrvErr {
|
|
if info == nil {
|
|
return nil
|
|
}
|
|
return slugx.Ensure(&info.Slug, utils.SlugSourceFromKeywordsOrTitle(info.SeoInfo.Keyworld, info.Name),
|
|
func(s string) bool { return topicExists(ctx, s, excludeId) })
|
|
}
|
|
|
|
func PrepareTopicOnUpdate(ctx context.Context, info *videotopicmod.VideoTopicModel, reqSlug *string, name string, keywords []string) (string, bool, *srvdef.SrvErr) {
|
|
if info == nil {
|
|
return "", false, nil
|
|
}
|
|
return slugx.PrepareOnUpdate(info.Slug, reqSlug, utils.SlugSourceFromKeywordsOrTitle(keywords, name),
|
|
func(s string) bool { return topicExists(ctx, s, info.Id) })
|
|
}
|
|
|
|
func EnsureCreater(ctx context.Context, info *videocreatermod.VideoCreaterInfo, excludeId int64) *srvdef.SrvErr {
|
|
if info == nil {
|
|
return nil
|
|
}
|
|
return slugx.Ensure(&info.Slug, utils.SlugSourceFromKeywordsOrTitle(info.SeoInfo.Keyworld, info.Name),
|
|
func(s string) bool { return createrExists(ctx, s, excludeId) })
|
|
}
|
|
|
|
func PrepareCreaterOnUpdate(ctx context.Context, info *videocreatermod.VideoCreaterInfo, reqSlug *string, name string, keywords []string) (string, bool, *srvdef.SrvErr) {
|
|
if info == nil {
|
|
return "", false, nil
|
|
}
|
|
return slugx.PrepareOnUpdate(info.Slug, reqSlug, utils.SlugSourceFromKeywordsOrTitle(keywords, name),
|
|
func(s string) bool { return createrExists(ctx, s, info.Id) })
|
|
}
|
|
|
|
func EnsureCategory(ctx context.Context, info *videocategorymod.VideoCategory, excludeId int64) *srvdef.SrvErr {
|
|
if info == nil {
|
|
return nil
|
|
}
|
|
return slugx.Ensure(&info.Slug, utils.SlugSourceFromKeywordsOrTitle(info.SeoInfo.Keyworld, info.Name),
|
|
func(s string) bool { return categoryExists(ctx, s, excludeId) })
|
|
}
|
|
|
|
func PrepareCategoryOnUpdate(ctx context.Context, info *videocategorymod.VideoCategory, reqSlug *string, name string, keywords []string) (string, bool, *srvdef.SrvErr) {
|
|
if info == nil {
|
|
return "", false, nil
|
|
}
|
|
return slugx.PrepareOnUpdate(info.Slug, reqSlug, utils.SlugSourceFromKeywordsOrTitle(keywords, name),
|
|
func(s string) bool { return categoryExists(ctx, s, info.Id) })
|
|
}
|
|
|
|
// SyncEmptySlugs 为视频、标签、题材、作者、分类中空 slug 自动生成。
|
|
func SyncEmptySlugs(ctx context.Context, req *proto.VideoSlugSyncReq) (*proto.VideoSlugSyncResp, *srvdef.SrvErr) {
|
|
maxSize := syncSlugDefaultSize
|
|
if req != nil && req.MaxSize > 0 {
|
|
maxSize = req.MaxSize
|
|
}
|
|
if maxSize > syncSlugMaxSize {
|
|
maxSize = syncSlugMaxSize
|
|
}
|
|
resp := &proto.VideoSlugSyncResp{}
|
|
remain := maxSize
|
|
|
|
categoryUpdated, categoryMore, err := syncCategoryEmptySlugs(ctx, remain)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
resp.CategoryUpdated = categoryUpdated
|
|
remain -= categoryUpdated
|
|
if remain <= 0 {
|
|
resp.HasMore = categoryMore || hasEmpty(ctx, "tag") || hasEmpty(ctx, "topic") || hasEmpty(ctx, "creater") || hasEmpty(ctx, "video")
|
|
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 || hasEmpty(ctx, "topic") || hasEmpty(ctx, "creater") || hasEmpty(ctx, "video")
|
|
return resp, nil
|
|
}
|
|
|
|
topicUpdated, topicMore, err := syncTopicEmptySlugs(ctx, remain)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
resp.TopicUpdated = topicUpdated
|
|
remain -= topicUpdated
|
|
if remain <= 0 {
|
|
resp.HasMore = topicMore || hasEmpty(ctx, "creater") || hasEmpty(ctx, "video")
|
|
return resp, nil
|
|
}
|
|
|
|
createrUpdated, createrMore, err := syncCreaterEmptySlugs(ctx, remain)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
resp.CreaterUpdated = createrUpdated
|
|
remain -= createrUpdated
|
|
if remain <= 0 {
|
|
resp.HasMore = createrMore || hasEmpty(ctx, "video")
|
|
return resp, nil
|
|
}
|
|
|
|
videoUpdated, videoMore, err := syncVideoEmptySlugs(ctx, remain)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
resp.VideoUpdated = videoUpdated
|
|
resp.HasMore = videoMore
|
|
return resp, nil
|
|
}
|
|
|
|
func hasEmpty(ctx context.Context, kind string) bool {
|
|
filter := slugx.EmptyFilter()
|
|
switch kind {
|
|
case "tag":
|
|
n, _ := videotagmod.CountVideoTagInfos(ctx, filter)
|
|
return n > 0
|
|
case "topic":
|
|
n, _ := videotopicmod.CountVideoTopicModels(ctx, filter)
|
|
return n > 0
|
|
case "creater":
|
|
n, _ := videocreatermod.CountVideoCreaterInfos(ctx, filter)
|
|
return n > 0
|
|
case "video":
|
|
n, _ := videoinformationmod.CountVideoInformations(ctx, filter)
|
|
return n > 0
|
|
default:
|
|
n, _ := videocategorymod.CountVideoCategorys(ctx, filter)
|
|
return n > 0
|
|
}
|
|
}
|
|
|
|
func syncCategoryEmptySlugs(ctx context.Context, maxSize int64) (int64, bool, *srvdef.SrvErr) {
|
|
list, err := videocategorymod.FindManyVideoCategorys(ctx, slugx.EmptyFilter(), options.Find().SetLimit(maxSize+1))
|
|
if err != nil {
|
|
return 0, false, err
|
|
}
|
|
more := int64(len(list)) > maxSize
|
|
if more {
|
|
list = list[:maxSize]
|
|
}
|
|
var updated int64
|
|
for _, item := range list {
|
|
if item == nil {
|
|
continue
|
|
}
|
|
if serr := EnsureCategory(ctx, item, item.Id); serr != nil || strings.TrimSpace(item.Slug) == "" {
|
|
continue
|
|
}
|
|
_ = videocategorymod.UpdateOneVideoCategory(ctx, bson.M{videocategorymod.FN.VideoCategory.Id: item.Id}, bson.M{
|
|
"$set": bson.M{videocategorymod.FN.VideoCategory.Slug: item.Slug},
|
|
})
|
|
updated++
|
|
}
|
|
return updated, more, nil
|
|
}
|
|
|
|
func syncTagEmptySlugs(ctx context.Context, maxSize int64) (int64, bool, *srvdef.SrvErr) {
|
|
list, err := videotagmod.FindManyVideoTagInfos(ctx, slugx.EmptyFilter(), options.Find().SetLimit(maxSize+1))
|
|
if err != nil {
|
|
return 0, false, err
|
|
}
|
|
more := int64(len(list)) > maxSize
|
|
if more {
|
|
list = list[:maxSize]
|
|
}
|
|
var updated int64
|
|
for _, item := range list {
|
|
if item == nil {
|
|
continue
|
|
}
|
|
if serr := EnsureTag(ctx, item, item.Id); serr != nil || strings.TrimSpace(item.Slug) == "" {
|
|
continue
|
|
}
|
|
_ = videotagmod.UpdateOneVideoTagInfo(ctx, bson.M{videotagmod.FN.VideoTagInfo.Id: item.Id}, bson.M{
|
|
"$set": bson.M{videotagmod.FN.VideoTagInfo.Slug: item.Slug},
|
|
})
|
|
updated++
|
|
}
|
|
return updated, more, nil
|
|
}
|
|
|
|
func syncTopicEmptySlugs(ctx context.Context, maxSize int64) (int64, bool, *srvdef.SrvErr) {
|
|
list, err := videotopicmod.FindManyVideoTopicModels(ctx, slugx.EmptyFilter(), options.Find().SetLimit(maxSize+1))
|
|
if err != nil {
|
|
return 0, false, err
|
|
}
|
|
more := int64(len(list)) > maxSize
|
|
if more {
|
|
list = list[:maxSize]
|
|
}
|
|
var updated int64
|
|
for _, item := range list {
|
|
if item == nil {
|
|
continue
|
|
}
|
|
if serr := EnsureTopic(ctx, item, item.Id); serr != nil || strings.TrimSpace(item.Slug) == "" {
|
|
continue
|
|
}
|
|
_ = videotopicmod.UpdateOneVideoTopicModel(ctx, bson.M{videotopicmod.FN.VideoTopicModel.Id: item.Id}, bson.M{
|
|
"$set": bson.M{videotopicmod.FN.VideoTopicModel.Slug: item.Slug},
|
|
})
|
|
updated++
|
|
}
|
|
return updated, more, nil
|
|
}
|
|
|
|
func syncCreaterEmptySlugs(ctx context.Context, maxSize int64) (int64, bool, *srvdef.SrvErr) {
|
|
list, err := videocreatermod.FindManyVideoCreaterInfos(ctx, slugx.EmptyFilter(), options.Find().SetLimit(maxSize+1))
|
|
if err != nil {
|
|
return 0, false, err
|
|
}
|
|
more := int64(len(list)) > maxSize
|
|
if more {
|
|
list = list[:maxSize]
|
|
}
|
|
var updated int64
|
|
for _, item := range list {
|
|
if item == nil {
|
|
continue
|
|
}
|
|
if serr := EnsureCreater(ctx, item, item.Id); serr != nil || strings.TrimSpace(item.Slug) == "" {
|
|
continue
|
|
}
|
|
_ = videocreatermod.UpdateOneVideoCreaterInfo(ctx, bson.M{videocreatermod.FN.VideoCreaterInfo.Id: item.Id}, bson.M{
|
|
"$set": bson.M{videocreatermod.FN.VideoCreaterInfo.Slug: item.Slug},
|
|
})
|
|
updated++
|
|
}
|
|
return updated, more, nil
|
|
}
|
|
|
|
func syncVideoEmptySlugs(ctx context.Context, maxSize int64) (int64, bool, *srvdef.SrvErr) {
|
|
list, err := videoinformationmod.FindManyVideoInformations(ctx, slugx.EmptyFilter(), options.Find().SetLimit(maxSize+1))
|
|
if err != nil {
|
|
return 0, false, err
|
|
}
|
|
more := int64(len(list)) > maxSize
|
|
if more {
|
|
list = list[:maxSize]
|
|
}
|
|
var updated int64
|
|
for _, item := range list {
|
|
if item == nil {
|
|
continue
|
|
}
|
|
if serr := EnsureVideo(ctx, item, item.Id); serr != nil || strings.TrimSpace(item.Slug) == "" {
|
|
continue
|
|
}
|
|
_ = videoinformationmod.UpdateOneVideoInformation(ctx, bson.M{videoinformationmod.FN.VideoInformation.Id: item.Id}, bson.M{
|
|
"$set": bson.M{videoinformationmod.FN.VideoInformation.Slug: item.Slug},
|
|
})
|
|
updated++
|
|
}
|
|
return updated, more, nil
|
|
}
|