2026-05-09 13:36:41 +08:00

89 lines
2.8 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Package invite runs asynchronous invite validity consumers.
package invite
import (
"context"
"log/slog"
"time"
"hyapp/pkg/logx"
invitedomain "hyapp/services/user-service/internal/domain/invite"
)
// Repository applies wallet recharge events to user-service invite progress.
type Repository interface {
ApplyRechargeEvent(ctx context.Context, event invitedomain.RechargeEvent) (invitedomain.RechargeApplyResult, error)
}
// WalletOutboxReader scans wallet recharge facts in creation order.
type WalletOutboxReader interface {
ListRechargeEventsAfter(ctx context.Context, cursor invitedomain.WalletRechargeCursor, batchSize int) ([]invitedomain.RechargeEvent, error)
}
// Service owns the invite validity event consumer.
type Service struct {
repository Repository
reader WalletOutboxReader
}
// WorkerOptions controls local wallet outbox polling.
type WorkerOptions struct {
WorkerID string
PollInterval time.Duration
BatchSize int
}
// New creates an invite service with persistence and wallet outbox reader dependencies.
func New(repository Repository, reader WalletOutboxReader) *Service {
return &Service{repository: repository, reader: reader}
}
// RunWalletRechargeWorker consumes WalletRechargeRecorded facts and advances valid_invite_count.
func (s *Service) RunWalletRechargeWorker(ctx context.Context, options WorkerOptions) {
if s == nil || s.repository == nil || s.reader == nil {
logx.Warn(ctx, "worker_disabled", slog.String("worker", "wallet_recharge"), slog.String("component", "user_invite"), slog.String("reason", "missing_dependency"))
return
}
if options.PollInterval <= 0 {
options.PollInterval = 5 * time.Second
}
if options.BatchSize <= 0 {
options.BatchSize = 100
}
var cursor invitedomain.WalletRechargeCursor
for {
processed, err := s.processBatch(ctx, &cursor, options.BatchSize)
if err != nil {
logx.Error(ctx, "worker_batch_failed", err, slog.String("worker", "wallet_recharge"), slog.String("component", "user_invite"), slog.String("worker_id", options.WorkerID), slog.Int("batch_size", options.BatchSize))
}
if ctx.Err() != nil {
return
}
if processed >= options.BatchSize {
// 有积压时不 sleep直到追上 wallet_outbox 当前末尾。
continue
}
select {
case <-ctx.Done():
return
case <-time.After(options.PollInterval):
}
}
}
func (s *Service) processBatch(ctx context.Context, cursor *invitedomain.WalletRechargeCursor, batchSize int) (int, error) {
events, err := s.reader.ListRechargeEventsAfter(ctx, *cursor, batchSize)
if err != nil {
return 0, err
}
for _, event := range events {
if _, err := s.repository.ApplyRechargeEvent(ctx, event); err != nil {
return 0, err
}
cursor.CreatedAtMs = event.OccurredAtMs
cursor.EventID = event.EventID
}
return len(events), nil
}