sdk_LinkedMall-20260828161641/README.md

555 lines
19 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

## 文档概述
- 接口总数25个
- API版本linkedmall/2023-09-30
- RegionIdcn-zhangjiakou华北3 张家口)
- Endpoint`linkedmall.cn-zhangjiakou.aliyuncs.com`
- HTTP基础Path前缀`/opensaas-s2b/opensaas-s2b-biz-trade/v2`
- 签名类型ROA签名SDK内部自动处理
- 金额单位整数long/int64
- 区域编码divisionCode必须使用五级乡镇街道级别编码
## 认证与安全
- 使用RAM子账号AccessKeyAccessKeyId / AccessKeySecret禁止主账号AK
- 授予LinkedMall对应OpenAPI最小权限
- 业务前置资源purchaserId采购方ID、shopId店铺ID由LinkedMall控制台分配
- 签名类型为ROA签名SDK内部自动处理业务无需实现签名算法
- 使用通用OpenApiClientdarabonba-openapi/v2发起ROA调用
## 公共返回结构体
所有接口返回外层统一格式:
```go
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"` // 业务数据不同接口Data内部结构不同
}
```
公共Code枚举
- `SUCCESS`:业务调用成功
- 其他业务失败看SubCode、SubMessage定位原因
> 注意HTTP 200 不代表业务成功,必须判断 `Success==true`
## SDK客户端初始化
```go
package main
import (
"github.com/alibabacloud-go/darabonba-openapi/v2/client"
"github.com/alibabacloud-go/tea/v2"
)
func NewLinkedMallClient(accessKeyId, accessKeySecret string) (*client.Client, error) {
config := &client.Config{
AccessKeyId: tea.String(accessKeyId),
AccessKeySecret: tea.String(accessKeySecret),
RegionId: tea.String("cn-zhangjiakou"),
Endpoint: tea.String("linkedmall.cn-zhangjiakou.aliyuncs.com"),
ReadTimeout: tea.Int(30000),
ConnectTimeout: tea.Int(10000),
}
return client.NewClient(config)
}
```
调用示例:
```go
cli, err := NewLinkedMallClient("AKxxx", "SKxxx")
resp, err := cli.RoaRequest(
tea.String("POST"),
tea.String("/opensaas-s2b/opensaas-s2b-biz-trade/v2/xxx"),
nil, // query map[string]*string
bodyObj, // post json body
nil, // header
)
```
## 接口列表
### 接口 1ListPurchaserShops 获取采购方店铺列表
- 路径:`/purchaser-shops`
- 方法GET
- 描述:分页获取采购方店铺列表
#### 请求参数Query
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| PageNum | int | 否 | 页码默认1 |
| PageSize | int | 否 | 每页条数最大100 |
#### 响应Data结构体
```go
type ListPurchaserShopsRespData struct {
Total int64 `json:"Total"`
PageNum int `json:"PageNum"`
PageSize int `json:"PageSize"`
ShopList []PurchaserShopItem `json:"ShopList"`
}
type PurchaserShopItem struct {
ShopId string `json:"ShopId"`
PurchaserId string `json:"PurchaserId"`
ShopName string `json:"ShopName"`
ShopStatus string `json:"ShopStatus"`
}
```
### 接口 2GetPurchaserShop 获取采购方店铺详情
- 路径:`/purchaser-shops/{ShopId}`
- 方法GET
- 描述:获取单个采购店铺详情
#### 请求参数
| 参数名 | 类型 | 位置 | 必填 | 说明 |
|--------|------|------|------|------|
| ShopId | string | Path | 是 | 店铺ID |
#### 响应Data结构体
```go
type GetPurchaserShopRespData struct {
ShopId string `json:"ShopId"`
PurchaserId string `json:"PurchaserId"`
ShopName string `json:"ShopName"`
ShopStatus string `json:"ShopStatus"`
ContactInfo string `json:"ContactInfo"`
}
```
### 接口 3ListSelectionProducts 查询选品池商品列表
- 路径:`/selection-products`
- 方法GET
- 描述:分页查询选品池商品列表
#### 请求参数Query
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| PageNum | int | 否 | 页码 |
| PageSize | int | 否 | 最大100 |
| PurchaserId | string | 是 | 采购方ID |
#### 响应Data结构体
```go
type ListSelectionProductsRespData struct {
Total int64 `json:"Total"`
PageNum int `json:"PageNum"`
PageSize int `json:"PageSize"`
ProductList []SelectionProductItem `json:"ProductList"`
}
type SelectionProductItem struct {
ProductId string `json:"ProductId"`
Title string `json:"Title"`
MainImage string `json:"MainImage"`
BrandName string `json:"BrandName"`
CategoryId string `json:"CategoryId"`
}
```
### 接口 4GetSelectionProduct 查询选品池商品详情
- 路径:`/selection-products/{ProductId}`
- 方法GET
- 描述查询单个商品详情支持传入divisionCode校验区域可售库存
#### 请求参数
| 参数名 | 类型 | 位置 | 必填 | 说明 |
|--------|------|------|------|------|
| ProductId | string | Path | 是 | 商品ID |
| PurchaserId | string | Query | 是 | 采购方ID |
| DivisionCode | string | Query | 否 | 五级乡镇区域编码,传后返回该区域可售状态 |
#### 响应Data结构体
```go
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"` // 传入divisionCode才有效该区域是否可售
StockQuantity int64 `json:"StockQuantity"`
}
type SelectionSkuItem struct {
SkuId string `json:"SkuId"`
SkuSpec string `json:"SkuSpec"`
SalePrice int64 `json:"SalePrice"` // 单位分
MarketPrice int64 `json:"MarketPrice"`
StockQuantity int64 `json:"StockQuantity"`
}
```
### 接口 5GetSelectionProductSaleInfo 查询选品池商品销售信息
- 路径:`/selection-products/{ProductId}/sale-info`
- 方法GET
- 描述:查询商品可售、价格库存快照
#### 请求参数
| 参数名 | 类型 | 位置 | 必填 | 说明 |
|--------|------|------|------|------|
| ProductId | string | Path | 是 | 商品ID |
| PurchaserId | string | Query | 是 | 采购方ID |
| DivisionCode | string | Query | 否 | 五级乡镇区域编码 |
### 接口 6ListSelectionProductSaleInfos 批量查询商品销售信息
- 路径:`/selection-products:batch-sale-info`
- 方法POST
- 描述:批量查询商品销售信息
#### 请求Body
```json
{
"PurchaserId": "xxx",
"ProductIdList": ["p1","p2"],
"DivisionCode": "五级编码"
}
```
### 接口 7ListSelectionSkuSaleInfos 批量SKU销售信息
- 路径:`/selection-skus:batch-sale-info`
- 方法POST
- 描述批量查询SKU销售信息
#### 请求Body参数
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| PurchaserId | string | 是 | 采购方ID |
| SkuIdList | []string | 是 | SKU ID列表 |
| DivisionCode | string | 否 | 五级乡镇区域编码 |
### 接口 8ListCategories 查询类目列表
- 路径:`/categories`
- 方法GET
- 描述:查询类目列表
#### 请求参数Query
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| PurchaserId | string | 是 | 采购方ID |
| ParentCategoryId | string | 否 | 父类目ID不传查一级类目 |
### 接口 9SearchProducts 搜索选品池商品
- 路径:`/selection-products:search`
- 方法POST
- 描述:搜索选品池商品
#### 请求Body参数
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| Keyword | string | 否 | 关键词 |
| CategoryId | string | 否 | 类目ID |
| PageNum | int | 否 | 页码 |
| PageSize | int | 否 | 每页条数 |
| PurchaserId | string | 是 | 采购方ID |
### 接口 10SelectionGroupAddProduct 选品池商品入库
- 路径:`/selection-group/products:add`
- 方法POST
- 描述:选品池商品入库
#### 请求Body参数
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| PurchaserId | string | 是 | 采购方ID |
| ShopId | string | 是 | 店铺ID |
| ProductIdList | []string | 是 | 商品ID列表 |
### 接口 11SelectionGroupRemoveProduct 选品池商品出库
- 路径:`/selection-group/products:remove`
- 方法POST
- 描述:选品池商品出库
#### 请求Body参数
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| PurchaserId | string | 是 | 采购方ID |
| ShopId | string | 是 | 店铺ID |
| ProductIdList | []string | 是 | 商品ID列表 |
### 接口 12RenderPurchaseOrder 采购单渲染(下单预校验)
- 路径:`/purchase-orders:render`
- 方法POST
- 描述:下单前必须调用,获取实时价格、校验商品是否可售
#### 请求Body
```go
type RenderPurchaseOrderReq struct {
PurchaserId string `json:"PurchaserId"`
ShopId string `json:"ShopId"`
DivisionCode string `json:"DivisionCode"` // 五级乡镇编码
ItemList []RenderOrderItem `json:"ItemList"`
ReceiverInfo ReceiverInfo `json:"ReceiverInfo"`
}
type RenderOrderItem struct {
SkuId string `json:"SkuId"`
Quantity int64 `json:"Quantity"`
}
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"`
}
```
#### 响应Data
渲染后的价格、商品校验结果、不可售原因、预估运费。
### 接口 13SplitPurchaseOrder 采购单渲染并拆单
- 路径:`/purchase-orders:split-render`
- 方法POST
- 描述:采购单渲染并拆单,返回拆成多份子单结构
#### 请求Body
结构和RenderPurchaseOrderReq完全一致
#### 响应Data
返回拆成多份子单结构,用于前端展示,拆单结果用于创建采购单入参。
### 接口 14CreatePurchaseOrder 创建采购单【异步】
- 路径:`/purchase-orders`
- 方法POST
- 描述:⚠️重要:接口只返回采购单号,不代表下单成功,必须等待回调通知,再查询订单接口
#### 请求Body
```go
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"` // SplitPurchaseOrder返回的子单
}
type CreateSubOrderItem struct {
SkuId string `json:"SkuId"`
Quantity int64 `json:"Quantity"`
}
```
#### 响应Data结构体
```go
type CreatePurchaseOrderRespData struct {
PurchaseOrderId string `json:"PurchaseOrderId"` // 阿里云采购单号
}
```
### 接口 15GetPurchaseOrderStatus 获取采购单状态
- 路径:`/purchase-orders/{PurchaseOrderId}/status`
- 方法GET
- 描述:获取采购单状态
#### 请求参数
| 参数名 | 类型 | 位置 | 必填 | 说明 |
|--------|------|------|------|------|
| PurchaseOrderId | string | Path | 是 | 采购单ID |
| PurchaserId | string | Query | 是 | 采购方ID |
#### 响应Data
采购单状态枚举INIT、PROCESS、SUCCESS、FAIL、CLOSED
### 接口 16GetOrder 获取订单详情
- 路径:`/orders/{OrderId}`
- 方法GET
- 描述:获取订单详情
#### 请求参数
| 参数名 | 类型 | 位置 | 必填 | 说明 |
|--------|------|------|------|------|
| OrderId | string | Path | 是 | 订单ID |
| PurchaserId | string | Query | 是 | 采购方ID |
#### 响应Data
订单主信息、商品明细、实付金额、状态、收货信息
### 接口 17QueryOrders 查询订单列表
- 路径:`/orders`
- 方法GET
- 描述:查询订单列表
#### 请求参数Query
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| PurchaserId | string | 是 | 采购方ID |
| PurchaseOrderId | string | 否 | 采购单ID |
| PageNum | int | 否 | 页码 |
| PageSize | int | 否 | 每页条数 |
| StartTime | string | 否 | 开始时间 |
| EndTime | string | 否 | 结束时间 |
### 接口 18ListLogisticsOrders 查询订单物流信息
- 路径:`/orders/{OrderId}/logistics`
- 方法GET
- 描述:查询订单物流信息
#### 请求参数
| 参数名 | 类型 | 位置 | 必填 | 说明 |
|--------|------|------|------|------|
| OrderId | string | Path | 是 | 订单ID |
| PurchaserId | string | Query | 是 | 采购方ID |
#### 响应Data
运单号、物流公司、物流轨迹数组
### 接口 19ConfirmDisburse 确认收货
- 路径:`/orders/{OrderId}:confirm-disburse`
- 方法POST
- 描述:确认收货
#### 请求参数
| 参数名 | 类型 | 位置 | 必填 | 说明 |
|--------|------|------|------|------|
| OrderId | string | Path | 是 | 订单ID |
#### 请求Body
```json
{"PurchaserId":"xxx"}
```
### 接口 20RenderRefundOrder 售后渲染预校验
- 路径:`/refund-orders:render`
- 方法POST
- 描述:售后渲染预校验
#### 请求Body参数
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| PurchaserId | string | 是 | 采购方ID |
| OrderId | string | 是 | 订单ID |
| OrderItemId | string | 是 | 订单商品明细ID |
| RefundQuantity | int64 | 是 | 退款数量 |
| RefundType | string | 是 | 退款类型(仅退款/退货退款) |
### 接口 21CreateRefundOrder 创建售后单
- 路径:`/refund-orders`
- 方法POST
- 描述:创建售后单
#### 请求Body参数
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| PurchaserId | string | 是 | 采购方ID |
| OrderId | string | 是 | 订单ID |
| OrderItemId | string | 是 | 订单商品明细ID |
| RefundQuantity | int64 | 是 | 退款数量 |
| RefundAmount | int64 | 是 | 退款金额(单位分) |
| RefundType | string | 是 | 退款类型 |
| RefundReason | string | 是 | 退款原因 |
| OuterRefundNo | string | 是 | 外部售后单号 |
### 接口 22CancelRefundOrder 取消售后单
- 路径:`/refund-orders/{RefundOrderId}:cancel`
- 方法POST
- 描述:取消售后单
#### 请求参数
| 参数名 | 类型 | 位置 | 必填 | 说明 |
|--------|------|------|------|------|
| RefundOrderId | string | Path | 是 | 售后单ID |
#### 请求Body
```json
{"PurchaserId":"xxx"}
```
### 接口 23GetRefundOrder 获取售后单详情
- 路径:`/refund-orders/{RefundOrderId}`
- 方法GET
- 描述:获取售后单详情
#### 请求参数
| 参数名 | 类型 | 位置 | 必填 | 说明 |
|--------|------|------|------|------|
| RefundOrderId | string | Path | 是 | 售后单ID |
| PurchaserId | string | Query | 是 | 采购方ID |
### 接口 24CreateGoodsShippingNotice 回填退货物流运单
- 路径:`/refund-orders/{RefundOrderId}:fill-logistics`
- 方法POST
- 描述:回填退货物流运单
#### 请求参数
| 参数名 | 类型 | 位置 | 必填 | 说明 |
|--------|------|------|------|------|
| RefundOrderId | string | Path | 是 | 售后单ID |
#### 请求Body参数
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| PurchaserId | string | 是 | 采购方ID |
| LogisticsCompany | string | 是 | 物流公司 |
| TrackingNumber | string | 是 | 运单号 |
### 接口 25QueryChildDivisionCode 查询子区域编码
- 路径:`/divisions:children`
- 方法GET
- 描述:查询子区域编码,不传返回省级;传入省返回市,传入市返回区,传入区返回乡镇(五级)
#### 请求参数Query
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| ParentDivisionCode | string | 否 | 父区域编码 |
#### 响应Data
DivisionCode列表、名称、层级下单、商品可售校验必须拿到乡镇级DivisionCode
## 错误码
| 错误码 | 说明 | 处理建议 |
|--------|------|----------|
| ShopTypeInvalid | SKU属于经销集采店铺不可下单 | 过滤SKU |
| SkuPriceUnique | SKU价格非最新 | 重新调用RenderPurchaseOrder获取最新价格再下单 |
| PurchaseOrderNotFound | 采购单号不存在 | 核对入参PurchaseOrderId |
| RefundNumberMustLessThanOrder | 退款数量大于订单商品数量 | 修正退款数量 |
| RefundAmountMustLessThanOrder | 退款金额大于实付金额 | 修正退款金额 |
| HasNoPrivilege | RAM无接口权限 | 检查RAM授权策略 |
| OrderNotFound | 订单ID不存在 | 核对OrderId |
| ShopNotFind | 店铺不存在 | 核对ShopId/PurchaserId |
| SavePurchaseOrderError | 保存采购单服务端异常 | 短间隔重试 |
| OuterPurchaseOrderIdExist | 外部业务单号重复 | 更换OuterPurchaseOrderId做好幂等 |
## 回调通知
### 消息回调(异步通知)规范
CreatePurchaseOrder创建采购单后阿里云主动POST回调业务配置的回调地址。
1. 请求方式POSTContent-Type: application/json
2. 业务侧需要返回 `{"success":true}` 告知阿里云消费成功;否则阿里云会重试推送。
3. 回调Body公共字段
```json
{
"EventCode": "PURCHASE_ORDER_CHANGE",
"PurchaseOrderId": "xxx",
"OrderIdList": ["附属真实订单ID数组"],
"Status": "SUCCESS/FAIL",
"RequestId": "xxx"
}
```
EventCode枚举
- PURCHASE_ORDER_CHANGE采购单状态变更
- ORDER_STATUS_CHANGE订单状态变更
- REFUND_ORDER_CHANGE售后单变更
业务处理逻辑:收到回调后,调用对应查询接口拉取最新业务数据做本地状态同步。
## 完整业务调用流程
1. 调用 `QueryChildDivisionCode` 获取五级乡镇divisionCode收货地址对应的区域编码
2. 调用 `ListSelectionProducts` / `SearchProducts` 获取选品池商品SkuId
3. 调用 `RenderPurchaseOrder` 预渲染校验商品可售、价格
4. 调用 `SplitPurchaseOrder` 获取拆单结果
5. 调用 `CreatePurchaseOrder`传入外部唯一业务单号OuterPurchaseOrderId拿到PurchaseOrderId
6. **等待阿里云消息回调通知**
7. 回调收到事件,调用`GetPurchaseOrderStatus`看采购单状态拿到附属OrderId调用`GetOrder`同步订单入库本地库
8. 后续:查物流、确认收货;产生售后调用售后接口
> ❗禁止CreatePurchaseOrder同步返回成功直接标记本地订单成功。
## 开发注意事项
1. 金额全部为**分int64**Go使用int64不要float64JSON序列化不要丢失long精度
2. DivisionCode必须五级乡镇否则商品可售校验错误
3. OuterPurchaseOrderId外部采购单号全局唯一用于幂等防重复下单
4. CreatePurchaseOrder异步依赖回调不能轮询频率过高
5. RAM子账号最小权限AK禁止硬编码代码环境变量配置
6. 所有接口必须判断外层返回`Success==true`HTTP 200不等于业务成功
7. 分页接口PageSize上限100不要传超大值
8. 回调接口做好幂等,同一个事件可能多次推送