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 }