56 lines
1.6 KiB
Go
56 lines
1.6 KiB
Go
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) }
|
|
}
|