- 后端 internal/service 按职责拆分: fulfillment.go(1397→527)拆出 wallet/timeout/data/order/query/dashboard/shipnotify delivery.go(1124→801)拆出 upstream/link/state/helpers merchant.go(855→251)拆出 member/product/api_client/catalog/helpers - 前端 MerchantCenter.tsx(1327→606)拆出 merchantCenterTabs/merchantCenterUtils - docker-compose backend 透传 CORS_ALLOWED_ORIGINS - CORS 白名单实现(config/router/README/.env.example 配套) - 修复 gofmt 与文件尾部多余空行
117 lines
3.8 KiB
Go
117 lines
3.8 KiB
Go
package service
|
|
|
|
import (
|
|
"errors"
|
|
"time"
|
|
|
|
"affiliate_dash/internal/model"
|
|
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
func (s *FulfillmentService) GetOrder(merchantID uint, orderNo string) (*model.FulfillmentOrder, error) {
|
|
var order model.FulfillmentOrder
|
|
err := s.db.Preload("MerchantProduct.Product").
|
|
Where("merchant_id = ? AND order_no = ?", merchantID, orderNo).
|
|
First(&order).Error
|
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
return nil, errors.New("订单不存在")
|
|
}
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &order, nil
|
|
}
|
|
|
|
func (s *FulfillmentService) ListOrders(merchantID uint, page, size int, orderStatus string) ([]model.FulfillmentOrder, int64, error) {
|
|
page, size = normalizePage(page, size)
|
|
tx := s.db.Model(&model.FulfillmentOrder{}).Where("merchant_id = ?", merchantID)
|
|
if orderStatus != "" {
|
|
tx = tx.Where("order_status = ?", orderStatus)
|
|
}
|
|
var total int64
|
|
if err := tx.Count(&total).Error; err != nil {
|
|
return nil, 0, err
|
|
}
|
|
var orders []model.FulfillmentOrder
|
|
err := tx.Preload("MerchantProduct.Product").Order("id DESC").Offset((page - 1) * size).Limit(size).Find(&orders).Error
|
|
return orders, total, err
|
|
}
|
|
|
|
type OpenOrderQuery struct {
|
|
OrderNo string `json:"order_no"`
|
|
Status string `json:"status"`
|
|
CanShip bool `json:"can_ship"`
|
|
CannotShipReason string `json:"cannot_ship_reason,omitempty"`
|
|
Product *OpenOrderProduct `json:"product,omitempty"`
|
|
BuyerName string `json:"buyer_name"`
|
|
Amount int64 `json:"amount"`
|
|
ProviderOrderNo string `json:"provider_order_no,omitempty"`
|
|
CreatedAt time.Time `json:"created_at"`
|
|
ShippedAt *time.Time `json:"shipped_at"`
|
|
ShipFailReason string `json:"ship_fail_reason,omitempty"`
|
|
GameChannel string `json:"game_channel,omitempty"`
|
|
GameUID string `json:"game_uid,omitempty"`
|
|
RoleName string `json:"role_name,omitempty"`
|
|
PayScore int `json:"pay_score,omitempty"`
|
|
}
|
|
|
|
// OpenOrderProduct 开放接口返回的商品快照。
|
|
type OpenOrderProduct struct {
|
|
Name string `json:"name"`
|
|
SKU string `json:"sku"`
|
|
Game string `json:"game"`
|
|
}
|
|
|
|
// GetByOrderNo 按订单号查询(不限定商户,供上游 SourceOpen 使用)。
|
|
func (s *FulfillmentService) GetByOrderNo(orderNo string) (*model.FulfillmentOrder, error) {
|
|
var order model.FulfillmentOrder
|
|
err := s.db.Preload("MerchantProduct.Product").Where("order_no = ?", orderNo).First(&order).Error
|
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
return nil, errors.New("订单不存在")
|
|
}
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &order, nil
|
|
}
|
|
|
|
// QueryOpenOrder 供上游查询:商品信息 + 是否可发货。
|
|
func (s *FulfillmentService) QueryOpenOrder(orderNo string) (*OpenOrderQuery, error) {
|
|
if orderNo == "" {
|
|
return nil, errors.New("订单号不能为空")
|
|
}
|
|
order, err := s.GetByOrderNo(orderNo)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
canShip, reason := CanFulfill(order)
|
|
out := &OpenOrderQuery{
|
|
OrderNo: order.OrderNo,
|
|
Status: normalizeOrderStatus(order),
|
|
CanShip: canShip,
|
|
CannotShipReason: reason,
|
|
BuyerName: order.BuyerReference,
|
|
Amount: order.Amount,
|
|
ProviderOrderNo: order.ProviderOrderNo,
|
|
CreatedAt: order.CreatedAt,
|
|
ShippedAt: order.DeliveredAt,
|
|
ShipFailReason: order.FailureReason,
|
|
}
|
|
if order.MerchantProduct != nil {
|
|
game := ""
|
|
if order.MerchantProduct.Product != nil {
|
|
game = order.MerchantProduct.Product.Category
|
|
}
|
|
out.Product = &OpenOrderProduct{
|
|
Name: order.ProductName,
|
|
SKU: order.ProductSKU,
|
|
Game: game,
|
|
}
|
|
}
|
|
// 新模型无独立的游戏字段列,从 RequestData / ResultData JSON 中还原。
|
|
extractGameFields(order.RequestData, out)
|
|
extractGameFields(order.ResultData, out)
|
|
return out, nil
|
|
}
|