- 发货提交记录阶段推进与失败分类,失败不再清空 result_data - 超时巡检区分已提交上游与提交中断两类卡单,避免误判 - 已提交上游的失败订单禁止自动重发,防止重复发货 - ship_attempts 仅在 claim 时计数,失败阶段只记录分类信息 - CanFulfill 对已提交上游的失败单返回不可发货 - 删除预留的 pending 订单状态,统一订单状态模型 - 契约改名:order.fulfillment.updated -> order.shipping.updated,fulfillment:read -> shipping:read - 文档修正 scope 为或关系
142 lines
4.0 KiB
Go
142 lines
4.0 KiB
Go
package service
|
|
|
|
import (
|
|
"crypto/sha256"
|
|
"encoding/hex"
|
|
"encoding/json"
|
|
"errors"
|
|
"time"
|
|
|
|
"affiliate_dash/internal/model"
|
|
)
|
|
|
|
type fulfillmentTransitionKind string
|
|
|
|
const (
|
|
fulfillmentTransitionUpdate fulfillmentTransitionKind = "update"
|
|
fulfillmentTransitionShipNotify fulfillmentTransitionKind = "ship_notify"
|
|
fulfillmentTransitionTimeout fulfillmentTransitionKind = "timeout"
|
|
)
|
|
|
|
type orderRequestFingerprintPayload struct {
|
|
ClientOrderNo string `json:"client_order_no"`
|
|
SKU string `json:"sku"`
|
|
Quantity int64 `json:"quantity"`
|
|
BuyerReference string `json:"buyer_reference"`
|
|
RequestData string `json:"request_data"`
|
|
}
|
|
|
|
func orderRequestFingerprint(in CreateFulfillmentOrderInput, requestData string) string {
|
|
raw, _ := json.Marshal(orderRequestFingerprintPayload{
|
|
ClientOrderNo: in.ClientOrderNo,
|
|
SKU: in.SKU,
|
|
Quantity: in.Quantity,
|
|
BuyerReference: in.BuyerReference,
|
|
RequestData: requestData,
|
|
})
|
|
sum := sha256.Sum256(raw)
|
|
return hex.EncodeToString(sum[:])
|
|
}
|
|
|
|
func storedOrderRequestFingerprint(order *model.FulfillmentOrder) string {
|
|
if order == nil {
|
|
return ""
|
|
}
|
|
if order.RequestFingerprint != "" {
|
|
return order.RequestFingerprint
|
|
}
|
|
return orderRequestFingerprint(CreateFulfillmentOrderInput{
|
|
ClientOrderNo: order.ClientOrderNo,
|
|
SKU: order.ProductSKU,
|
|
Quantity: order.Quantity,
|
|
BuyerReference: order.BuyerReference,
|
|
}, order.RequestData)
|
|
}
|
|
|
|
func ensureSameIdempotentOrder(existing *model.FulfillmentOrder, fingerprint string) error {
|
|
if existing == nil {
|
|
return errors.New("订单不存在")
|
|
}
|
|
if storedOrderRequestFingerprint(existing) != fingerprint {
|
|
return errors.New("client_order_no 已存在,但本次请求参数与原订单不一致")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func normalizeOrderStatus(order *model.FulfillmentOrder) string {
|
|
if order == nil {
|
|
return ""
|
|
}
|
|
if order.OrderStatus != "" {
|
|
return order.OrderStatus
|
|
}
|
|
return model.OrderStatusPaid
|
|
}
|
|
|
|
func validateOrderStatusTransition(order *model.FulfillmentOrder, next string, kind fulfillmentTransitionKind) error {
|
|
if order == nil {
|
|
return errors.New("订单不存在")
|
|
}
|
|
current := normalizeOrderStatus(order)
|
|
if current == next {
|
|
return nil
|
|
}
|
|
switch current {
|
|
case model.OrderStatusCancelled:
|
|
return errors.New("订单已取消,不能更新状态")
|
|
case model.OrderStatusDelivered:
|
|
return errors.New("订单已交付,不能回退状态")
|
|
}
|
|
switch next {
|
|
case model.OrderStatusDelivering:
|
|
if current == model.OrderStatusPaid || current == model.OrderStatusShipFailed {
|
|
return nil
|
|
}
|
|
case model.OrderStatusDelivered:
|
|
if current == model.OrderStatusPaid ||
|
|
current == model.OrderStatusShipFailed ||
|
|
current == model.OrderStatusDelivering {
|
|
return nil
|
|
}
|
|
case model.OrderStatusShipFailed:
|
|
if kind == fulfillmentTransitionTimeout && current != model.OrderStatusDelivering {
|
|
return errors.New("只有发货中的订单可以标记超时")
|
|
}
|
|
if current == model.OrderStatusPaid ||
|
|
current == model.OrderStatusShipFailed ||
|
|
current == model.OrderStatusDelivering {
|
|
return nil
|
|
}
|
|
case model.OrderStatusCancelled:
|
|
if current == model.OrderStatusPaid || current == model.OrderStatusShipFailed {
|
|
return nil
|
|
}
|
|
}
|
|
return errors.New("订单当前状态不允许该状态变更")
|
|
}
|
|
|
|
func canCancelOrder(order *model.FulfillmentOrder) error {
|
|
if order == nil {
|
|
return errors.New("订单不存在")
|
|
}
|
|
status := normalizeOrderStatus(order)
|
|
if status == model.OrderStatusCancelled {
|
|
return nil
|
|
}
|
|
switch status {
|
|
case model.OrderStatusPaid, model.OrderStatusShipFailed:
|
|
return nil
|
|
case model.OrderStatusDelivering, model.OrderStatusDelivered:
|
|
return errors.New("订单已进入发货流程,不能取消")
|
|
default:
|
|
return errors.New("订单当前状态不能取消")
|
|
}
|
|
}
|
|
|
|
func processingTimedOut(order *model.FulfillmentOrder, timeout time.Duration, now time.Time) bool {
|
|
if order == nil || timeout <= 0 {
|
|
return false
|
|
}
|
|
return normalizeOrderStatus(order) == model.OrderStatusDelivering && order.UpdatedAt.Before(now.Add(-timeout))
|
|
}
|