sdk_LinkedMall-20260828161641/sdk_LinkedMall/valid.md

39 KiB
Raw Blame History

以下是修复后的完整代码:

// 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
// 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"
)
// 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 为 *CreatePurchaseOrderRespDataPurchaseOrderIdfunc (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 为 *CreateRefundOrderRespDataRefundOrderIdfunc (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 }
// 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)
}
// 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
}
// 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")
	}
}