T46 顺云宝(SYB)接口客户端移植与登录会话 #48

Closed
opened 2026-08-19 16:13:41 +08:00 by ila · 5 comments
Owner

背景

模板项目 cmautobuy/admin 已有一套经抓包还原、带 31 个 httptest 用例的顺云宝 ERP 客户端(syb/client.go 854 行、syb/ocr.go、docs/admin/08-顺运宝接口.md)。#41 当初留下的「没有导入端点」缺口,可以靠移植这套代码补上,不必从零实现。

来源代码与本仓库同属一个负责人,可直接复用;但两边数据层不同(对方是 database/sql 手写 SQL,本仓库是 gorm),编排层需要重写。

已核对的关键接口结论(来自对方文档,非官方文档)

  • 认证靠 Cookie,登录响应里的 JWT 从不参与后续请求。
  • 统一信封:成功只看 status === true,业务失败也返回 HTTP 200;data 可能是裸整数。
  • /am/stock/list 响应里的 total 是当前页条数,不是筛选范围总数。
  • /am/stock/detail/listByStock 单批最多 100 个 id,超出由调用方分批。

决策:登录验证码使用线上 OCR 服务

用户已明确:SYB 登录验证码必须走线上 OCR 服务。原永久规则「不使用 OCR/VLM」是针对 Android Agent 端采集写的,本工单需要按端重新划定边界:

  • Android Agent 端:维持禁止 OCR/VLM 不变。
  • 服务端 SYB 登录:允许调用 OCR 服务识别验证码。

需要同步修改 AGENTS.md 与 docs/03-business-rules-and-glossary.md(Wiki 优先)。

待确认:验证码图片会被发送到外部 OCR 服务,这一点要在文档中显式记录。

交付拆分

Stage 1(不需要凭据,可独立验证)

  • 移植 syb/client.go 至 server/app/goauto/sybclient/:登录、LoginWithOCR、会话保活 CheckSession、ListPage / ListTotal / ListByOrderNumber、DetailListByStock。
  • 移植 syb/ocr.go 验证码识别客户端。
  • 会话缓存改为 gorm 模型(对方是 syb_session 表)。
  • 配置项接入 config.yaml:base_url / username / password / page_size / max_matches / ocr_url。凭据只进 config.yaml(已 gitignore),config.example.yaml 只放占位符。
  • 移植 31 个 httptest 用例,全部不打真实网络。

Stage 2(需要真实凭据)

  • 同步编排:日期范围、分页、上限、断点、同步运行记录。
  • 触发端点 POST /api/admin/v1/syb-products/import,对接已有的 sybimport.ApplyDetail。
  • SYB 商品页「从 SYB 导入」入口,移除现有的缺口提示 alert。
  • 真实网络验证:必须真打一次顺云宝接口,确认字段未变、分页正确、错误处理充分。此项不能只靠单元测试。

门禁

  • Stage 1 完成后停下,等待用户验收再进 Stage 2。
  • Stage 2 的真机/真实网络验证由用户执行;未执行前不得声称通过。

安全约束

  • 顺云宝账号密码、Cookie、会话不得写入代码、日志、工单和文档。
  • 抓包样本(HAR)不得含真实凭据。
