bugfix
This commit is contained in:
+23
-11
@@ -300,10 +300,16 @@ func consumeCosQuota(ctx context.Context, userID int64, n int64, relatedID int64
|
||||
if delta > remain {
|
||||
delta = remain
|
||||
}
|
||||
if err := tx.Model(&mysqlmodel.QuotaGrant{}).
|
||||
res := tx.Model(&mysqlmodel.QuotaGrant{}).
|
||||
Where("id = ? AND used + ? <= amount", grants[i].ID, delta).
|
||||
UpdateColumn("used", gorm.Expr("used + ?", delta)).Error; err != nil {
|
||||
return err
|
||||
UpdateColumn("used", gorm.Expr("used + ?", delta))
|
||||
if res.Error != nil {
|
||||
return res.Error
|
||||
}
|
||||
if res.RowsAffected == 0 {
|
||||
// 凭证在行锁下被并发改写(理论上不应发生)。此时若继续累加会少扣额度,
|
||||
// 直接回滚整个事务并报额度不足,宁可让用户重试也不超额放行。
|
||||
return errcode.QuotaInsufficient
|
||||
}
|
||||
remain -= delta
|
||||
}
|
||||
@@ -415,25 +421,31 @@ func refundCosQuotaTx(tx *gorm.DB, userID int64, vipExpire int64, n int64, relat
|
||||
}
|
||||
remain -= delta
|
||||
}
|
||||
if remain > 0 {
|
||||
// 无足够已用额度可回退(数据异常),跳过写流水。
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 写回退流水。
|
||||
// 3. 写回退流水:变更量取「实际回退量」。
|
||||
// 此前 `remain > 0` 时直接 return,导致额度已被部分改回却没有流水(账实不一致);
|
||||
// 同时 ChangeNum 写的是请求量 n,退不满时数字虚高。现统一按实际回退量记账,
|
||||
// 退不满/退不动时在原因里标注并告警(已用额度不足通常意味着重复回退或数据异常,必须留痕)。
|
||||
refunded := n - remain
|
||||
flowReason := reason
|
||||
if refunded < n {
|
||||
flowReason = fmt.Sprintf("%s(实际回退 %d/%d,可用已退额度不足)", reason, refunded, n)
|
||||
slog.WarnContext(context.Background(), "refund cos quota partially applied",
|
||||
"user_id", userID, "want", n, "refunded", refunded, "related_id", relatedID)
|
||||
}
|
||||
after := cosRemainTx(tx, userID, vipExpire, now)
|
||||
flow := &mysqlmodel.QuotaFlowLog{
|
||||
QuotaFlowUUID: genUUID(),
|
||||
UserID: userID,
|
||||
ChangeTarget: QuotaChangeTargetUse,
|
||||
ChangeType: QuotaChangeTypeRefund,
|
||||
ChangeNum: n,
|
||||
OldValue: after - n,
|
||||
ChangeNum: refunded,
|
||||
OldValue: after - refunded,
|
||||
NewValue: after,
|
||||
QuotaType: QuotaTypeCos,
|
||||
RelatedID: relatedID,
|
||||
Reason: reason,
|
||||
Reason: flowReason,
|
||||
}
|
||||
// 回退类流水无业务订单号,用流水 UUID 填充 order_no 避免唯一索引空串冲突。
|
||||
flow.OrderNo = flow.QuotaFlowUUID
|
||||
|
||||
Reference in New Issue
Block a user