init: web_hgdjc_server

This commit is contained in:
2026-11-07 10:44:09 +08:00
commit 71a4f4e175
713 changed files with 147749 additions and 0 deletions
+355
View File
@@ -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
}