feat: 支持从失败状态重新发起退款,添加相关逻辑和测试用例
This commit is contained in:
@@ -512,7 +512,11 @@ func (s *order) AdminOrderDetail(ctx context.Context, tenantID, orderID int64) (
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// AdminRefundOrder 退款已支付订单(支持强制退款),并立即回收已授予的内容权益。
|
||||
// AdminRefundOrder 发起已支付订单退款(支持强制退款)。
|
||||
//
|
||||
// 语义:
|
||||
// - 该方法只负责将订单从 paid 推进到 refunding,并入队异步退款任务;
|
||||
// - 退款入账与权益回收由 job/worker 异步完成(见 ProcessRefundingOrder)。
|
||||
func (s *order) AdminRefundOrder(
|
||||
ctx context.Context,
|
||||
tenantID, operatorUserID, orderID int64,
|
||||
@@ -557,7 +561,9 @@ func (s *order) AdminRefundOrder(
|
||||
out = &orderModel
|
||||
return nil
|
||||
}
|
||||
if orderModel.Status != consts.OrderStatusPaid {
|
||||
|
||||
// 允许从 failed 重新发起退款:失败状态表示“上一次异步退款未完成/被标记失败”,可由管理员重试推进到 refunding。
|
||||
if orderModel.Status != consts.OrderStatusPaid && orderModel.Status != consts.OrderStatusFailed {
|
||||
return errorx.ErrStatusConflict.WithMsg("订单非已支付状态,无法退款")
|
||||
}
|
||||
if orderModel.PaidAt.IsZero() {
|
||||
@@ -765,6 +771,39 @@ func (s *order) ProcessRefundingOrder(ctx context.Context, params *ProcessRefund
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// MarkRefundFailed marks an order as failed during async refund processing.
|
||||
// 仅用于 worker 在判定“不可重试”错误时落终态,避免订单长期停留在 refunding。
|
||||
func (s *order) MarkRefundFailed(ctx context.Context, tenantID, orderID int64, now time.Time) error {
|
||||
if tenantID <= 0 || orderID <= 0 {
|
||||
return errorx.ErrInvalidParameter.WithMsg("tenant_id/order_id must be > 0")
|
||||
}
|
||||
if now.IsZero() {
|
||||
now = time.Now()
|
||||
}
|
||||
|
||||
return s.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
var orderModel models.Order
|
||||
if err := tx.
|
||||
Clauses(clause.Locking{Strength: "UPDATE"}).
|
||||
Where("tenant_id = ? AND id = ?", tenantID, orderID).
|
||||
First(&orderModel).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 已退款/已失败都无需变更。
|
||||
if orderModel.Status == consts.OrderStatusRefunded || orderModel.Status == consts.OrderStatusFailed {
|
||||
return nil
|
||||
}
|
||||
|
||||
return tx.Table(models.TableNameOrder).
|
||||
Where("id = ?", orderModel.ID).
|
||||
Updates(map[string]any{
|
||||
"status": consts.OrderStatusFailed,
|
||||
"updated_at": now,
|
||||
}).Error
|
||||
})
|
||||
}
|
||||
|
||||
func (s *order) PurchaseContent(ctx context.Context, params *PurchaseContentParams) (*PurchaseContentResult, error) {
|
||||
if params == nil {
|
||||
return nil, errorx.ErrInvalidParameter.WithMsg("params is required")
|
||||
|
||||
Reference in New Issue
Block a user