feat(t226): add Go ERP session login

This commit is contained in:
QiuSW
2026-07-29 09:51:41 +08:00
parent c2f340d067
commit a5a61c05d0
23 changed files with 1633 additions and 33 deletions
+8 -4
View File
@@ -16,6 +16,8 @@
+--> SQLite / 文件存储
|
+--> loopback ERP Connector ----> 顺运宝 ERP
|
+--> Go 内存 ERP 会话 ---------> 顺运宝 ERP
^
|
Android 采购 App --------------------> VLM Provider
@@ -156,10 +158,12 @@ Admin 创建 sync run
缓存和个人信息处理权限。
T-225 已在 `internal/platform/shunyunbao` 冻结直接 Go 实现的协议常量、请求 header、
完整单号/日期范围条件、分页、详情批量和 allowlist 归一化;它尚未接入运行时。货运
用例只识别来源中立的“未配置、会话失效、未找到、协议异常、暂时不可用”错误,不依赖
Connector 包。T-226 负责单进程内受锁保护的验证码会话,T-227 才将异步 sync worker
从 loopback Connector 切换到 Go source,T-228 删除旧 Python 进程和服务密钥。
完整单号/日期范围条件、分页、详情批量和 allowlist 归一化。T-226 已增加一个受互斥
保护的 Go 内存会话:Admin 以短期 ticket 读取验证码图片并人工输入验证码,服务端才
持有 Cookie jar;账号密码只从启动环境读取。进程重启即失去该会话,不用 Redis 或持久化
Cookie。货运用例只识别来源中立的“未配置、会话失效、未找到、协议异常、暂时不可用”
错误,不依赖 Connector 包。T-227 才将异步 sync worker 从 loopback Connector 切换到
Go source,T-228 删除旧 Python 进程和服务密钥。
模式在 execution 开始时固定并写入结果;AI 失败后只能由人员明确切换,不能静默降级。
App 同时固定 provider ID、model、prompt/schema version 和证据 SHA-256,作为非秘密
+28
View File
@@ -216,6 +216,34 @@ Connector 是 loopback 内部服务,不复用 ADMIN/BUYER 凭证。除 `/healt
不得返回 receiver、receiverTel、receiverAddr、Cookie、JWT、ERP 用户资料或完整原始
对象。多商品必须全部保留;缺失详情返回协议错误,不允许部分成功。
### Go ERP 会话(T-226)
以下接口均要求 ADMIN session;所有 `POST` 还要求现有 `X-CSRF-Token` 与 CSRF
Cookie。它们只管理单个后端进程内的顺运宝会话,不创建货运同步,也不下发 ERP 账号、
密码、Cookie、JWT、完整用户资料或原始响应。
| 接口 | 说明 |
| --- | --- |
| `GET /api/v1/erp-session` | 返回 `configured`、`authenticated`、`captcha_ready` 三个布尔状态。 |
| `POST /api/v1/erp-session/captcha` | 同一服务端 Cookie jar 获取一次验证码,返回短期 `captcha_ticket` 和仅供当前 ADMIN 读取图片的 `captcha_url`。 |
| `GET /api/v1/erp-session/captcha/{ticket}` | 返回 `image/*` 验证码,始终 `Cache-Control: no-store`。ticket 过期或不匹配返回稳定匿名错误。 |
| `POST /api/v1/erp-session/login` | 仅接受 `{"captcha_ticket":"...","captcha_code":"..."}`,使用启动环境中的账号/密码完成登录和用户校验。 |
成功状态响应不包含 ticket,验证码接口以外也不会返回验证码图片。稳定错误为
`ERP_NOT_CONFIGURED`、`ERP_SESSION_REQUIRED`、`ERP_CAPTCHA_INVALID`、
`ERP_LOGIN_REJECTED`、`ERP_RESPONSE_INVALID` 和 `ERP_UNAVAILABLE`;均不包含 ERP
正文或凭证。管理员 Web 的等价入口为 `/erp`、`/erp/captcha` 和 `/erp/login`,表单没有
账号或密码字段。
启动环境变量:
- `CMROUBAO_SHUNYUNBAO_URL`:默认 `https://www.shunyunbaoerp.com`;只接受无路径、
userinfo、query 或 fragment 的 HTTPS origin。
- `CMROUBAO_SHUNYUNBAO_USERNAME` 与 `CMROUBAO_SHUNYUNBAO_PASSWORD`:必须同时设置;
为空时连接页明确显示未配置,服务进程仍可用于其他本地功能。
会话不写 SQLite、不加载 `.env`、不使用 OCR 或 Redis;服务重启后必须重新人工登录。
### `POST /api/v1/freight-syncs`
ADMIN 创建异步同步记录,必须带 `Idempotency-Key`:
+14 -10
View File
@@ -5,7 +5,7 @@
## 当前快照
- 日期:2026-07-29
- 阶段:T-225 Go 直连 ERP 协议与安全边界已完成;运行时切换待 T-226 至 T-228
- 阶段:T-226 Go ERP 内存会话与 Admin 人工验证码登录已完成;运行时切换待 T-227 至 T-228
- Git:当前分支为 `main`;T-001 至 T-004、T-101 至 T-104、T-201 至 T-219
均按文档提交、实现提交的顺序纳入历史
- 生产代码:`android-buyer/` 已接入 Roubao Android 源码
@@ -18,9 +18,11 @@
完整单号同步;v12 保存同步记录、货运头和全部明细,canonical hash 控制 revision,
Admin 已有 `/freight`、`/freight/import`、`/freight/{id}` 与对应 JSON API。
- ERP Go 迁移:T-225 已用脱敏 fixture 固定 `internal/platform/shunyunbao` 的 header、
单号/日期查询、分页、详情批量和字段 allowlist,并使货运用例依赖来源中立错误;尚未
发起真实 ERP 请求,也未切换 Python Connector 运行时。T-226 将加入单进程人工验证码
会话,T-227 才切换异步货运 source,T-228 删除旧 Connector。
单号/日期查询、分页、详情批量和字段 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 分钟所在自然日回看。
货运落库、同步成功和水位推进同事务完成;失败与较旧范围成功不推进水位。Admin
@@ -32,8 +34,9 @@
- Android Studio:未安装;`winget` 静默安装卡住后已终止,不阻塞命令行构建
- 测试:T-219 Android Debug/Release 单元测试与构建和根 `init.ps1` 通过;
Debug APK `1.4.16 (21)` 已覆盖安装到 PKG110
- 后端测试:T-225 运行 `go test ./...`、`go test -race ./...`、`go vet ./...` 和三个 Go
入口构建;
- 后端测试:T-226 已运行 `go test ./...`、`go test -race ./...`、`go vet ./...` 和三个 Go
入口构建;新增配置、会话并发/过期、ADMIN API、SSR 验证码页面以及隔离 Gin/Chrome
桌面/手机视口 smoke,均只用伪 ERP 或未配置状态;
覆盖 v14 上下迁移、7 天切窗、水位重叠、中途失败、空窗口、重复页、来源 revision、
水位事务/不回退、Admin API/SSR 和 Connector 严格响应窗口;Python 22 项伪响应
测试通过,未访问真实 ERP
@@ -188,11 +191,12 @@
## 任务摘要
- 已完成:T-001 至 T-004、T-101 至 T-104、T-201 至 T-219。
- 已完成:另含 T-220 至 T-225 ERP 契约、Connector、货运存储、采购需求生成、日期
增量同步和 Go 直连协议安全边界。
- 已完成:另含 T-220 至 T-226 ERP 契约、Connector、货运存储、采购需求生成、日期
增量同步、Go 直连协议安全边界和人工验证码会话。
- 进行中:无。
- 下一步:T-226 建立 Go 后端内存会话和 Admin 人工验证码登录;真实 ERP 上线前仍需
确认开放 API、数据使用权限并由人员完成验证码登录。
- 下一步:T-227 将已验证 Go 会话接入 `FreightSource`,保留现有异步单号/日期同步、
水位、revision 和采购任务语义;真实 ERP 上线前仍需确认开放 API、数据使用权限并由
人员完成验证码登录。
## 当前可运行内容
+3 -1
View File
@@ -14,9 +14,10 @@ T-202 的离线 P0 页面入口见[原型索引](design/index.html)。原型仅
| `/freight` | ERP 货运列表 | 查看已导入货运单并进入全部商品明细 | US-011 | IX-012 |
| `/freight/import` | ERP 货运导入 | 创建精确单号、最多 7 天日期范围或“同步至现在”任务,并查看成功水位 | US-011 | IX-012 |
| `/freight/{id}` | ERP 货运详情 | 查看全部商品明细、来源变化、补图和采购任务生成状态 | US-011、US-012 | IX-012、IX-013 |
| `/erp` | ERP 连接 | 查看匿名会话状态、获取验证码并人工输入验证码;不显示或编辑 ERP 凭证 | US-011 | IX-012 |
MVP 登录后默认进入 `/tasks`。未登录访问受保护页面时跳转 `/login` 并携带安全的
站内返回路径;只接受 `/tasks`、`/freight` 及其本站子路径,拒绝绝对 URL、`//`
站内返回路径;只接受 `/tasks`、`/freight`、`/erp` 及其本站子路径,拒绝绝对 URL、`//`
和反斜杠。
不存在和无权限必须使用不同内部原因,但页面均不得泄露任务内容。
@@ -91,6 +92,7 @@ T-207 的 events/candidates/complete/fail 接受原设备对授权内已产生
| `PendingPaymentSummary` | 管理 Web/App | 只读展示对账订单与人工付款提醒,不提供付款或重提动作 |
| `FreightSyncForm` | 管理 Web | 创建精确单号/日期同步记录、查看成功水位,不持有 ERP 凭证 |
| `FreightItemReview` | 管理 Web | 显示规范化来源字段、缺失项和采购任务生成操作 |
| `ERPConnectionPanel` | 管理 Web | 仅展示匿名连接状态、验证码图片和验证码输入,不接收 ERP 账号或密码 |
具体状态反馈以[交互清单](08-interaction-checklist.md)为准,组件命名可在接入真实框架后
调整并同步本文。
+19 -5
View File
@@ -4,7 +4,7 @@ title: Go ERP 会话管理与 Admin 验证码登录
phase: 2
deps:
- T-225
status: TODO
status: DONE
created: 2026-07-29
context_ref: d62a4af
work_branch: null
@@ -48,10 +48,10 @@ write_paths:
## 验收要点
- [ ] 配置缺失、验证码获取、错误/过期验证码、登录成功、会话过期和并发请求有单元/HTTP 测试。
- [ ] Admin 页面在桌面和手机视口显示状态、验证码和可修复错误,不显示密码、Cookie、JWT 或验证码文本。
- [ ] 会话请求和查询共享同一受控 Cookie jar;进程重启后明确要求重新登录。
- [ ] `go test ./...`、`go vet ./...` 通过;不访问线上 ERP。
- [x] 配置缺失、验证码获取、错误/过期验证码、登录成功、会话过期和并发请求有单元/HTTP 测试。
- [x] Admin 页面在桌面和手机视口显示状态、验证码和可修复错误,不显示密码、Cookie、JWT 或验证码文本。
- [x] 会话请求和查询共享同一受控 Cookie jar;进程重启后明确要求重新登录。
- [x] `go test ./...`、`go vet ./...` 通过;未访问线上 ERP。
## 边界
@@ -62,3 +62,17 @@ write_paths:
## 执行记录
- 2026-07-29:由 T-225 依赖创建,等待协议契约完成。
- 2026-07-29:开始实施;T-225 已由 `c2f340d` 完成,当前仍未访问线上 ERP。
- 2026-07-29:新增 `SessionManager`,以同一受锁 Cookie jar 完成验证码、登录和
`/am/user/get` 校验;只保存内存会话,验证码 ticket 默认 5 分钟后失效。账号/密码仅由
`CMROUBAO_SHUNYUNBAO_*` 环境读取,未配置不会阻止其他后端功能启动。
- 2026-07-29:新增 ADMIN 的 `/erp` 连接页和 `/api/v1/erp-session*` 接口;所有写接口
继续受 ADMIN CSRF middleware 保护。浏览器页面只输入验证码,图片和 ticket 均 `no-store`,
不返回 ERP Cookie、JWT、用户资料、密码或原始错误正文。
- 验证:在 `backend-api/` 执行 `$env:GOTOOLCHAIN='local'; go test ./...; go test -race ./...;`
`go vet ./...; go build ./cmd/api; go build ./cmd/migrate; go build ./cmd/authctl`,全部通过。
会话/HTTP/SSR 使用 `httptest` 伪 ERP;未访问线上 ERP。
- UI smoke:隔离 SQLite 上以 `smoke-admin` 登录实际 Gin 服务,Chrome 以 1440x900 和
390x844 验证 `/erp` 未配置状态无横向溢出;截图只写入系统临时目录,未进仓库。
- 未验证项:真实 ERP 凭证、人工验证码和厂商授权检查有意留给部署前人员操作;T-227
才把会话接入货运同步 source。