移除履约任务表
This commit is contained in:
@@ -0,0 +1,2 @@
|
|||||||
|
-- 当前履约流程由上游通过 URL 获取订单号后主动查单和回传结果,不再维护独立任务表。
|
||||||
|
DROP TABLE IF EXISTS fulfillment_jobs;
|
||||||
@@ -45,11 +45,6 @@ const (
|
|||||||
FulfillmentStatusFailed = "failed"
|
FulfillmentStatusFailed = "failed"
|
||||||
FulfillmentStatusCancelled = "cancelled"
|
FulfillmentStatusCancelled = "cancelled"
|
||||||
|
|
||||||
FulfillmentJobStatusPending = "pending"
|
|
||||||
FulfillmentJobStatusProcessing = "processing"
|
|
||||||
FulfillmentJobStatusSucceeded = "succeeded"
|
|
||||||
FulfillmentJobStatusFailed = "failed"
|
|
||||||
|
|
||||||
WalletLedgerCredit = "credit"
|
WalletLedgerCredit = "credit"
|
||||||
WalletLedgerDebit = "debit"
|
WalletLedgerDebit = "debit"
|
||||||
WalletLedgerRefund = "refund"
|
WalletLedgerRefund = "refund"
|
||||||
@@ -228,23 +223,6 @@ type FulfillmentOrder struct {
|
|||||||
MerchantProduct *MerchantProduct `gorm:"foreignKey:MerchantProductID" json:"merchant_product,omitempty"`
|
MerchantProduct *MerchantProduct `gorm:"foreignKey:MerchantProductID" json:"merchant_product,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// FulfillmentJob 为后续履约器保留可重试的持久化任务边界。
|
|
||||||
type FulfillmentJob struct {
|
|
||||||
ID uint `gorm:"primarykey" json:"id"`
|
|
||||||
CreatedAt time.Time `json:"created_at"`
|
|
||||||
UpdatedAt time.Time `json:"updated_at"`
|
|
||||||
|
|
||||||
MerchantID uint `gorm:"not null;index" json:"merchant_id"`
|
|
||||||
OrderID uint `gorm:"not null;uniqueIndex" json:"order_id"`
|
|
||||||
Status string `gorm:"size:16;not null;default:pending;index" json:"status"`
|
|
||||||
Attempts int `gorm:"not null;default:0" json:"attempts"`
|
|
||||||
NextRunAt time.Time `gorm:"not null;index" json:"next_run_at"`
|
|
||||||
ProviderOrderNo string `gorm:"size:96;index" json:"provider_order_no"`
|
|
||||||
RequestPayload string `gorm:"type:text" json:"request_payload"`
|
|
||||||
ResultPayload string `gorm:"type:text" json:"result_payload"`
|
|
||||||
LastError string `gorm:"size:512" json:"last_error"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// CallbackSubscription 为商户提供独立、可禁用的事件订阅。
|
// CallbackSubscription 为商户提供独立、可禁用的事件订阅。
|
||||||
type CallbackSubscription struct {
|
type CallbackSubscription struct {
|
||||||
ID uint `gorm:"primarykey" json:"id"`
|
ID uint `gorm:"primarykey" json:"id"`
|
||||||
|
|||||||
@@ -181,14 +181,6 @@ func (s *FulfillmentService) CreateOrder(in CreateFulfillmentOrderInput) (*Creat
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err := tx.Create(&model.FulfillmentJob{
|
|
||||||
MerchantID: in.MerchantID,
|
|
||||||
OrderID: order.ID,
|
|
||||||
Status: model.FulfillmentJobStatusPending,
|
|
||||||
NextRunAt: time.Now(),
|
|
||||||
}).Error; err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := writeAudit(tx, &in.MerchantID, nil, &in.APIClientID, "open_order.create", "fulfillment_order", order.OrderNo, map[string]interface{}{"client_order_no": in.ClientOrderNo, "sku": in.SKU}); err != nil {
|
if err := writeAudit(tx, &in.MerchantID, nil, &in.APIClientID, "open_order.create", "fulfillment_order", order.OrderNo, map[string]interface{}{"client_order_no": in.ClientOrderNo, "sku": in.SKU}); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -415,20 +407,6 @@ func (s *FulfillmentService) UpdateFulfillment(in FulfillmentUpdateInput) (*mode
|
|||||||
if err := tx.Model(&order).Updates(updates).Error; err != nil {
|
if err := tx.Model(&order).Updates(updates).Error; err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
jobStatus := model.FulfillmentJobStatusProcessing
|
|
||||||
if in.Status == model.FulfillmentStatusSucceeded {
|
|
||||||
jobStatus = model.FulfillmentJobStatusSucceeded
|
|
||||||
} else if in.Status == model.FulfillmentStatusFailed {
|
|
||||||
jobStatus = model.FulfillmentJobStatusFailed
|
|
||||||
}
|
|
||||||
if err := tx.Model(&model.FulfillmentJob{}).Where("order_id = ?", order.ID).Updates(map[string]interface{}{
|
|
||||||
"status": jobStatus,
|
|
||||||
"provider_order_no": in.ProviderOrderNo,
|
|
||||||
"result_payload": resultData,
|
|
||||||
"last_error": in.FailureReason,
|
|
||||||
}).Error; err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := tx.First(&out, order.ID).Error; err != nil {
|
if err := tx.First(&out, order.ID).Error; err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -509,12 +487,6 @@ func (s *FulfillmentService) CancelOrder(merchantID, apiClientID uint, orderNo,
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err := tx.Model(&model.FulfillmentJob{}).Where("order_id = ?", order.ID).Updates(map[string]interface{}{
|
|
||||||
"status": model.FulfillmentJobStatusFailed,
|
|
||||||
"last_error": "订单已取消",
|
|
||||||
}).Error; err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := tx.First(&out, order.ID).Error; err != nil {
|
if err := tx.First(&out, order.ID).Error; err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -941,22 +913,6 @@ func (s *FulfillmentService) HandleShipNotify(in ShipNotifyInput) (*ShipNotifyRe
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
jobStatus := model.FulfillmentJobStatusFailed
|
|
||||||
if nextStatus == model.FulfillmentStatusSucceeded {
|
|
||||||
jobStatus = model.FulfillmentJobStatusSucceeded
|
|
||||||
}
|
|
||||||
jobUpdates := map[string]interface{}{
|
|
||||||
"status": jobStatus,
|
|
||||||
"result_payload": resultData,
|
|
||||||
"last_error": in.FailReason,
|
|
||||||
}
|
|
||||||
if in.ProviderOrderNo != "" {
|
|
||||||
jobUpdates["provider_order_no"] = in.ProviderOrderNo
|
|
||||||
}
|
|
||||||
if err := tx.Model(&model.FulfillmentJob{}).Where("order_id = ?", order.ID).Updates(jobUpdates).Error; err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := writeAudit(tx, &order.MerchantID, nil, nil, "ship.notify", "fulfillment_order", order.OrderNo,
|
if err := writeAudit(tx, &order.MerchantID, nil, nil, "ship.notify", "fulfillment_order", order.OrderNo,
|
||||||
shipNotifyAuditMetadata(in, nextStatus, message)); err != nil {
|
shipNotifyAuditMetadata(in, nextStatus, message)); err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ func newServiceTestDB(t *testing.T) *gorm.DB {
|
|||||||
&model.WalletAccount{},
|
&model.WalletAccount{},
|
||||||
&model.WalletLedgerEntry{},
|
&model.WalletLedgerEntry{},
|
||||||
&model.FulfillmentOrder{},
|
&model.FulfillmentOrder{},
|
||||||
&model.FulfillmentJob{},
|
|
||||||
&model.CallbackSubscription{},
|
&model.CallbackSubscription{},
|
||||||
&model.CallbackDelivery{},
|
&model.CallbackDelivery{},
|
||||||
&model.AuditLog{},
|
&model.AuditLog{},
|
||||||
@@ -458,7 +457,7 @@ func TestCreateTestOrderSupportsFailedFulfillableStatus(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestHandleShipNotifyUpdatesOrderJobAndEnqueuesMerchantCallback(t *testing.T) {
|
func TestHandleShipNotifyUpdatesOrderAndEnqueuesMerchantCallback(t *testing.T) {
|
||||||
db := newServiceTestDB(t)
|
db := newServiceTestDB(t)
|
||||||
merchantID, product := seedFulfillmentMerchant(t, db, "merchant-source-notify", 1000, 2, 100)
|
merchantID, product := seedFulfillmentMerchant(t, db, "merchant-source-notify", 1000, 2, 100)
|
||||||
codec, err := NewSecretCodec("test-master-key")
|
codec, err := NewSecretCodec("test-master-key")
|
||||||
@@ -504,13 +503,6 @@ func TestHandleShipNotifyUpdatesOrderJobAndEnqueuesMerchantCallback(t *testing.T
|
|||||||
if order.FulfillmentStatus != model.FulfillmentStatusSucceeded || order.ProviderOrderNo != "SRC-10001" || order.DeliveredAt == nil {
|
if order.FulfillmentStatus != model.FulfillmentStatusSucceeded || order.ProviderOrderNo != "SRC-10001" || order.DeliveredAt == nil {
|
||||||
t.Fatalf("unexpected order after source notify: %+v", order)
|
t.Fatalf("unexpected order after source notify: %+v", order)
|
||||||
}
|
}
|
||||||
var job model.FulfillmentJob
|
|
||||||
if err := db.Where("order_id = ?", created.Order.ID).First(&job).Error; err != nil {
|
|
||||||
t.Fatalf("query fulfillment job: %v", err)
|
|
||||||
}
|
|
||||||
if job.Status != model.FulfillmentJobStatusSucceeded || job.ProviderOrderNo != "SRC-10001" {
|
|
||||||
t.Fatalf("unexpected job after source notify: %+v", job)
|
|
||||||
}
|
|
||||||
var delivery model.CallbackDelivery
|
var delivery model.CallbackDelivery
|
||||||
if err := db.Where("merchant_id = ? AND event = ?", merchantID, "order.fulfillment.updated").First(&delivery).Error; err != nil {
|
if err := db.Where("merchant_id = ? AND event = ?", merchantID, "order.fulfillment.updated").First(&delivery).Error; err != nil {
|
||||||
t.Fatalf("query callback delivery: %v", err)
|
t.Fatalf("query callback delivery: %v", err)
|
||||||
|
|||||||
@@ -33,6 +33,8 @@
|
|||||||
商户回调 / 商户查询订单
|
商户回调 / 商户查询订单
|
||||||
```
|
```
|
||||||
|
|
||||||
|
当前流程不再维护独立的 `fulfillment_jobs` 任务表;履约事实以订单表里的 `fulfillment_status`、上游单号、发货时间和失败原因等字段为准。
|
||||||
|
|
||||||
## 3. 鉴权不要混用
|
## 3. 鉴权不要混用
|
||||||
|
|
||||||
| 项 | 商户侧 `/api/client/v1` | 源头侧 `/api/open/v1` |
|
| 项 | 商户侧 `/api/client/v1` | 源头侧 `/api/open/v1` |
|
||||||
|
|||||||
Reference in New Issue
Block a user