feat(t227): query ERP directly from Go
This commit is contained in:
+17
-18
@@ -15,9 +15,7 @@
|
||||
|
|
||||
+--> SQLite / 文件存储
|
||||
|
|
||||
+--> loopback ERP Connector ----> 顺运宝 ERP
|
||||
|
|
||||
+--> Go 内存 ERP 会话 ---------> 顺运宝 ERP
|
||||
+--> Go 内存 ERP 会话 / 直连 source ----> 顺运宝 ERP
|
||||
^
|
||||
|
|
||||
Android 采购 App --------------------> VLM Provider
|
||||
@@ -33,8 +31,8 @@ Android 采购 App --------------------> VLM Provider
|
||||
- 拼多多是第三方受控边界,只能由 Android 设备在已登录会话中操作。
|
||||
- VLM 配置和 Key 位于手机,Key 使用 Android Keystore 包装的加密存储;管理后端不
|
||||
保存、下发或代理模型调用。
|
||||
- ERP 账号、验证码会话、Cookie 和 JWT 只存在于 loopback Python Connector;
|
||||
Go 后端只接收最小化规范货运数据。
|
||||
- ERP 账号、验证码会话、Cookie 和可能的 JWT 仅存在于 Go API 进程的受锁内存会话;
|
||||
浏览器和 SQLite 只接收最小化规范货运数据。
|
||||
|
||||
## 二、模块职责
|
||||
|
||||
@@ -136,34 +134,35 @@ App 支持两个显式模式:
|
||||
|
||||
### 2.4 顺运宝 ERP 适配层
|
||||
|
||||
Connector 是外部系统防腐层,不属于采购任务状态机:
|
||||
Go 直连 source 是外部系统防腐层,不属于采购任务状态机:
|
||||
|
||||
```text
|
||||
Admin 创建 sync run
|
||||
-> Go 后台 worker 调用 loopback Connector
|
||||
-> Connector 使用受控 ERP 会话查询 list/listTotal/listByStock
|
||||
-> Connector 规范化并去除收件 PII
|
||||
-> Go 后台 worker 复用受锁 ERP 内存会话
|
||||
-> Go source 查询 listTotal/list/listByStock
|
||||
-> Go source 规范化并去除收件 PII
|
||||
-> Go 同事务 upsert freight order/items
|
||||
-> Admin 复核 procurement request
|
||||
-> 显式生成不可变 purchase task
|
||||
```
|
||||
|
||||
- Python 进程默认只监听 `127.0.0.1`,使用独立服务密钥;Go 不传输或保存 ERP 密码。
|
||||
- ERP 账号/密码只由 API 启动环境读取;浏览器不提交凭证,SQLite 和普通日志不保存凭证。
|
||||
- 精确单号只是查询条件,外部身份固定为 `stock.id` 和 `details[].id`。
|
||||
- Connector 不写本项目 SQLite,不调用 Roubao/拼多多/VLM,也不打印响应 body。
|
||||
- source 不调用 Roubao/拼多多/VLM,不打印原始响应 body,只返回 allowlist 规范结构。
|
||||
- Go handler 只创建 sync run;外部查询由有界 worker 执行,避免把验证码或 ERP
|
||||
延迟绑定到浏览器请求。
|
||||
- 未确认的 `productThumb` 只按 ERP 引用保存,不能拼接 URL 或越权下载。
|
||||
- ERP 接口由页面协议观察得到,正式生产前需确认开放 API、服务账号、调用频率、
|
||||
缓存和个人信息处理权限。
|
||||
|
||||
T-225 已在 `internal/platform/shunyunbao` 冻结直接 Go 实现的协议常量、请求 header、
|
||||
完整单号/日期范围条件、分页、详情批量和 allowlist 归一化。T-226 已增加一个受互斥
|
||||
保护的 Go 内存会话:Admin 以短期 ticket 读取验证码图片并人工输入验证码,服务端才
|
||||
持有 Cookie jar;账号密码只从启动环境读取。进程重启即失去该会话,不用 Redis 或持久化
|
||||
Cookie。货运用例只识别来源中立的“未配置、会话失效、未找到、协议异常、暂时不可用”
|
||||
错误,不依赖 Connector 包。T-227 才将异步 sync worker 从 loopback Connector 切换到
|
||||
Go source,T-228 删除旧 Python 进程和服务密钥。
|
||||
T-225 冻结了 `internal/platform/shunyunbao` 的协议常量、请求 header、完整单号/日期范围
|
||||
条件、分页、详情批量和 allowlist 归一化。T-226 增加受互斥保护的 Go 内存会话:Admin
|
||||
以短期 ticket 读取验证码图片并人工输入验证码,服务端才持有 Cookie jar;账号密码只从
|
||||
启动环境读取。T-227 将此会话直接作为 `FreightSource`:每次同步先校验会话,再以最多
|
||||
100 条、每页 20 条和每批最多 100 个详情 ID 查询;响应不完整、身份冲突或会话失效均使
|
||||
整批失败。进程重启即失去会话,不用 Redis 或持久化 Cookie。货运用例只识别来源中立的
|
||||
“未配置、会话失效、未找到、协议异常、暂时不可用”错误。T-228 再删除未参与运行的旧
|
||||
Python Connector、loopback 端口和服务密钥。
|
||||
|
||||
模式在 execution 开始时固定并写入结果;AI 失败后只能由人员明确切换,不能静默降级。
|
||||
App 同时固定 provider ID、model、prompt/schema version 和证据 SHA-256,作为非秘密
|
||||
|
||||
+11
-34
@@ -164,28 +164,10 @@ T-203 成功返回 `201`。使用相同 `Idempotency-Key` 和相同图片内容
|
||||
|
||||
受鉴权的文件流。只能读取用户有权查看的任务资产;不返回服务端文件路径。
|
||||
|
||||
## ERP Connector 与货运信息
|
||||
## Go ERP 会话与货运信息
|
||||
|
||||
Connector 是 loopback 内部服务,不复用 ADMIN/BUYER 凭证。除 `/health` 外要求
|
||||
`X-API-Key`,密钥至少 32 字节并由两进程环境变量注入;请求和响应不得写 body 日志。
|
||||
|
||||
### Connector `POST /v1/freight/query`
|
||||
|
||||
```json
|
||||
{"mode":"ORDER_NUMBER","order_number":"完整单号"}
|
||||
```
|
||||
|
||||
或按 Asia/Shanghai 自然日闭区间查询,单次最多 7 天:
|
||||
|
||||
```json
|
||||
{
|
||||
"mode":"CREATED_RANGE",
|
||||
"created_from":"2026-07-22",
|
||||
"created_to":"2026-07-28"
|
||||
}
|
||||
```
|
||||
|
||||
成功返回最小化规范结构:
|
||||
顺运宝仅由 Go API 进程直连;浏览器不复用 ERP 凭证,也不接收 Cookie、JWT、用户资料或
|
||||
原始响应。货运同步只接收以下最小化规范结构:
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -267,7 +249,7 @@ T-224 增加:
|
||||
从成功水位前回看 10 分钟对应的自然日开始,后端再按最多 7 天切窗。
|
||||
|
||||
响应 `202`,返回 sync id/status。订单号不进入 URL、事件 message 或访问日志;数据库
|
||||
只保存规范值及用于审计/检索的受控字段,不保存 Connector 密钥。
|
||||
只保存规范值及用于审计/检索的受控字段,不保存 ERP 凭证、Cookie 或 JWT。
|
||||
|
||||
### `GET /api/v1/freight-syncs/{sync_id}`
|
||||
|
||||
@@ -283,24 +265,19 @@ T-224 增加:
|
||||
### `GET /api/v1/freight-orders`
|
||||
|
||||
返回当前 ADMIN creator 最近更新的货运单并支持 `limit`(1..100)。响应不包含
|
||||
收件人字段或 Connector 原文;列表筛选和 cursor 后置,不属于本次日期同步闭环。
|
||||
收件人字段或 ERP 原文;列表筛选和 cursor 后置,不属于本次日期同步闭环。
|
||||
|
||||
### `GET /api/v1/freight-orders/{id}`
|
||||
|
||||
T-222 返回货运头、全部当前商品明细和 revision/hash 状态。T-223 再增加采购需求和
|
||||
已生成 task 引用。不存在和跨 creator 统一 404;响应 `Cache-Control: no-store`。
|
||||
|
||||
T-222 精确单号同步需要 Go 后端配置:
|
||||
|
||||
- `CMROUBAO_ERP_CONNECTOR_URL`:默认 `http://127.0.0.1:8091`,只接受 HTTP
|
||||
loopback origin。
|
||||
- `CMROUBAO_ERP_CONNECTOR_API_KEY`:与 Connector 的
|
||||
`SHUNYUNBAO_SERVICE_API_KEY` 相同,至少 32 UTF-8 字节;未设置时后端可启动,
|
||||
但同步任务稳定失败为 `ERP_CONNECTOR_NOT_CONFIGURED`。
|
||||
|
||||
Connector 未登录、找不到货运单、响应协议错误和暂时不可用分别落为
|
||||
`ERP_SESSION_REQUIRED`、`ERP_FREIGHT_NOT_FOUND`、`ERP_RESPONSE_INVALID` 和
|
||||
`ERP_CONNECTOR_UNAVAILABLE`,不返回 ERP 原始错误 body。
|
||||
T-227 的后台 worker 使用当前 Go 内存会话,按 `listTotal -> list 分页 -> listByStock`
|
||||
查询。每次先校验会话;列表最多 100 条、每页 20 条,详情每批最多 100 个外部 stock ID。
|
||||
未配置、未登录、找不到货运单、响应协议错误和暂时不可用分别落为
|
||||
`ERP_NOT_CONFIGURED`、`ERP_SESSION_REQUIRED`、`ERP_FREIGHT_NOT_FOUND`、
|
||||
`ERP_RESPONSE_INVALID` 和 `ERP_UNAVAILABLE`,不返回 ERP 原始错误 body。旧 Python
|
||||
Connector 仅在仓库中等待 T-228 删除,不再是运行时依赖。
|
||||
|
||||
### `POST /api/v1/freight-items/{item_id}/procurement-request`
|
||||
|
||||
|
||||
+19
-17
@@ -5,7 +5,7 @@
|
||||
## 当前快照
|
||||
|
||||
- 日期:2026-07-29
|
||||
- 阶段:T-226 Go ERP 内存会话与 Admin 人工验证码登录已完成;运行时切换待 T-227 至 T-228
|
||||
- 阶段:T-227 Go 直连 ERP 查询已完成;T-228 待删除遗留 Python Connector
|
||||
- Git:当前分支为 `main`;T-001 至 T-004、T-101 至 T-104、T-201 至 T-219
|
||||
均按文档提交、实现提交的顺序纳入历史
|
||||
- 生产代码:`android-buyer/` 已接入 Roubao Android 源码
|
||||
@@ -14,17 +14,17 @@
|
||||
- 后端:Go 1.23.0 + Gin 1.11.0 + SQLite + Goose 3.26.0;已实现图片/任务业务、
|
||||
SSR 管理 Web、ADMIN/BUYER 联合认证、设备 readiness、原子 claim、租约状态机、
|
||||
task-scoped 参考图和 `authctl`
|
||||
- ERP 货运:Go 后端通过仅限 loopback、服务密钥鉴权的 Python Connector 异步按
|
||||
完整单号同步;v12 保存同步记录、货运头和全部明细,canonical hash 控制 revision,
|
||||
Admin 已有 `/freight`、`/freight/import`、`/freight/{id}` 与对应 JSON API。
|
||||
- ERP 货运:Go 后端以受锁内存 ERP 会话直连异步按完整单号或创建日期同步;v12 保存
|
||||
同步记录、货运头和全部明细,canonical hash 控制 revision,Admin 已有 `/freight`、
|
||||
`/freight/import`、`/freight/{id}` 与对应 JSON API。
|
||||
- ERP Go 迁移:T-225 已用脱敏 fixture 固定 `internal/platform/shunyunbao` 的 header、
|
||||
单号/日期查询、分页、详情批量和字段 allowlist,并使货运用例依赖来源中立错误。T-226
|
||||
已增加受锁保护的 Go 内存 Cookie jar、验证码 ticket、登录和用户校验,以及 ADMIN 的
|
||||
`/erp` 页面/API;账号密码仅由 `CMROUBAO_SHUNYUNBAO_*` 启动环境读取,重启后需人工
|
||||
重新登录。未访问真实 ERP,Python Connector 仍是货运同步运行时;T-227 才切换 source,
|
||||
T-228 删除旧 Connector。
|
||||
- ERP 增量同步:v14 支持 Asia/Shanghai 创建日期闭区间和“同步至现在”,Connector
|
||||
单窗最多 7 天,后端对较长水位范围切窗并从成功水位前 10 分钟所在自然日回看。
|
||||
重新登录。T-227 已将其作为 `FreightSource`,查询先校验会话、再执行有界分页/详情
|
||||
批量并返回 allowlist;未访问真实 ERP。旧 Python Connector 仅待 T-228 删除。
|
||||
- ERP 增量同步:v14 支持 Asia/Shanghai 创建日期闭区间和“同步至现在”,source 单窗
|
||||
最多 7 天,后端对较长水位范围切窗并从成功水位前 10 分钟所在自然日回看。
|
||||
货运落库、同步成功和水位推进同事务完成;失败与较旧范围成功不推进水位。Admin
|
||||
可查看查询范围、计数、稳定错误码和最近成功水位。
|
||||
- 采购需求:v13 按货运商品 revision 保存可复核快照;ADMIN 显式确认采购、上传参考
|
||||
@@ -35,11 +35,11 @@
|
||||
- 测试:T-219 Android Debug/Release 单元测试与构建和根 `init.ps1` 通过;
|
||||
Debug APK `1.4.16 (21)` 已覆盖安装到 PKG110
|
||||
- 后端测试:T-226 已运行 `go test ./...`、`go test -race ./...`、`go vet ./...` 和三个 Go
|
||||
入口构建;新增配置、会话并发/过期、ADMIN API、SSR 验证码页面以及隔离 Gin/Chrome
|
||||
桌面/手机视口 smoke,均只用伪 ERP 或未配置状态;
|
||||
入口构建;T-227 增加 Go source 的伪 ERP 会话预检、完整单号、日期分页去重、详情
|
||||
allowlist 和稳定错误码覆盖,均未访问真实 ERP;
|
||||
覆盖 v14 上下迁移、7 天切窗、水位重叠、中途失败、空窗口、重复页、来源 revision、
|
||||
水位事务/不回退、Admin API/SSR 和 Connector 严格响应窗口;Python 22 项伪响应
|
||||
测试通过,未访问真实 ERP
|
||||
水位事务/不回退、Admin API/SSR 和旧 Connector 严格响应窗口;Python 22 项伪响应
|
||||
测试保留至 T-228 删除前,未访问真实 ERP
|
||||
- 原型:4 个管理 Web 页面和 7 个 Android 页面均可离线独立打开;Playwright
|
||||
以 1440×900、390×844、360×800 验证 36 个页面/视口组合,无页面横向溢出、
|
||||
脚本错误或外部请求,Android 可见交互控件均不小于 44px
|
||||
@@ -179,6 +179,9 @@
|
||||
| `docs/tasks/T-222.md` | DONE | 货运信息存储、API 与 Admin 页面 |
|
||||
| `docs/tasks/T-223.md` | DONE | 待采购需求提取与任务生成 |
|
||||
| `docs/tasks/T-224.md` | DONE | ERP 日期增量同步 |
|
||||
| `docs/tasks/T-225.md` | DONE | 冻结 Go 直连 ERP 协议与安全边界 |
|
||||
| `docs/tasks/T-226.md` | DONE | Go ERP 会话、验证码登录与 Admin 连接页 |
|
||||
| `docs/tasks/T-227.md` | DONE | Go 直连顺运宝查询接入货运同步 |
|
||||
| `docs/design/` | 已确认 | T-202 原型索引、4 个管理页和 7 个 Android 页面 |
|
||||
| `deepseek总结.txt` | 已有 | 历史讨论摘要,不是正式需求权威 |
|
||||
| `android-buyer/` | 已有 | Roubao `main` 固定 commit 的 Android 基线 |
|
||||
@@ -191,12 +194,11 @@
|
||||
## 任务摘要
|
||||
|
||||
- 已完成:T-001 至 T-004、T-101 至 T-104、T-201 至 T-219。
|
||||
- 已完成:另含 T-220 至 T-226 ERP 契约、Connector、货运存储、采购需求生成、日期
|
||||
增量同步、Go 直连协议安全边界和人工验证码会话。
|
||||
- 已完成:另含 T-220 至 T-227 ERP 契约、货运存储、采购需求生成、日期增量同步、Go
|
||||
直连协议、人工验证码会话和直连 `FreightSource`。
|
||||
- 进行中:无。
|
||||
- 下一步:T-227 将已验证 Go 会话接入 `FreightSource`,保留现有异步单号/日期同步、
|
||||
水位、revision 和采购任务语义;真实 ERP 上线前仍需确认开放 API、数据使用权限并由
|
||||
人员完成验证码登录。
|
||||
- 下一步:T-228 删除旧 Python Connector、loopback 端口/服务密钥及其文档;真实 ERP
|
||||
上线前仍需确认开放 API、数据使用权限并由人员完成验证码登录。
|
||||
|
||||
## 当前可运行内容
|
||||
|
||||
|
||||
+20
-6
@@ -4,7 +4,7 @@ title: Go 直连顺运宝查询接入货运同步
|
||||
phase: 2
|
||||
deps:
|
||||
- T-226
|
||||
status: TODO
|
||||
status: DONE
|
||||
created: 2026-07-29
|
||||
context_ref: d62a4af
|
||||
work_branch: null
|
||||
@@ -50,11 +50,11 @@ T-226 能在 Go 后端建立受控 ERP 会话,但货运同步仍通过 Python
|
||||
|
||||
## 验收要点
|
||||
|
||||
- [ ] 完整单号、日期范围、重叠水位、分页去重、详情多商品、失败不推进水位均有 Go 集成测试。
|
||||
- [ ] Admin 输入单号后创建异步 run,并由 Go source 直接得到相同规范化货运/明细结果。
|
||||
- [ ] 缺会话显示 `ERP_SESSION_REQUIRED`;不再出现 Connector URL/API Key 相关错误。
|
||||
- [ ] 真实受控单号 smoke 可选且不记录订单内容;未具备凭证时明确标记未运行。
|
||||
- [ ] `go test ./...`、`go test -race ./...`、`go vet ./...` 通过。
|
||||
- [x] 完整单号、日期范围、重叠水位、分页去重、详情多商品、失败不推进水位均有 Go 集成测试。
|
||||
- [x] Admin 输入单号后创建异步 run,并由 Go source 直接得到相同规范化货运/明细结果。
|
||||
- [x] 缺会话显示 `ERP_SESSION_REQUIRED`;运行时不再出现 Connector URL/API Key 相关错误。
|
||||
- [x] 真实受控单号 smoke 可选且不记录订单内容;未具备凭证,明确未运行。
|
||||
- [x] `go test ./...`、`go test -race ./...`、`go vet ./...` 通过。
|
||||
|
||||
## 边界
|
||||
|
||||
@@ -65,3 +65,17 @@ T-226 能在 Go 后端建立受控 ERP 会话,但货运同步仍通过 Python
|
||||
## 执行记录
|
||||
|
||||
- 2026-07-29:由 T-226 依赖创建,等待 Admin 会话能力完成。
|
||||
- 2026-07-29:开始实施;T-226 已由 `a5a61c0` 完成。货运同步保留既有异步 run、
|
||||
日期水位和 SQLite 幂等语义,仅将运行时 `FreightSource` 切换为 Go 直连实现。
|
||||
- 2026-07-29:`SessionManager` 现实现 `FreightSource`;每次查询先以同一 Cookie jar
|
||||
校验 `/am/user/get`,再按 `listTotal -> list -> listByStock?hist=0` 执行。查询上限为
|
||||
100 条、分页为 20 条、详情批量为 100 个 ID;重复的完全相同外部身份去重,缺失详情、
|
||||
冲突身份、超限和协议异常均整批失败。
|
||||
- 2026-07-29:API 进程已直接注入 Go source,不再构造 `erpconnector.Client`。同步失败码
|
||||
调整为 `ERP_NOT_CONFIGURED`、`ERP_SESSION_REQUIRED`、`ERP_FREIGHT_NOT_FOUND`、
|
||||
`ERP_RESPONSE_INVALID`、`ERP_UNAVAILABLE`;旧 Connector 代码和配置仅留待 T-228 删除。
|
||||
- 验证:在 `backend-api/` 执行 `$env:GOTOOLCHAIN='local'; go test ./...; go test -race ./...;`
|
||||
`go vet ./...; go build ./cmd/api; go build ./cmd/migrate; go build ./cmd/authctl`,全部通过。
|
||||
`httptest` 覆盖同 Cookie jar、完整单号、多商品详情、日期分页去重、allowlist、未配置/
|
||||
未登录和稳定错误码;既有 Go 集成测试继续覆盖日期切窗、重叠水位与失败不推进水位。
|
||||
- 未运行:没有读取或使用真实 ERP 凭证,因此未执行真实受控单号 smoke;未记录订单内容。
|
||||
|
||||
Reference in New Issue
Block a user