添加 README 文档

This commit is contained in:
renzhiyuan 2026-08-27 16:19:12 +08:00
parent f2f9b7d335
commit d3d9ee0387
1 changed files with 555 additions and 0 deletions

555
README.md Normal file
View File

@ -0,0 +1,555 @@
## 文档概述
- 接口总数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. 回调接口做好幂等,同一个事件可能多次推送