121 lines
3.8 KiB
Go
121 lines
3.8 KiB
Go
package user
|
||
|
||
import (
|
||
"bindbox-game/internal/repository/mysql/dao"
|
||
"bindbox-game/internal/repository/mysql/model"
|
||
"context"
|
||
"fmt"
|
||
"time"
|
||
)
|
||
|
||
// RecordOrderCouponUsage 记录订单使用的用户券与本次抵扣金额
|
||
func (s *service) RecordOrderCouponUsage(ctx context.Context, orderID int64, userCouponID int64, appliedAmount int64) error {
|
||
if orderID <= 0 || userCouponID <= 0 || appliedAmount <= 0 {
|
||
return nil
|
||
}
|
||
// 根治方案:使用唯一索引保证记录不重复
|
||
return s.repo.GetDbW().Exec("INSERT IGNORE INTO order_coupons (order_id, user_coupon_id, applied_amount, created_at) VALUES (?,?,?,NOW(3))", orderID, userCouponID, appliedAmount).Error
|
||
}
|
||
|
||
// DeductCouponsForPaidOrder 支付成功后确认优惠券预扣
|
||
// 新逻辑:优惠券余额已在下单时预扣(status=4),支付成功后只需确认状态
|
||
// 金额券:status 4 → 2(若余额为0)或保持 4(若还有余额,等后续订单继续使用)
|
||
// 满减/折扣券:status 4 → 2
|
||
func (s *service) DeductCouponsForPaidOrder(ctx context.Context, tx *dao.Query, userID int64, orderID int64, paidAt time.Time) error {
|
||
type row struct {
|
||
UserCouponID int64
|
||
AppliedAmount int64
|
||
Status int32
|
||
UsedOrderID int64
|
||
DiscountType int32
|
||
BalanceAmount int64
|
||
}
|
||
var rows []row
|
||
|
||
// 确定使用的数据库查询对象
|
||
db := s.writeDB
|
||
if tx != nil {
|
||
db = tx
|
||
}
|
||
readDb := s.readDB
|
||
if tx != nil {
|
||
readDb = tx
|
||
}
|
||
|
||
// 1. 获取该订单关联的所有优惠券使用记录(包含当前余额)
|
||
_ = readDb.OrderCoupons.WithContext(ctx).UnderlyingDB().Raw(`
|
||
SELECT oc.user_coupon_id AS user_coupon_id,
|
||
oc.applied_amount AS applied_amount,
|
||
uc.status AS status,
|
||
uc.used_order_id AS used_order_id,
|
||
sc.discount_type AS discount_type,
|
||
uc.balance_amount AS balance_amount
|
||
FROM order_coupons oc
|
||
JOIN user_coupons uc ON uc.id=oc.user_coupon_id
|
||
JOIN system_coupons sc ON sc.id=uc.coupon_id
|
||
WHERE oc.order_id=?
|
||
`, orderID).Scan(&rows).Error
|
||
|
||
for _, r := range rows {
|
||
// 幂等校验:检查是否已有确认流水
|
||
ledgerExists, _ := readDb.UserCouponLedger.WithContext(ctx).Where(
|
||
readDb.UserCouponLedger.UserCouponID.Eq(r.UserCouponID),
|
||
readDb.UserCouponLedger.OrderID.Eq(orderID),
|
||
readDb.UserCouponLedger.Action.Eq("confirm"),
|
||
).Count()
|
||
if ledgerExists > 0 {
|
||
continue
|
||
}
|
||
|
||
// 2. 确认预扣:将 status=4 (预扣中) 更新为最终状态
|
||
if r.DiscountType == 1 { // 金额券
|
||
// 容错:如果券因历史 Bug 仍为 status=4,根据余额修正为正确状态
|
||
if r.Status == 4 {
|
||
finalStatus := int32(1) // 还有余额 → 未使用
|
||
if r.BalanceAmount <= 0 {
|
||
finalStatus = 2 // 余额已扣完 → 已使用
|
||
}
|
||
db.UserCoupons.WithContext(ctx).UnderlyingDB().Exec(
|
||
"UPDATE user_coupons SET status = ? WHERE id = ? AND status = 4",
|
||
finalStatus, r.UserCouponID)
|
||
}
|
||
|
||
// 记录确认流水
|
||
ledger := &model.UserCouponLedger{
|
||
UserID: userID,
|
||
UserCouponID: r.UserCouponID,
|
||
ChangeAmount: 0, // 确认操作不改变余额
|
||
BalanceAfter: r.BalanceAmount,
|
||
OrderID: orderID,
|
||
Action: "confirm",
|
||
CreatedAt: time.Now(),
|
||
}
|
||
_ = db.UserCouponLedger.WithContext(ctx).Create(ledger)
|
||
|
||
} else { // 满减/折扣券:一次性核销
|
||
res := db.UserCoupons.WithContext(ctx).UnderlyingDB().Exec(`
|
||
UPDATE user_coupons
|
||
SET status = 2
|
||
WHERE id = ? AND status = 4
|
||
`, r.UserCouponID)
|
||
|
||
if res.Error != nil {
|
||
return fmt.Errorf("failed to confirm coupon: %w", res.Error)
|
||
}
|
||
|
||
// 记录确认流水
|
||
ledger := &model.UserCouponLedger{
|
||
UserID: userID,
|
||
UserCouponID: r.UserCouponID,
|
||
ChangeAmount: 0,
|
||
BalanceAfter: 0,
|
||
OrderID: orderID,
|
||
Action: "confirm",
|
||
CreatedAt: time.Now(),
|
||
}
|
||
_ = db.UserCouponLedger.WithContext(ctx).Create(ledger)
|
||
}
|
||
}
|
||
return nil
|
||
}
|