docs: 新增顺运宝接口契约;config.yaml 加入 gitignore
从 4 份 HAR 抓包和示例脚本还原 docs/admin/08-顺运宝接口.md。 全部结论标了出处,只有单一样本支撑的都标了 [待定]。 抓包验出三件和现有假设不符的事: 1. 登录响应的 JWT 从不参与请求,认证全靠 Cookie。示例脚本里 self.token 只用于算缓存有效期,没进过任何请求头。Go 侧存 Cookie 即可,token 都不用存。 2. _capture_refreshed_token 是死代码——它从响应头 X-Requested-With 读刷新后的 JWT,而 4 份 HAR 共 18 个响应里带该头的是 0 个。 会话就是 24 小时硬上限,没有滚动续期,不要移植这段逻辑。 3. 金额单位在同一个响应里不统一:amtOrder 在列表接口是分(61200 对应 612.0),escrowAmount 却不是(505 对应 505.0)。不能假设 "列表接口的金额都是分",逐字段确认。这条只有一个样本,已标 [待定]。 还推翻了「货运单规格能直接对上蝦皮商品規格ID」这个前提:顺运宝给的 productId 是 11 位商品ID,蝦皮規格ID 是 12 位。但 productSpec 的格式 与蝦皮报表完全一致,可直接复用 #38 的 ParseSpec,匹配走 "productId 定位商品 → 解析规格 → 在该商品的 SKU 里比对"。 admin/config.yaml 含明文密码且此前没有任何 gitignore 规则挡它, 一次目录级 git add 就会进历史。已加规则,并补 config.example.yaml 作为模板(不含真实凭据,进 git)。 已确认历史提交中从未出现过该凭据。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -22,6 +22,12 @@ admin/tmp/
|
||||
# 自动化测试用 admin/testdata/ 下已脱敏的小样本
|
||||
raw_data/
|
||||
|
||||
# 本机配置:含顺运宝等外部系统的账号密码,明文保存,绝不进 Git。
|
||||
# 一旦提交,密码会永久留在 git 历史里,很难清干净。
|
||||
# 模板见 admin/config.example.yaml(那份不含真实凭据,是进 Git 的)。
|
||||
# 这个文件也不要随打包产物发给别人。
|
||||
admin/config.yaml
|
||||
|
||||
# Python
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
# Admin 本机配置模板
|
||||
#
|
||||
# 用法:把本文件复制成 admin/config.yaml,填入真实账号密码。
|
||||
#
|
||||
# Windows: copy admin\config.example.yaml admin\config.yaml
|
||||
# Linux: cp admin/config.example.yaml admin/config.yaml
|
||||
#
|
||||
# `[必须]` config.yaml 已在 .gitignore 里,绝不能提交进 Git——
|
||||
# 密码一旦进了 git 历史就很难清干净。
|
||||
# `[必须]` 打包发给别人时也不要带上 config.yaml。
|
||||
#
|
||||
# 接口细节见 docs/admin/08-顺运宝接口.md。
|
||||
|
||||
syb:
|
||||
# 顺运宝 ERP 地址。一般不用改,域名变了才改。
|
||||
base_url: https://www.shunyunbaoerp.com
|
||||
|
||||
username: 你的顺运宝账号
|
||||
|
||||
# `[必须]` 密码要加引号。纯数字密码不加引号会被 YAML 解析成整数,
|
||||
# 反序列化到 string 字段会直接报错,前导 0 也会丢。
|
||||
password: "你的密码"
|
||||
|
||||
# 列表接口每页条数。抓包里网页用的就是 20。
|
||||
page_size: 20
|
||||
|
||||
# 单次同步的条数上限,超过就报错而不是硬拉。
|
||||
# 防止日期范围手滑填成一整年,把整库拉下来。
|
||||
max_matches: 500
|
||||
|
||||
# 首次同步的起始日期。之后按「上次同步时间」增量拉,不再用这个值。
|
||||
sync_from: "2026-07-01"
|
||||
@@ -49,6 +49,7 @@
|
||||
| 改页面、加表格列 | [05 界面规范](admin/05-ui-specification.md) | [02 架构](admin/02-architecture.md) §4 模板 |
|
||||
| 加字段、改表、写 SQL | [03 数据模型](admin/03-data-model.md) | [02 架构](admin/02-architecture.md) §2 分层 |
|
||||
| 改 Excel 导入 | [03 数据模型](admin/03-data-model.md) §3.3 | [00 术语表](admin/00-glossary.md) §3 upsert |
|
||||
| 改顺运宝同步 | [08 顺运宝接口](admin/08-顺运宝接口.md) | [03 数据模型](admin/03-data-model.md) |
|
||||
| 改给 Client 的接口 | [04 Client 接口实现](admin/04-client-api.md) | [Client 侧契约](client/04-admin-api-contract.md) |
|
||||
| 和 Admin 联调、登记新设备 | [07 设备登记联调手册](admin/07-设备登记联调手册.md) | [Client 侧契约](client/04-admin-api-contract.md) §5 |
|
||||
| 写测试 | [06 质量与安全](admin/06-quality-security.md) §2 | — |
|
||||
@@ -88,6 +89,7 @@
|
||||
| [05 界面规范](admin/05-ui-specification.md) | 三段式布局、五个页面、弹窗 |
|
||||
| [06 质量、安全与测试](admin/06-quality-security.md) | 测试、Web 安全、发布门禁 |
|
||||
| [07 设备登记联调手册](admin/07-设备登记联调手册.md) | **给 Client 开发者**:怎么让新设备登记成功 |
|
||||
| [08 顺运宝接口](admin/08-顺运宝接口.md) | 从抓包还原的外部 ERP 契约:登录、会话、货运单列表与明细 |
|
||||
|
||||
## 文档标注说明
|
||||
|
||||
|
||||
@@ -0,0 +1,399 @@
|
||||
# 08 顺运宝 ERP 接口契约
|
||||
|
||||
- 文档状态:**从 HAR 抓包还原**,未经官方文档核对
|
||||
- 来源:`raw_data/shunyunbaoerp_*.har`(4 份,2026-07-28 抓)
|
||||
和 `raw_data/shunyunbaoerp_single.py`(962 行的可运行示例)
|
||||
- 基址:`https://www.shunyunbaoerp.com`
|
||||
|
||||
本文档中 `[必须]` / `[建议]` / `[待定]` 的含义见 [文档索引](../README.md#文档标注说明)。
|
||||
没有标注的默认是 `[必须]`。看不懂的词查 [术语表](00-glossary.md)。
|
||||
|
||||
> `[必须]` **`raw_data/` 和 `admin/config.yaml` 里有真实账号密码,两者都已在
|
||||
> `.gitignore` 里,绝不能进 Git,也不要随打包产物发给别人。**
|
||||
> 本文档里的账号、单号、金额全部是脱敏或示例值。
|
||||
|
||||
---
|
||||
|
||||
## 1. 这份文档是怎么来的,可信度如何
|
||||
|
||||
全部结论来自抓包和示例脚本,**不是官方文档**。凡是只有一个样本支撑的判断,
|
||||
下面都标了 `[待定]`,实现时要在真实数据上再确认一次。
|
||||
|
||||
已核对过的 4 份抓包:
|
||||
|
||||
| 文件 | 覆盖 |
|
||||
|---|---|
|
||||
| `shunyunbaoerp_login.har` | 验证码、登录 |
|
||||
| `shunyunbaoerp_stock_list.har` | **按日期范围**列表查询 |
|
||||
| `shunyunbaoerp_stock_query.har` | 按单号查询 + 货运明细 |
|
||||
| `shunyunbaoerp_userinfo.har` | 个人信息(用来探测会话是否还有效) |
|
||||
|
||||
---
|
||||
|
||||
## 2. 统一响应信封
|
||||
|
||||
所有 `/am/**` 接口都是这个形状:
|
||||
|
||||
```json
|
||||
{ "status": true, "msg": "获取成功", "data": <任意>, "code": null }
|
||||
```
|
||||
|
||||
`[必须]` 判断成功**只看 `status === true`**,不要看 HTTP 状态码——
|
||||
服务端在业务失败时也可能返回 200。
|
||||
|
||||
`[必须]` `data` 的类型随接口变:可能是对象、数组,也可能是**裸整数**
|
||||
(`listTotal` 就返回 `"data": 1`)。不要假设它一定是对象。
|
||||
|
||||
`[必须]` 失败时把 `msg` 和 `code` 一起带进错误信息,否则排查时看不出原因。
|
||||
|
||||
---
|
||||
|
||||
## 3. 认证
|
||||
|
||||
### 3.1 认证靠 Cookie,不是 token
|
||||
|
||||
`[必须]` **登录响应里的 JWT 从来不参与后续请求。**
|
||||
|
||||
已核对示例脚本全文:`self.token` 只出现在「登录时赋值 / 存缓存 / 读缓存 /
|
||||
算缓存有效期」四处,**从没被放进任何请求头**。认证完全靠
|
||||
`requests.Session` 自动维护的 Cookie。
|
||||
|
||||
所以 Go 侧要持久化的是 **Cookie**,token 只用来算过期时间——
|
||||
甚至可以不存 token,直接存算好的 `expires_at`。
|
||||
|
||||
### 3.2 登录流程
|
||||
|
||||
```text
|
||||
① GET /api/p/code1?<毫秒时间戳> → image/jpeg,约 2.3KB,4 位字母数字
|
||||
② POST /am/auth/login → {"username","password","code"}
|
||||
→ {"status":true,"data":{"user":{...},"token":"<JWT>"}}
|
||||
```
|
||||
|
||||
`[必须]` 三步必须用**同一个 HTTP 客户端**(同一个 Cookie Jar)——
|
||||
验证码是和会话绑定的,换客户端拿到的验证码对不上。
|
||||
|
||||
`[必须]` 密码**明文提交**(走 HTTPS)。客户端不做哈希。
|
||||
|
||||
`[必须]` 时间戳参数是为了绕开缓存,每次取验证码都要换。
|
||||
|
||||
### 3.3 会话有效期正好 24 小时
|
||||
|
||||
实测 JWT 载荷:
|
||||
|
||||
```json
|
||||
{"authLogin": false, "exp": 1785294742, "iat": 1785208342,
|
||||
"jti": "<用户名>", "username": "<用户名>"}
|
||||
```
|
||||
|
||||
`exp - iat = 86400` 秒,**整 24 小时**。
|
||||
|
||||
`[必须]` 缓存有效期取 `min(自定义上限, JWT 剩余时间)`,缓存不能活得比会话长。
|
||||
|
||||
### 3.4 没有滚动续期
|
||||
|
||||
`[必须]` 示例脚本里的 `_capture_refreshed_token()` 从响应头
|
||||
`X-Requested-With` 读刷新后的 JWT,**这是死代码**。
|
||||
|
||||
实测 4 份 HAR 共 18 个接口响应,**带该响应头的:0 个**。
|
||||
(`X-Requested-With` 本来就是**请求**头,脚本自己也设了 `XMLHttpRequest`。)
|
||||
|
||||
**不要把这段逻辑移植到 Go。** 会话就是 24 小时硬上限,到点重新登录。
|
||||
|
||||
### 3.5 怎么判断会话还活着
|
||||
|
||||
```text
|
||||
GET /am/user/get?id=<登录响应里的 user.id>
|
||||
```
|
||||
|
||||
`[必须]` **必须区分「明确未登录」和「网络故障」**:
|
||||
|
||||
| 情况 | 处理 |
|
||||
|---|---|
|
||||
| HTTP 401 / 403 | 判定未登录,清会话 |
|
||||
| `msg` 含「未登录」「登录过期」,或 `code=-2` | 判定未登录,清会话 |
|
||||
| 超时、5xx、响应格式错 | **抛错,不要判定未登录** |
|
||||
|
||||
理由:网络抖一下就判定登出的话,会触发重新登录,验证码弹个不停,
|
||||
而且可能把本来有效的会话丢掉。示例脚本这一点做对了,照抄。
|
||||
|
||||
`[必须]` 还要核对返回的 `id` / `username` 与缓存的一致——
|
||||
不一致说明串号了,同样清会话。
|
||||
|
||||
---
|
||||
|
||||
## 4. 货运单列表
|
||||
|
||||
两个接口配合,**payload 完全相同**:
|
||||
|
||||
```text
|
||||
POST /am/stock/listTotal → data 是裸整数,总条数
|
||||
POST /am/stock/list → data.list 是数组
|
||||
```
|
||||
|
||||
### 4.1 请求体
|
||||
|
||||
```json
|
||||
{
|
||||
"history": 0,
|
||||
"length": 20, // 每页条数
|
||||
"start": 0, // 偏移
|
||||
"pageTotal": 0,
|
||||
"pageIndex": 1, // 从 1 开始
|
||||
"store": false,
|
||||
"columns": [ ... 72 个列定义 ... ],
|
||||
"queries": [ ... 查询条件 ... ]
|
||||
}
|
||||
```
|
||||
|
||||
`[必须]` `columns` 是**要返回哪些列**的声明,72 项,每项形如:
|
||||
|
||||
```json
|
||||
{"tableName":"t_stock","colName":"created","fieldName":"created",
|
||||
"hasAlias":0,"tableAlias":"t"}
|
||||
```
|
||||
|
||||
完整清单见示例脚本的 `COLUMN_SPECS`(第 51–124 行)。
|
||||
`[建议]` 直接照搬,不要自己删减——服务端可能依赖这批列做联表。
|
||||
|
||||
### 4.2 两种查询条件
|
||||
|
||||
**按日期范围(同步用这个)** —— 出自 `shunyunbaoerp_stock_list.har`:
|
||||
|
||||
```json
|
||||
{"dvalue": "2026-07-25,2026-07-28", "tableName": "t_stock",
|
||||
"colName": "created", "op": 0, "type": 3, "tableAlias": "t", "optType": 0}
|
||||
```
|
||||
|
||||
`[必须]` `dvalue` 是 `起始日期,结束日期`,逗号分隔,`YYYY-MM-DD`。
|
||||
`type: 3` 表示日期类型,`op: 0` 表示范围。
|
||||
|
||||
**按单号(查单条用这个)** —— 出自 `shunyunbaoerp_stock_query.har`:
|
||||
|
||||
```json
|
||||
{"dvalue": "<单号>", "tableName": "t_stock",
|
||||
"colName": "allcode", "op": 6, "type": 0, "tableAlias": "t", "optType": 1}
|
||||
```
|
||||
|
||||
`[必须]` `queries` 是**数组**,理论上可以组合多个条件。
|
||||
示例脚本里那句 `if not order_number: raise ValueError` 是**脚本自己的限制**,
|
||||
接口没有这个限制。
|
||||
|
||||
`[待定]` 空 `queries`(不加任何条件)能否拉全量未验证。同步用日期范围就够,
|
||||
不需要冒这个险。
|
||||
|
||||
### 4.3 分页
|
||||
|
||||
`[必须]` 先调 `listTotal` 拿总数,再按 `length` 翻页调 `list`。
|
||||
|
||||
`[必须]` **必须有单次同步的条数上限**,超了报错而不是硬拉。
|
||||
示例脚本用的是 `max_matches = 100`。日期范围拉全量时这个上限要调大,
|
||||
但不能没有——手滑填成一年的范围会把整库拉下来。
|
||||
|
||||
---
|
||||
|
||||
## 5. 货运单字段
|
||||
|
||||
`data.list[]` 每行 **77 个字段**。业务上要紧的:
|
||||
|
||||
| 字段 | 示例 | 说明 |
|
||||
|---|---|---|
|
||||
| `id` | `75104587` | **货运单主键**,取明细要用它 |
|
||||
| `code` | `260728TB95MJTQ` | 单号(界面上搜的就是这个) |
|
||||
| `created` | `2026-07-28 10:37:59` | 创建时间,日期范围筛的就是它 |
|
||||
| `status` | `13` | 数字状态码 |
|
||||
| `orderStatus` | `待出货` | 中文状态 |
|
||||
| `purchaseStatus` | `0` | 采购状态 |
|
||||
| `shopName` | `<店铺名>` | 蝦皮店铺 |
|
||||
| `productName` | `純棉上衣` | **只有一个商品名**,一单多商品时不完整 |
|
||||
| `orderQty` / `detailQty` | `2` / `2` | 商品件数 |
|
||||
| `isCancel` | `0` | 是否取消 |
|
||||
| `expCompany` | `蝦皮店到店` | 物流方式 |
|
||||
| `receiver` / `receiverTel` / `receiverAddr` | — | **收件人信息,属个人数据** |
|
||||
|
||||
`[必须]` **收件人姓名、电话、地址属于个人信息**,落库要考虑是否必要。
|
||||
不做采购决策用不到它们,`[建议]` 不入库,或只存脱敏后的。
|
||||
|
||||
### 5.1 金额单位不统一 —— 最容易算错钱的地方
|
||||
|
||||
`[必须]` 实测同一张单、同一行里,两个金额字段**单位不一样**:
|
||||
|
||||
| 字段 | `/am/stock/list` | `detail/listByStock` | 关系 |
|
||||
|---|---|---|---|
|
||||
| `amtOrder` | `61200` | `612.0` | list 是**分**,×100 |
|
||||
| `escrowAmount` | `505` | `505.0` | list **不是分**,未 ×100 |
|
||||
|
||||
`61200 / 100 = 612` 对得上,`505 / 100 = 5.05` **对不上**。
|
||||
|
||||
`[必须]` **不要假设「列表接口的金额都是分」。** 逐个字段确认,
|
||||
并且在代码里为每个金额字段写清它的单位。
|
||||
|
||||
`[建议]` 金额一律以 **`detail/listByStock` 的值为准**,那边是统一的元/TWD。
|
||||
需要存整数分时自己 ×100,不要用列表接口的原值。
|
||||
|
||||
`[待定]` 只有一个样本。实现前**必须再取几张单核对**,尤其是
|
||||
`escrowAmount` 不是整数元的情况。
|
||||
|
||||
### 5.2 金额合计对不上是正常的
|
||||
|
||||
```text
|
||||
明细单价合计 239.0 + 439.0 = 678.0
|
||||
amtOrder 612.0
|
||||
```
|
||||
|
||||
差 66,应该是优惠。`[必须]` **不要用「明细合计 == amtOrder」做校验**,
|
||||
会误报。
|
||||
|
||||
---
|
||||
|
||||
## 6. 货运明细
|
||||
|
||||
```text
|
||||
POST /am/stock/detail/listByStock?hist=0
|
||||
{"ids": [75104587, ...]}
|
||||
→ data.list[]
|
||||
```
|
||||
|
||||
`[必须]` 一次最多传 **100 个 id**(示例脚本的分批大小),超了分批。
|
||||
|
||||
### 6.1 一单多商品是嵌套,不是多行
|
||||
|
||||
返回的每一行对应**一张货运单**,商品在嵌套的 `details[]` 里:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": 75104587, // 与 stock.id 相同,可直接对应
|
||||
"code": "260728TB95MJTQ",
|
||||
"shopName": "<店铺名>",
|
||||
"productName": "純棉上衣",
|
||||
"amtOrder": 612.0,
|
||||
"details": [
|
||||
{
|
||||
"id": 145306175,
|
||||
"productId": 50209124255,
|
||||
"productTitle": "蕾絲花邊拼接背心女 上衣 背心 無袖打底衫 …",
|
||||
"detailProductName": "打底衫",
|
||||
"productSpec": "白色,L【建議50-60公斤】",
|
||||
"productQty": 1,
|
||||
"productPrice": 239.0,
|
||||
"productThumb": 190639637,
|
||||
"shopId": 999611342,
|
||||
"pruchaseId": 38884195
|
||||
},
|
||||
{ ... 第二个商品 ... }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
`[必须]` 外层 `id` **就是** `stock.id`,可以直接按它把列表和明细对起来。
|
||||
|
||||
`[必须]` **一张货运单可以有多个商品**(示例这张就有 2 个)。
|
||||
落库时必须一对多拆开,不能只取 `productName`——那个字段只有一个商品名。
|
||||
|
||||
### 6.2 `productId` 是蝦皮商品 ID,不是規格 ID
|
||||
|
||||
`[必须]` 这一条推翻了之前「货运单的规格 SKU 能直接对上蝦皮商品規格ID」的假设。
|
||||
|
||||
| | 位数 | 例 |
|
||||
|---|---|---|
|
||||
| 顺运宝 `productId` | 11 | `50209124255` |
|
||||
| 蝦皮 `商品ID` | 11 | 实测导入的 5195 个**全是 11 位** |
|
||||
| 蝦皮 `商品規格ID` | 12 | 实测 5742/6092 是 12 位 |
|
||||
|
||||
顺运宝给的是**商品级 ID + 规格原文**,没有規格 ID。
|
||||
|
||||
### 6.3 `productSpec` 的格式和蝦皮报表完全一致
|
||||
|
||||
```text
|
||||
蝦皮 Excel「商品規格」 黑色,M【建議40-50公斤】
|
||||
顺运宝 productSpec 白色,L【建議50-60公斤】
|
||||
黑色+白色【純棉兩件裝】 簡約親膚,L【建議52.5-60公斤】
|
||||
```
|
||||
|
||||
`[必须]` **直接复用 `service.ParseSpec()`**(#38 实现),不要另写一份解析。
|
||||
两处各写一份,规则迟早不一致,而解析出的颜色尺码要拿去下单。
|
||||
|
||||
`[必须]` 第二个例子里颜色部分自带 `【】`,`ParseSpec` 已覆盖这种情况
|
||||
(按第一个逗号切开)。
|
||||
|
||||
### 6.4 由此推导出的匹配路径
|
||||
|
||||
```text
|
||||
productId ──→ shopee_products.goods_id 商品级,直接相等
|
||||
productSpec ──→ ParseSpec() ──→ 颜色 + 尺码
|
||||
──→ 在该商品的 shopee_skus 里比对 ──→ 拿到 sku_id
|
||||
```
|
||||
|
||||
`[必须]` 比对不上时**不要猜**,标成待人工匹配。蝦皮报表只含有销售成绩的
|
||||
SKU(平均每商品 1.17 个),**查无此 SKU 是常态**,不是异常。
|
||||
|
||||
---
|
||||
|
||||
## 7. 其他接口
|
||||
|
||||
| 接口 | 用途 | 备注 |
|
||||
|---|---|---|
|
||||
| `POST /am/store/listByUsed` | 仓库列表 | 请求体为空;返回 `[{"name":"京发仓","id":129}, …]` |
|
||||
| `POST /am/wallet/showTip` | 登录后弹提示 | 同步不需要 |
|
||||
| `GET /am/menu/curr` | 当前菜单 | 同步不需要 |
|
||||
| `POST /am/notice/myList` | 通知列表 | 同步不需要 |
|
||||
| `GET /am/user/checkNeedAgreement` | 是否需同意协议 | 同步不需要 |
|
||||
|
||||
`[建议]` 登录后**只调 `/am/user/get` 验证会话**,其余几个是网页自己的初始化请求,
|
||||
Go 侧不用跟着调。
|
||||
|
||||
---
|
||||
|
||||
## 8. 实现时的固定约束
|
||||
|
||||
这几条不是抓包结论,是本项目的决定,写在这里避免每次重新讨论:
|
||||
|
||||
`[必须]` **会话缓存存 SQLite,不引入 Redis。** Admin 的定位是「双击 exe 就能跑」,
|
||||
`data/` 在 exe 旁边。示例脚本用 Redis 是因为它是反复启动的一次性脚本,
|
||||
进程间要传会话;Admin 是常驻进程,没有这个需求,持久化只为重启后免登录。
|
||||
|
||||
`[必须]` **不引入 OCR 服务。** 会话 24 小时,一天登录一次。
|
||||
为省一次手工输验证码而依赖 `127.0.0.1:8000` 不划算——多一个必须先启动的东西,
|
||||
而且 OCR 会失败(示例脚本自己写了 5 次重试),失败了照样要人工。
|
||||
界面上显示验证码图片、操作员输一次即可。
|
||||
|
||||
`[必须]` **凭据放 `admin/config.yaml`**,已在 `.gitignore` 里。
|
||||
仓库里提供 `admin/config.example.yaml` 作为模板(不含真实凭据)。
|
||||
|
||||
`[必须]` **密码在任何日志里都要打码。** 日志可能被贴进工单排查问题。
|
||||
|
||||
`[必须]` YAML 里**密码要加引号**。纯数字密码不加引号会被 YAML 解析成整数,
|
||||
反序列化到 `string` 字段直接报错:
|
||||
|
||||
```yaml
|
||||
password: 0012345 # ✗ 解析成整数 12345,前导 0 也丢了
|
||||
password: "0012345" # ✓
|
||||
```
|
||||
|
||||
`[建议]` 依赖用 `github.com/goccy/go-yaml`,它已经在依赖树里(indirect),
|
||||
不会引入新的传递依赖。
|
||||
|
||||
---
|
||||
|
||||
## 9. 已知未验证的部分
|
||||
|
||||
实现前应逐条确认,都只有单一样本支撑:
|
||||
|
||||
- [ ] `escrowAmount` 等金额字段在列表接口里的**单位**(见 §5.1)
|
||||
- [ ] 空 `queries` 能否拉全量(同步用日期范围,可不验)
|
||||
- [ ] 日期范围跨度很大时服务端是否限流或超时
|
||||
- [ ] `status` 数字码与 `orderStatus` 中文的完整对应表
|
||||
- [ ] 会话失效时服务端返回的**确切**形态(HTTP 码 / `code` / `msg` 文案)
|
||||
- [ ] 同一账号多处登录是否互踢
|
||||
- [ ] 验证码错误、密码错误分别返回什么,能否区分
|
||||
|
||||
`[必须]` 最后两条影响错误提示的准确性:分不清「密码错」和「验证码错」的话,
|
||||
操作员会一直重输密码。
|
||||
|
||||
---
|
||||
|
||||
## 10. 相关文档
|
||||
|
||||
- 数据落库:[03 数据模型](03-data-model.md)
|
||||
- 界面:[05 界面规范](05-ui-specification.md) §6 顺运宝数据页
|
||||
- 安全要求:[06 质量与安全](06-quality-security.md)
|
||||
- 规格解析:`admin/service/shopee_import.go` 的 `ParseSpec()`
|
||||
Reference in New Issue
Block a user