## 背景 模板项目 `cmautobuy/admin` 已有一套经抓包还原、带 31 个 httptest 用例的顺云宝 ERP 客户端(`syb/client.go` 854 行、`syb/ocr.go`、`docs/admin/08-顺运宝接口.md`)。#41 当初留下的「没有导入端点」缺口,可以靠移植这套代码补上,不必从零实现。 来源代码与本仓库同属一个负责人,可直接复用;但两边数据层不同(对方是 `database/sql` 手写 SQL,本仓库是 gorm),编排层需要重写。 ## 已核对的关键接口结论(来自对方文档,非官方文档) - 认证靠 **Cookie**,登录响应里的 JWT 从不参与后续请求。 - 统一信封:成功只看 `status === true`,业务失败也返回 HTTP 200;`data` 可能是裸整数。 - `/am/stock/list` 响应里的 `total` 是**当前页条数**,不是筛选范围总数。 - `/am/stock/detail/listByStock` 单批最多 100 个 id,超出由调用方分批。 ## 决策:登录验证码使用线上 OCR 服务 用户已明确:SYB 登录验证码必须走线上 OCR 服务。原永久规则「不使用 OCR/VLM」是针对 **Android Agent 端采集**写的,本工单需要按端重新划定边界: - Android Agent 端:维持禁止 OCR/VLM 不变。 - 服务端 SYB 登录:允许调用 OCR 服务识别验证码。 需要同步修改 `AGENTS.md` 与 `docs/03-business-rules-and-glossary.md`(Wiki 优先)。 **待确认**:验证码图片会被发送到外部 OCR 服务,这一点要在文档中显式记录。 ## 交付拆分 ### Stage 1(不需要凭据,可独立验证) - 移植 `syb/client.go` 至 `server/app/goauto/sybclient/`:登录、`LoginWithOCR`、会话保活 `CheckSession`、`ListPage` / `ListTotal` / `ListByOrderNumber`、`DetailListByStock`。 - 移植 `syb/ocr.go` 验证码识别客户端。 - 会话缓存改为 gorm 模型(对方是 `syb_session` 表)。 - 配置项接入 `config.yaml`:`base_url` / `username` / `password` / `page_size` / `max_matches` / `ocr_url`。凭据只进 `config.yaml`(已 gitignore),`config.example.yaml` 只放占位符。 - 移植 31 个 httptest 用例,全部不打真实网络。 ### Stage 2(需要真实凭据) - 同步编排:日期范围、分页、上限、断点、同步运行记录。 - 触发端点 `POST /api/admin/v1/syb-products/import`,对接已有的 `sybimport.ApplyDetail`。 - SYB 商品页「从 SYB 导入」入口,移除现有的缺口提示 alert。 - **真实网络验证**:必须真打一次顺云宝接口,确认字段未变、分页正确、错误处理充分。此项不能只靠单元测试。 ## 门禁 - Stage 1 完成后停下,等待用户验收再进 Stage 2。 - Stage 2 的真机/真实网络验证由用户执行;未执行前不得声称通过。 ## 安全约束 - 顺云宝账号密码、Cookie、会话不得写入代码、日志、工单和文档。 - 抓包样本(HAR)不得含真实凭据。
Author
Owner

Stage 1 完成,等待验收

提交 a31c477。

做了什么

内容 位置
客户端移植(登录、OCR、会话保活、列表、明细) server/app/goauto/sybclient/
会话持久化(gorm) sybclient/session.go + models.SYBSession
配置接入 config/extend.go、config/settings.yml
接口契约文档 Wiki SYB-ERP-Interface-Contract → docs/12-syb-erp-interface.md
规则按端重新划定 AGENTS.md、Business-Rules-and-Glossary、Architecture-and-Code-Map(提交 b4861f5)

上游的 client.go / columns.go / ocr.go 只依赖标准库,不碰数据库也不认识 *gin.Context,所以移植几乎是原样搬:只改了包名和文档引用。上游 31 个 httptest 用例一并搬入,第一次运行即全部通过,没有改动任何断言。

凭据处理与上游不同

上游把账号密码放 config.yaml(gitignore)。GoAuto 已有 ApplyEnvironment() 的现成模式,改成:

  • 非机密项(baseurl / pagesize / maxmatches / ocrurl / ocrmaxattempts)进 settings.yml
  • 账号密码只走 GOAUTO_SYB_USERNAME / GOAUTO_SYB_PASSWORD

顺带避开了上游踩过的 YAML 坑(纯数字密码不加引号会被解析成整数、前导零丢失)。密码读取时不做 trim,首尾空白可能是密码的一部分。

