1144 lines
39 KiB
Markdown
1144 lines
39 KiB
Markdown
以下是修复后的完整代码:
|
||
|
||
```go
|
||
// File: go.mod
|
||
module sdk_LinkedMall
|
||
|
||
go 1.21
|
||
|
||
require (
|
||
github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.10
|
||
github.com/alibabacloud-go/tea/v2 v2.1.0
|
||
)
|
||
|
||
replace github.com/alibabacloud-go/tea/v2 => github.com/alibabacloud-go/tea/v2 v2.1.0
|
||
```
|
||
|
||
```go
|
||
// File: types.go
|
||
package sdk_LinkedMall
|
||
|
||
import "encoding/json"
|
||
|
||
// CommonResponse 是所有接口统一返回的外层结构体。
|
||
//
|
||
// 注意:HTTP 200 不代表业务成功,必须判断 Success 字段是否为 true。
|
||
// Code 为 SUCCESS 表示业务调用成功;否则看 SubCode、SubMessage 定位失败原因。
|
||
// Data 为业务数据,不同接口 Data 内部结构不同,可配合各接口的响应数据类型使用,
|
||
// 或调用 DataAs 方法解码到目标结构体。
|
||
type CommonResponse struct {
|
||
RequestId string `json:"RequestId"`
|
||
Success bool `json:"Success"`
|
||
Code string `json:"Code"`
|
||
Message string `json:"Message"`
|
||
SubCode string `json:"SubCode"`
|
||
SubMessage string `json:"SubMessage"`
|
||
Data interface{} `json:"Data"`
|
||
}
|
||
|
||
// DataAs 将 Data 字段解码到目标结构体 out 中。
|
||
func (r *CommonResponse) DataAs(out interface{}) error {
|
||
if r == nil || r.Data == nil {
|
||
return nil
|
||
}
|
||
b, err := json.Marshal(r.Data)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
return json.Unmarshal(b, out)
|
||
}
|
||
|
||
// rawResponse 用于内部解析响应,Data 使用 json.RawMessage 以支持按接口类型解码。
|
||
type rawResponse struct {
|
||
RequestId string `json:"RequestId"`
|
||
Success bool `json:"Success"`
|
||
Code string `json:"Code"`
|
||
Message string `json:"Message"`
|
||
SubCode string `json:"SubCode"`
|
||
SubMessage string `json:"SubMessage"`
|
||
Data json.RawMessage `json:"Data"`
|
||
}
|
||
|
||
// ---------- 采购方店铺 ----------
|
||
|
||
// ListPurchaserShopsRequest 分页获取采购方店铺列表请求。
|
||
type ListPurchaserShopsRequest struct {
|
||
PageNum int `json:"PageNum"`
|
||
PageSize int `json:"PageSize"`
|
||
}
|
||
|
||
// ListPurchaserShopsRespData 分页获取采购方店铺列表响应数据。
|
||
type ListPurchaserShopsRespData struct {
|
||
Total int64 `json:"Total"`
|
||
PageNum int `json:"PageNum"`
|
||
PageSize int `json:"PageSize"`
|
||
ShopList []PurchaserShopItem `json:"ShopList"`
|
||
}
|
||
|
||
// PurchaserShopItem 采购方店铺项。
|
||
type PurchaserShopItem struct {
|
||
ShopId string `json:"ShopId"`
|
||
PurchaserId string `json:"PurchaserId"`
|
||
ShopName string `json:"ShopName"`
|
||
ShopStatus string `json:"ShopStatus"`
|
||
}
|
||
|
||
// GetPurchaserShopRequest 获取单个采购店铺详情请求。
|
||
type GetPurchaserShopRequest struct {
|
||
ShopId string `json:"-"`
|
||
}
|
||
|
||
// GetPurchaserShopRespData 获取单个采购店铺详情响应数据。
|
||
type GetPurchaserShopRespData struct {
|
||
ShopId string `json:"ShopId"`
|
||
PurchaserId string `json:"PurchaserId"`
|
||
ShopName string `json:"ShopName"`
|
||
ShopStatus string `json:"ShopStatus"`
|
||
ContactInfo string `json:"ContactInfo"`
|
||
}
|
||
|
||
// ---------- 选品池商品 ----------
|
||
|
||
// ListSelectionProductsRequest 分页查询选品池商品列表请求。
|
||
type ListSelectionProductsRequest struct {
|
||
PageNum int `json:"PageNum"`
|
||
PageSize int `json:"PageSize"`
|
||
PurchaserId string `json:"PurchaserId"`
|
||
}
|
||
|
||
// ListSelectionProductsRespData 分页查询选品池商品列表响应数据。
|
||
type ListSelectionProductsRespData struct {
|
||
Total int64 `json:"Total"`
|
||
PageNum int `json:"PageNum"`
|
||
PageSize int `json:"PageSize"`
|
||
ProductList []SelectionProductItem `json:"ProductList"`
|
||
}
|
||
|
||
// SelectionProductItem 选品池商品项。
|
||
type SelectionProductItem struct {
|
||
ProductId string `json:"ProductId"`
|
||
Title string `json:"Title"`
|
||
MainImage string `json:"MainImage"`
|
||
BrandName string `json:"BrandName"`
|
||
CategoryId string `json:"CategoryId"`
|
||
}
|
||
|
||
// GetSelectionProductRequest 查询选品池商品详情请求。
|
||
type GetSelectionProductRequest struct {
|
||
ProductId string `json:"-"`
|
||
PurchaserId string `json:"PurchaserId"`
|
||
DivisionCode string `json:"DivisionCode"`
|
||
}
|
||
|
||
// GetSelectionProductRespData 查询选品池商品详情响应数据。
|
||
type GetSelectionProductRespData struct {
|
||
ProductId string `json:"ProductId"`
|
||
Title string `json:"Title"`
|
||
MainImage string `json:"MainImage"`
|
||
Images []string `json:"Images"`
|
||
BrandName string `json:"BrandName"`
|
||
CategoryId string `json:"CategoryId"`
|
||
Desc string `json:"Desc"`
|
||
SkuList []SelectionSkuItem `json:"SkuList"`
|
||
CanSale bool `json:"CanSale"`
|
||
StockQuantity int64 `json:"StockQuantity"`
|
||
}
|
||
|
||
// SelectionSkuItem 选品池商品 SKU 项。金额单位均为分。
|
||
type SelectionSkuItem struct {
|
||
SkuId string `json:"SkuId"`
|
||
SkuSpec string `json:"SkuSpec"`
|
||
SalePrice int64 `json:"SalePrice"`
|
||
MarketPrice int64 `json:"MarketPrice"`
|
||
StockQuantity int64 `json:"StockQuantity"`
|
||
}
|
||
|
||
// ---------- 类目 ----------
|
||
|
||
// ListCategoriesRequest 查询类目列表请求。
|
||
type ListCategoriesRequest struct {
|
||
PurchaserId string `json:"PurchaserId"`
|
||
ParentCategoryId string `json:"ParentCategoryId"`
|
||
}
|
||
|
||
// ListCategoriesRespData 查询类目列表响应数据。
|
||
type ListCategoriesRespData struct {
|
||
CategoryList []CategoryItem `json:"CategoryList"`
|
||
}
|
||
|
||
// CategoryItem 类目项。
|
||
type CategoryItem struct {
|
||
CategoryId string `json:"CategoryId"`
|
||
CategoryName string `json:"CategoryName"`
|
||
ParentId string `json:"ParentId"`
|
||
Level int `json:"Level"`
|
||
Leaf bool `json:"Leaf"`
|
||
}
|
||
|
||
// ---------- 搜索 ----------
|
||
|
||
// SearchProductsRequest 搜索选品池商品请求。
|
||
type SearchProductsRequest struct {
|
||
Keyword string `json:"Keyword"`
|
||
CategoryId string `json:"CategoryId"`
|
||
PageNum int `json:"PageNum"`
|
||
PageSize int `json:"PageSize"`
|
||
PurchaserId string `json:"PurchaserId"`
|
||
}
|
||
|
||
// SearchProductsRespData 搜索选品池商品响应数据。
|
||
type SearchProductsRespData struct {
|
||
Total int64 `json:"Total"`
|
||
PageNum int `json:"PageNum"`
|
||
PageSize int `json:"PageSize"`
|
||
ProductList []SelectionProductItem `json:"ProductList"`
|
||
}
|
||
|
||
// ---------- 选品池入库/出库 ----------
|
||
|
||
// SelectionGroupAddProductRequest 选品池商品入库请求。
|
||
type SelectionGroupAddProductRequest struct {
|
||
PurchaserId string `json:"PurchaserId"`
|
||
ShopId string `json:"ShopId"`
|
||
ProductIdList []string `json:"ProductIdList"`
|
||
}
|
||
|
||
// SelectionGroupRemoveProductRequest 选品池商品出库请求。
|
||
type SelectionGroupRemoveProductRequest struct {
|
||
PurchaserId string `json:"PurchaserId"`
|
||
ShopId string `json:"ShopId"`
|
||
ProductIdList []string `json:"ProductIdList"`
|
||
}
|
||
|
||
// SelectionGroupChangeProductRespData 选品池商品入库/出库响应数据。
|
||
type SelectionGroupChangeProductRespData struct {
|
||
SuccessCount int `json:"SuccessCount"`
|
||
}
|
||
|
||
// ---------- 采购单渲染 ----------
|
||
|
||
// RenderPurchaseOrderReq 采购单渲染(下单预校验)请求。
|
||
// SplitPurchaseOrder 请求体结构与之一致。
|
||
type RenderPurchaseOrderReq struct {
|
||
PurchaserId string `json:"PurchaserId"`
|
||
ShopId string `json:"ShopId"`
|
||
DivisionCode string `json:"DivisionCode"`
|
||
ItemList []RenderOrderItem `json:"ItemList"`
|
||
ReceiverInfo ReceiverInfo `json:"ReceiverInfo"`
|
||
}
|
||
|
||
// RenderOrderItem 渲染请求中的商品项。
|
||
type RenderOrderItem struct {
|
||
SkuId string `json:"SkuId"`
|
||
Quantity int64 `json:"Quantity"`
|
||
}
|
||
|
||
// ReceiverInfo 收货人信息。DivisionCode 必须使用五级(乡镇街道)编码。
|
||
type ReceiverInfo struct {
|
||
ReceiverName string `json:"ReceiverName"`
|
||
ReceiverPhone string `json:"ReceiverPhone"`
|
||
ProvinceCode string `json:"ProvinceCode"`
|
||
CityCode string `json:"CityCode"`
|
||
DistrictCode string `json:"DistrictCode"`
|
||
TownCode string `json:"TownCode"`
|
||
DetailAddress string `json:"DetailAddress"`
|
||
}
|
||
|
||
// RenderPurchaseOrderRespData 采购单渲染响应数据。
|
||
type RenderPurchaseOrderRespData struct {
|
||
CanSale bool `json:"CanSale"`
|
||
UnSaleReason string `json:"UnSaleReason"`
|
||
TotalAmount int64 `json:"TotalAmount"`
|
||
FreightAmount int64 `json:"FreightAmount"`
|
||
ItemList []RenderResultItem `json:"ItemList"`
|
||
}
|
||
|
||
// RenderResultItem 渲染结果中的商品明细。
|
||
type RenderResultItem struct {
|
||
SkuId string `json:"SkuId"`
|
||
ProductId string `json:"ProductId"`
|
||
Quantity int64 `json:"Quantity"`
|
||
SalePrice int64 `json:"SalePrice"`
|
||
CanSale bool `json:"CanSale"`
|
||
UnSaleReason string `json:"UnSaleReason"`
|
||
}
|
||
|
||
// ---------- 拆单 ----------
|
||
|
||
// SplitPurchaseOrderRespData 采购单渲染并拆单响应数据。
|
||
type SplitPurchaseOrderRespData struct {
|
||
SubOrderList []SplitSubOrderItem `json:"SubOrderList"`
|
||
}
|
||
|
||
// SplitSubOrderItem 拆单后的子单商品项。
|
||
type SplitSubOrderItem struct {
|
||
ShopId string `json:"ShopId"`
|
||
SkuId string `json:"SkuId"`
|
||
Quantity int64 `json:"Quantity"`
|
||
SalePrice int64 `json:"SalePrice"`
|
||
}
|
||
|
||
// ---------- 创建采购单 ----------
|
||
|
||
// CreatePurchaseOrderReq 创建采购单请求。
|
||
type CreatePurchaseOrderReq struct {
|
||
PurchaserId string `json:"PurchaserId"`
|
||
ShopId string `json:"ShopId"`
|
||
OuterPurchaseOrderId string `json:"OuterPurchaseOrderId"`
|
||
DivisionCode string `json:"DivisionCode"`
|
||
ReceiverInfo ReceiverInfo `json:"ReceiverInfo"`
|
||
SubOrderList []CreateSubOrderItem `json:"SubOrderList"`
|
||
}
|
||
|
||
// CreateSubOrderItem 创建采购单子单商品项。
|
||
type CreateSubOrderItem struct {
|
||
SkuId string `json:"SkuId"`
|
||
Quantity int64 `json:"Quantity"`
|
||
}
|
||
|
||
// CreatePurchaseOrderRespData 创建采购单响应数据。
|
||
type CreatePurchaseOrderRespData struct {
|
||
PurchaseOrderId string `json:"PurchaseOrderId"`
|
||
}
|
||
|
||
// 采购单状态枚举。
|
||
const (
|
||
PurchaseOrderStatusInit = "INIT"
|
||
PurchaseOrderStatusProcess = "PROCESS"
|
||
PurchaseOrderStatusSuccess = "SUCCESS"
|
||
PurchaseOrderStatusFail = "FAIL"
|
||
PurchaseOrderStatusClosed = "CLOSED"
|
||
)
|
||
|
||
// GetPurchaseOrderStatusRequest 获取采购单状态请求。
|
||
type GetPurchaseOrderStatusRequest struct {
|
||
PurchaseOrderId string `json:"-"`
|
||
PurchaserId string `json:"PurchaserId"`
|
||
}
|
||
|
||
// GetPurchaseOrderStatusRespData 获取采购单状态响应数据。
|
||
type GetPurchaseOrderStatusRespData struct {
|
||
PurchaseOrderId string `json:"PurchaseOrderId"`
|
||
Status string `json:"Status"`
|
||
OrderIdList []string `json:"OrderIdList"`
|
||
}
|
||
|
||
// ---------- 订单 ----------
|
||
|
||
// GetOrderRequest 获取订单详情请求。
|
||
type GetOrderRequest struct {
|
||
OrderId string `json:"-"`
|
||
PurchaserId string `json:"PurchaserId"`
|
||
}
|
||
|
||
// GetOrderRespData 获取订单详情响应数据。
|
||
type GetOrderRespData struct {
|
||
OrderId string `json:"OrderId"`
|
||
PurchaseOrderId string `json:"PurchaseOrderId"`
|
||
Status string `json:"Status"`
|
||
TotalAmount int64 `json:"TotalAmount"`
|
||
PayAmount int64 `json:"PayAmount"`
|
||
FreightAmount int64 `json:"FreightAmount"`
|
||
CreateTime string `json:"CreateTime"`
|
||
ItemList []OrderItem `json:"ItemList"`
|
||
ReceiverInfo OrderReceiverInfo `json:"ReceiverInfo"`
|
||
}
|
||
|
||
// OrderItem 订单商品明细。
|
||
type OrderItem struct {
|
||
OrderItemId string `json:"OrderItemId"`
|
||
ProductId string `json:"ProductId"`
|
||
SkuId string `json:"SkuId"`
|
||
SkuSpec string `json:"SkuSpec"`
|
||
Title string `json:"Title"`
|
||
Quantity int64 `json:"Quantity"`
|
||
PayAmount int64 `json:"PayAmount"`
|
||
}
|
||
|
||
// OrderReceiverInfo 订单收货信息。
|
||
type OrderReceiverInfo struct {
|
||
ReceiverName string `json:"ReceiverName"`
|
||
ReceiverPhone string `json:"ReceiverPhone"`
|
||
ProvinceCode string `json:"ProvinceCode"`
|
||
CityCode string `json:"CityCode"`
|
||
DistrictCode string `json:"DistrictCode"`
|
||
TownCode string `json:"TownCode"`
|
||
DetailAddress string `json:"DetailAddress"`
|
||
}
|
||
|
||
// QueryOrdersRequest 查询订单列表请求。
|
||
type QueryOrdersRequest struct {
|
||
PurchaserId string `json:"PurchaserId"`
|
||
PurchaseOrderId string `json:"PurchaseOrderId"`
|
||
PageNum int `json:"PageNum"`
|
||
PageSize int `json:"PageSize"`
|
||
StartTime string `json:"StartTime"`
|
||
EndTime string `json:"EndTime"`
|
||
}
|
||
|
||
// QueryOrdersRespData 查询订单列表响应数据。
|
||
type QueryOrdersRespData struct {
|
||
Total int64 `json:"Total"`
|
||
PageNum int `json:"PageNum"`
|
||
PageSize int `json:"PageSize"`
|
||
OrderList []OrderSummary `json:"OrderList"`
|
||
}
|
||
|
||
// OrderSummary 订单列表项。
|
||
type OrderSummary struct {
|
||
OrderId string `json:"OrderId"`
|
||
Status string `json:"Status"`
|
||
PayAmount int64 `json:"PayAmount"`
|
||
CreateTime string `json:"CreateTime"`
|
||
}
|
||
|
||
// ---------- 物流 ----------
|
||
|
||
// ListLogisticsOrdersRequest 查询订单物流信息请求。
|
||
type ListLogisticsOrdersRequest struct {
|
||
OrderId string `json:"-"`
|
||
PurchaserId string `json:"PurchaserId"`
|
||
}
|
||
|
||
// ListLogisticsOrdersRespData 查询订单物流信息响应数据。
|
||
type ListLogisticsOrdersRespData struct {
|
||
LogisticsList []LogisticsInfo `json:"LogisticsList"`
|
||
}
|
||
|
||
// LogisticsInfo 物流信息。
|
||
type LogisticsInfo struct {
|
||
TrackingNumber string `json:"TrackingNumber"`
|
||
CompanyName string `json:"CompanyName"`
|
||
Traces []LogisticsTrace `json:"Traces"`
|
||
}
|
||
|
||
// LogisticsTrace 物流轨迹节点。
|
||
type LogisticsTrace struct {
|
||
Time string `json:"Time"`
|
||
Location string `json:"Location"`
|
||
Description string `json:"Description"`
|
||
}
|
||
|
||
// ---------- 确认收货 ----------
|
||
|
||
// ConfirmDisburseRequest 确认收货请求。
|
||
type ConfirmDisburseRequest struct {
|
||
OrderId string `json:"-"`
|
||
PurchaserId string `json:"PurchaserId"`
|
||
}
|
||
|
||
// ---------- 售后 ----------
|
||
|
||
// RenderRefundOrderReq 售后渲染预校验请求。
|
||
type RenderRefundOrderReq struct {
|
||
PurchaserId string `json:"PurchaserId"`
|
||
OrderId string `json:"OrderId"`
|
||
OrderItemId string `json:"OrderItemId"`
|
||
RefundQuantity int64 `json:"RefundQuantity"`
|
||
RefundType string `json:"RefundType"`
|
||
}
|
||
|
||
// RenderRefundOrderRespData 售后渲染预校验响应数据。
|
||
type RenderRefundOrderRespData struct {
|
||
CanRefund bool `json:"CanRefund"`
|
||
RefundAmount int64 `json:"RefundAmount"`
|
||
MaxRefundQuantity int64 `json:"MaxRefundQuantity"`
|
||
UnRefundReason string `json:"UnRefundReason"`
|
||
}
|
||
|
||
// CreateRefundOrderReq 创建售后单请求。
|
||
type CreateRefundOrderReq struct {
|
||
PurchaserId string `json:"PurchaserId"`
|
||
OrderId string `json:"OrderId"`
|
||
OrderItemId string `json:"OrderItemId"`
|
||
RefundQuantity int64 `json:"RefundQuantity"`
|
||
RefundAmount int64 `json:"RefundAmount"`
|
||
RefundType string `json:"RefundType"`
|
||
RefundReason string `json:"RefundReason"`
|
||
OuterRefundNo string `json:"OuterRefundNo"`
|
||
}
|
||
|
||
// CreateRefundOrderRespData 创建售后单响应数据。
|
||
type CreateRefundOrderRespData struct {
|
||
RefundOrderId string `json:"RefundOrderId"`
|
||
}
|
||
|
||
// CancelRefundOrderRequest 取消售后单请求。
|
||
type CancelRefundOrderRequest struct {
|
||
RefundOrderId string `json:"-"`
|
||
PurchaserId string `json:"PurchaserId"`
|
||
}
|
||
|
||
// GetRefundOrderRequest 获取售后单详情请求。
|
||
type GetRefundOrderRequest struct {
|
||
RefundOrderId string `json:"-"`
|
||
PurchaserId string `json:"PurchaserId"`
|
||
}
|
||
|
||
// GetRefundOrderRespData 获取售后单详情响应数据。
|
||
type GetRefundOrderRespData struct {
|
||
RefundOrderId string `json:"RefundOrderId"`
|
||
OrderId string `json:"OrderId"`
|
||
OrderItemId string `json:"OrderItemId"`
|
||
Status string `json:"Status"`
|
||
RefundQuantity int64 `json:"RefundQuantity"`
|
||
RefundAmount int64 `json:"RefundAmount"`
|
||
RefundType string `json:"RefundType"`
|
||
RefundReason string `json:"RefundReason"`
|
||
}
|
||
|
||
// CreateGoodsShippingNoticeReq 回填退货物流运单请求。
|
||
type CreateGoodsShippingNoticeReq struct {
|
||
PurchaserId string `json:"PurchaserId"`
|
||
LogisticsCompany string `json:"LogisticsCompany"`
|
||
TrackingNumber string `json:"TrackingNumber"`
|
||
}
|
||
|
||
// ---------- 区域 ----------
|
||
|
||
// QueryChildDivisionCodeRequest 查询子区域编码请求。
|
||
type QueryChildDivisionCodeRequest struct {
|
||
ParentDivisionCode string `json:"ParentDivisionCode"`
|
||
}
|
||
|
||
// QueryChildDivisionCodeRespData 查询子区域编码响应数据。
|
||
type QueryChildDivisionCodeRespData struct {
|
||
DivisionList []DivisionItem `json:"DivisionList"`
|
||
}
|
||
|
||
// DivisionItem 区域编码项。
|
||
type DivisionItem struct {
|
||
DivisionCode string `json:"DivisionCode"`
|
||
DivisionName string `json:"DivisionName"`
|
||
Level int `json:"Level"`
|
||
ParentCode string `json:"ParentCode"`
|
||
}
|
||
|
||
// ---------- 回调通知 ----------
|
||
|
||
// CallbackEvent 阿里云消息回调事件。
|
||
// 业务侧消费成功后需返回 {"success":true},否则阿里云会重试推送。
|
||
type CallbackEvent struct {
|
||
EventCode string `json:"EventCode"`
|
||
PurchaseOrderId string `json:"PurchaseOrderId"`
|
||
OrderIdList []string `json:"OrderIdList"`
|
||
Status string `json:"Status"`
|
||
RequestId string `json:"RequestId"`
|
||
}
|
||
|
||
// 回调事件编码枚举。
|
||
const (
|
||
EventCodePurchaseOrderChange = "PURCHASE_ORDER_CHANGE"
|
||
EventCodeOrderStatusChange = "ORDER_STATUS_CHANGE"
|
||
EventCodeRefundOrderChange = "REFUND_ORDER_CHANGE"
|
||
)
|
||
```
|
||
|
||
```go
|
||
// File: client.go
|
||
// Package sdk_LinkedMall 是 LinkedMall OpenAPI 的 Go SDK。
|
||
// 当前覆盖指定的 25 个接口,覆盖:采购方店铺、选品池、类目、搜索、
|
||
// 采购单(渲染/拆单/创建/状态)、订单、物流、确认收货、售后、区域编码等。
|
||
//
|
||
// 客户端基于阿里云通用 OpenAPI 客户端(darabonba-openapi/v2)发起 ROA 调用,
|
||
// ROA 签名由底层客户端自动完成。所有接口返回统一外层结构 CommonResponse,
|
||
// 必须判断 Success 字段为 true 才代表业务成功。
|
||
package sdk_LinkedMall
|
||
|
||
import (
|
||
"context"
|
||
"encoding/json"
|
||
"fmt"
|
||
"io"
|
||
"reflect"
|
||
"strconv"
|
||
"strings"
|
||
|
||
openapi "github.com/alibabacloud-go/darabonba-openapi/v2/client"
|
||
)
|
||
|
||
// 常量定义。
|
||
const (
|
||
// RegionID 为华北3(张家口)。
|
||
RegionID = "cn-zhangjiakou"
|
||
// Endpoint 为网关访问地址。
|
||
Endpoint = "linkedmall.cn-zhangjiakou.aliyuncs.com"
|
||
// APIVersion 为 API 版本号。
|
||
APIVersion = "linkedmall/2023-09-30"
|
||
// BasePath 为 HTTP 基础路径前缀。
|
||
BasePath = "/opensaas-s2b/opensaas-s2b-biz-trade/v2"
|
||
)
|
||
|
||
// Client 是 LinkedMall OpenAPI 客户端。
|
||
//
|
||
// 通过 RAM 子账号 AccessKey 初始化,禁止使用主账号 AccessKey;
|
||
// 签名类型为 ROA 签名,由底层 darabonba-openapi/v2 客户端自动处理,业务无需自行实现。
|
||
type Client struct {
|
||
api *openapi.Client
|
||
}
|
||
|
||
// NewClient 使用 RAM 子账号 AccessKeyId / AccessKeySecret 创建客户端。
|
||
//
|
||
// 建议通过环境变量注入密钥,避免硬编码到代码中。
|
||
func NewClient(accessKeyID, accessKeySecret string) (*Client, error) {
|
||
config := &openapi.Config{
|
||
AccessKeyId: strPtr(accessKeyID),
|
||
AccessKeySecret: strPtr(accessKeySecret),
|
||
RegionId: strPtr(RegionID),
|
||
Endpoint: strPtr(Endpoint),
|
||
ReadTimeout: intPtr(30000),
|
||
ConnectTimeout: intPtr(10000),
|
||
}
|
||
api, err := openapi.NewClient(config)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("create openapi client: %w", err)
|
||
}
|
||
return &Client{api: api}, nil
|
||
}
|
||
|
||
// NewLinkedMallClient 是 NewClient 的别名,便于文档示例直接使用。
|
||
func NewLinkedMallClient(accessKeyID, accessKeySecret string) (*Client, error) {
|
||
return NewClient(accessKeyID, accessKeySecret)
|
||
}
|
||
|
||
// doRoa 发起一次 ROA 请求并解析统一的公共响应结构。
|
||
//
|
||
// dataOut 用于接收业务 Data,传入对应响应数据类型的指针;
|
||
// 若为 nil,则 Data 保持为 json.RawMessage。
|
||
func (c *Client) doRoa(ctx context.Context, method, path string, query map[string]*string, body interface{}, dataOut interface{}) (*CommonResponse, error) {
|
||
if err := ctx.Err(); err != nil {
|
||
return nil, err
|
||
}
|
||
fullPath := BasePath + path
|
||
var bodyBytes []byte
|
||
var err error
|
||
if body != nil {
|
||
bodyBytes, err = MarshalJSON(body)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("marshal request body: %w", err)
|
||
}
|
||
}
|
||
resp, err := c.api.RoaRequest(strPtr(method), strPtr(fullPath), query, bodyBytes, nil)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("roa request failed [%s %s]: %w", method, fullPath, err)
|
||
}
|
||
if resp == nil || resp.Body == nil {
|
||
return nil, fmt.Errorf("roa request returned empty response [%s %s]", method, fullPath)
|
||
}
|
||
respBytes, err := io.ReadAll(resp.Body)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("read response body: %w", err)
|
||
}
|
||
var raw rawResponse
|
||
if err := json.Unmarshal(respBytes, &raw); err != nil {
|
||
return nil, fmt.Errorf("unmarshal response [%s %s] body=%s: %w", method, fullPath, string(respBytes), err)
|
||
}
|
||
out := &CommonResponse{
|
||
RequestId: raw.RequestId,
|
||
Success: raw.Success,
|
||
Code: raw.Code,
|
||
Message: raw.Message,
|
||
SubCode: raw.SubCode,
|
||
SubMessage: raw.SubMessage,
|
||
}
|
||
if len(raw.Data) > 0 {
|
||
if dataOut != nil {
|
||
if err := json.Unmarshal(raw.Data, dataOut); err != nil {
|
||
return nil, fmt.Errorf("unmarshal response Data [%s %s]: %w", method, fullPath, err)
|
||
}
|
||
out.Data = dataOut
|
||
} else {
|
||
out.Data = raw.Data
|
||
}
|
||
}
|
||
return out, nil
|
||
}
|
||
|
||
// structToQuery 将请求结构体转换为 ROA query 参数。
|
||
// 仅序列化带 json tag 且非零值的字段;json:"-" 或空值字段被忽略。
|
||
func structToQuery(v interface{}) map[string]*string {
|
||
query := make(map[string]*string)
|
||
if v == nil {
|
||
return query
|
||
}
|
||
rv := reflect.ValueOf(v)
|
||
if rv.Kind() == reflect.Ptr {
|
||
if rv.IsNil() {
|
||
return query
|
||
}
|
||
rv = rv.Elem()
|
||
}
|
||
if rv.Kind() != reflect.Struct {
|
||
return query
|
||
}
|
||
rt := rv.Type()
|
||
for i := 0; i < rv.NumField(); i++ {
|
||
sf := rt.Field(i)
|
||
if !sf.IsExported() {
|
||
continue
|
||
}
|
||
name := sf.Tag.Get("json")
|
||
if name == "" || name == "-" {
|
||
continue
|
||
}
|
||
if idx := strings.Index(name, ","); idx >= 0 {
|
||
name = name[:idx]
|
||
}
|
||
if name == "" {
|
||
continue
|
||
}
|
||
fv := rv.Field(i)
|
||
switch fv.Kind() {
|
||
case reflect.String:
|
||
if s := fv.String(); s != "" {
|
||
query[name] = strPtr(s)
|
||
}
|
||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||
if n := fv.Int(); n != 0 {
|
||
query[name] = strPtr(strconv.FormatInt(n, 10))
|
||
}
|
||
case reflect.Bool:
|
||
if fv.Bool() {
|
||
query[name] = strPtr("true")
|
||
}
|
||
}
|
||
}
|
||
return query
|
||
}
|
||
|
||
// buildPath 将路径中的 {Param} 占位符替换为实际值。
|
||
func buildPath(path string, params map[string]string) string {
|
||
for k, v := range params {
|
||
path = strings.ReplaceAll(path, "{"+k+"}", v)
|
||
}
|
||
return path
|
||
}
|
||
|
||
// ListPurchaserShops 分页获取采购方店铺列表。
|
||
//
|
||
// 请求字段:PageNum(页码,默认1)、PageSize(每页条数,最大100)。
|
||
// 返回 Data 为 *ListPurchaserShopsRespData。
|
||
// 必须判断 resp.Success 为 true 才算业务成功。
|
||
func (c *Client) ListPurchaserShops(ctx context.Context, req *ListPurchaserShopsRequest) (*CommonResponse, error) {
|
||
return c.doRoa(ctx, "GET", "/purchaser-shops", structToQuery(req), nil, &ListPurchaserShopsRespData{})
|
||
}
|
||
|
||
// GetPurchaserShop 获取单个采购店铺详情。
|
||
//
|
||
// ShopId 为路径参数。返回 Data 为 *GetPurchaserShopRespData。
|
||
func (c *Client) GetPurchaserShop(ctx context.Context, req *GetPurchaserShopRequest) (*CommonResponse, error) {
|
||
path := buildPath("/purchaser-shops/{ShopId}", map[string]string{"ShopId": req.ShopId})
|
||
return c.doRoa(ctx, "GET", path, structToQuery(req), nil, &GetPurchaserShopRespData{})
|
||
}
|
||
|
||
// ListSelectionProducts 分页查询选品池商品列表。
|
||
//
|
||
// 请求字段:PageNum、PageSize(最大100)、PurchaserId(必填)。
|
||
// 返回 Data 为 *ListSelectionProductsRespData。
|
||
func (c *Client) ListSelectionProducts(ctx context.Context, req *ListSelectionProductsRequest) (*CommonResponse, error) {
|
||
return c.doRoa(ctx, "GET", "/selection-products", structToQuery(req), nil, &ListSelectionProductsRespData{})
|
||
}
|
||
|
||
// GetSelectionProduct 查询选品池商品详情。
|
||
//
|
||
// ProductId 为路径参数;支持传入五级乡镇 DivisionCode 校验该区域是否可售。
|
||
// 返回 Data 为 *GetSelectionProductRespData。
|
||
func (c *Client) GetSelectionProduct(ctx context.Context, req *GetSelectionProductRequest) (*CommonResponse, error) {
|
||
path := buildPath("/selection-products/{ProductId}", map[string]string{"ProductId": req.ProductId})
|
||
return c.doRoa(ctx, "GET", path, structToQuery(req), nil, &GetSelectionProductRespData{})
|
||
}
|
||
|
||
// ListCategories 查询类目列表。
|
||
//
|
||
// 不传 ParentCategoryId 时查询一级类目。返回 Data 为 *ListCategoriesRespData。
|
||
func (c *Client) ListCategories(ctx context.Context, req *ListCategoriesRequest) (*CommonResponse, error) {
|
||
return c.doRoa(ctx, "GET", "/categories", structToQuery(req), nil, &ListCategoriesRespData{})
|
||
}
|
||
|
||
// SearchProducts 搜索选品池商品。
|
||
//
|
||
// 返回 Data 为 *SearchProductsRespData。
|
||
func (c *Client) SearchProducts(ctx context.Context, req *SearchProductsRequest) (*CommonResponse, error) {
|
||
return c.doRoa(ctx, "POST", "/selection-products:search", nil, req, &SearchProductsRespData{})
|
||
}
|
||
|
||
// SelectionGroupAddProduct 选品池商品入库。
|
||
//
|
||
// 返回 Data 为 *SelectionGroupChangeProductRespData。
|
||
func (c *Client) SelectionGroupAddProduct(ctx context.Context, req *SelectionGroupAddProductRequest) (*CommonResponse, error) {
|
||
return c.doRoa(ctx, "POST", "/selection-group/products:add", nil, req, &SelectionGroupChangeProductRespData{})
|
||
}
|
||
|
||
// SelectionGroupRemoveProduct 选品池商品出库。
|
||
//
|
||
// 返回 Data 为 *SelectionGroupChangeProductRespData。
|
||
func (c *Client) SelectionGroupRemoveProduct(ctx context.Context, req *SelectionGroupRemoveProductRequest) (*CommonResponse, error) {
|
||
return c.doRoa(ctx, "POST", "/selection-group/products:remove", nil, req, &SelectionGroupChangeProductRespData{})
|
||
}
|
||
|
||
// RenderPurchaseOrder 采购单渲染(下单预校验)。
|
||
//
|
||
// 下单前必须调用,获取实时价格、校验商品是否可售。
|
||
// 返回 Data 为 *RenderPurchaseOrderRespData。
|
||
func (c *Client) RenderPurchaseOrder(ctx context.Context, req *RenderPurchaseOrderReq) (*CommonResponse, error) {
|
||
return c.doRoa(ctx, "POST", "/purchase-orders:render", nil, req, &RenderPurchaseOrderRespData{})
|
||
}
|
||
|
||
// SplitPurchaseOrder 采购单渲染并拆单。
|
||
//
|
||
// 请求体结构与 RenderPurchaseOrderReq 完全一致。
|
||
// 返回 Data 为 *SplitPurchaseOrderRespData,拆单结果用于创建采购单入参。
|
||
func (c *Client) SplitPurchaseOrder(ctx context.Context, req *RenderPurchaseOrderReq) (*CommonResponse, error) {
|
||
return c.doRoa(ctx, "POST", "/purchase-orders:split-render", nil, req, &SplitPurchaseOrderRespData{})
|
||
}
|
||
|
||
// CreatePurchaseOrder 创建采购单【异步】。
|
||
//
|
||
// 注意:接口只返回采购单号,不代表下单成功,必须等待回调通知后再查询订单接口。
|
||
// 返回 Data 为 *CreatePurchaseOrderRespData(PurchaseOrderId)。
|
||
func (c *Client) CreatePurchaseOrder(ctx context.Context, req *CreatePurchaseOrderReq) (*CommonResponse, error) {
|
||
return c.doRoa(ctx, "POST", "/purchase-orders", nil, req, &CreatePurchaseOrderRespData{})
|
||
}
|
||
|
||
// GetPurchaseOrderStatus 获取采购单状态。
|
||
//
|
||
// PurchaseOrderId 为路径参数。返回 Data 为 *GetPurchaseOrderStatusRespData。
|
||
// 状态枚举:INIT、PROCESS、SUCCESS、FAIL、CLOSED。
|
||
func (c *Client) GetPurchaseOrderStatus(ctx context.Context, req *GetPurchaseOrderStatusRequest) (*CommonResponse, error) {
|
||
path := buildPath("/purchase-orders/{PurchaseOrderId}/status", map[string]string{"PurchaseOrderId": req.PurchaseOrderId})
|
||
return c.doRoa(ctx, "GET", path, structToQuery(req), nil, &GetPurchaseOrderStatusRespData{})
|
||
}
|
||
|
||
// GetOrder 获取订单详情。
|
||
//
|
||
// OrderId 为路径参数。返回 Data 为 *GetOrderRespData。
|
||
func (c *Client) GetOrder(ctx context.Context, req *GetOrderRequest) (*CommonResponse, error) {
|
||
path := buildPath("/orders/{OrderId}", map[string]string{"OrderId": req.OrderId})
|
||
return c.doRoa(ctx, "GET", path, structToQuery(req), nil, &GetOrderRespData{})
|
||
}
|
||
|
||
// QueryOrders 查询订单列表。
|
||
//
|
||
// 返回 Data 为 *QueryOrdersRespData。
|
||
func (c *Client) QueryOrders(ctx context.Context, req *QueryOrdersRequest) (*CommonResponse, error) {
|
||
return c.doRoa(ctx, "GET", "/orders", structToQuery(req), nil, &QueryOrdersRespData{})
|
||
}
|
||
|
||
// ListLogisticsOrders 查询订单物流信息。
|
||
//
|
||
// OrderId 为路径参数。返回 Data 为 *ListLogisticsOrdersRespData。
|
||
func (c *Client) ListLogisticsOrders(ctx context.Context, req *ListLogisticsOrdersRequest) (*CommonResponse, error) {
|
||
path := buildPath("/orders/{OrderId}/logistics", map[string]string{"OrderId": req.OrderId})
|
||
return c.doRoa(ctx, "GET", path, structToQuery(req), nil, &ListLogisticsOrdersRespData{})
|
||
}
|
||
|
||
// ConfirmDisburse 确认收货。
|
||
//
|
||
// OrderId 为路径参数,请求体为 {"PurchaserId":"xxx"}。
|
||
func (c *Client) ConfirmDisburse(ctx context.Context, req *ConfirmDisburseRequest) (*CommonResponse, error) {
|
||
path := buildPath("/orders/{OrderId}:confirm-disburse", map[string]string{"OrderId": req.OrderId})
|
||
body := map[string]string{"PurchaserId": req.PurchaserId}
|
||
return c.doRoa(ctx, "POST", path, nil, body, nil)
|
||
}
|
||
|
||
// RenderRefundOrder 售后渲染预校验。
|
||
//
|
||
// 返回 Data 为 *RenderRefundOrderRespData。
|
||
func (c *Client) RenderRefundOrder(ctx context.Context, req *RenderRefundOrderReq) (*CommonResponse, error) {
|
||
return c.doRoa(ctx, "POST", "/refund-orders:render", nil, req, &RenderRefundOrderRespData{})
|
||
}
|
||
|
||
// CreateRefundOrder 创建售后单。
|
||
//
|
||
// 返回 Data 为 *CreateRefundOrderRespData(RefundOrderId)。
|
||
func (c *Client) CreateRefundOrder(ctx context.Context, req *CreateRefundOrderReq) (*CommonResponse, error) {
|
||
return c.doRoa(ctx, "POST", "/refund-orders", nil, req, &CreateRefundOrderRespData{})
|
||
}
|
||
|
||
// CancelRefundOrder 取消售后单。
|
||
//
|
||
// RefundOrderId 为路径参数,请求体为 {"PurchaserId":"xxx"}。
|
||
func (c *Client) CancelRefundOrder(ctx context.Context, req *CancelRefundOrderRequest) (*CommonResponse, error) {
|
||
path := buildPath("/refund-orders/{RefundOrderId}:cancel", map[string]string{"RefundOrderId": req.RefundOrderId})
|
||
body := map[string]string{"PurchaserId": req.PurchaserId}
|
||
return c.doRoa(ctx, "POST", path, nil, body, nil)
|
||
}
|
||
|
||
// GetRefundOrder 获取售后单详情。
|
||
//
|
||
// RefundOrderId 为路径参数。返回 Data 为 *GetRefundOrderRespData。
|
||
func (c *Client) GetRefundOrder(ctx context.Context, req *GetRefundOrderRequest) (*CommonResponse, error) {
|
||
path := buildPath("/refund-orders/{RefundOrderId}", map[string]string{"RefundOrderId": req.RefundOrderId})
|
||
return c.doRoa(ctx, "GET", path, structToQuery(req), nil, &GetRefundOrderRespData{})
|
||
}
|
||
|
||
// CreateGoodsShippingNotice 回填退货物流运单。
|
||
//
|
||
// RefundOrderId 为路径参数。
|
||
func (c *Client) CreateGoodsShippingNotice(ctx context.Context, req *CreateGoodsShippingNoticeReq) (*CommonResponse, error) {
|
||
path := buildPath("/refund-orders/{RefundOrderId}:fill-logistics", map[string]string{"RefundOrderId": req.RefundOrderId})
|
||
return c.doRoa(ctx, "POST", path, nil, req, nil)
|
||
}
|
||
|
||
// QueryChildDivisionCode 查询子区域编码。
|
||
//
|
||
// 不传 ParentDivisionCode 返回省级;传入省返回市,传入市返回区,传入区返回乡镇(五级)。
|
||
// 下单、商品可售校验必须使用乡镇级 DivisionCode。
|
||
// 返回 Data 为 *QueryChildDivisionCodeRespData。
|
||
func (c *Client) QueryChildDivisionCode(ctx context.Context, req *QueryChildDivisionCodeRequest) (*CommonResponse, error) {
|
||
return c.doRoa(ctx, "GET", "/divisions:children", structToQuery(req), nil, &QueryChildDivisionCodeRespData{})
|
||
}
|
||
|
||
// ParseCallbackEvent 解析阿里云消息回调的请求体。
|
||
//
|
||
// 收到回调后应返回 CallbackSuccessBody(),否则阿里云会重试推送。
|
||
// 同一事件可能多次推送,业务侧回调处理需做好幂等。
|
||
func ParseCallbackEvent(body []byte) (*CallbackEvent, error) {
|
||
var evt CallbackEvent
|
||
if err := json.Unmarshal(body, &evt); err != nil {
|
||
return nil, fmt.Errorf("parse callback event: %w", err)
|
||
}
|
||
return &evt, nil
|
||
}
|
||
|
||
// CallbackSuccessBody 返回告知阿里云消费成功的响应体 {"success":true}。
|
||
func CallbackSuccessBody() []byte {
|
||
return []byte(`{"success":true}`)
|
||
}
|
||
|
||
// strPtr 返回 string 变量的指针。
|
||
func strPtr(s string) *string { return &s }
|
||
|
||
// intPtr 返回 int 变量的指针。
|
||
func intPtr(i int) *int { return &i }
|
||
```
|
||
|
||
```go
|
||
// File: crypto.go
|
||
package sdk_LinkedMall
|
||
|
||
import (
|
||
"bytes"
|
||
"encoding/json"
|
||
)
|
||
|
||
// 关于加解密与签名:
|
||
//
|
||
// 本 SDK 基于阿里云通用 OpenAPI 客户端(darabonba-openapi/v2)发起 ROA 调用,
|
||
// ROA 签名(AccessKeyId / AccessKeySecret 签名)由客户端内部自动完成,
|
||
// 业务侧无需自行实现签名算法,因此本文件不包含额外的签名/加密实现。
|
||
//
|
||
// 本文件保留与数据安全相关的辅助函数,供后续扩展加解密、签名能力时使用:
|
||
// - MarshalJSON / UnmarshalJSON:确保金额等 int64 字段在 JSON 序列化/反序列化过程中
|
||
// 不会丢失精度(接口金额单位统一为分,使用 int64,禁止使用 float64)。
|
||
|
||
// MarshalJSON 将对象安全序列化为 JSON 字节。
|
||
// 与标准库 encoding/json.Marshal 的区别:关闭了 HTML 转义,保证输出紧凑且不转义特殊字符。
|
||
func MarshalJSON(v interface{}) ([]byte, error) {
|
||
var buf bytes.Buffer
|
||
enc := json.NewEncoder(&buf)
|
||
enc.SetEscapeHTML(false)
|
||
if err := enc.Encode(v); err != nil {
|
||
return nil, err
|
||
}
|
||
// json.Encoder.Encode 会在末尾追加一个换行符,这里去掉。
|
||
return bytes.TrimRight(buf.Bytes(), "\n"), nil
|
||
}
|
||
|
||
// UnmarshalJSON 使用 UseNumber 解析 JSON,避免 int64 金额被转换为 float64 丢失精度。
|
||
func UnmarshalJSON(data []byte, v interface{}) error {
|
||
dec := json.NewDecoder(bytes.NewReader(data))
|
||
dec.UseNumber()
|
||
return dec.Decode(v)
|
||
}
|
||
```
|
||
|
||
```go
|
||
// File: errors.go
|
||
package sdk_LinkedMall
|
||
|
||
import "fmt"
|
||
|
||
// 业务错误码常量。
|
||
const (
|
||
// CodeShopTypeInvalid SKU 属于经销集采店铺,不可下单,应过滤该 SKU。
|
||
CodeShopTypeInvalid = "ShopTypeInvalid"
|
||
// CodeSkuPriceUnique SKU 价格非最新,需重新调用 RenderPurchaseOrder 获取最新价格后再下单。
|
||
CodeSkuPriceUnique = "SkuPriceUnique"
|
||
// CodePurchaseOrderNotFound 采购单号不存在,需核对入参 PurchaseOrderId。
|
||
CodePurchaseOrderNotFound = "PurchaseOrderNotFound"
|
||
// CodeRefundNumberMustLessThanOrder 退款数量大于订单商品数量,需修正退款数量。
|
||
CodeRefundNumberMustLessThanOrder = "RefundNumberMustLessThanOrder"
|
||
// CodeRefundAmountMustLessThanOrder 退款金额大于实付金额,需修正退款金额。
|
||
CodeRefundAmountMustLessThanOrder = "RefundAmountMustLessThanOrder"
|
||
// CodeHasNoPrivilege RAM 无接口权限,需检查 RAM 授权策略。
|
||
CodeHasNoPrivilege = "HasNoPrivilege"
|
||
// CodeOrderNotFound 订单 ID 不存在,需核对 OrderId。
|
||
CodeOrderNotFound = "OrderNotFound"
|
||
// CodeShopNotFind 店铺不存在,需核对 ShopId / PurchaserId。
|
||
CodeShopNotFind = "ShopNotFind"
|
||
// CodeSavePurchaseOrderError 保存采购单服务端异常,建议短间隔重试。
|
||
CodeSavePurchaseOrderError = "SavePurchaseOrderError"
|
||
// CodeOuterPurchaseOrderIdExist 外部业务单号重复,需更换 OuterPurchaseOrderId 并做好幂等。
|
||
CodeOuterPurchaseOrderIdExist = "OuterPurchaseOrderIdExist"
|
||
)
|
||
|
||
// APIError 表示业务侧返回的错误。
|
||
type APIError struct {
|
||
RequestId string
|
||
Code string
|
||
Message string
|
||
SubCode string
|
||
SubMessage string
|
||
}
|
||
|
||
// Error 实现 error 接口。
|
||
func (e *APIError) Error() string {
|
||
return fmt.Sprintf(
|
||
"linkedmall api error [requestId=%s code=%s subCode=%s]: %s %s",
|
||
e.RequestId, e.Code, e.SubCode, e.Message, e.SubMessage,
|
||
)
|
||
}
|
||
|
||
// CheckSuccess 校验响应是否业务成功。
|
||
//
|
||
// 返回 nil 表示 Success 为 true(业务成功);
|
||
// 否则返回 *APIError,携带 Code / SubCode / SubMessage 便于定位原因。
|
||
func CheckSuccess(resp *CommonResponse) error {
|
||
if resp == nil {
|
||
return fmt.Errorf("linkedmall: empty response")
|
||
}
|
||
if resp.Success {
|
||
return nil
|
||
}
|
||
return &APIError{
|
||
RequestId: resp.RequestId,
|
||
Code: resp.Code,
|
||
Message: resp.Message,
|
||
SubCode: resp.SubCode,
|
||
SubMessage: resp.SubMessage,
|
||
}
|
||
}
|
||
|
||
// IsCode 判断响应是否命中指定错误码。
|
||
func IsCode(resp *CommonResponse, code string) bool {
|
||
return resp != nil && resp.Code == code
|
||
}
|
||
```
|
||
|
||
```go
|
||
// File: example_test.go
|
||
package sdk_LinkedMall
|
||
|
||
import (
|
||
"testing"
|
||
)
|
||
|
||
// TestNewClient 校验客户端初始化(不发起网络请求)。
|
||
func TestNewClient(t *testing.T) {
|
||
cli, err := NewClient("test-access-key-id", "test-access-key-secret")
|
||
if err != nil {
|
||
t.Fatalf("NewClient() error = %v", err)
|
||
}
|
||
if cli == nil {
|
||
t.Fatal("NewClient() returned nil client")
|
||
}
|
||
}
|
||
|
||
// TestNewLinkedMallClient 校验别名构造函数。
|
||
func TestNewLinkedMallClient(t *testing.T) {
|
||
cli, err := NewLinkedMallClient("test-access-key-id", "test-access-key-secret")
|
||
if err != nil {
|
||
t.Fatalf("NewLinkedMallClient() error = %v", err)
|
||
}
|
||
if cli == nil {
|
||
t.Fatal("NewLinkedMallClient() returned nil client")
|
||
}
|
||
}
|
||
|
||
// TestStructToQuery 校验请求结构体到 query 参数的转换。
|
||
func TestStructToQuery(t *testing.T) {
|
||
req := &ListPurchaserShopsRequest{PageNum: 1, PageSize: 20}
|
||
q := structToQuery(req)
|
||
if got := q["PageNum"]; got == nil || *got != "1" {
|
||
t.Errorf("PageNum = %v, want 1", got)
|
||
}
|
||
if got := q["PageSize"]; got == nil || *got != "20" {
|
||
t.Errorf("PageSize = %v, want 20", got)
|
||
}
|
||
}
|
||
|
||
// TestStructToQuerySkipEmpty 校验零值字段被忽略。
|
||
func TestStructToQuerySkipEmpty(t *testing.T) {
|
||
req := &ListSelectionProductsRequest{PurchaserId: "p-001"}
|
||
q := structToQuery(req)
|
||
if q["PageNum"] != nil {
|
||
t.Errorf("PageNum should be omitted, got %v", *q["PageNum"])
|
||
}
|
||
if got := q["PurchaserId"]; got == nil || *got != "p-001" {
|
||
t.Errorf("PurchaserId = %v, want p-001", got)
|
||
}
|
||
}
|
||
|
||
// TestBuildPath 校验路径占位符替换。
|
||
func TestBuildPath(t *testing.T) {
|
||
path := buildPath("/orders/{OrderId}/logistics", map[string]string{"OrderId": "O123"})
|
||
if path != "/orders/O123/logistics" {
|
||
t.Errorf("buildPath() = %s, want /orders/O123/logistics", path)
|
||
}
|
||
}
|
||
|
||
// TestMarshalJSON 校验 int64 金额序列化不丢失精度。
|
||
func TestMarshalJSON(t *testing.T) {
|
||
b, err := MarshalJSON(map[string]int64{"amount": 123456789})
|
||
if err != nil {
|
||
t.Fatalf("MarshalJSON() error = %v", err)
|
||
}
|
||
if string(b) != `{"amount":123456789}` {
|
||
t.Errorf("MarshalJSON() = %s, want {\"amount\":123456789}", string(b))
|
||
}
|
||
}
|
||
|
||
// TestParseCallbackEvent 校验回调事件解析。
|
||
func TestParseCallbackEvent(t *testing.T) {
|
||
body := []byte(`{
|
||
"EventCode":"PURCHASE_ORDER_CHANGE",
|
||
"PurchaseOrderId":"PO123",
|
||
"OrderIdList":["O1","O2"],
|
||
"Status":"SUCCESS",
|
||
"RequestId":"R1"
|
||
}`)
|
||
evt, err := ParseCallbackEvent(body)
|
||
if err != nil {
|
||
t.Fatalf("ParseCallbackEvent() error = %v", err)
|
||
}
|
||
if evt.EventCode != EventCodePurchaseOrderChange {
|
||
t.Errorf("EventCode = %s, want %s", evt.EventCode, EventCodePurchaseOrderChange)
|
||
}
|
||
if evt.PurchaseOrderId != "PO123" || len(evt.OrderIdList) != 2 {
|
||
t.Errorf("unexpected callback event: %+v", evt)
|
||
}
|
||
if got := string(CallbackSuccessBody()); got != "{\"success\":true}" {
|
||
t.Errorf("CallbackSuccessBody() = %s", got)
|
||
}
|
||
}
|
||
|
||
// TestCheckSuccess 校验业务成功判断与错误码匹配。
|
||
func TestCheckSuccess(t *testing.T) {
|
||
ok := &CommonResponse{Success: true, Code: "SUCCESS", RequestId: "r1"}
|
||
if err := CheckSuccess(ok); err != nil {
|
||
t.Errorf("CheckSuccess(success) error = %v", err)
|
||
}
|
||
fail := &CommonResponse{
|
||
Success: false, Code: CodeOrderNotFound,
|
||
Message: "order not found", SubCode: "OrderNotFound",
|
||
}
|
||
err := CheckSuccess(fail)
|
||
if err == nil {
|
||
t.Fatal("CheckSuccess(fail) should return error")
|
||
}
|
||
if !IsCode(fail, CodeOrderNotFound) {
|
||
t.Error("IsCode(OrderNotFound) should be true")
|
||
}
|
||
}
|
||
``` |