纯文件级移动,无行为变更: - 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 声明完全一致(无缺失、无多出、无重复)。
208 lines
6.0 KiB
Go
208 lines
6.0 KiB
Go
package service
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"strings"
|
|
|
|
"affiliate_dash/internal/model"
|
|
)
|
|
|
|
func (s *DeliveryService) Bind(orderNo, gameAccount string, auth DeliveryLinkAuth) (*DeliveryBindResult, error) {
|
|
return s.bindWithOrder(orderNo, gameAccount, &auth, 0)
|
|
}
|
|
|
|
func (s *DeliveryService) BindForMerchant(merchantID uint, orderNo, gameAccount string) (*DeliveryBindResult, error) {
|
|
return s.bindWithOrder(orderNo, gameAccount, nil, merchantID)
|
|
}
|
|
|
|
// GetBindResult 查询绑定结果(自建发货页轮询用)。
|
|
// 绑定未完成时返回 bound=false(不报错),由前端提示继续扫码。
|
|
func (s *DeliveryService) GetBindResult(merchantID uint, orderNo, bindUUID string) (*DeliveryBindResult, error) {
|
|
bindUUID = strings.TrimSpace(bindUUID)
|
|
if bindUUID == "" {
|
|
return nil, errors.New("bind_uuid 不能为空")
|
|
}
|
|
openOrder, order, goodID, err := s.loadMerchantOrder(merchantID, orderNo)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if _, _, _, err := s.buildDeliveryState(openOrder, order, goodID, false); err != nil {
|
|
return nil, err
|
|
}
|
|
providers, _ := s.router.ResolveProviders(order)
|
|
if len(providers) == 0 {
|
|
return nil, errors.New("未配置发货渠道")
|
|
}
|
|
p := providers[0]
|
|
res, err := p.CheckBind(context.Background(), order, bindUUID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &DeliveryBindResult{
|
|
BindUUID: res.BindUUID,
|
|
BindURL: res.BindURL,
|
|
QRURL: res.QRURL,
|
|
Bound: res.Bound,
|
|
GameAccount: res.GameAccount,
|
|
RoleName: res.RoleName,
|
|
GameChannel: res.GameChannel,
|
|
ExpectedGameAccount: res.ExpectedGameAccount,
|
|
Mismatch: res.Mismatch,
|
|
Message: res.Message,
|
|
}, nil
|
|
}
|
|
|
|
// GetBindResultLinked 为公开发货链接场景提供查询绑定结果的能力。
|
|
func (s *DeliveryService) GetBindResultLinked(orderNo, bindUUID string, auth DeliveryLinkAuth) (*DeliveryBindResult, error) {
|
|
bindUUID = strings.TrimSpace(bindUUID)
|
|
if bindUUID == "" {
|
|
return nil, errors.New("bind_uuid 不能为空")
|
|
}
|
|
openOrder, order, goodID, err := s.loadLinkedOrder(orderNo, &auth)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if _, _, _, err := s.buildDeliveryState(openOrder, order, goodID, false); err != nil {
|
|
return nil, err
|
|
}
|
|
providers, _ := s.router.ResolveProviders(order)
|
|
if len(providers) == 0 {
|
|
return nil, errors.New("未配置发货渠道")
|
|
}
|
|
p := providers[0]
|
|
res, err := p.CheckBind(context.Background(), order, bindUUID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &DeliveryBindResult{
|
|
BindUUID: res.BindUUID,
|
|
BindURL: res.BindURL,
|
|
QRURL: res.QRURL,
|
|
Bound: res.Bound,
|
|
GameAccount: res.GameAccount,
|
|
RoleName: res.RoleName,
|
|
GameChannel: res.GameChannel,
|
|
ExpectedGameAccount: res.ExpectedGameAccount,
|
|
Mismatch: res.Mismatch,
|
|
Message: res.Message,
|
|
}, nil
|
|
}
|
|
|
|
func (s *DeliveryService) bindWithOrder(orderNo, gameAccount string, auth *DeliveryLinkAuth, merchantID uint) (*DeliveryBindResult, error) {
|
|
gameAccount = strings.TrimSpace(gameAccount)
|
|
if gameAccount == "" {
|
|
return nil, errors.New("请输入玩家编号")
|
|
}
|
|
var (
|
|
openOrder *OpenOrderQuery
|
|
order *model.FulfillmentOrder
|
|
goodID string
|
|
err error
|
|
)
|
|
if merchantID > 0 {
|
|
openOrder, order, goodID, err = s.loadMerchantOrder(merchantID, orderNo)
|
|
} else {
|
|
openOrder, order, goodID, err = s.loadLinkedOrder(orderNo, auth)
|
|
}
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
_, _, _, err = s.buildDeliveryState(openOrder, order, goodID, true)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
providers, _ := s.router.ResolveProviders(order)
|
|
if len(providers) == 0 {
|
|
return nil, errors.New("未配置发货渠道")
|
|
}
|
|
|
|
var lastErr error
|
|
var bindRes *ProviderBindResult
|
|
var usedProvider FulfillmentProvider
|
|
tried := map[string]bool{}
|
|
for _, p := range providers {
|
|
tried[p.Name()] = true
|
|
res, err := p.Bind(context.Background(), order, gameAccount)
|
|
if err == nil && res != nil {
|
|
bindRes = res
|
|
usedProvider = p
|
|
break
|
|
}
|
|
lastErr = err
|
|
}
|
|
|
|
// Prepare 可能因 GET 成功先锁住乐玩;随后写接口凭据/IP 校验失败时,清除锁定并尝试其余渠道(如 DLC)。
|
|
if bindRes == nil {
|
|
lockedName := resultDataString(order.ResultData, "provider")
|
|
if lockedName != "" || lastErr != nil {
|
|
s.clearSelectedProvider(order)
|
|
fallbackProviders, _ := s.router.ResolveProviders(order)
|
|
for _, p := range fallbackProviders {
|
|
if tried[p.Name()] {
|
|
continue
|
|
}
|
|
tried[p.Name()] = true
|
|
res, err := p.Bind(context.Background(), order, gameAccount)
|
|
if err == nil && res != nil {
|
|
bindRes = res
|
|
usedProvider = p
|
|
break
|
|
}
|
|
lastErr = err
|
|
}
|
|
}
|
|
}
|
|
|
|
if bindRes == nil {
|
|
if lastErr != nil {
|
|
return nil, lastErr
|
|
}
|
|
return nil, errors.New("发货渠道未就绪")
|
|
}
|
|
|
|
// 记录选定渠道与上游单号(如 lewan_order_id),不提前写入 submit claimed stage
|
|
if usedProvider != nil {
|
|
patch := map[string]interface{}{
|
|
"provider": usedProvider.Name(),
|
|
}
|
|
for k, v := range bindRes.PatchResultData {
|
|
patch[k] = v
|
|
}
|
|
_ = s.persistOrderResultPatch(order, patch)
|
|
}
|
|
|
|
return &DeliveryBindResult{
|
|
BindUUID: bindRes.BindUUID,
|
|
BindURL: bindRes.BindURL,
|
|
QRURL: bindRes.QRURL,
|
|
Bound: bindRes.Bound,
|
|
GameAccount: bindRes.GameAccount,
|
|
RoleName: bindRes.RoleName,
|
|
GameChannel: bindRes.GameChannel,
|
|
ExpectedGameAccount: bindRes.ExpectedGameAccount,
|
|
Mismatch: bindRes.Mismatch,
|
|
Message: bindRes.Message,
|
|
}, nil
|
|
}
|
|
|
|
func (s *DeliveryService) bindGood(gameAccount, goodID string) (*DeliveryBindResult, error) {
|
|
dlc, ok := s.router.GetProvider(ProviderDLC).(*DLCProvider)
|
|
if !ok || dlc == nil {
|
|
return nil, errors.New("DLC 渠道未就绪")
|
|
}
|
|
res, err := dlc.Bind(context.Background(), &model.FulfillmentOrder{
|
|
ProductSKU: "",
|
|
}, gameAccount)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &DeliveryBindResult{
|
|
BindUUID: res.BindUUID,
|
|
BindURL: res.BindURL,
|
|
QRURL: res.QRURL,
|
|
Bound: res.Bound,
|
|
}, nil
|
|
}
|