验证结果

  • go build ./... 通过
  • go vet ./app/goauto/... ./config/... 通过
  • go test ./... 全部通过;sybclient 49 个用例,config 新增 6 个
  • 全部测试用 httptest 假服务端,没有打过顺云宝真实接口

两个守卫测试做了变异验证,确认不是空转的断言:

  • 把 SYBSession.CookiesJSON 的 json:"-" 改回去 → TestCookiesJSONIsNotSerialisedOverHTTP 失败
  • 把 settings.yml 的 pagesize 改成 page_size → TestSYBSettingsInRepoBindToExtendStruct 失败(绑定静默失效正是这里最容易出的错)

需要用户验证

  • MySQL 迁移:syb_session 表能建出来(本机 MySQL 在 Windows 侧,我连不上)
  • 服务端能正常启动,settings.yml 新增的 extend.syb 不影响现有配置加载

明确没做

  • 没有任何写接口。DeleteInnerCode / CreateInnerCodeDetail / UpdateDetailCode 虽然随客户端一起搬了进来(它们是上游客户端的一部分),但本工单不调用它们;对应的是 #37。
  • 没有导入端点、没有同步编排、没有页面入口。SYB 商品页的缺口提示 alert 保持原样。
  • 没有做过任何真实网络验证。这是 Stage 2 的核心内容,且必须有真实凭据才能做。

Stage 2 待办(需要凭据)

  • 同步编排:日期范围、分页、上限、断点、同步运行记录
  • POST /api/admin/v1/syb-products/import,对接已有的 sybimport.ApplyDetail
  • SYB 商品页「从 SYB 导入」入口,移除缺口提示
  • 真打一次顺云宝接口,确认字段未变、分页正确、错误处理充分
## Stage 1 完成,等待验收 提交 `a31c477`。 ### 做了什么 | 内容 | 位置 | |---|---| | 客户端移植(登录、OCR、会话保活、列表、明细) | `server/app/goauto/sybclient/` | | 会话持久化(gorm) | `sybclient/session.go` + `models.SYBSession` | | 配置接入 | `config/extend.go`、`config/settings.yml` | | 接口契约文档 | Wiki `SYB-ERP-Interface-Contract` → `docs/12-syb-erp-interface.md` | | 规则按端重新划定 | `AGENTS.md`、`Business-Rules-and-Glossary`、`Architecture-and-Code-Map`(提交 `b4861f5`) | 上游的 `client.go` / `columns.go` / `ocr.go` 只依赖标准库,不碰数据库也不认识 `*gin.Context`,所以移植几乎是原样搬:只改了包名和文档引用。上游 31 个 httptest 用例一并搬入,**第一次运行即全部通过,没有改动任何断言**。 ### 凭据处理与上游不同 上游把账号密码放 `config.yaml`(gitignore)。GoAuto 已有 `ApplyEnvironment()` 的现成模式,改成: - 非机密项(`baseurl` / `pagesize` / `maxmatches` / `ocrurl` / `ocrmaxattempts`)进 `settings.yml` - 账号密码只走 `GOAUTO_SYB_USERNAME` / `GOAUTO_SYB_PASSWORD` 顺带避开了上游踩过的 YAML 坑(纯数字密码不加引号会被解析成整数、前导零丢失)。密码读取时**不做 trim**,首尾空白可能是密码的一部分。 ### 验证结果 - `go build ./...` 通过 - `go vet ./app/goauto/... ./config/...` 通过 - `go test ./...` 全部通过;`sybclient` 49 个用例,`config` 新增 6 个 - 全部测试用 `httptest` 假服务端,**没有打过顺云宝真实接口** 两个守卫测试做了变异验证,确认不是空转的断言: - 把 `SYBSession.CookiesJSON` 的 `json:"-"` 改回去 → `TestCookiesJSONIsNotSerialisedOverHTTP` 失败 - 把 `settings.yml` 的 `pagesize` 改成 `page_size` → `TestSYBSettingsInRepoBindToExtendStruct` 失败(绑定静默失效正是这里最容易出的错) ### 需要用户验证 - [ ] MySQL 迁移:`syb_session` 表能建出来(本机 MySQL 在 Windows 侧,我连不上) - [ ] 服务端能正常启动,`settings.yml` 新增的 `extend.syb` 不影响现有配置加载 ### 明确没做 - **没有任何写接口**。`DeleteInnerCode` / `CreateInnerCodeDetail` / `UpdateDetailCode` 虽然随客户端一起搬了进来(它们是上游客户端的一部分),但本工单不调用它们;对应的是 #37。 - **没有导入端点、没有同步编排、没有页面入口**。SYB 商品页的缺口提示 alert 保持原样。 - **没有做过任何真实网络验证**。这是 Stage 2 的核心内容,且必须有真实凭据才能做。 ### Stage 2 待办(需要凭据) - 同步编排:日期范围、分页、上限、断点、同步运行记录 - `POST /api/admin/v1/syb-products/import`,对接已有的 `sybimport.ApplyDetail` - SYB 商品页「从 SYB 导入」入口,移除缺口提示 - 真打一次顺云宝接口,确认字段未变、分页正确、错误处理充分
Author
Owner

