89 lines
2.8 KiB
Go
89 lines
2.8 KiB
Go
// 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
|
||
}
|