package broadcast import ( "context" "log/slog" "time" "hyapp/pkg/appcode" "hyapp/pkg/logx" broadcastdomain "hyapp/services/activity-service/internal/domain/broadcast" ) type RedPacketWalletOutboxReader interface { ListRedPacketCreatedEventsAfter(ctx context.Context, cursor broadcastdomain.RedPacketWalletCursor, batchSize int) ([]broadcastdomain.RedPacketCreatedEvent, error) } type RedPacketBroadcastWorkerOptions struct { WorkerID string PollInterval time.Duration BatchSize int } // RunRedPacketBroadcastWorker 把 wallet-service 已提交的红包创建事实转成区域飘屏 outbox。 func (s *Service) RunRedPacketBroadcastWorker(ctx context.Context, reader RedPacketWalletOutboxReader, options RedPacketBroadcastWorkerOptions) { if s == nil || reader == nil { logx.Warn(ctx, "worker_disabled", slog.String("worker", "red_packet_broadcast"), slog.String("reason", "missing_dependency")) return } if options.PollInterval <= 0 { options.PollInterval = 5 * time.Second } if options.BatchSize <= 0 { options.BatchSize = 100 } var cursor broadcastdomain.RedPacketWalletCursor for { processed, err := s.processRedPacketBroadcastBatch(ctx, reader, &cursor, options.BatchSize) if err != nil { logx.Error(ctx, "worker_batch_failed", err, slog.String("worker", "red_packet_broadcast"), slog.String("worker_id", options.WorkerID)) } if ctx.Err() != nil { return } if processed >= options.BatchSize { continue } select { case <-ctx.Done(): return case <-time.After(options.PollInterval): } } } func (s *Service) processRedPacketBroadcastBatch(ctx context.Context, reader RedPacketWalletOutboxReader, cursor *broadcastdomain.RedPacketWalletCursor, batchSize int) (int, error) { events, err := reader.ListRedPacketCreatedEventsAfter(ctx, *cursor, batchSize) if err != nil { return 0, err } for _, event := range events { eventCtx := appcode.WithContext(ctx, event.AppCode) if _, err := s.PublishRegionBroadcast(eventCtx, PublishInput{ EventID: event.EventID, BroadcastType: broadcastdomain.TypeRedPacket, RegionID: event.RegionID, PayloadJSON: event.PayloadJSON, }); err != nil { return 0, err } cursor.CreatedAtMS = event.CreatedAtMS cursor.EventID = event.EventID } return len(events), nil }