真实网络验证通过(提交 4886e0d)

用户提供凭据后,已对顺云宝真站跑通验证。测试文件带 syblive 构建标签,默认不参与 go test ./...;凭据只走环境变量,文件里不含凭据;只调读接口,输出只有结构和计数,不打印货运单号、标题和金额。

--- PASS: TestLiveLoginAndSession   (6.33s)
--- PASS: TestLiveListPagingContract (5.12s)
--- PASS: TestLiveDetailShape        (5.17s)

用真实数据确认的结论

文档结论 实测
§3.1 认证靠 Cookie 不靠 JWT 登录后 Cookie Jar 213 字节,非空
§3.3 会话正好 24 小时 登录返回 ExpiresAt = 24h0m0s
§4.3 listTotal 是筛选总数 最近 7 天 = 7404
§4.3 list.total 是当前页条数 第一页 20 行,total = 20
§5.1 productPrice 单位是元 最大单价 403.00,量级合理
§6.1 一单多商品是嵌套 20 张单 28 件商品,5 张是一单多件
OCR 自动识别可用于无人值守 一次通过,未降级到手工

顺带关闭一个 #41 的已知缺口

details[].id 的唯一性此前记录为「未验证」,而 #41 拿它当业务唯一键的一半。本批 28 个 id 互不重复。

[待定] 这只证明了同一批内唯一,没有证明全局唯一。#41 的唯一键是 (order_code, detail_id) 复合键,不依赖 detail_id 单独全局唯一,所以当前设计是安全的。

一个影响 Stage 2 设计的实测数字

最近 7 天就有 7404 张货运单。 配置里的 max_matches: 10000 大约 9~10 天就会触顶。Stage 2 的同步编排必须:

  • 默认同步窗口取小(几天,不是几个月)
  • 触顶时明确失败并告知实际数量,不能静默截断
  • 分页循环不能用 list.total 当总数——按实测它恒等于当前页行数,循环会停在第一页

修正一处上游客户端的调用约定

CheckSession(ctx, userID, username) 必须传登录返回的真实 user id。传 0 时顺云宝返回 msg=品名不能为空 code=-100。

值得记一笔的是:客户端没有把这个业务错误误判成「未登录」,正是 §3.5 要求的行为——只有明确的未登录信号才包成 ErrSessionInvalid,其余一律是别的错误。这条防御在真实数据上生效了。

MySQL 侧的情况

用户提供的 MySQL(185.216.248.75:3307,TLS verify_ca)只读探测结果:

autobuy
autobuy_contract_test
information_schema
performance_schema

没有 goauto 库——这台是 cmautobuy 的库,不是 GoAuto 的。GoAuto 配置指向 127.0.0.1:3307/goauto,是另一台(Windows 本机)。

