package srv_videoslug import ( "context" "strings" "videoweb/pub/common/slugx" "videoweb/pub/common/srvdef" "videoweb/pub/common/utils" "videoweb/pub/models/videocategorymod" "videoweb/pub/models/videocreatermod" "videoweb/pub/models/videoinformationmod" "videoweb/pub/models/videotagmod" "videoweb/pub/models/videotopicmod" "videoweb/web/api/proto" "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 }