以下是修复后的完整代码: ```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") } } ```