所以 syb_session 表的迁移验证仍未完成,等用户确认在哪个库上做。未经确认不会在对方生产库上建 GoAuto 的表。

## 真实网络验证通过(提交 `4886e0d`) 用户提供凭据后,已对顺云宝真站跑通验证。测试文件带 `syblive` 构建标签,默认不参与 `go test ./...`;凭据只走环境变量,文件里不含凭据;**只调读接口**,输出只有结构和计数,不打印货运单号、标题和金额。 ``` --- PASS: TestLiveLoginAndSession (6.33s) --- PASS: TestLiveListPagingContract (5.12s) --- PASS: TestLiveDetailShape (5.17s) ``` ### 用真实数据确认的结论 | 文档结论 | 实测 | |---|---| | §3.1 认证靠 Cookie 不靠 JWT | 登录后 Cookie Jar 213 字节,非空 | | §3.3 会话正好 24 小时 | 登录返回 `ExpiresAt` = 24h0m0s | | §4.3 `listTotal` 是筛选总数 | 最近 7 天 = **7404** | | §4.3 `list.total` 是当前页条数 | 第一页 20 行,`total` = 20 | | §5.1 `productPrice` 单位是元 | 最大单价 403.00,量级合理 | | §6.1 一单多商品是嵌套 | 20 张单 28 件商品,5 张是一单多件 | | OCR 自动识别可用于无人值守 | 一次通过,未降级到手工 | ### 顺带关闭一个 #41 的已知缺口 `details[].id` 的唯一性此前记录为「未验证」,而 #41 拿它当业务唯一键的一半。本批 28 个 id 互不重复。 `[待定]` 这只证明了**同一批内**唯一,没有证明全局唯一。#41 的唯一键是 `(order_code, detail_id)` 复合键,不依赖 `detail_id` 单独全局唯一,所以当前设计是安全的。 ### 一个影响 Stage 2 设计的实测数字 **最近 7 天就有 7404 张货运单。** 配置里的 `max_matches: 10000` 大约 9~10 天就会触顶。Stage 2 的同步编排必须: - 默认同步窗口取小(几天,不是几个月) - 触顶时明确失败并告知实际数量,不能静默截断 - 分页循环**不能**用 `list.total` 当总数——按实测它恒等于当前页行数,循环会停在第一页 ### 修正一处上游客户端的调用约定 `CheckSession(ctx, userID, username)` 必须传登录返回的真实 user id。传 0 时顺云宝返回 `msg=品名不能为空 code=-100`。 值得记一笔的是:客户端**没有**把这个业务错误误判成「未登录」,正是 §3.5 要求的行为——只有明确的未登录信号才包成 `ErrSessionInvalid`,其余一律是别的错误。这条防御在真实数据上生效了。 ### MySQL 侧的情况 用户提供的 MySQL(`185.216.248.75:3307`,TLS `verify_ca`)只读探测结果: ``` autobuy autobuy_contract_test information_schema performance_schema ``` **没有 `goauto` 库**——这台是 cmautobuy 的库,不是 GoAuto 的。GoAuto 配置指向 `127.0.0.1:3307/goauto`,是另一台(Windows 本机)。 所以 `syb_session` 表的迁移验证仍未完成,等用户确认在哪个库上做。**未经确认不会在对方生产库上建 GoAuto 的表。**
Author
Owner

Stage 2 完成,等待验收(提交 7edb4e7)

从 SYB 接口到入库的整条路径打通了。

内容 位置
同步编排 sybimport/sync.go 的 Sync
登录 / 会话复用 sybimport/sync.go 的 Connect
导入端点 POST /api/admin/v1/syb-products/import sybimport/import_handler.go
页面「从 SYB 导入」入口 web/src/views/goauto/syb-products/index.vue

几个基于实测数据的设计决定

