Files
creator-hub/internal/controlplane/api/event_cover_worker.go
T
rogee 360bf96bab
douyin-release-gate / verify (push) Failing after 18m0s
feat: push Douyin events through shared WebSocket and SSE
2026-10-07 23:45:32 +08:00

87 lines
2.7 KiB
Go

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 = &copy
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 = &copy
}
}
return noop
}
// No other producer can fill this capacity between prepare and commit.
return func() { jobs <- pending }
}
}