// Package seopush 为搜索引擎推送做兜底:补推已上架短剧,并重试失败的任务。 package seopush import ( "context" "strings" "sync" "time" "videoweb/pub/common/log" "videoweb/pub/models/playletmod" "videoweb/pub/models/seopushmod" "videoweb/pub/pubdef" "videoweb/web/service/srv_seopush" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/mongo/options" ) const ( backfillLimit = 100 retryLimit = 50 taskKeepDays = 30 ) var jobMu sync.Mutex // Run 定时兜底任务 func Run() { if !jobMu.TryLock() { log.Warn("seopush: skip job, previous run still in progress") return } defer jobMu.Unlock() ctx := context.Background() if !pubdef.Enabled() { discardPendingTasks(ctx) return } webAddr := srv_seopush.SEOBaseURL(ctx) if webAddr == "" { return } backfillVisiblePlaylets(ctx) retryPendingTasks(ctx, webAddr) cleanupExpiredTasks(ctx) } func discardPendingTasks(ctx context.Context) { filter := bson.M{"$or": []bson.M{ {seopushmod.FN.SeoPushTask.GoogleStatus: seopushmod.PushStatusPending}, {seopushmod.FN.SeoPushTask.IndexNowStatus: seopushmod.PushStatusPending}, }} list, serr := seopushmod.FindManySeoPushTasks(ctx, filter) if serr != nil { log.Warn("seopush: find pending tasks to discard failed", log.E(serr)) return } if len(list) == 0 { return } ids := make([]int64, 0, len(list)) seen := make(map[int64]struct{}, len(list)) for _, task := range list { if task == nil || task.VideoId <= 0 { continue } if _, ok := seen[task.VideoId]; ok { continue } seen[task.VideoId] = struct{}{} ids = append(ids, task.VideoId) } count, serr := seopushmod.DelManySeoPushTask(ctx, filter) if serr != nil { log.Warn("seopush: discard pending tasks failed", log.E(serr)) return } if len(ids) > 0 { if serr := playletmod.UpdateManyPlaylet(ctx, bson.M{playletmod.FN.Playlet.Id: bson.M{"$in": ids}}, bson.M{"$set": bson.M{playletmod.FN.Playlet.IndexNow: int64(0)}}); serr != nil { log.Warn("seopush: reset playlet indexNow failed", log.E(serr)) } } log.Info("seopush=skip=disabled", log.Any("discarded", count)) } func backfillVisiblePlaylets(ctx context.Context) { filter := bson.M{ playletmod.FN.Playlet.Status: playletmod.StatusUp, "$or": []bson.M{ {playletmod.FN.Playlet.IndexNow: 0}, {playletmod.FN.Playlet.IndexNow: bson.M{"$exists": false}}, }, } opt := options.Find(). SetSort(bson.M{playletmod.FN.Playlet.OnlineAt: -1}). SetLimit(backfillLimit) list, serr := playletmod.FindManyPlaylet(ctx, filter, opt) if serr != nil { log.Warn("seopush: backfill find playlets failed", log.E(serr)) return } if len(list) == 0 { return } for _, item := range list { if item == nil { continue } srv_seopush.RegisterPlayletUpdated(ctx, item.Id, item.Status, item.OnlineAt) } log.Info("seopush: backfill registered", log.Any("count", len(list))) } func retryPendingTasks(ctx context.Context, webAddr string) { conds := []bson.M{ { seopushmod.FN.SeoPushTask.GoogleStatus: seopushmod.PushStatusPending, seopushmod.FN.SeoPushTask.GoogleRetry: bson.M{"$lt": seopushmod.MaxPushRetry}, }, { seopushmod.FN.SeoPushTask.IndexNowStatus: seopushmod.PushStatusPending, seopushmod.FN.SeoPushTask.IndexNowRetry: bson.M{"$lt": seopushmod.MaxPushRetry}, }, } opt := options.Find(). SetSort(bson.M{seopushmod.FN.SeoPushTask.CreatedAt: 1}). SetLimit(retryLimit) taskList, serr := seopushmod.FindManySeoPushTasks(ctx, bson.M{"$or": conds}, opt) if serr != nil { log.Warn("seopush: find pending tasks failed", log.E(serr)) return } if len(taskList) == 0 { return } for _, task := range taskList { if task == nil || strings.Contains(task.Url, "?") { continue } srv_seopush.PushTask(ctx, webAddr, task) } log.Info("seopush: pending tasks processed", log.Any("count", len(taskList))) } func cleanupExpiredTasks(ctx context.Context) { deadline := time.Now().AddDate(0, 0, -taskKeepDays) count, serr := seopushmod.DelManySeoPushTask(ctx, bson.M{ seopushmod.FN.SeoPushTask.UpdatedAt: bson.M{"$lt": deadline}, }) if serr != nil { log.Warn("seopush: cleanup expired tasks failed", log.E(serr)) return } if count > 0 { log.Info("seopush: expired tasks cleaned", log.Any("count", count)) } }