feat(gateway): 闭合积分环 —— 充值/发放 + 提交前余额硬拦截(默认关)
让预付费积分成为闭环(发放→消耗→见底拦住): - POST /admin/credits/grant:给租户充值/发放(正=grant,负=adjust 校正), 复用 store.GrantCredits(账本分录 + 物化余额,一事务)。 - 提交门控:credit_enforce 开启且租户余额≤0 → 拒绝新任务 402;默认关=软扣不拦。 开关入 billing-config(sundynix_setting KV,后台可切)。 - 修 bug:GrantCredits 是跨租户管理操作,须 store.WithoutTenant——否则 tenant 插件 会把账本分录的 tenant_id 覆盖成 admin 自己的租户(余额记目标、分录记 admin,破坏对账)。 live 验证:enforce 关→余额0可提交;开→余额0拒 402;充值后可提交、余额递减; 修复后 grant 分录落到目标租户、balance==SUM(ledger) 不变量成立。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -155,6 +155,9 @@ func (p *Postgres) GrantCredits(ctx context.Context, tenantID, kind string, cred
|
||||
if kind == "" {
|
||||
kind = LedgerGrant
|
||||
}
|
||||
// 跨租户管理操作:admin 请求 ctx 带着 admin 自己的租户,须旁路,否则 tenant 插件会把
|
||||
// 账本分录的 tenant_id 覆盖成 admin 的租户(余额记到目标租户、分录却记到 admin,破坏对账)。
|
||||
ctx = WithoutTenant(ctx)
|
||||
return p.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
if err := tx.Create(&CreditLedger{
|
||||
TenantID: tenantID, Kind: kind, CreditsMicro: creditsMicro, Ref: ref, Memo: memo,
|
||||
|
||||
Reference in New Issue
Block a user