fix(billing): 异步任务退款时同步减少 used_quota (#6795)
* fix(billing): 异步任务退款时同步减少 used_quota 退款时仅恢复了 quota(剩余额度),但未同步减少 used_quota(已用额度), 导致"总额度"(quota + used_quota)随退款次数持续虚增,超出用户实际充值金额。 修复三处退款路径: - RefundTaskQuota:任务失败完整退款 - RecalculateTaskQuota:差额结算退款分支 - controller/midjourney.go:Midjourney 任务失败退款 新增 model.UpdateUserUsedQuota 公开函数,仅调整 used_quota 不影响 request_count。 * fix(billing): 任务退款时同步扣减渠道 used_quota * fix(billing): complete async task refund accounting * style(model): group internal Midjourney fields --------- Co-authored-by: CaIon <i@caion.me>
This commit is contained in:
+11
-5
@@ -161,7 +161,7 @@ func taskModelName(task *model.Task) string {
|
||||
}
|
||||
|
||||
// RefundTaskQuota 统一的任务失败退款逻辑。
|
||||
// 当异步任务失败时,将预扣的 quota 退还给用户(支持钱包和订阅),并退还令牌额度。
|
||||
// 当异步任务失败时,退还资金与令牌额度,并回减用户和渠道用量。
|
||||
// 返回资金来源是否已成功退还;失败时保留 quota,供显式重试或人工对账。
|
||||
func RefundTaskQuota(ctx context.Context, task *model.Task, reason string) bool {
|
||||
quota := task.Quota
|
||||
@@ -178,7 +178,11 @@ func RefundTaskQuota(ctx context.Context, task *model.Task, reason string) bool
|
||||
// 2. 退还令牌额度
|
||||
taskAdjustTokenQuota(ctx, task, -quota)
|
||||
|
||||
// 3. 记录日志
|
||||
// 3. 回减预扣时累计的用户和渠道用量,请求次数保持不变
|
||||
model.UpdateUserUsedQuota(task.UserId, -quota)
|
||||
model.UpdateChannelUsedQuota(task.ChannelId, -quota)
|
||||
|
||||
// 4. 记录日志
|
||||
other := taskBillingOther(task)
|
||||
other["task_id"] = task.TaskID
|
||||
other["reason"] = reason
|
||||
@@ -194,7 +198,7 @@ func RefundTaskQuota(ctx context.Context, task *model.Task, reason string) bool
|
||||
Other: other,
|
||||
})
|
||||
|
||||
// 4. 资金退款完成后再清除持久化标记。
|
||||
// 5. 资金退款完成后再清除持久化标记。
|
||||
// 回写失败必须显式告警,避免漏掉潜在的重复退款风险。
|
||||
task.Quota = 0
|
||||
if err := task.UpdateQuota(); err != nil {
|
||||
@@ -242,13 +246,15 @@ func RecalculateTaskQuota(ctx context.Context, task *model.Task, actualQuota int
|
||||
logger.LogError(ctx, fmt.Sprintf("差额结算回写 quota 失败 task %s: %s", task.TaskID, err.Error()))
|
||||
}
|
||||
|
||||
// 提交阶段已经累计过一次请求;结算阶段只调整最终用量。
|
||||
model.UpdateUserUsedQuota(task.UserId, quotaDelta)
|
||||
model.UpdateChannelUsedQuota(task.ChannelId, quotaDelta)
|
||||
|
||||
var logType int
|
||||
var logQuota int
|
||||
if quotaDelta > 0 {
|
||||
logType = model.LogTypeConsume
|
||||
logQuota = quotaDelta
|
||||
model.UpdateUserUsedQuotaAndRequestCount(task.UserId, quotaDelta)
|
||||
model.UpdateChannelUsedQuota(task.ChannelId, quotaDelta)
|
||||
} else {
|
||||
logType = model.LogTypeRefund
|
||||
logQuota = -quotaDelta
|
||||
|
||||
Reference in New Issue
Block a user