130 lines
3.9 KiB
Go
130 lines
3.9 KiB
Go
package user
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"time"
|
|
|
|
"bindbox-game/internal/repository/mysql/dao"
|
|
"bindbox-game/internal/repository/mysql/model"
|
|
|
|
"gorm.io/gorm/clause"
|
|
)
|
|
|
|
// CancelOrder 取消订单
|
|
func (s *service) CancelOrder(ctx context.Context, userID int64, orderID int64, reason string) (*model.Orders, error) {
|
|
var updatedOrder *model.Orders
|
|
err := s.writeDB.Transaction(func(tx *dao.Query) error {
|
|
// 1. 查询订单
|
|
order, err := tx.Orders.WithContext(ctx).Clauses(clause.Locking{Strength: "UPDATE"}).Where(tx.Orders.ID.Eq(orderID), tx.Orders.UserID.Eq(userID)).First()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if order == nil {
|
|
return errors.New("order not found")
|
|
}
|
|
|
|
// 2. 校验状态
|
|
if order.Status != 1 {
|
|
return errors.New("order cannot be cancelled")
|
|
}
|
|
|
|
// 3. 退还积分
|
|
if order.PointsAmount > 0 {
|
|
refundReason := "cancel_order"
|
|
if reason != "" {
|
|
refundReason = refundReason + ":" + reason
|
|
}
|
|
|
|
// order.PointsAmount 已经是分单位,直接退还
|
|
pointsToRefund := order.PointsAmount
|
|
|
|
// Update User Points
|
|
existing, _ := tx.UserPoints.WithContext(ctx).Clauses(clause.Locking{Strength: "UPDATE"}).Where(tx.UserPoints.UserID.Eq(userID)).First()
|
|
if existing == nil {
|
|
if err := tx.UserPoints.WithContext(ctx).Omit(tx.UserPoints.ValidEnd).Create(&model.UserPoints{UserID: userID, Points: pointsToRefund}); err != nil {
|
|
return err
|
|
}
|
|
} else {
|
|
if _, err := tx.UserPoints.WithContext(ctx).Where(tx.UserPoints.ID.Eq(existing.ID)).Updates(map[string]any{"points": existing.Points + pointsToRefund}); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
// Log
|
|
led := &model.UserPointsLedger{UserID: userID, Action: "refund_points", Points: pointsToRefund, RefTable: "orders", RefID: order.OrderNo, Remark: refundReason}
|
|
if err := tx.UserPointsLedger.WithContext(ctx).Create(led); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
// 4. 退还优惠券(恢复预扣的余额和状态)
|
|
if order.CouponID > 0 {
|
|
// 查询订单使用的优惠券及扣减金额
|
|
type couponRow struct {
|
|
AppliedAmount int64
|
|
DiscountType int32
|
|
}
|
|
var cr couponRow
|
|
_ = tx.OrderCoupons.WithContext(ctx).UnderlyingDB().Raw(`
|
|
SELECT oc.applied_amount AS applied_amount, 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 = ? AND oc.user_coupon_id = ?
|
|
`, order.ID, order.CouponID).Scan(&cr)
|
|
|
|
if cr.AppliedAmount > 0 {
|
|
if cr.DiscountType == 1 { // 金额券:恢复余额
|
|
res := tx.UserCoupons.WithContext(ctx).UnderlyingDB().Exec(`
|
|
UPDATE user_coupons
|
|
SET balance_amount = balance_amount + ?,
|
|
status = 1,
|
|
used_order_id = NULL,
|
|
used_at = NULL
|
|
WHERE id = ?
|
|
`, cr.AppliedAmount, order.CouponID)
|
|
|
|
if res.RowsAffected > 0 {
|
|
// 记录退还流水
|
|
_ = tx.UserCouponLedger.WithContext(ctx).Create(&model.UserCouponLedger{
|
|
UserID: userID,
|
|
UserCouponID: order.CouponID,
|
|
ChangeAmount: cr.AppliedAmount,
|
|
BalanceAfter: 0, // 简化处理,后续可查询精确值
|
|
OrderID: order.ID,
|
|
Action: "refund",
|
|
CreatedAt: time.Now(),
|
|
})
|
|
}
|
|
} else { // 满减/折扣券:恢复状态
|
|
tx.UserCoupons.WithContext(ctx).UnderlyingDB().Exec(`
|
|
UPDATE user_coupons
|
|
SET status = 1,
|
|
used_order_id = NULL,
|
|
used_at = NULL
|
|
WHERE id = ? AND status = 4
|
|
`, order.CouponID)
|
|
}
|
|
}
|
|
}
|
|
|
|
// 5. 更新订单状态
|
|
updates := map[string]any{
|
|
tx.Orders.Status.ColumnName().String(): 3,
|
|
tx.Orders.CancelledAt.ColumnName().String(): time.Now(),
|
|
}
|
|
if _, err := tx.Orders.WithContext(ctx).Where(tx.Orders.ID.Eq(order.ID)).Updates(updates); err != nil {
|
|
return err
|
|
}
|
|
|
|
updatedOrder = order
|
|
updatedOrder.Status = 3
|
|
return nil
|
|
})
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return updatedOrder, nil
|
|
}
|