分页由 listTotal 驱动,不用 list 响应里的 total。 实测确认后者是当前页行数,用它当总数循环会停在第一页。这条写进了代码注释和测试。

完整性证明不了就停。 缺行的分页、分页期间总数漂移、明细响应少返回货运单,三种情况都立即停止并报错。已写入的数据保留——ApplyDetail 对 (order_code, detail_id) 幂等,重跑覆盖。不能把跑了一半的同步报成成功,否则漏掉的单永远补不回来。

会话缓存必须连 user id 一起存。 这是实现过程中发现的真问题:CheckSession 调 /am/user/get?id=,id 不对时顺云宝返回的是业务错误(msg=品名不能为空)而不是「未登录」。只存 Cookie 的话恢复出来的会话永远校验不过,缓存等于白做,每次都要重新登录。已给 models.SYBSession 加 UserID 列。

导入端点单飞(single-flight)且脱离请求上下文。 两个并发导入会在同一批行上互相覆盖,数据没事但两份报告都会少算;操作员关掉标签页也不该让跑了一半的范围中断。

页面在范围过大时警告。 实测一周 7000+ 张,默认范围给 3 天。

验证结果

  • go build ./...、go vet、go test ./... 全部通过
  • 新增 sybimport 9 个同步用例 + 1 个路由注册用例,全部用 httptest 假服务端
  • 前端 eslint 干净,SFC 编译通过
  • 总数漂移检测做了变异验证:注释掉该检查后 TestSyncStopsWhenTotalDriftsDuringPaging 失败

一个值得记的插曲:写假服务端时我把请求体字段写成了 pageSize,真实字段是 length,测试立刻以 panic 暴露了这个错。

需要用户验证

  • MySQL 迁移:syb_session 表和它的 user_id 列能建出来(本机 MySQL 在 Windows 侧)
  • vite build 通过(WSL 缺 rolldown 原生依赖,我跑不了)
  • 浏览器实走:SYB 商品页点「从 SYB 导入」→ 选 2~3 天 → 确认导入结果和列表刷新
  • 启动前设置 GOAUTO_SYB_USERNAME / GOAUTO_SYB_PASSWORD 环境变量

明确没做

  • 没有跑过端到端的真实导入(需要 MySQL 落库,本机 MySQL 我连不上)。真实网络验证只覆盖到客户端读取那一层,见上一条评论。
  • 没有定时/自动增量同步,只有手动触发。上游的 last_synced_at 覆盖游标机制没有移植——手动触发场景用不到,等真需要无人值守时再单独做。
  • 没有同步运行历史记录表。
