作废链接和取消订单
This commit is contained in:
@@ -404,6 +404,103 @@ func TestFulfillmentCancelRefundsOnceAndRestoresStock(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestFulfillmentCancelByUserRefundsAndAuditsActor(t *testing.T) {
|
||||
db := newServiceTestDB(t)
|
||||
merchantID, product := seedFulfillmentMerchant(t, db, "merchant-bu", 1000, 2, 300)
|
||||
svc := NewFulfillmentService(db, nil)
|
||||
created, err := svc.CreateOrder(CreateFulfillmentOrderInput{
|
||||
MerchantID: merchantID,
|
||||
APIClientID: 12,
|
||||
ClientOrderNo: "client-cancel-byuser",
|
||||
SKU: product.SKU,
|
||||
Quantity: 1,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("create order: %v", err)
|
||||
}
|
||||
cancelled, err := svc.CancelOrderByUser(merchantID, 99, created.Order.OrderNo, "商户后台取消")
|
||||
if err != nil {
|
||||
t.Fatalf("cancel by user: %v", err)
|
||||
}
|
||||
if cancelled.OrderStatus != model.OrderStatusCancelled {
|
||||
t.Fatalf("unexpected cancelled order: %+v", cancelled)
|
||||
}
|
||||
var wallet model.WalletAccount
|
||||
_ = db.Where("merchant_id = ?", merchantID).First(&wallet).Error
|
||||
if wallet.AvailableBalance != 1000 {
|
||||
t.Fatalf("wallet should refund full amount, got %d", wallet.AvailableBalance)
|
||||
}
|
||||
var audit model.AuditLog
|
||||
if err := db.Where("action = ? AND entity_id = ?", "merchant_order.cancel", created.Order.OrderNo).First(&audit).Error; err != nil {
|
||||
t.Fatalf("cancel audit missing: %v", err)
|
||||
}
|
||||
if audit.ActorUserID == nil || *audit.ActorUserID != 99 {
|
||||
t.Fatalf("cancel audit should record actor user 99, got %v", audit.ActorUserID)
|
||||
}
|
||||
if audit.APIClientID != nil {
|
||||
t.Fatalf("cancel audit should not record api client for backend path, got %v", *audit.APIClientID)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFulfillmentCancelRejectedWhenSubmittedUpstream(t *testing.T) {
|
||||
db := newServiceTestDB(t)
|
||||
merchantID, product := seedFulfillmentMerchant(t, db, "merchant-bd", 1000, 2, 300)
|
||||
svc := NewFulfillmentService(db, nil)
|
||||
create := func(clientOrderNo string) *model.FulfillmentOrder {
|
||||
t.Helper()
|
||||
created, err := svc.CreateOrder(CreateFulfillmentOrderInput{
|
||||
MerchantID: merchantID,
|
||||
APIClientID: 12,
|
||||
ClientOrderNo: clientOrderNo,
|
||||
SKU: product.SKU,
|
||||
Quantity: 1,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("create order: %v", err)
|
||||
}
|
||||
return created.Order
|
||||
}
|
||||
|
||||
// 已提交上游(provider_order_no 非空)的失败订单禁止取消,避免退款后上游仍发货。
|
||||
submitted := create("client-cancel-submitted")
|
||||
if err := db.Model(&model.FulfillmentOrder{}).Where("id = ?", submitted.ID).Updates(map[string]interface{}{
|
||||
"order_status": model.OrderStatusShipFailed,
|
||||
"provider_order_no": "PROV-UPSTREAM-001",
|
||||
"failure_reason": "上游角色校验失败",
|
||||
}).Error; err != nil {
|
||||
t.Fatalf("seed submitted failed order: %v", err)
|
||||
}
|
||||
if _, err := svc.CancelOrder(merchantID, 12, submitted.OrderNo, "想取消"); err == nil {
|
||||
t.Fatalf("cancel must be rejected when order already submitted upstream")
|
||||
}
|
||||
var walletAfterReject model.WalletAccount
|
||||
_ = db.Where("merchant_id = ?", merchantID).First(&walletAfterReject).Error
|
||||
if walletAfterReject.AvailableBalance != 700 {
|
||||
t.Fatalf("wallet must not be refunded after rejected cancel, got %d", walletAfterReject.AvailableBalance)
|
||||
}
|
||||
|
||||
// 未提交上游的失败订单(提交中断)可以正常取消退款。
|
||||
interrupted := create("client-cancel-interrupted")
|
||||
if err := db.Model(&model.FulfillmentOrder{}).Where("id = ?", interrupted.ID).Updates(map[string]interface{}{
|
||||
"order_status": model.OrderStatusShipFailed,
|
||||
"failure_reason": "发货提交中断",
|
||||
}).Error; err != nil {
|
||||
t.Fatalf("seed interrupted failed order: %v", err)
|
||||
}
|
||||
cancelled, err := svc.CancelOrder(merchantID, 12, interrupted.OrderNo, "确认中断,取消退款")
|
||||
if err != nil {
|
||||
t.Fatalf("cancel interrupted failed order: %v", err)
|
||||
}
|
||||
if cancelled.OrderStatus != model.OrderStatusCancelled {
|
||||
t.Fatalf("expected cancelled, got %s", cancelled.OrderStatus)
|
||||
}
|
||||
var walletFinal model.WalletAccount
|
||||
_ = db.Where("merchant_id = ?", merchantID).First(&walletFinal).Error
|
||||
if walletFinal.AvailableBalance != 700 {
|
||||
t.Fatalf("wallet should refund the interrupted order (1000-300-300+300), got %d", walletFinal.AvailableBalance)
|
||||
}
|
||||
}
|
||||
|
||||
func TestOrderStatusTransitions(t *testing.T) {
|
||||
db := newServiceTestDB(t)
|
||||
merchantID, product := seedFulfillmentMerchant(t, db, "merchant-c", 1000, -1, 100)
|
||||
|
||||
Reference in New Issue
Block a user