package api import ( "context" "errors" "git.ipao.vip/rogee/creator-hub/internal/creator" "github.com/sirupsen/logrus" ) type listenerCoverWriter interface { UpdateListenerEventCovers(context.Context, string, string, []creator.ListenerDelivery) error } type listenerCoverEnricher interface { enrich(context.Context, []creator.ListenerDelivery) ([]creator.ListenerDelivery, error) } const coverQueueFullReason = "封面待处理队列已满,等待历史核对重试" // The account receiver is the sole producer: prepare before persistence, submit // after commit. Queue overload is recorded in that same transaction, never by // a second synchronous database write on the event acknowledgement path. func startListenerCoverWorker(ctx context.Context, store listenerWriter, accountID, generation string, session listenerSession) func([]creator.ListenerDelivery) func() { noop := func() {} writer, ok := store.(listenerCoverWriter) enricher, has := session.(listenerCoverEnricher) if !ok || !has { return func([]creator.ListenerDelivery) func() { return noop } } jobs := make(chan []creator.ListenerDelivery, 32) log := logrus.WithFields(logrus.Fields{"account_id": accountID, "generation": generation}) save := func(items []creator.ListenerDelivery) { if err := writer.UpdateListenerEventCovers(ctx, accountID, generation, items); err != nil { if !errors.Is(err, creator.ErrConflict) && ctx.Err() == nil { log.WithError(err).Error("persist event cover result failed") } return } publishEventChanges(ctx) } go func() { for { select { case <-ctx.Done(): return case items := <-jobs: result, err := enricher.enrich(ctx, items) if err != nil { log.WithError(err).Error("event cover enrichment failed") for i := range items { items[i].Notice.WorkCoverError = err.Error() } save(items) } else { save(result) } } } }() return func(items []creator.ListenerDelivery) func() { pending := make([]creator.ListenerDelivery, 0, len(items)) for _, d := range items { if d.Notice != nil && d.Notice.WorkID != "" && d.Notice.WorkCoverURL != "" { copy := *d.Notice d.Notice = © pending = append(pending, d) } } if len(pending) == 0 { return noop } if len(jobs) == cap(jobs) { log.Error("event cover queue full; recording overload with notification persistence") for i, d := range items { if d.Notice != nil && d.Notice.WorkID != "" && d.Notice.WorkCoverURL != "" { copy := *d.Notice copy.WorkCoverError = coverQueueFullReason items[i].Notice = © } } return noop } // No other producer can fill this capacity between prepare and commit. return func() { jobs <- pending } } }