fix(coupon): 修复订单取消时金额券未退还的bug
订单取消退券逻辑依赖 used_order_id 匹配,但金额券在下单时 不设置 used_order_id(仅在支付确认后设置),导致未支付订单 取消时 WHERE 条件匹配不到行,退券静默失败。 修复:去掉 used_order_id 条件,按券 ID 直接退还,增加幂等 校验和错误处理,兜底从流水回推预扣金额。
This commit is contained in:
parent
d1ee319f0e
commit
b9a40df5c5
@ -59,32 +59,66 @@ func (s *service) CancelOrder(ctx context.Context, userID int64, orderID int64,
|
|||||||
|
|
||||||
// 4. 退还优惠券(恢复预扣的余额和状态)
|
// 4. 退还优惠券(恢复预扣的余额和状态)
|
||||||
if order.CouponID > 0 {
|
if order.CouponID > 0 {
|
||||||
var oc struct {
|
// 幂等校验:若已记录过 cancel_refund 流水则跳过
|
||||||
AppliedAmount int64
|
refundExists, err := tx.UserCouponLedger.WithContext(ctx).Where(
|
||||||
|
tx.UserCouponLedger.UserCouponID.Eq(order.CouponID),
|
||||||
|
tx.UserCouponLedger.OrderID.Eq(order.ID),
|
||||||
|
tx.UserCouponLedger.Action.Eq("cancel_refund"),
|
||||||
|
).Count()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
// 获取该订单实际扣减的优惠券金额
|
|
||||||
_ = tx.OrderCoupons.WithContext(ctx).Where(tx.OrderCoupons.OrderID.Eq(order.ID), tx.OrderCoupons.UserCouponID.Eq(order.CouponID)).Scan(&oc)
|
|
||||||
|
|
||||||
// 执行原子回退:增加余额 + 重置状态 + 清除占用订单
|
if refundExists == 0 {
|
||||||
res := tx.UserCoupons.WithContext(ctx).UnderlyingDB().Exec(`
|
var oc struct {
|
||||||
UPDATE user_coupons
|
AppliedAmount int64
|
||||||
SET balance_amount = balance_amount + ?,
|
}
|
||||||
status = 1,
|
// 优先从 order_coupons 获取实际抵扣金额
|
||||||
used_order_id = 0,
|
if err := tx.OrderCoupons.WithContext(ctx).Where(
|
||||||
used_at = NULL
|
tx.OrderCoupons.OrderID.Eq(order.ID),
|
||||||
WHERE id = ? AND used_order_id = ?
|
tx.OrderCoupons.UserCouponID.Eq(order.CouponID),
|
||||||
`, oc.AppliedAmount, order.CouponID, order.ID)
|
).Scan(&oc); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
if res.RowsAffected > 0 {
|
// 兜底:order_coupons 无记录时,从流水中回推预扣金额
|
||||||
// 记录退还流水
|
if oc.AppliedAmount <= 0 {
|
||||||
_ = tx.UserCouponLedger.WithContext(ctx).Create(&model.UserCouponLedger{
|
if err := tx.UserCouponLedger.WithContext(ctx).UnderlyingDB().Raw(`
|
||||||
UserID: userID,
|
SELECT COALESCE(SUM(CASE WHEN change_amount < 0 THEN -change_amount ELSE 0 END), 0) AS applied_amount
|
||||||
UserCouponID: order.CouponID,
|
FROM user_coupon_ledger
|
||||||
ChangeAmount: oc.AppliedAmount,
|
WHERE user_id = ? AND user_coupon_id = ? AND order_id = ? AND action IN ('reserve', 'usage')
|
||||||
OrderID: order.ID,
|
`, userID, order.CouponID, order.ID).Scan(&oc).Error; err != nil {
|
||||||
Action: "cancel_refund",
|
return err
|
||||||
CreatedAt: time.Now(),
|
}
|
||||||
})
|
}
|
||||||
|
|
||||||
|
if oc.AppliedAmount > 0 {
|
||||||
|
// 恢复余额 + 重置状态(不依赖 used_order_id)
|
||||||
|
res := tx.UserCoupons.WithContext(ctx).UnderlyingDB().Exec(`
|
||||||
|
UPDATE user_coupons
|
||||||
|
SET balance_amount = balance_amount + ?,
|
||||||
|
status = 1,
|
||||||
|
used_order_id = 0,
|
||||||
|
used_at = NULL
|
||||||
|
WHERE id = ?
|
||||||
|
`, oc.AppliedAmount, order.CouponID)
|
||||||
|
if res.Error != nil {
|
||||||
|
return res.Error
|
||||||
|
}
|
||||||
|
|
||||||
|
if res.RowsAffected > 0 {
|
||||||
|
if err := tx.UserCouponLedger.WithContext(ctx).Create(&model.UserCouponLedger{
|
||||||
|
UserID: userID,
|
||||||
|
UserCouponID: order.CouponID,
|
||||||
|
ChangeAmount: oc.AppliedAmount,
|
||||||
|
OrderID: order.ID,
|
||||||
|
Action: "cancel_refund",
|
||||||
|
CreatedAt: time.Now(),
|
||||||
|
}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user