## Stage 2 完成,等待验收(提交 `7edb4e7`) 从 SYB 接口到入库的整条路径打通了。 | 内容 | 位置 | |---|---| | 同步编排 | `sybimport/sync.go` 的 `Sync` | | 登录 / 会话复用 | `sybimport/sync.go` 的 `Connect` | | 导入端点 `POST /api/admin/v1/syb-products/import` | `sybimport/import_handler.go` | | 页面「从 SYB 导入」入口 | `web/src/views/goauto/syb-products/index.vue` | ### 几个基于实测数据的设计决定 **分页由 `listTotal` 驱动,不用 `list` 响应里的 `total`。** 实测确认后者是当前页行数,用它当总数循环会停在第一页。这条写进了代码注释和测试。 **完整性证明不了就停。** 缺行的分页、分页期间总数漂移、明细响应少返回货运单,三种情况都立即停止并报错。已写入的数据保留——`ApplyDetail` 对 `(order_code, detail_id)` 幂等,重跑覆盖。**不能把跑了一半的同步报成成功**,否则漏掉的单永远补不回来。 **会话缓存必须连 user id 一起存。** 这是实现过程中发现的真问题:`CheckSession` 调 `/am/user/get?id=`,id 不对时顺云宝返回的是业务错误(`msg=品名不能为空`)而不是「未登录」。只存 Cookie 的话恢复出来的会话永远校验不过,缓存等于白做,每次都要重新登录。已给 `models.SYBSession` 加 `UserID` 列。 **导入端点单飞(single-flight)且脱离请求上下文。** 两个并发导入会在同一批行上互相覆盖,数据没事但两份报告都会少算;操作员关掉标签页也不该让跑了一半的范围中断。 **页面在范围过大时警告。** 实测一周 7000+ 张,默认范围给 3 天。 ### 验证结果 - `go build ./...`、`go vet`、`go test ./...` 全部通过 - 新增 `sybimport` 9 个同步用例 + 1 个路由注册用例,全部用 httptest 假服务端 - 前端 `eslint` 干净,SFC 编译通过 - 总数漂移检测做了变异验证:注释掉该检查后 `TestSyncStopsWhenTotalDriftsDuringPaging` 失败 一个值得记的插曲:写假服务端时我把请求体字段写成了 `pageSize`,真实字段是 `length`,测试立刻以 panic 暴露了这个错。 ### 需要用户验证 - [ ] MySQL 迁移:`syb_session` 表和它的 `user_id` 列能建出来(本机 MySQL 在 Windows 侧) - [ ] `vite build` 通过(WSL 缺 rolldown 原生依赖,我跑不了) - [ ] 浏览器实走:SYB 商品页点「从 SYB 导入」→ 选 2~3 天 → 确认导入结果和列表刷新 - [ ] 启动前设置 `GOAUTO_SYB_USERNAME` / `GOAUTO_SYB_PASSWORD` 环境变量 ### 明确没做 - **没有跑过端到端的真实导入**(需要 MySQL 落库,本机 MySQL 我连不上)。真实网络验证只覆盖到客户端读取那一层,见上一条评论。 - 没有定时/自动增量同步,只有手动触发。上游的 `last_synced_at` 覆盖游标机制没有移植——手动触发场景用不到,等真需要无人值守时再单独做。 - 没有同步运行历史记录表。
Author
Owner

端到端真实导入跑通了

提交 4f7f3a6。用真实凭据对顺云宝真站跑了一次完整导入(落库到 SQLite,不是 MySQL,原因见下)。

① 登录成功,会话已写入
② 首次导入 2026-08-19:货运单 986 张,明细 1524 条,新增 1524,覆盖 0
③ 落库:syb_product 1524 行,shopee_product 1056 行
   解析 success 1395 / uncertain 125 / failed 4
④ 会话复用成功,未重新登录(第二次 Connect 走缓存,没有再过验证码)

解析质量:1485/1524(97.4%)拿到了完整的颜色+尺码。

状态 数量 原因
uncertain 90 颜色部分含备注文本或多个分隔符
uncertain 34 无分隔符,仅识别出单一规格值
uncertain 1 按逗号拆分后有一侧为空
failed 4 productSpec 为空

4 条 failed 都是源数据本身为空——按规则标记失败而不是伪造缺失字段,符合预期。125 条 uncertain 进入人工复核路径。

顺带发现并修掉一个真缺陷

models.SYBSession 漏了 TableName(),gorm 悄悄建成了 syb_sessions(复数),而仓库里其它模型全部显式声明单数表名。

这个缺陷的麻烦之处在于迁移照样成功——不报错,直到有人按约定的名字查询才会发现。是这次真实导入把它暴露出来的(syb_session 0 行)。发现得算及时:MySQL 上还没有跑过迁移,不用处理已经建错的表。

已修,并且把 Migrate 的模型清单提取成 MigratedModels(),让新增的约定测试覆盖的正是 Migrate 真正创建的那批模型,而不是一份会漂移的手抄清单。变异验证:去掉 TableName() 后测试失败。

MySQL 仍然没验证,原因是具体的

WSL 里能连到 Windows 侧的 3307 端口,但被 MySQL 拒绝:

Error 1130: Host '192.168.181.191' is not allowed to connect to this MySQL server

