package reconcile import ( "context" "sync" "time" "github.com/wangjia/pay/internal/store" ) // UsageSource 满足 accounts.UsageSource:持「账户→当日已收 minor」快照,对账 job 周期 Refresh。 // Pick 路径读快照(零 DB);Refresh 从 attempts 聚合。跨天由 now() 的自然日窗自动滚动。 type UsageSource struct { orders *store.OrderStore now func() time.Time mu sync.RWMutex snap map[string]int64 } func NewUsageSource(orders *store.OrderStore, now func() time.Time) *UsageSource { if now == nil { now = time.Now } return &UsageSource{orders: orders, now: now, snap: map[string]int64{}} } // TodayUsedMinor 读快照(accounts.UsageSource 接口);未刷新/未知账户返回 0(不误拒)。 func (u *UsageSource) TodayUsedMinor(accountID string) int64 { u.mu.RLock() defer u.mu.RUnlock() return u.snap[accountID] } // Refresh 从 attempts 重算当日快照(幂等覆盖)。当日起点用 now() 的 UTC 日期。 func (u *UsageSource) Refresh(ctx context.Context) error { m, err := u.orders.SumPaidAttemptMinorByAccountSince(StartOfDay(u.now())) if err != nil { return err } u.mu.Lock() u.snap = m u.mu.Unlock() return nil } // StartOfDay 返回 t 所在 UTC 自然日 00:00(DailyLimit 按自然日结算)。 func StartOfDay(t time.Time) time.Time { y, mo, d := t.UTC().Date() return time.Date(y, mo, d, 0, 0, 0, 0, time.UTC) } // RefreshUsageTask 把 Refresh 包成周期任务体。 func RefreshUsageTask(u *UsageSource) func(ctx context.Context) error { return func(ctx context.Context) error { return u.Refresh(ctx) } }