Files
web_huakui_server/web/service/srv_videoslug/slug.go
T
rootandClaude Opus 4.7 85c0071fd8 feat: 从黄果 NET hgdjnet-server 9bfbafe 导入 HKDJ 官网后端
来源: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>
2026-10-16 11:20:04 +08:00

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
}