fix: 禁止后台取消已支付订单,避免退回钱包与释放已消耗库存
This commit is contained in:
1 parent
f627efce94
commit
71ea31ddc5
4 files changed
+58
-2
No files matched your search
@@ -434,7 +434,7 @@ watch(
|
||||
<SelectItem value="partially_refunded">{{ t('order.status.partially_refunded') }}</SelectItem>
|
||||
<SelectItem value="delivered">{{ t('order.status.delivered') }}</SelectItem>
|
||||
<SelectItem value="completed">{{ t('order.status.completed') }}</SelectItem>
|
||||
<SelectItem value="canceled">{{ t('order.status.canceled') }}</SelectItem>
|
||||
<SelectItem v-if="order.status === 'pending_payment'" value="canceled">{{ t('order.status.canceled') }}</SelectItem>
|
||||
<SelectItem value="refunded">{{ t('order.status.refunded') }}</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
|
||||
@@ -39,6 +39,7 @@ func TestOrderServiceTestsAreSplitByResponsibility(t *testing.T) {
|
||||
"TestCalcParentStatus", "TestCalcParentStatusAllRefunded", "TestCalcParentStatusPartiallyRefunded",
|
||||
"TestExpectedRefundStatus", "TestResolvedParentStatusPrefersOwnRefund",
|
||||
"TestIsTransitionAllowedRefunded", "TestUpdateOrderStatusParentToPartiallyRefundedSyncsChildren",
|
||||
"TestUpdateOrderStatusRejectsCancelingPaidParent",
|
||||
"TestUpdateOrderStatusRejectsManualPaidTransition",
|
||||
"TestCanCompleteParentOrder", "TestCanCompleteParentOrderRejectInvalidStatus",
|
||||
"TestCanCompleteParentOrderRejectInvalidChild",
|
||||
|
||||
@@ -30,7 +30,8 @@ func (s *OrderService) cancelOrderWithChildren(order *orderdomain.Order, rollbac
|
||||
if locked == nil {
|
||||
return ErrOrderNotFound
|
||||
}
|
||||
if locked.Status != order.Status {
|
||||
// 只有未支付订单可以取消:已支付订单取消会退回钱包、释放已消耗库存,必须走退款流程
|
||||
if locked.Status != order.Status || locked.Status != constants.OrderStatusPendingPayment {
|
||||
return ErrOrderCancelNotAllowed
|
||||
}
|
||||
updates := map[string]interface{}{
|
||||
@@ -181,6 +182,9 @@ func (s *OrderService) UpdateOrderStatus(orderID uint, targetStatus string) (*or
|
||||
if isParent {
|
||||
switch target {
|
||||
case constants.OrderStatusCanceled:
|
||||
if order.Status != constants.OrderStatusPendingPayment {
|
||||
return nil, ErrOrderStatusInvalid
|
||||
}
|
||||
if err := s.cancelOrderWithChildren(order, true); err != nil {
|
||||
return nil, ErrOrderUpdateFailed
|
||||
}
|
||||
|
||||
@@ -319,3 +319,54 @@ func TestCanCompleteParentOrderRejectInvalidChild(t *testing.T) {
|
||||
t.Fatalf("expected parent order with paid child to be rejected")
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateOrderStatusRejectsCancelingPaidParent(t *testing.T) {
|
||||
dsn := fmt.Sprintf("file:order_service_reject_cancel_paid_parent_%d?mode=memory&cache=shared", time.Now().UnixNano())
|
||||
db, err := gorm.Open(sqlite.Open(dsn), &gorm.Config{})
|
||||
if err != nil {
|
||||
t.Fatalf("open sqlite failed: %v", err)
|
||||
}
|
||||
if err := db.AutoMigrate(&orderdomain.Order{}, &orderdomain.OrderItem{}, &fulfillmentdomain.Fulfillment{}); err != nil {
|
||||
t.Fatalf("auto migrate failed: %v", err)
|
||||
}
|
||||
now := time.Now()
|
||||
parent := &orderdomain.Order{
|
||||
OrderNo: "PAID-PARENT-NO-CANCEL",
|
||||
Status: constants.OrderStatusDelivered,
|
||||
Currency: "CNY",
|
||||
TotalAmount: money.FromDecimal(decimal.NewFromInt(100)),
|
||||
OriginalAmount: money.FromDecimal(decimal.NewFromInt(100)),
|
||||
WalletPaidAmount: money.FromDecimal(decimal.NewFromInt(100)),
|
||||
PaidAt: &now,
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
}
|
||||
if err := db.Create(parent).Error; err != nil {
|
||||
t.Fatalf("create parent failed: %v", err)
|
||||
}
|
||||
child := &orderdomain.Order{
|
||||
OrderNo: "PAID-PARENT-NO-CANCEL-A",
|
||||
ParentID: &parent.ID,
|
||||
Status: constants.OrderStatusDelivered,
|
||||
Currency: "CNY",
|
||||
TotalAmount: money.FromDecimal(decimal.NewFromInt(100)),
|
||||
PaidAt: &now,
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
}
|
||||
if err := db.Create(child).Error; err != nil {
|
||||
t.Fatalf("create child failed: %v", err)
|
||||
}
|
||||
svc := NewOrderService(OrderServiceOptions{OrderStore: ordergormstore.New(db, "test-guest-credential-secret-with-32-bytes")})
|
||||
|
||||
if _, err := svc.UpdateOrderStatus(parent.ID, constants.OrderStatusCanceled); err != ErrOrderStatusInvalid {
|
||||
t.Fatalf("cancel paid parent error = %v, want %v", err, ErrOrderStatusInvalid)
|
||||
}
|
||||
var stored orderdomain.Order
|
||||
if err := db.First(&stored, parent.ID).Error; err != nil {
|
||||
t.Fatalf("reload parent failed: %v", err)
|
||||
}
|
||||
if stored.Status != constants.OrderStatusDelivered {
|
||||
t.Fatalf("paid parent status mutated to %s", stored.Status)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user