root 账号只授权了本机。这不是代码问题。两个办法二选一:

  1. 在 Windows 侧直接跑迁移(启动一次服务端即可),我给验证清单;
  2. 给 WSL 网段授权(CREATE USER 'root'@'192.168.%' 之类)——这是改用户的数据库权限,未经明确要求我不会做。

结论:能用,但有一条没验证

采集→解析→落库→幂等这条链路已经在真实数据上验证过。唯一没验证的是 MySQL 建表——SQLite 和 MySQL 的 DDL 差异在这个项目里出过事(之前 image_url 的 TEXT DEFAULT 问题),所以这一条必须真跑过才能算数。

## 端到端真实导入跑通了 提交 `4f7f3a6`。用真实凭据对顺云宝真站跑了一次完整导入(落库到 SQLite,不是 MySQL,原因见下)。 ``` ① 登录成功,会话已写入 ② 首次导入 2026-08-19:货运单 986 张,明细 1524 条,新增 1524,覆盖 0 ③ 落库:syb_product 1524 行,shopee_product 1056 行 解析 success 1395 / uncertain 125 / failed 4 ④ 会话复用成功,未重新登录(第二次 Connect 走缓存,没有再过验证码) ``` **解析质量**:1485/1524(97.4%)拿到了完整的颜色+尺码。 | 状态 | 数量 | 原因 | |---|---|---| | uncertain | 90 | 颜色部分含备注文本或多个分隔符 | | uncertain | 34 | 无分隔符,仅识别出单一规格值 | | uncertain | 1 | 按逗号拆分后有一侧为空 | | failed | 4 | `productSpec` 为空 | 4 条 failed 都是源数据本身为空——按规则标记失败而不是伪造缺失字段,符合预期。125 条 uncertain 进入人工复核路径。 ## 顺带发现并修掉一个真缺陷 `models.SYBSession` 漏了 `TableName()`,gorm 悄悄建成了 **`syb_sessions`**(复数),而仓库里其它模型全部显式声明单数表名。 这个缺陷的麻烦之处在于**迁移照样成功**——不报错,直到有人按约定的名字查询才会发现。是这次真实导入把它暴露出来的(`syb_session 0 行`)。**发现得算及时:MySQL 上还没有跑过迁移,不用处理已经建错的表。** 已修,并且把 `Migrate` 的模型清单提取成 `MigratedModels()`,让新增的约定测试覆盖的正是 `Migrate` 真正创建的那批模型,而不是一份会漂移的手抄清单。变异验证:去掉 `TableName()` 后测试失败。 ## MySQL 仍然没验证,原因是具体的 WSL 里能连到 Windows 侧的 `3307` 端口,但被 MySQL 拒绝: ``` Error 1130: Host '192.168.181.191' is not allowed to connect to this MySQL server ``` `root` 账号只授权了本机。这不是代码问题。两个办法二选一: 1. 在 Windows 侧直接跑迁移(启动一次服务端即可),我给验证清单; 2. 给 WSL 网段授权(`CREATE USER 'root'@'192.168.%'` 之类)——这是改用户的数据库权限,**未经明确要求我不会做**。 ## 结论:能用,但有一条没验证 采集→解析→落库→幂等这条链路已经在真实数据上验证过。**唯一没验证的是 MySQL 建表**——SQLite 和 MySQL 的 DDL 差异在这个项目里出过事(之前 `image_url` 的 `TEXT DEFAULT` 问题),所以这一条必须真跑过才能算数。
Author
Owner

用户于 2026-08-28 明确确认本工单验收通过。按项目流程记录验收结论并关闭工单;本次仅更新工单状态,无新增长期文档事实,不重复同步 Wiki。

用户于 2026-08-28 明确确认本工单验收通过。按项目流程记录验收结论并关闭工单;本次仅更新工单状态,无新增长期文档事实,不重复同步 Wiki。
ila closed this issue 2026-08-28 15:07:35 +08:00
Sign in to join this conversation.
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: OPC/goauto#48