27 KiB
08 顺运宝 ERP 接口契约
- 文档状态:从 HAR 抓包还原,未经官方文档核对
- 来源:
raw_data/shunyunbaoerp_*.har(4 份,2026-07-28 抓) 和raw_data/shunyunbaoerp_single.py(962 行的可运行示例) - 基址:
https://www.shunyunbaoerp.com
本文档中 [必须] / [建议] / [待定] 的含义见 文档索引。
没有标注的默认是 [必须]。看不懂的词查 术语表。
[必须]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/** 接口都是这个形状:
{ "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 登录流程
① 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 载荷:
{"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 怎么判断会话还活着
GET /am/user/get?id=<登录响应里的 user.id>
[必须] 必须区分「明确未登录」和「网络故障」:
| 情况 | 处理 |
|---|---|
| HTTP 401 / 403 | 判定未登录,清会话 |
msg 含「未登录」「登录过期」,或 code=-2 |
判定未登录,清会话 |
| 超时、5xx、响应格式错 | 抛错,不要判定未登录 |
理由:网络抖一下就判定登出的话,会触发重新登录,验证码弹个不停, 而且可能把本来有效的会话丢掉。示例脚本这一点做对了,照抄。
[必须] 还要核对返回的 id / username 与缓存的一致——
不一致说明串号了,同样清会话。
4. 货运单列表
两个接口配合,payload 完全相同:
POST /am/stock/listTotal → data 是裸整数,总条数
POST /am/stock/list → data.list 是数组,data.total 是当前页条数
[必须] 不要把 list.data.total 当成筛选范围总数。实测 HAR 中范围总数为
3846 时,第一页返回 20 行且 list.data.total = 20;范围总数只能以
listTotal.data 为准。
4.1 请求体
{
"history": 0,
"length": 20, // 每页条数
"start": 0, // 偏移
"pageTotal": 0,
"pageIndex": 1, // 从 1 开始
"store": false,
"columns": [ ... 72 个列定义 ... ],
"queries": [ ... 查询条件 ... ]
}
[必须] columns 是要返回哪些列的声明,72 项,每项形如:
{"tableName":"t_stock","colName":"created","fieldName":"created",
"hasAlias":0,"tableAlias":"t"}
完整清单见示例脚本的 COLUMN_SPECS(第 51–124 行)。
[建议] 直接照搬,不要自己删减——服务端可能依赖这批列做联表。
4.2 两种查询条件
按日期范围(同步用这个) —— 出自 shunyunbaoerp_stock_list.har:
{"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:
{"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 = 20 翻页调用 list。明细仍按
最多 100 个货运单 ID 一批读取。
[必须] 必须有单次同步的条数上限,超了报错而不是硬拉。
Admin 默认 max_matches = 10000,可以在配置中调整;上限针对整个日期范围的
逐日总数合计,不是每天各算一次。任何一天的列表或明细都不得在容量预检通过前
开始拉取,避免超限后已经产生部分写入。
[必须] 每一页 list.data.total 必须等于该页 list 数组长度。非最后一页
必须返回 length 条,最后一页必须返回预检总数对应的剩余条数。每天翻页结束后
再次调用 listTotal,前后总数必须一致;全部页去重后的货运单 ID 数还必须等于
预检总数。历史日期出现前后总数变化、短页、重复 ID 或唯一 ID 不足时立即失败,
不推进游标。
今天的货运单会在同步期间持续新增。只有 UTC+8 下的今天发生上述快照漂移时,
允许只重试今天的列表分页,最多 3 次;已经完成的历史日期不得重复拉取,每次尝试
也必须使用独立 ID 集合。第三次仍不稳定时,可以对最后一次取得的合法唯一 ID
读取完整明细并按既有 upsert 保存,但本次同步仍记为失败、明确提示当天未形成
稳定快照且不推进游标,下一次继续覆盖今天。任何尝试都不得突破 max_matches;
网络/业务错误、非法 ID 或不完整明细不属于可放宽的快照漂移。
4.4 统一日期范围同步与覆盖游标
页面只有一个同步入口,操作员确认工具条上的开始日和结束日后发起。首次打开页面
固定默认昨天到今天,不因 last_synced_at 更早而自动扩大范围;需要补历史缺口时
由操作员明确选择日期,单次仍不得超过 31 天。
[必须] 首次成功同步以所选结束日建立覆盖游标。已有游标时,只有日期范围从
游标当天或更早开始、并且结束日在游标之后,全部成功后才推进游标。局部历史补拉
或跳过缺口的范围只 upsert 数据,不动游标。
例如覆盖游标为 2026-08-01,同步 2026-08-01 ~ 2026-08-09 可以推进到
2026-08-09;只同步 2026-08-07 ~ 2026-08-08 不能推进,因为中间有缺口。
[必须] 日期格式固定 YYYY-MM-DD,按 UTC+8 解释;两端必须同时填写,
开始不得晚于结束,结束不得晚于 UTC+8 下的今天;闭区间最多 31 天。中途失败
或超过 max_matches 时不推进游标。
[必须] 每批明细响应必须与请求的货运单 ID 一一对应。缺失、重复、出现未请求
ID,或某张货运单返回空商品明细,都视为不完整并停止同步;已经写入的幂等数据
可以保留,但只有所有日期全部成功才推进游标。
[必须] 登录和验证码只是同步前置步骤。日期范围经过自动 OCR 降级、手工
输入验证码和 303 跳转时必须原样保留。
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 金额合计对不上是正常的
明细单价合计 239.0 + 439.0 = 678.0
amtOrder 612.0
差 66,应该是优惠。[必须] 不要用「明细合计 == amtOrder」做校验,
会误报。
5.3 created 是 UTC+8,不是 UTC —— 日期范围查询最容易算错的地方
[必须] 实测 raw_data/shunyunbaoerp_stock_query.har:
HAR 记录的抓包时刻 startedDateTime 2026-07-28T03:31:45Z (= 11:31:45 UTC+8)
同一次请求响应里的 created 2026-07-28 10:37:59
10:37:59 作为 UTC+8 讲得通(比抓包时刻早 54 分钟,正常)。
若把它当成 UTC,换算成 UTC+8 就是 18:37,比抓包时刻晚 7 小时——
订单创建于尚未发生的未来,不成立。所以 created 是 UTC+8,不是 UTC。
[必须] §4.2「按日期范围」的 dvalue 筛的就是这个 created,
所以换算"今天是哪一天"也必须用 UTC+8,不能用 UTC 或本机系统时区
(本机系统时区不一定是 UTC+8,取决于部署环境)。用 UTC 算的话,
在 UTC+8 的 00:00–08:00 这段时间会把"今天"算成昨天,当天早晨创建的单
这一轮同步拉不到——虽然下一轮的起始日期仍是"上次同步日",范围会覆盖
回来、不会永久丢单,但操作员当场点同步会以为同步坏了。
[必须] 代码里固定用 time.FixedZone("UTC+8", 8*60*60),不要用
time.LoadLocation("Asia/Shanghai")——那个要读系统 tzdata,Windows 上
默认没有,打包成 exe 后会在运行时报错。
[待定] 只有一个样本(一次抓包)支撑这个结论,且没有拿到顺运宝官方
文档确认。以后如果日期范围附近出现"该有的单没同步到",先来这里核对
这条结论是否仍然成立。
6. 货运明细
POST /am/stock/detail/listByStock?hist=0
{"ids": [75104587, ...]}
→ data.list[]
[必须] 一次最多传 100 个 id(示例脚本的分批大小),超了分批。
6.1 一单多商品是嵌套,不是多行
返回的每一行对应一张货运单,商品在嵌套的 details[] 里:
{
"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 的格式和蝦皮报表完全一致
蝦皮目录 `spec_raw` 黑色,M【建議40-50公斤】
顺运宝 productSpec 白色,L【建議50-60公斤】
黑色+白色【純棉兩件裝】 簡約親膚,L【建議52.5-60公斤】
[必须] 规格身份统一复用 spec.SpecKey():只折叠空白,不猜颜色、尺码或建议。
第三方目录脚本提交 spec_raw 和明确的解析结果;顺运宝匹配不到时交给人工确认。
6.4 由此推导出的匹配路径
productId ──→ shopee_products.goods_id 商品级,直接相等
productSpec ──→ SpecKey() ──→ 稳定规格身份键
──→ 在该商品目录和人工映射中查找
[必须] 比对不上时不要猜,标成待人工匹配。蝦皮报表只含有销售成绩的
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 侧不用跟着调。
7.1 档口入库码逐件写入(工单 #234、#250、#278、#289)
档口入库码最终写入货运明细的 innerExpCode(页面名称“快递单号”)。接口来自
现有 Python 流程和 HAR 响应样本:
GET /am/stock/detail/deleteInnerCode?detailId={detailID}
POST /am/stock/detail/createDetail
POST /am/stock/detail/updateDetailCode?t=0&id={stockID}&detailId={detailID}&code={单件innerCode}
[必须] t 固定为 0。顺运宝把该参数绑成 Java Integer(上限 2147483647)。
#250 改成当前毫秒时间戳后,2026-08-19 全部回写被拒:
Can not parse the parameter "1787108752485" to Integer value。不得再传毫秒,
也不得改成 Unix 秒——秒目前能进 Integer,2038 年仍会溢出。#234 的 t=0
已通过参数绑定;#250 保留的 POST 方法不要改回 GET。
createDetail 使用 JSON 请求体:id=null、稳定占位标题、productSpec=null、
productQty=1、productPrice=0、stockId;成功响应的 data 是新 detailId。
- 回写前必须同时核对目标码数量、Excel 合并源行数和原商品
productQty;任一不一致时 零写入。一个远端明细只写一个单件码,原明细为空时承载第一个缺失码,其余缺失码创建 零价占位明细。 - 顺运宝也可能已经用多条数量 1 的相同商品明细表达同一聚合记录。只有原始 SKU 重复
候选数、候选总件数、Excel 源行数和单件码数完全一致,并且全部候选的规格、
sku、variationSku、档口身份和采购状态一致时,才按稳定detailId与 Excel 单件码顺序形成existing_matched逐件规划。回写复用这些现成明细,不再创建占位明细;任一绑定在写前 变化时整条记录零写入并要求重新匹配。其他重复候选仍按真实歧义阻断。 - 同一货运单任意明细已有目标码时直接复用,不删除、不搬移。稳定占位标题可用于识别 “已创建但检查点未保存”的明细,防止崩溃后重复创建。
- 规划时的旧值为空时不调用删除;旧值非空且仍与规划快照一致时,只有删除明确成功才继续。
- 删除和写入请求都只允许发送一次,不使用自动重试。超时、5xx、响应读取失败或成功响应 无法解析时,结果可能已经在远端生效,必须标为“需核对”。
- 每件动作前后把检查点写入
remote_items_json;每个单件码写入后重新读取整张货运单, 只有它唯一出现在预期明细中才继续。全部目标码确认后才标为已回写。 - “重新核对”只调用
listByStock,绝不能再次调用上述两个写接口。 - 页面提交数量不限制为 20 条。所选记录先在 MySQL 同一业务表中进入
queued,HTTP 请求 随即返回;后台每次最多读取 20 条,再逐条执行上述远端门禁。Admin 重启不会自动继续 未开始的真实远端写入:queued恢复为可回写,applying转为需核对。
8. 实现时的固定约束
这几条不是抓包结论,是本项目的决定,写在这里避免每次重新讨论:
[必须] 同步先校验原始全量,再做店铺准入。 顺序固定为:按日期查询原始总数
并执行单次容量熔断 → 拉完当天原始列表并核对分页前后总数、页长和唯一 ID → 按
shopName 去除首尾空白后与启用店铺精确匹配 → 只为接受的货运单请求明细和入库。
不能先过滤再做完整性校验,否则非目标店铺的分页漂移会被掩盖。
[必须] 同步开始时只读取一次启用店铺,整次运行使用同一个快照。列表允许但明细
响应中的 shopName 变为空或非允许店铺时再次拦截。没有启用店铺时在会话/OCR/
验证码等任何顺运宝请求之前停止,并且不推进覆盖游标。该过滤只影响后续入库,
不清理历史货运单。
[必须] 会话缓存存 Admin 的 MySQL,不引入 Redis。 示例脚本用 Redis 是因为
它是反复启动的一次性脚本,进程间要传会话;Admin 是常驻进程,没有这个需求,
持久化只为重启后免登录,继续复用现有数据库即可。
[决定已变更] 不引入 OCR 服务。 这条判断在工单 #47 里被推翻了,
原文和推翻理由都留在这里,方便后来人知道这个决定变过、为什么变:
原判断(工单 #46):会话 24 小时,一天登录一次。为省一次手工输验证码 而依赖
127.0.0.1:8000不划算——多一个必须先启动的东西,而且 OCR 会失败(示例脚本自己写了 5 次重试),失败了照样要人工。界面上显示 验证码图片、操作员输一次即可。
[必须] 这条判断的前提是"本机服务 127.0.0.1:8000",托管服务不适用。
工单 #47 里用户提供了托管地址 https://ocr.ilapage.cn/ocr:没有要启动的
东西,就是一次 HTTP 调用,"多一个必须先启动的东西"这条理由不成立了。
而"每天第一次同步都要人在场"这个代价是实打实的——会话 24 小时过期,
意味着做不了无人值守的定时同步。于是工单 #47 引入了 OCR 自动识别,
失败或服务不可达时降级到原有的手工输入弹窗(那条兜底路径没有变),
不是"失败了照样要人工"变成了"失败了才要人工"。详见下面「验证码自动识别」一节。
[必须] 凭据放 admin/config.yaml,已在 .gitignore 里。
仓库里提供 admin/config.example.yaml 作为模板(不含真实凭据)。
[必须] 密码在任何日志里都要打码。 日志可能被贴进工单排查问题。
[必须] YAML 里密码要加引号。纯数字密码不加引号会被 YAML 解析成整数,
反序列化到 string 字段直接报错:
password: 0012345 # ✗ 解析成整数 12345,前导 0 也丢了
password: "0012345" # ✓
[建议] 依赖用 github.com/goccy/go-yaml,它已经在依赖树里(indirect),
不会引入新的传递依赖。
8.1 验证码自动识别(工单 #47)
响应格式(已实测)
POST https://ocr.ilapage.cn/ocr
Content-Type: multipart/form-data,字段名 file
→ 200 {"code":200,"message":"Success","data":"kycv"}
实测耗时约 1.4 秒。
[必须] 识别失败也是 code:200,这是最容易写错的地方。
实测用一张无文字的图片探测,返回的是 {"code":200,"message":"Success","data":""}——
不是错误码,是 code:200 加空 data。判断这一次调用真正"识别出了点什么",
必须同时满足:HTTP 200、code == 200、data 非空。只看 code 会把
"没识别出来"当成功,拿空字符串去登录。
[必须] 顺运宝验证码固定 4 位字母数字(08 §3.2 实测)。识别结果过滤
空格标点后长度不是 4,说明识别错了,不要拿去登录——直接换一张图重试。
拿明知不对的验证码去登录白费一次尝试,而且频繁的错误登录可能触发对方风控。
重试与降级
点同步 → 会话过期
├─ ocr_url 已配置
│ └─ 循环 ocr_max_attempts 次:取新验证码图 → OCR 识别 →
│ 校验(非空 && 4位字母数字) → 登录
│ ├─ 登录成功 ─────────────────→ 直接同步,无人值守
│ └─ 次数用完仍失败 ────────────┐
└─ ocr_url 未配置 / 请求本身失败 ────────┴─→ 弹手工输入框(#46 已有的兜底路径)
[必须] OCR 不可达要降级,不是报错。 外部服务挂了不该让整个同步功能
不可用——手工路径一直在,走它就是了。
[必须] 每次重试都要重新取一张验证码图。同一张图再识别一次结果一样,
纯属浪费;而且验证码可能已经被上一次失败的登录作废。
[必须] OCR 请求本身失败(连不上、超时、返回非法 JSON、code != 200)判定为
"服务不可用",立即降级,不占用重试次数——重试对"服务本身连不上"这种情况
没有意义。只有"HTTP 调用成功但识别结果不合格(空/长度不对)"才占用一次重试。
[必须] 调 OCR 不带顺运宝的 Cookie,用独立的 http.Client(独立的
Cookie Jar、独立的超时),避免把顺运宝会话泄漏给另一个服务;也不共用
顺运宝请求的超时,OCR 慢不该拖垮整个登录流程([建议] 10 秒)。
[必须] 验证码图片全程在内存里传字节,不写文件。 参考实现
raw_data/shunyunbaoerp_single.py 把图片存成 captcha.jpg 是命令行脚本
的做法,Admin 是常驻进程,写文件只会在 data/ 里堆垃圾。
[必须] 验证码图片会被发送到 ocr_url 配置的外部服务。
当前 https://ocr.ilapage.cn/ocr 是用户自己的服务,不算交给第三方;
把 ocr_url 换成别人运营的服务前,必须重新评估这一点。
配置
syb:
# 验证码自动识别服务地址。留空则只用手工输入弹窗,不报错。
ocr_url: https://ocr.ilapage.cn/ocr
ocr_max_attempts: 5
[必须] ocr_url 留空 = 禁用,直接走手工输入弹窗,不报错。
9. 已知未验证的部分
实现前应逐条确认,都只有单一样本支撑:
escrowAmount等金额字段在列表接口里的单位(见 §5.1)- 空
queries能否拉全量(同步用日期范围,可不验) - 日期范围跨度很大时服务端是否限流或超时
status数字码与orderStatus中文的完整对应表- 会话失效时服务端返回的确切形态(HTTP 码 /
code/msg文案) - 同一账号多处登录是否互踢
- 验证码错误、密码错误分别返回什么,能否区分
created是 UTC+8 这一条(见 §5.3)只有一次抓包支撑, 没有官方文档确认,也没有跨夏令时/时区配置的验证
[必须] 最后两条影响错误提示的准确性:分不清「密码错」和「验证码错」的话,
操作员会一直重输密码。