init: web_hgdjc_server
This commit is contained in:
@@ -0,0 +1,355 @@
|
||||
package srv_aicomment
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
|
||||
"videoweb/pub/common/log"
|
||||
"videoweb/pub/common/srvdef"
|
||||
"videoweb/pub/models/playletmod"
|
||||
"videoweb/pub/models/playlettagmod"
|
||||
"videoweb/pub/models/playletthememod"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
)
|
||||
|
||||
const (
|
||||
homepagePlayletCandidateLimit int64 = 60
|
||||
homepagePlayletFeedThemeLimit int64 = 30
|
||||
maxSubmitRequestsPerRun = 7
|
||||
)
|
||||
|
||||
var homepagePlayletProjection = bson.D{
|
||||
{Key: playletmod.FN.Playlet.Id, Value: 1},
|
||||
{Key: playletmod.FN.Playlet.Title, Value: 1},
|
||||
{Key: playletmod.FN.Playlet.Desc, Value: 1},
|
||||
{Key: playletmod.FN.Playlet.TagIds, Value: 1},
|
||||
{Key: playletmod.FN.Playlet.OnlineAt, Value: 1},
|
||||
{Key: playletmod.FN.Playlet.CreatedAt, Value: 1},
|
||||
{Key: playletmod.FN.Playlet.IsFeatured, Value: 1},
|
||||
{Key: playletmod.FN.Playlet.IsRecommend, Value: 1},
|
||||
{Key: playletmod.FN.Playlet.ShowOnBanner, Value: 1},
|
||||
{Key: playletmod.FN.Playlet.TopRank, Value: 1},
|
||||
{Key: playletmod.FN.Playlet.RecommendRank, Value: 1},
|
||||
{Key: playletmod.FN.Playlet.CountBrowse, Value: 1},
|
||||
{Key: playletmod.FN.Playlet.CountComment, Value: 1},
|
||||
}
|
||||
|
||||
var homepageSubmitRunning atomic.Bool
|
||||
|
||||
func AutoProcessHomepageVisibleContent(ctx context.Context) error {
|
||||
if !homepageSubmitRunning.CompareAndSwap(false, true) {
|
||||
log.Debug("首页内容自动评论补提交正在执行中,本次跳过")
|
||||
return nil
|
||||
}
|
||||
defer homepageSubmitRunning.Store(false)
|
||||
|
||||
service := GetAiCommentService()
|
||||
if !service.Configured() {
|
||||
log.Warn("自动评论服务未配置,跳过首页内容补提交")
|
||||
return nil
|
||||
}
|
||||
|
||||
playlets, serr := collectHomepagePlaylets(ctx)
|
||||
if serr != nil {
|
||||
return fmt.Errorf("查询首页短剧失败: %w", serr)
|
||||
}
|
||||
videos, serr := buildHomepagePlayletVideoItems(ctx, playlets)
|
||||
if serr != nil {
|
||||
return fmt.Errorf("组装首页短剧AI评论参数失败: %w", serr)
|
||||
}
|
||||
if err := submitAndRecordInBatches(ctx, service, videos); err != nil {
|
||||
return fmt.Errorf("提交首页短剧AI评论任务失败: %w", err)
|
||||
}
|
||||
log.Info("自动评论首页内容补提交完成",
|
||||
log.Any("playletCandidates", len(playlets)),
|
||||
log.Any("playletPending", len(videos)),
|
||||
)
|
||||
return nil
|
||||
}
|
||||
|
||||
func submitAndRecordInBatches(ctx context.Context, service *AiCommentService, videos []VideoItem) error {
|
||||
batches, deferred := buildAICommentSubmissionBatches(videos, maxSubmitRequestsPerRun)
|
||||
for _, batch := range batches {
|
||||
if err := service.SubmitAndRecord(ctx, batch); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if deferred {
|
||||
log.Info("自动评论达到单轮提交请求上限,剩余任务等待下次续传",
|
||||
log.Any("requestLimit", maxSubmitRequestsPerRun),
|
||||
log.Any("videoCount", len(videos)))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func buildAICommentSubmissionBatches(videos []VideoItem, requestLimit int) ([][]VideoItem, bool) {
|
||||
batches := make([][]VideoItem, 0)
|
||||
for _, round := range buildAICommentSubmissionRounds(videos) {
|
||||
for start := 0; start < len(round); start += maxSubmitVideoCount {
|
||||
end := start + maxSubmitVideoCount
|
||||
if end > len(round) {
|
||||
end = len(round)
|
||||
}
|
||||
batches = append(batches, round[start:end])
|
||||
}
|
||||
}
|
||||
if requestLimit <= 0 || len(batches) <= requestLimit {
|
||||
return batches, false
|
||||
}
|
||||
return batches[:requestLimit], true
|
||||
}
|
||||
|
||||
func buildAICommentSubmissionRounds(videos []VideoItem) [][]VideoItem {
|
||||
pending := append([]VideoItem(nil), videos...)
|
||||
rounds := make([][]VideoItem, 0)
|
||||
for len(pending) > 0 {
|
||||
round := make([]VideoItem, 0, len(pending))
|
||||
next := make([]VideoItem, 0, len(pending))
|
||||
for _, video := range pending {
|
||||
remaining := video.CommentTotal
|
||||
if remaining <= 0 {
|
||||
remaining = commentsPerGenerationTask
|
||||
}
|
||||
retainCount := min(remaining, commentsPerGenerationTask)
|
||||
taskVideo := video
|
||||
taskVideo.CommentCount = randomCommentLengthLimit()
|
||||
taskVideo.RetainCount = retainCount
|
||||
taskVideo.CommentTotal = 0
|
||||
round = append(round, taskVideo)
|
||||
|
||||
remaining -= retainCount
|
||||
if remaining > 0 {
|
||||
video.CommentTotal = remaining
|
||||
next = append(next, video)
|
||||
}
|
||||
}
|
||||
rounds = append(rounds, round)
|
||||
pending = next
|
||||
}
|
||||
return rounds
|
||||
}
|
||||
|
||||
func collectHomepagePlaylets(ctx context.Context) (playletmod.PlayletList, *srvdef.SrvErr) {
|
||||
playlets := make(playletmod.PlayletList, 0, maxSubmitVideoCount)
|
||||
seen := make(map[int64]struct{})
|
||||
if serr := appendHomepagePlaylets(ctx, &playlets, seen,
|
||||
homepagePlayletFilter(bson.M{playletmod.FN.Playlet.ShowOnBanner: true}),
|
||||
bson.D{
|
||||
{Key: playletmod.FN.Playlet.TopRank, Value: -1},
|
||||
{Key: playletmod.FN.Playlet.MediaUpdatedAt, Value: -1},
|
||||
{Key: playletmod.FN.Playlet.Id, Value: -1},
|
||||
},
|
||||
6,
|
||||
); serr != nil {
|
||||
return nil, serr
|
||||
}
|
||||
if serr := appendHomepageFeedThemePlaylets(ctx, &playlets, seen); serr != nil {
|
||||
return nil, serr
|
||||
}
|
||||
queries := []struct {
|
||||
filter bson.M
|
||||
sort bson.D
|
||||
limit int64
|
||||
}{
|
||||
{
|
||||
filter: homepagePlayletFilter(bson.M{playletmod.FN.Playlet.IsRecommend: true}),
|
||||
sort: bson.D{
|
||||
{Key: playletmod.FN.Playlet.RecommendRank, Value: -1},
|
||||
{Key: playletmod.FN.Playlet.CreatedAt, Value: -1},
|
||||
{Key: playletmod.FN.Playlet.Id, Value: -1},
|
||||
},
|
||||
limit: homepagePlayletCandidateLimit,
|
||||
},
|
||||
{
|
||||
filter: homepagePlayletFilter(nil),
|
||||
sort: bson.D{
|
||||
{Key: playletmod.FN.Playlet.RecommendRank, Value: -1},
|
||||
{Key: playletmod.FN.Playlet.MediaUpdatedAt, Value: -1},
|
||||
{Key: playletmod.FN.Playlet.Id, Value: -1},
|
||||
},
|
||||
limit: homepagePlayletCandidateLimit,
|
||||
},
|
||||
{
|
||||
filter: homepagePlayletFilter(nil),
|
||||
sort: bson.D{
|
||||
{Key: playletmod.FN.Playlet.CountCollect, Value: -1},
|
||||
{Key: playletmod.FN.Playlet.Id, Value: -1},
|
||||
},
|
||||
limit: homepagePlayletCandidateLimit,
|
||||
},
|
||||
{
|
||||
filter: homepagePlayletFilter(nil),
|
||||
sort: bson.D{
|
||||
{Key: playletmod.FN.Playlet.UpdatedAt, Value: -1},
|
||||
{Key: playletmod.FN.Playlet.Id, Value: -1},
|
||||
},
|
||||
limit: homepagePlayletCandidateLimit,
|
||||
},
|
||||
{
|
||||
filter: homepagePlayletFilter(nil),
|
||||
sort: bson.D{
|
||||
{Key: playletmod.FN.Playlet.CountBrowse, Value: -1},
|
||||
{Key: playletmod.FN.Playlet.Id, Value: -1},
|
||||
},
|
||||
limit: homepagePlayletCandidateLimit,
|
||||
},
|
||||
}
|
||||
for _, query := range queries {
|
||||
if serr := appendHomepagePlaylets(ctx, &playlets, seen, query.filter, query.sort, query.limit); serr != nil {
|
||||
return nil, serr
|
||||
}
|
||||
}
|
||||
return playlets, nil
|
||||
}
|
||||
|
||||
func appendHomepageFeedThemePlaylets(ctx context.Context, playlets *playletmod.PlayletList, seen map[int64]struct{}) *srvdef.SrvErr {
|
||||
themes, serr := playletthememod.FindManyPlayletTheme(ctx, bson.M{
|
||||
playletthememod.FN.PlayletTheme.Status: true,
|
||||
}, options.Find().
|
||||
SetProjection(bson.D{{Key: playletthememod.FN.PlayletTheme.Id, Value: 1}}).
|
||||
SetSort(bson.D{
|
||||
{Key: playletthememod.FN.PlayletTheme.Rank, Value: -1},
|
||||
{Key: playletthememod.FN.PlayletTheme.Id, Value: -1},
|
||||
}),
|
||||
)
|
||||
if serr != nil {
|
||||
return serr
|
||||
}
|
||||
sort := bson.D{
|
||||
{Key: playletmod.FN.Playlet.RecommendRank, Value: -1},
|
||||
{Key: playletmod.FN.Playlet.MediaUpdatedAt, Value: -1},
|
||||
{Key: playletmod.FN.Playlet.Id, Value: -1},
|
||||
}
|
||||
for _, theme := range themes {
|
||||
if theme == nil || theme.Id <= 0 {
|
||||
continue
|
||||
}
|
||||
filter := homepagePlayletFilter(bson.M{
|
||||
playletmod.FN.Playlet.ThemeIds: bson.M{"$in": []int64{theme.Id}},
|
||||
})
|
||||
if serr := appendHomepagePlaylets(ctx, playlets, seen, filter, sort, homepagePlayletFeedThemeLimit); serr != nil {
|
||||
return serr
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func appendHomepagePlaylets(ctx context.Context, playlets *playletmod.PlayletList, seen map[int64]struct{}, filter bson.M, sort bson.D, limit int64) *srvdef.SrvErr {
|
||||
list, serr := playletmod.FindManyPlaylet(ctx, filter, options.Find().
|
||||
SetProjection(homepagePlayletProjection).
|
||||
SetSort(sort).
|
||||
SetLimit(limit),
|
||||
)
|
||||
if serr != nil {
|
||||
return serr
|
||||
}
|
||||
for _, playlet := range list {
|
||||
if playlet == nil || playlet.Id <= 0 {
|
||||
continue
|
||||
}
|
||||
if _, ok := seen[playlet.Id]; ok {
|
||||
continue
|
||||
}
|
||||
seen[playlet.Id] = struct{}{}
|
||||
*playlets = append(*playlets, playlet)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func buildHomepagePlayletVideoItems(ctx context.Context, playlets playletmod.PlayletList) ([]VideoItem, *srvdef.SrvErr) {
|
||||
tagNames := loadPlayletTagNames(ctx, playlets)
|
||||
videos := make([]VideoItem, 0, len(playlets))
|
||||
for _, playlet := range playlets {
|
||||
if playlet == nil {
|
||||
continue
|
||||
}
|
||||
videos = append(videos, VideoItem{
|
||||
VideoId: strconv.FormatInt(playlet.Id, 10),
|
||||
Title: playlet.Title,
|
||||
Tags: playletTagNames(playlet.TagIds, tagNames),
|
||||
Description: playlet.Desc,
|
||||
CommentCount: randomCommentLengthLimit(),
|
||||
ObjType: objectTypePlaylet,
|
||||
CommentTotal: aiCommentPlayletTotal,
|
||||
CurrentCount: playlet.CountComment,
|
||||
})
|
||||
}
|
||||
pending, serr := preparePendingAICommentVideos(ctx, videos)
|
||||
if serr != nil {
|
||||
return nil, serr
|
||||
}
|
||||
return pending, nil
|
||||
}
|
||||
|
||||
func homepagePlayletFilter(extra bson.M) bson.M {
|
||||
filter := bson.M{playletmod.FN.Playlet.Status: playletmod.StatusUp}
|
||||
for key, value := range extra {
|
||||
filter[key] = value
|
||||
}
|
||||
return filter
|
||||
}
|
||||
|
||||
func loadPlayletTagNames(ctx context.Context, playlets playletmod.PlayletList) map[int64]string {
|
||||
tagIDs := make([]int64, 0)
|
||||
seen := make(map[int64]struct{})
|
||||
for _, playlet := range playlets {
|
||||
if playlet == nil {
|
||||
continue
|
||||
}
|
||||
for _, tagID := range playlet.TagIds {
|
||||
if tagID <= 0 {
|
||||
continue
|
||||
}
|
||||
if _, ok := seen[tagID]; ok {
|
||||
continue
|
||||
}
|
||||
seen[tagID] = struct{}{}
|
||||
tagIDs = append(tagIDs, tagID)
|
||||
}
|
||||
}
|
||||
if len(tagIDs) == 0 {
|
||||
return map[int64]string{}
|
||||
}
|
||||
tags, serr := playlettagmod.FindManyPlayletTag(ctx, bson.M{
|
||||
playlettagmod.FN.PlayletTag.Id: bson.M{"$in": tagIDs},
|
||||
}, options.Find().SetProjection(bson.D{
|
||||
{Key: playlettagmod.FN.PlayletTag.Id, Value: 1},
|
||||
{Key: playlettagmod.FN.PlayletTag.Name, Value: 1},
|
||||
}))
|
||||
if serr != nil {
|
||||
log.Warn("自动评论查询短剧标签失败", log.E(serr), log.Any("tagIDs", tagIDs))
|
||||
return map[int64]string{}
|
||||
}
|
||||
result := make(map[int64]string, len(tags))
|
||||
for _, tag := range tags {
|
||||
if tag != nil {
|
||||
result[tag.Id] = tag.Name
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func playletTagNames(tagIDs []int64, tagNames map[int64]string) []string {
|
||||
result := make([]string, 0, len(tagIDs))
|
||||
seen := make(map[string]struct{}, len(tagIDs))
|
||||
for _, tagID := range tagIDs {
|
||||
name := strings.TrimSpace(tagNames[tagID])
|
||||
if name == "" {
|
||||
continue
|
||||
}
|
||||
if _, ok := seen[name]; ok {
|
||||
continue
|
||||
}
|
||||
seen[name] = struct{}{}
|
||||
result = append(result, name)
|
||||
if len(result) >= maxVideoTags {
|
||||
break
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
Reference in New Issue
Block a user