纯文件级移动,无行为变更: - delivery.go 常量、DeliveryService 结构、构造与依赖注入 - delivery_types.go 对外 DTO 与 deliveryHTTPError - delivery_provider.go 渠道取消/同步与 result_data 维护 - delivery_query.go 订单查询与发货状态构建 - delivery_bind.go 绑定与绑定结果查询 - delivery_submit.go 提交发货与提交阶段状态机 - deliveryGoodID 并入 delivery_helpers.go,链接管理并入 delivery_link.go 校验:拆分前后 55 个 func/type/const 声明完全一致(无缺失、无多出、无重复)。
248 lines
7.7 KiB
Go
248 lines
7.7 KiB
Go
package service
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"net/http"
|
|
"strings"
|
|
|
|
"affiliate_dash/internal/model"
|
|
)
|
|
|
|
func (s *DeliveryService) GetOrder(orderNo string, auth DeliveryLinkAuth) (*DeliveryOrderInfo, error) {
|
|
info, _, _, err := s.prepareOrder(orderNo, false, &auth)
|
|
return info, err
|
|
}
|
|
|
|
func (s *DeliveryService) GetMerchantOrder(merchantID uint, orderNo string) (*DeliveryOrderInfo, error) {
|
|
openOrder, order, goodID, err := s.loadMerchantOrder(merchantID, orderNo)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
info, _, _, err := s.buildDeliveryState(openOrder, order, goodID, false)
|
|
return info, err
|
|
}
|
|
|
|
func (s *DeliveryService) loadLinkedOrder(orderNo string, auth *DeliveryLinkAuth) (*OpenOrderQuery, *model.FulfillmentOrder, string, error) {
|
|
openOrder, err := s.fulfillment.QueryOpenOrder(orderNo)
|
|
if err != nil {
|
|
return nil, nil, "", err
|
|
}
|
|
order, err := s.fulfillment.GetByOrderNo(orderNo)
|
|
if err != nil {
|
|
return nil, nil, "", err
|
|
}
|
|
if auth != nil {
|
|
if err := s.authorizeDeliveryLink(order, *auth); err != nil {
|
|
return nil, nil, "", err
|
|
}
|
|
}
|
|
goodID := ""
|
|
if openOrder.Product != nil {
|
|
goodID = deliveryGoodID(s.channel, openOrder.Product.SKU)
|
|
}
|
|
return openOrder, order, goodID, nil
|
|
}
|
|
|
|
func (s *DeliveryService) loadMerchantOrder(merchantID uint, orderNo string) (*OpenOrderQuery, *model.FulfillmentOrder, string, error) {
|
|
if merchantID == 0 {
|
|
return nil, nil, "", errors.New("无效的商户")
|
|
}
|
|
openOrder, err := s.fulfillment.QueryOpenOrder(orderNo)
|
|
if err != nil {
|
|
return nil, nil, "", err
|
|
}
|
|
order, err := s.fulfillment.GetOrder(merchantID, orderNo)
|
|
if err != nil {
|
|
return nil, nil, "", err
|
|
}
|
|
if order.OrderStatus == model.OrderStatusDelivering {
|
|
if synced, _ := s.SyncDeliveringOrder(context.Background(), order); synced {
|
|
openOrder, _ = s.fulfillment.QueryOpenOrder(orderNo)
|
|
order, _ = s.fulfillment.GetOrder(merchantID, orderNo)
|
|
}
|
|
}
|
|
goodID := ""
|
|
if openOrder != nil && openOrder.Product != nil {
|
|
goodID = deliveryGoodID(s.channel, openOrder.Product.SKU)
|
|
}
|
|
return openOrder, order, goodID, nil
|
|
}
|
|
|
|
func (s *DeliveryService) buildDeliveryState(openOrder *OpenOrderQuery, order *model.FulfillmentOrder, goodID string, requireCanShip bool) (*DeliveryOrderInfo, *model.FulfillmentOrder, string, error) {
|
|
if openOrder == nil || order == nil {
|
|
return nil, nil, "", errors.New("订单不存在")
|
|
}
|
|
canShip, reason := CanFulfill(order)
|
|
if !openOrder.CanShip {
|
|
reason = openOrder.CannotShipReason
|
|
canShip = false
|
|
}
|
|
if !canShip && reason == "" {
|
|
reason = "订单暂不可发货"
|
|
}
|
|
if openOrder.Product == nil || openOrder.Product.SKU == "" {
|
|
canShip = false
|
|
reason = "订单缺少商品 SKU"
|
|
}
|
|
|
|
var (
|
|
deliveryProd *DeliveryProduct
|
|
goodData map[string]interface{}
|
|
)
|
|
|
|
providers, _ := s.router.ResolveProviders(order)
|
|
if len(providers) > 0 {
|
|
locked := resultDataString(order.ResultData, "provider") != ""
|
|
matchedState, lastReason := s.tryPrepareProviders(order, openOrder, providers)
|
|
// 已锁定渠道不可发时,清除锁定并按完整优先级重新选择
|
|
if matchedState == nil && locked {
|
|
s.clearSelectedProvider(order)
|
|
providers, _ = s.router.ResolveProviders(order)
|
|
matchedState, lastReason = s.tryPrepareProviders(order, openOrder, providers)
|
|
}
|
|
if matchedState != nil {
|
|
deliveryProd = matchedState.Product
|
|
goodData = matchedState.Good
|
|
if matchedState.Provider != "" {
|
|
_ = s.persistOrderResultPatch(order, map[string]interface{}{
|
|
"provider": matchedState.Provider,
|
|
})
|
|
}
|
|
} else {
|
|
canShip = false
|
|
if reason == "" || reason == "订单暂不可发货" {
|
|
reason = lastReason
|
|
if reason == "" {
|
|
reason = "未找到适用的履约渠道配置"
|
|
}
|
|
}
|
|
}
|
|
} else if goodID != "" {
|
|
goodData, _ = s.goodsDetail(goodID)
|
|
}
|
|
|
|
if deliveryProd == nil && openOrder.Product != nil {
|
|
deliveryProd = buildDeliveryProduct(openOrder.Product, goodData)
|
|
}
|
|
|
|
if requireCanShip && !canShip {
|
|
return nil, nil, "", newDeliveryHTTPError(http.StatusBadRequest, reason)
|
|
}
|
|
|
|
providerOrderNo := strings.TrimSpace(openOrder.ProviderOrderNo)
|
|
if providerOrderNo == "" {
|
|
providerOrderNo = strings.TrimSpace(order.ProviderOrderNo)
|
|
}
|
|
if providerOrderNo == "" {
|
|
providerOrderNo = resultDataString(order.ResultData, "lewan_order_no")
|
|
}
|
|
if providerOrderNo == "" {
|
|
providerOrderNo = resultDataString(order.ResultData, "provider_order_no")
|
|
}
|
|
|
|
providerName := resultDataString(order.ResultData, "provider")
|
|
if providerName == "" {
|
|
if strings.TrimSpace(order.ProviderOrderNo) != "" || resultDataString(order.ResultData, "lewan_order_id") != "" {
|
|
providerName = "lewan"
|
|
}
|
|
}
|
|
|
|
roleName := openOrder.RoleName
|
|
if roleName == "" {
|
|
roleName = resultDataString(order.ResultData, "role_name")
|
|
}
|
|
gameChannel := openOrder.GameChannel
|
|
if gameChannel == "" {
|
|
gameChannel = resultDataString(order.ResultData, "game_channel")
|
|
}
|
|
// 若角色名或区服为空,尝试从关联的 rock_tokens 表反查并自动回填
|
|
if (roleName == "" || gameChannel == "") && s.fulfillment != nil && s.fulfillment.db != nil {
|
|
var rockToken model.RockToken
|
|
if err := s.fulfillment.db.Where("assigned_order_no = ?", order.OrderNo).First(&rockToken).Error; err == nil {
|
|
patches := map[string]interface{}{}
|
|
if roleName == "" && rockToken.ActualRoleName != "" {
|
|
roleName = rockToken.ActualRoleName
|
|
patches["role_name"] = roleName
|
|
}
|
|
if gameChannel == "" && rockToken.ActualChannel != "" {
|
|
gameChannel = rockToken.ActualChannel
|
|
patches["game_channel"] = gameChannel
|
|
}
|
|
if len(patches) > 0 {
|
|
_ = s.persistOrderResultPatch(order, patches)
|
|
}
|
|
}
|
|
}
|
|
|
|
info := &DeliveryOrderInfo{
|
|
OrderNo: openOrder.OrderNo,
|
|
Status: openOrder.Status,
|
|
CanShip: canShip,
|
|
CannotShipReason: reason,
|
|
Product: deliveryProd,
|
|
BuyerName: openOrder.BuyerName,
|
|
Amount: openOrder.Amount,
|
|
ProviderOrderNo: providerOrderNo,
|
|
ProviderName: providerName,
|
|
CreatedAt: openOrder.CreatedAt,
|
|
ShippedAt: openOrder.ShippedAt,
|
|
ShipFailReason: openOrder.ShipFailReason,
|
|
GameChannel: gameChannel,
|
|
GameUID: openOrder.GameUID,
|
|
RoleName: roleName,
|
|
PayScore: openOrder.PayScore,
|
|
Data: requestDataMap(order.RequestData),
|
|
Good: goodData,
|
|
}
|
|
return info, order, goodID, nil
|
|
}
|
|
|
|
// tryPrepareProviders 按优先级尝试 Prepare,返回第一个 CanShip 的渠道状态。
|
|
func (s *DeliveryService) tryPrepareProviders(order *model.FulfillmentOrder, openOrder *OpenOrderQuery, providers []FulfillmentProvider) (*DeliveryOrderState, string) {
|
|
lastReason := ""
|
|
for _, p := range providers {
|
|
state, err := p.PrepareDelivery(context.Background(), order, openOrder)
|
|
if err != nil || state == nil {
|
|
continue
|
|
}
|
|
if state.Provider == "" {
|
|
state.Provider = p.Name()
|
|
}
|
|
if state.CanShip {
|
|
return state, ""
|
|
}
|
|
if state.CannotShipReason != "" {
|
|
lastReason = state.CannotShipReason
|
|
}
|
|
}
|
|
return nil, lastReason
|
|
}
|
|
|
|
func (s *DeliveryService) prepareOrder(orderNo string, requireCanShip bool, auth *DeliveryLinkAuth) (*DeliveryOrderInfo, *model.FulfillmentOrder, string, error) {
|
|
orderNo = strings.TrimSpace(orderNo)
|
|
if orderNo == "" {
|
|
return nil, nil, "", errors.New("订单号不能为空")
|
|
}
|
|
openOrder, err := s.fulfillment.QueryOpenOrder(orderNo)
|
|
if err != nil {
|
|
return nil, nil, "", err
|
|
}
|
|
order, err := s.fulfillment.GetByOrderNo(orderNo)
|
|
if err != nil {
|
|
return nil, nil, "", err
|
|
}
|
|
if auth != nil {
|
|
if err := s.authorizeDeliveryLink(order, *auth); err != nil {
|
|
return nil, nil, "", err
|
|
}
|
|
}
|
|
if order.OrderStatus == model.OrderStatusDelivering {
|
|
if synced, _ := s.SyncDeliveringOrder(context.Background(), order); synced {
|
|
openOrder, _ = s.fulfillment.QueryOpenOrder(orderNo)
|
|
order, _ = s.fulfillment.GetByOrderNo(orderNo)
|
|
}
|
|
}
|
|
return s.buildDeliveryState(openOrder, order, "", requireCanShip)
|
|
}
|