110 lines
3.3 KiB
Go
Executable File
110 lines
3.3 KiB
Go
Executable File
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 {
|
|
var oc struct {
|
|
AppliedAmount int64
|
|
}
|
|
// 获取该订单实际扣减的优惠券金额
|
|
_ = tx.OrderCoupons.WithContext(ctx).Where(tx.OrderCoupons.OrderID.Eq(order.ID), tx.OrderCoupons.UserCouponID.Eq(order.CouponID)).Scan(&oc)
|
|
|
|
// 执行原子回退:增加余额 + 重置状态 + 清除占用订单
|
|
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 = ? AND used_order_id = ?
|
|
`, oc.AppliedAmount, order.CouponID, order.ID)
|
|
|
|
if res.RowsAffected > 0 {
|
|
// 记录退还流水
|
|
_ = tx.UserCouponLedger.WithContext(ctx).Create(&model.UserCouponLedger{
|
|
UserID: userID,
|
|
UserCouponID: order.CouponID,
|
|
ChangeAmount: oc.AppliedAmount,
|
|
OrderID: order.ID,
|
|
Action: "cancel_refund",
|
|
CreatedAt: time.Now(),
|
|
})
|
|
}
|
|
}
|
|
|
|
// 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
|
|
}
|