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 INTO order_coupons (order_id, user_coupon_id, applied_amount, created_at) VALUES (?,?,?,NOW(3))", orderID, userCouponID, appliedAmount).Error } // DeductCouponsForPaidOrder 支付成功后扣减金额券余额或核销一次性券 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 } 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 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 { // 幂等校验:已核销且绑定本订单,则跳过 if r.Status == 2 && r.UsedOrderID == orderID { continue } var currentBal int64 var ucUserID int64 uc, err := readDb.UserCoupons.WithContext(ctx).Where(readDb.UserCoupons.ID.Eq(r.UserCouponID)).First() if err != nil || uc == nil { continue // 或记录日志 } currentBal = uc.BalanceAmount ucUserID = uc.UserID if r.DiscountType == 1 { // 金额券:按量扣减 nb := currentBal - r.AppliedAmount if nb < 0 { nb = 0 } upd := map[string]any{ "balance_amount": nb, "used_order_id": orderID, "used_at": paidAt, } if nb == 0 { upd["status"] = 2 // 余额扣完,标记为已完成 } _, err = db.UserCoupons.WithContext(ctx).Where(db.UserCoupons.ID.Eq(r.UserCouponID)).Updates(upd) if err != nil { return fmt.Errorf("failed to update user coupon balance: %w", err) } // 记录账变流水 ledger := &model.UserCouponLedger{ UserID: ucUserID, UserCouponID: r.UserCouponID, ChangeAmount: -r.AppliedAmount, BalanceAfter: nb, OrderID: orderID, Action: "apply", CreatedAt: time.Now(), } _ = db.UserCouponLedger.WithContext(ctx).Create(ledger) } else { // 满减/折扣券:一次性核销 _, err = db.UserCoupons.WithContext(ctx).Where(db.UserCoupons.ID.Eq(r.UserCouponID)).Updates(map[string]any{ "status": 2, "used_order_id": orderID, "used_at": paidAt, }) if err != nil { return fmt.Errorf("failed to verify user coupon: %w", err) } // 记录账变流水 ledger := &model.UserCouponLedger{ UserID: ucUserID, UserCouponID: r.UserCouponID, ChangeAmount: -r.AppliedAmount, BalanceAfter: 0, OrderID: orderID, Action: "apply", CreatedAt: time.Now(), } _ = db.UserCouponLedger.WithContext(ctx).Create(ledger) } } return nil }