404 lines
15 KiB
Go
404 lines
15 KiB
Go
package srv_slug
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
"time"
|
|
"videoweb/pub/common/slugx"
|
|
"videoweb/pub/common/srvdef"
|
|
"videoweb/pub/common/utils"
|
|
"videoweb/pub/models/actress"
|
|
"videoweb/pub/models/contenttagmod"
|
|
"videoweb/pub/models/playletcategorymod"
|
|
"videoweb/pub/models/playletchaptermod"
|
|
"videoweb/pub/models/playletmod"
|
|
"videoweb/pub/models/playletthememod"
|
|
"videoweb/pub/models/videocategorymod"
|
|
"videoweb/pub/models/videocreatermod"
|
|
"videoweb/pub/models/videoinformationmod"
|
|
"videoweb/web/api/proto"
|
|
|
|
"go.mongodb.org/mongo-driver/bson"
|
|
"go.mongodb.org/mongo-driver/mongo/options"
|
|
)
|
|
|
|
const (
|
|
syncSlugDefaultSize = int64(500)
|
|
syncSlugMaxSize = int64(2000)
|
|
|
|
TypeVideo = "video"
|
|
TypeVideoCategory = "videoCategory"
|
|
TypeActress = "actress"
|
|
TypeVideoCreater = "videoCreater"
|
|
TypePlaylet = "playlet"
|
|
TypePlayletCategory = "playletCategory"
|
|
TypePlayletTheme = "playletTheme"
|
|
TypeContentTag = "contentTag"
|
|
)
|
|
|
|
type slugJob struct {
|
|
kind string
|
|
set func(*proto.SlugSyncResp, int64)
|
|
run func(context.Context, int64) (int64, bool, *srvdef.SrvErr)
|
|
hasMore func(context.Context) bool
|
|
}
|
|
|
|
// SyncEmptySlugs 为视频、分类、女优、作者、短剧、统一标签中空 slug 自动生成。可重复调用,hasMore 为 true 时继续请求。
|
|
func SyncEmptySlugs(ctx context.Context, req *proto.SlugSyncReq) (*proto.SlugSyncResp, *srvdef.SrvErr) {
|
|
maxSize := syncSlugDefaultSize
|
|
if req != nil && req.MaxSize > 0 {
|
|
maxSize = req.MaxSize
|
|
}
|
|
if maxSize > syncSlugMaxSize {
|
|
maxSize = syncSlugMaxSize
|
|
}
|
|
kind := ""
|
|
if req != nil {
|
|
kind = strings.TrimSpace(req.Type)
|
|
}
|
|
|
|
jobs := allSlugJobs()
|
|
if kind != "" {
|
|
job, ok := jobByKind(jobs, kind)
|
|
if !ok {
|
|
return nil, srvdef.New(srvdef.ErrParamError, "不支持的type", "不支持的type")
|
|
}
|
|
jobs = []slugJob{job}
|
|
}
|
|
|
|
resp := &proto.SlugSyncResp{}
|
|
remain := maxSize
|
|
for i, job := range jobs {
|
|
if remain <= 0 {
|
|
resp.HasMore = anyHasEmpty(ctx, jobs[i:])
|
|
resp.TotalUpdated = sumUpdated(resp)
|
|
return resp, nil
|
|
}
|
|
updated, more, err := job.run(ctx, remain)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
job.set(resp, updated)
|
|
remain -= updated
|
|
if more {
|
|
resp.HasMore = true
|
|
resp.TotalUpdated = sumUpdated(resp)
|
|
return resp, nil
|
|
}
|
|
}
|
|
resp.TotalUpdated = sumUpdated(resp)
|
|
return resp, nil
|
|
}
|
|
|
|
func allSlugJobs() []slugJob {
|
|
return []slugJob{
|
|
{TypeVideoCategory, func(r *proto.SlugSyncResp, n int64) { r.VideoCategoryUpdated = n }, syncVideoCategoryEmptySlugs, hasVideoCategoryEmpty},
|
|
{TypePlayletCategory, func(r *proto.SlugSyncResp, n int64) { r.PlayletCategoryUpdated = n }, syncPlayletCategoryEmptySlugs, hasPlayletCategoryEmpty},
|
|
{TypePlayletTheme, func(r *proto.SlugSyncResp, n int64) { r.PlayletThemeUpdated = n }, syncPlayletThemeEmptySlugs, hasPlayletThemeEmpty},
|
|
{TypeContentTag, func(r *proto.SlugSyncResp, n int64) { r.ContentTagUpdated = n }, syncContentTagEmptySlugs, hasContentTagEmpty},
|
|
{TypeActress, func(r *proto.SlugSyncResp, n int64) { r.ActressUpdated = n }, syncActressEmptySlugs, hasActressEmpty},
|
|
{TypeVideoCreater, func(r *proto.SlugSyncResp, n int64) { r.VideoCreaterUpdated = n }, syncVideoCreaterEmptySlugs, hasVideoCreaterEmpty},
|
|
{TypePlaylet, func(r *proto.SlugSyncResp, n int64) { r.PlayletUpdated = n }, syncPlayletEmptySlugs, hasPlayletEmpty},
|
|
{TypeVideo, func(r *proto.SlugSyncResp, n int64) { r.VideoUpdated = n }, syncVideoEmptySlugs, hasVideoEmpty},
|
|
}
|
|
}
|
|
|
|
func jobByKind(jobs []slugJob, kind string) (slugJob, bool) {
|
|
for _, job := range jobs {
|
|
if job.kind == kind {
|
|
return job, true
|
|
}
|
|
}
|
|
return slugJob{}, false
|
|
}
|
|
|
|
func anyHasEmpty(ctx context.Context, jobs []slugJob) bool {
|
|
for _, job := range jobs {
|
|
if job.hasMore(ctx) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func sumUpdated(resp *proto.SlugSyncResp) int64 {
|
|
return resp.VideoUpdated + resp.VideoCategoryUpdated + resp.ActressUpdated +
|
|
resp.VideoCreaterUpdated +
|
|
resp.PlayletUpdated + resp.PlayletCategoryUpdated +
|
|
resp.PlayletThemeUpdated + resp.ContentTagUpdated
|
|
}
|
|
|
|
func findLimit(maxSize int64) *options.FindOptions {
|
|
return options.Find().SetSort(bson.D{{Key: "_id", Value: 1}}).SetLimit(maxSize + 1)
|
|
}
|
|
|
|
func fillEmptySlugs[T any](
|
|
ctx context.Context,
|
|
list []T,
|
|
maxSize int64,
|
|
idOf func(T) int64,
|
|
sourceOf func(T) string,
|
|
exists func(slug string, excludeId int64) bool,
|
|
update func(context.Context, int64, string) *srvdef.SrvErr,
|
|
) (int64, bool, *srvdef.SrvErr) {
|
|
hasMore := int64(len(list)) > maxSize
|
|
if hasMore {
|
|
list = list[:maxSize]
|
|
}
|
|
var updated int64
|
|
for _, item := range list {
|
|
if any(item) == nil {
|
|
continue
|
|
}
|
|
id := idOf(item)
|
|
if id <= 0 {
|
|
continue
|
|
}
|
|
slug := ""
|
|
if serr := slugx.Ensure(&slug, sourceOf(item), func(s string) bool {
|
|
return exists(s, id)
|
|
}); serr != nil {
|
|
return updated, hasMore, serr
|
|
}
|
|
if serr := update(ctx, id, slug); serr != nil {
|
|
return updated, hasMore, serr
|
|
}
|
|
updated++
|
|
}
|
|
return updated, hasMore, nil
|
|
}
|
|
|
|
func setSlug(ctx context.Context, updateOne func(context.Context, bson.M, bson.M) *srvdef.SrvErr, idField, slugField, updatedAtField string, id int64, slug string) *srvdef.SrvErr {
|
|
setDoc := bson.M{slugField: slug}
|
|
if updatedAtField != "" {
|
|
setDoc[updatedAtField] = time.Now()
|
|
}
|
|
return updateOne(ctx, bson.M{idField: id}, bson.M{"$set": setDoc})
|
|
}
|
|
|
|
func videoEmptySlugFilter(ctx context.Context) bson.M {
|
|
filter := slugx.EmptyFilter()
|
|
playletIDs := playletchaptermod.ListLinkedMediaIDs(ctx)
|
|
if len(playletIDs) == 0 {
|
|
return filter
|
|
}
|
|
return bson.M{
|
|
"$and": []bson.M{
|
|
filter,
|
|
{
|
|
"$nor": []bson.M{
|
|
{videoinformationmod.FN.VideoInformation.SyncId: bson.M{"$in": playletIDs}},
|
|
{videoinformationmod.FN.VideoInformation.Video.FileId: bson.M{"$in": playletIDs}},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
func hasVideoEmpty(ctx context.Context) bool {
|
|
n, _ := videoinformationmod.CountVideoInformations(ctx, videoEmptySlugFilter(ctx))
|
|
return n > 0
|
|
}
|
|
func hasVideoCategoryEmpty(ctx context.Context) bool {
|
|
n, _ := videocategorymod.CountVideoCategorys(ctx, slugx.EmptyFilter())
|
|
return n > 0
|
|
}
|
|
func hasActressEmpty(ctx context.Context) bool {
|
|
n, _ := actress.CountActresss(ctx, slugx.EmptyFilter())
|
|
return n > 0
|
|
}
|
|
func hasVideoCreaterEmpty(ctx context.Context) bool {
|
|
n, _ := videocreatermod.CountVideoCreaterInfos(ctx, slugx.EmptyFilter())
|
|
return n > 0
|
|
}
|
|
func hasPlayletEmpty(ctx context.Context) bool {
|
|
n, _ := playletmod.CountPlaylet(ctx, slugx.EmptyFilter())
|
|
return n > 0
|
|
}
|
|
func hasPlayletCategoryEmpty(ctx context.Context) bool {
|
|
n, _ := playletcategorymod.CountPlayletCategory(ctx, slugx.EmptyFilter())
|
|
return n > 0
|
|
}
|
|
func hasPlayletThemeEmpty(ctx context.Context) bool {
|
|
n, _ := playletthememod.CountPlayletTheme(ctx, slugx.EmptyFilter())
|
|
return n > 0
|
|
}
|
|
func hasContentTagEmpty(ctx context.Context) bool {
|
|
n, _ := contenttagmod.CountContentTag(ctx, slugx.EmptyFilter())
|
|
return n > 0
|
|
}
|
|
|
|
func syncVideoEmptySlugs(ctx context.Context, maxSize int64) (int64, bool, *srvdef.SrvErr) {
|
|
list, err := videoinformationmod.FindManyVideoInformations(ctx, videoEmptySlugFilter(ctx), findLimit(maxSize))
|
|
if err != nil {
|
|
return 0, false, err
|
|
}
|
|
return fillEmptySlugs(ctx, []*videoinformationmod.VideoInformation(list), maxSize,
|
|
func(item *videoinformationmod.VideoInformation) int64 { return item.Id },
|
|
func(item *videoinformationmod.VideoInformation) string {
|
|
return utils.SlugSourceFromKeywordsOrTitle(item.Keyworld, item.Title)
|
|
},
|
|
func(s string, excludeId int64) bool {
|
|
doc, _ := videoinformationmod.FindOneVideoInformation(ctx, slugx.Filter(
|
|
videoinformationmod.FN.VideoInformation.Slug, videoinformationmod.FN.VideoInformation.Id, s, excludeId))
|
|
return doc != nil
|
|
},
|
|
func(ctx context.Context, id int64, slug string) *srvdef.SrvErr {
|
|
return setSlug(ctx, videoinformationmod.UpdateOneVideoInformation, videoinformationmod.FN.VideoInformation.Id,
|
|
videoinformationmod.FN.VideoInformation.Slug, videoinformationmod.FN.VideoInformation.UpdatedAt, id, slug)
|
|
},
|
|
)
|
|
}
|
|
|
|
func syncVideoCategoryEmptySlugs(ctx context.Context, maxSize int64) (int64, bool, *srvdef.SrvErr) {
|
|
list, err := videocategorymod.FindManyVideoCategorys(ctx, slugx.EmptyFilter(), findLimit(maxSize))
|
|
if err != nil {
|
|
return 0, false, err
|
|
}
|
|
return fillEmptySlugs(ctx, []*videocategorymod.VideoCategory(list), maxSize,
|
|
func(item *videocategorymod.VideoCategory) int64 { return item.Id },
|
|
func(item *videocategorymod.VideoCategory) string {
|
|
return utils.SlugSourceFromKeywordsOrTitle(item.SeoInfo.Keyworld, item.Name)
|
|
},
|
|
func(s string, excludeId int64) bool {
|
|
doc, _ := videocategorymod.FindOneVideoCategory(ctx, slugx.Filter(
|
|
videocategorymod.FN.VideoCategory.Slug, videocategorymod.FN.VideoCategory.Id, s, excludeId))
|
|
return doc != nil
|
|
},
|
|
func(ctx context.Context, id int64, slug string) *srvdef.SrvErr {
|
|
return setSlug(ctx, videocategorymod.UpdateOneVideoCategory, videocategorymod.FN.VideoCategory.Id,
|
|
videocategorymod.FN.VideoCategory.Slug, videocategorymod.FN.VideoCategory.UpdatedAt, id, slug)
|
|
},
|
|
)
|
|
}
|
|
|
|
func syncActressEmptySlugs(ctx context.Context, maxSize int64) (int64, bool, *srvdef.SrvErr) {
|
|
list, err := actress.FindManyActresss(ctx, slugx.EmptyFilter(), findLimit(maxSize))
|
|
if err != nil {
|
|
return 0, false, err
|
|
}
|
|
return fillEmptySlugs(ctx, []*actress.Actress(list), maxSize,
|
|
func(item *actress.Actress) int64 { return item.Id },
|
|
func(item *actress.Actress) string {
|
|
return utils.SlugSourceFromKeywordsOrTitle(item.SeoInfo.Keyworld, item.Alias)
|
|
},
|
|
func(s string, excludeId int64) bool {
|
|
doc, _ := actress.FindOneActress(ctx, slugx.Filter(actress.FN.Actress.Slug, actress.FN.Actress.Id, s, excludeId))
|
|
return doc != nil
|
|
},
|
|
func(ctx context.Context, id int64, slug string) *srvdef.SrvErr {
|
|
return setSlug(ctx, actress.UpdateOneActress, actress.FN.Actress.Id,
|
|
actress.FN.Actress.Slug, actress.FN.Actress.UpdatedAt, id, slug)
|
|
},
|
|
)
|
|
}
|
|
|
|
func syncVideoCreaterEmptySlugs(ctx context.Context, maxSize int64) (int64, bool, *srvdef.SrvErr) {
|
|
list, err := videocreatermod.FindManyVideoCreaterInfos(ctx, slugx.EmptyFilter(), findLimit(maxSize))
|
|
if err != nil {
|
|
return 0, false, err
|
|
}
|
|
return fillEmptySlugs(ctx, []*videocreatermod.VideoCreaterInfo(list), maxSize,
|
|
func(item *videocreatermod.VideoCreaterInfo) int64 { return item.Id },
|
|
func(item *videocreatermod.VideoCreaterInfo) string {
|
|
return utils.SlugSourceFromKeywordsOrTitle(item.SeoInfo.Keyworld, item.Name)
|
|
},
|
|
func(s string, excludeId int64) bool {
|
|
doc, _ := videocreatermod.FindOneVideoCreaterInfo(ctx, slugx.Filter(
|
|
videocreatermod.FN.VideoCreaterInfo.Slug, videocreatermod.FN.VideoCreaterInfo.Id, s, excludeId))
|
|
return doc != nil
|
|
},
|
|
func(ctx context.Context, id int64, slug string) *srvdef.SrvErr {
|
|
return setSlug(ctx, videocreatermod.UpdateOneVideoCreaterInfo, videocreatermod.FN.VideoCreaterInfo.Id,
|
|
videocreatermod.FN.VideoCreaterInfo.Slug, videocreatermod.FN.VideoCreaterInfo.UpdatedAt, id, slug)
|
|
},
|
|
)
|
|
}
|
|
|
|
func syncPlayletEmptySlugs(ctx context.Context, maxSize int64) (int64, bool, *srvdef.SrvErr) {
|
|
list, err := playletmod.FindManyPlaylet(ctx, slugx.EmptyFilter(), findLimit(maxSize))
|
|
if err != nil {
|
|
return 0, false, err
|
|
}
|
|
return fillEmptySlugs(ctx, []*playletmod.Playlet(list), maxSize,
|
|
func(item *playletmod.Playlet) int64 { return item.Id },
|
|
func(item *playletmod.Playlet) string {
|
|
return utils.SlugSourceFromKeywordsOrTitle(item.SeoInfo.Keyworld, item.Title)
|
|
},
|
|
func(s string, excludeId int64) bool {
|
|
doc, _ := playletmod.FindOnePlaylet(ctx, slugx.Filter(playletmod.FN.Playlet.Slug, playletmod.FN.Playlet.Id, s, excludeId))
|
|
return doc != nil
|
|
},
|
|
func(ctx context.Context, id int64, slug string) *srvdef.SrvErr {
|
|
return setSlug(ctx, playletmod.UpdateOnePlaylet, playletmod.FN.Playlet.Id,
|
|
playletmod.FN.Playlet.Slug, playletmod.FN.Playlet.UpdatedAt, id, slug)
|
|
},
|
|
)
|
|
}
|
|
|
|
func syncPlayletCategoryEmptySlugs(ctx context.Context, maxSize int64) (int64, bool, *srvdef.SrvErr) {
|
|
list, err := playletcategorymod.FindManyPlayletCategory(ctx, slugx.EmptyFilter(), findLimit(maxSize))
|
|
if err != nil {
|
|
return 0, false, err
|
|
}
|
|
return fillEmptySlugs(ctx, []*playletcategorymod.PlayletCategory(list), maxSize,
|
|
func(item *playletcategorymod.PlayletCategory) int64 { return item.Id },
|
|
func(item *playletcategorymod.PlayletCategory) string {
|
|
return utils.SlugSourceFromKeywordsOrTitle(item.SeoInfo.Keyworld, item.Name)
|
|
},
|
|
func(s string, excludeId int64) bool {
|
|
doc, _ := playletcategorymod.FindOnePlayletCategory(ctx, slugx.Filter(
|
|
playletcategorymod.FN.PlayletCategory.Slug, playletcategorymod.FN.PlayletCategory.Id, s, excludeId))
|
|
return doc != nil
|
|
},
|
|
func(ctx context.Context, id int64, slug string) *srvdef.SrvErr {
|
|
return setSlug(ctx, playletcategorymod.UpdateOnePlayletCategory, playletcategorymod.FN.PlayletCategory.Id,
|
|
playletcategorymod.FN.PlayletCategory.Slug, playletcategorymod.FN.PlayletCategory.UpdatedAt, id, slug)
|
|
},
|
|
)
|
|
}
|
|
|
|
func syncPlayletThemeEmptySlugs(ctx context.Context, maxSize int64) (int64, bool, *srvdef.SrvErr) {
|
|
list, err := playletthememod.FindManyPlayletTheme(ctx, slugx.EmptyFilter(), findLimit(maxSize))
|
|
if err != nil {
|
|
return 0, false, err
|
|
}
|
|
return fillEmptySlugs(ctx, []*playletthememod.PlayletTheme(list), maxSize,
|
|
func(item *playletthememod.PlayletTheme) int64 { return item.Id },
|
|
func(item *playletthememod.PlayletTheme) string {
|
|
return utils.SlugSourceFromKeywordsOrTitle(item.SeoInfo.Keyworld, item.Name)
|
|
},
|
|
func(s string, excludeId int64) bool {
|
|
doc, _ := playletthememod.FindOnePlayletTheme(ctx, slugx.Filter(
|
|
playletthememod.FN.PlayletTheme.Slug, playletthememod.FN.PlayletTheme.Id, s, excludeId))
|
|
return doc != nil
|
|
},
|
|
func(ctx context.Context, id int64, slug string) *srvdef.SrvErr {
|
|
return setSlug(ctx, playletthememod.UpdateOnePlayletTheme, playletthememod.FN.PlayletTheme.Id,
|
|
playletthememod.FN.PlayletTheme.Slug, playletthememod.FN.PlayletTheme.UpdatedAt, id, slug)
|
|
},
|
|
)
|
|
}
|
|
|
|
func syncContentTagEmptySlugs(ctx context.Context, maxSize int64) (int64, bool, *srvdef.SrvErr) {
|
|
list, err := contenttagmod.FindManyContentTag(ctx, slugx.EmptyFilter(), findLimit(maxSize))
|
|
if err != nil {
|
|
return 0, false, err
|
|
}
|
|
return fillEmptySlugs(ctx, []*contenttagmod.ContentTag(list), maxSize,
|
|
func(item *contenttagmod.ContentTag) int64 { return item.Id },
|
|
func(item *contenttagmod.ContentTag) string {
|
|
return utils.SlugSourceFromKeywordsOrTitle(item.SeoInfo.Keyworld, item.Name)
|
|
},
|
|
func(s string, excludeId int64) bool {
|
|
doc, _ := contenttagmod.FindOneContentTag(ctx, slugx.Filter(
|
|
contenttagmod.FN.ContentTag.Slug, contenttagmod.FN.ContentTag.Id, s, excludeId))
|
|
return doc != nil
|
|
},
|
|
func(ctx context.Context, id int64, slug string) *srvdef.SrvErr {
|
|
return setSlug(ctx, contenttagmod.UpdateOneContentTag, contenttagmod.FN.ContentTag.Id,
|
|
contenttagmod.FN.ContentTag.Slug, contenttagmod.FN.ContentTag.UpdatedAt, id, slug)
|
|
},
|
|
)
|
|
}
|