档口入库码:对齐 cmautobuy 的档口货号匹配规则(#273/#289),为替代 cmautobuy 做准备 #344

Closed
opened 2026-09-28 09:55:15 +08:00 by ila · 5 comments
Owner

来源与原始需求摘要

  • 来源:用户 2026-09-28 要求对比 D:\chengma\cmautobuy 与本项目的档口入库码模块。
  • 用户原话:「准备用goauto的档口入库码代替cmautobuy的档口入库码,建单更新goauto的档口入库码」。
  • 目标:goauto 档口入库码模块在匹配正确性上与 cmautobuy 当前实现对齐,达到可替代 cmautobuy 的程度。

前置依赖

  • 无代码依赖;可与 #338~#340 并行(只改 server/app/goauto/sybinnercode/,不碰 SYB 商品页、采购与退货代码)。基于 main 开分支。

当前事实(2026-09-28 核对)

  • cmautobuy:admin/service/inner_code_*.go 等约 5200 行,自 #231 起持续修复至 #318(HEAD 398b1da)。
  • goauto:server/app/goauto/sybinnercode/ 约 2800 行,#119~#121 一次实现(5c6655f),此后仅 #139、#237 顺带修改,匹配逻辑未演进。
  • goauto 线上使用情况:syb_inner_code_record 仅 2026-08-22 的 33 条试用记录(already_filled 31、ready 1、skipped 1),syb_inner_code_apply_batch 为 0——未投入生产使用。

已对齐(无需改动)

能力 cmautobuy goauto
业务日期只取 Excel「生成日期」,短日期用文件名补年份 #237 parser.go parseBusinessDate
同营业日期重导:无匹配/执行证据才整批替换 #304/#306 service.go Import
物理删除,排队/回写中/需复核不可删 #318 service.go Delete
按货运单号直查 SYB #245 planRecord → ListByOrderNumber
规格归一化 NormalizeInnerCodeSpecKey NormalizeSpecKey(逻辑一致)
原始 SKU 优先、与档口货号冲突拒绝 #259 matchEvidence
后台串行批量回写、检查点、只读复核、中断恢复、勾选部分重新匹配、分页 20/50/100/200 #234/#235/#244/#246 已有

不一致(本单范围)

  1. 档口货号匹配(高风险,#273):goauto match.go strictStall 只做子串包含:strings.Contains(blob, stall) 或 同时包含档口名与货号(strings.Cut(stall, "#") 取第一个 #)。已知误配:
    • 规格中的重量数字被当作货号(货号 50 命中 50公斤);
    • 短数字货号命中长编号内部数字(PDD256437);
    • 前导零货号 067 与 67 不能对齐;
    • 档口名本身含 # 时拆分错误。
      后果:入库码可能回写到错误的 SYB 商品明细。
      cmautobuy 现行规则(inner_code_match.go innerCodeStallMatches 及相关函数):按最后一个 # 拆分;货号只与「连续字母/数字」令牌精确比较(标点、【】、空格为边界);纯数字货号要求候选同时包含档口名才允许前导零等价;规格仅允许以货号开头(首个空格/逗号前)。
  2. 多件逐件绑定保留原绑定(中风险,#289,待测试确认):cmautobuy planExistingMatchedInnerCodeItems 在「多个单件码 ↔ 多条数量为 1 的现成明细」时,已存在目标码的明细优先保留原绑定、只补缺失;goauto planRecord 按明细 ID 排序顺序分配,可能把已写好的码换到另一条明细上(多余写入或覆盖)。需先用测试复现再决定是否修改。

方案

  • 移植 cmautobuy 的档口货号匹配规则到 sybinnercode/match.go(拆分、令牌化、数字货号前导零与档口名约束、规格前缀规则),strictStall 与无 SKU 回退路径统一使用新规则。
  • 移植 cmautobuy 对应测试用例(inner_code_match_test.go 中 #273/#259 相关场景)为 goauto 回归测试。
  • 为 #289 场景写测试:已有部分明细带目标码、顺序与 ID 顺序不同;若 goauto 会改绑,则按 cmautobuy 规则修正(保留原绑定,只补缺失);若不会,记录证据不改代码。
  • 不改导入、删除、回写执行、页面与接口。

非目标

  • 不迁移 cmautobuy 的历史入库码数据(如需迁移另建单)。
  • 不做 cmautobuy 下线操作;不改 cmautobuy 代码。
  • 不改页面与 API。

验收

  1. 移植的 #273 测试场景全部通过:重量数字不当货号;长编号内部数字不命中;067/67 在同档口名时等价、不同档口名时不等价;档口名含 # 正确拆分;规格以货号开头可命中。
  2. #289 场景有测试并通过(保留原绑定、只补缺失,或给出 goauto 已正确的证据)。
  3. sybinnercode 既有测试全部通过;gofmt / build / vet 通过。
  4. 使用 2026-08-22 线上试用样本(或本地数据)对新旧规则各跑一次只读匹配,比较结果差异并逐条说明(只读,不回写)。

风险

  • 匹配规则变严后,部分原本「命中」的记录会变为「跳过/需人工」——这是预期(宁可跳过不可错写),需在工单记录差异。
  • 回写 SYB 属外部写操作;本单不执行任何真实回写。

设计证据

  • 纯后端匹配规则修正,无 UI 变化,不需原型。

文档影响

  • 若 Wiki 业务规则页记录了档口货号匹配规则,需更新为新规则;否则说明无长期文档影响。实施时核对。

替代 cmautobuy 前的后续事项(不在本单)

  • 历史数据是否迁移;
  • 切换期间两个系统不能同时回写同一营业日期(避免重复写入 SYB);
  • cmautobuy 该模块的停用时间点。
## 来源与原始需求摘要 - 来源:用户 2026-09-28 要求对比 `D:\chengma\cmautobuy` 与本项目的档口入库码模块。 - 用户原话:「准备用goauto的档口入库码代替cmautobuy的档口入库码,建单更新goauto的档口入库码」。 - 目标:goauto 档口入库码模块在**匹配正确性**上与 cmautobuy 当前实现对齐,达到可替代 cmautobuy 的程度。 ## 前置依赖 - 无代码依赖;可与 #338~#340 并行(只改 `server/app/goauto/sybinnercode/`,不碰 SYB 商品页、采购与退货代码)。基于 main 开分支。 ## 当前事实(2026-09-28 核对) - **cmautobuy**:`admin/service/inner_code_*.go` 等约 5200 行,自 #231 起持续修复至 #318(HEAD `398b1da`)。 - **goauto**:`server/app/goauto/sybinnercode/` 约 2800 行,#119~#121 一次实现(`5c6655f`),此后仅 #139、#237 顺带修改,匹配逻辑未演进。 - **goauto 线上使用情况**:`syb_inner_code_record` 仅 2026-08-22 的 33 条试用记录(already_filled 31、ready 1、skipped 1),`syb_inner_code_apply_batch` 为 0——**未投入生产使用**。 ### 已对齐(无需改动) | 能力 | cmautobuy | goauto | |---|---|---| | 业务日期只取 Excel「生成日期」,短日期用文件名补年份 | #237 | `parser.go parseBusinessDate` | | 同营业日期重导:无匹配/执行证据才整批替换 | #304/#306 | `service.go Import` | | 物理删除,排队/回写中/需复核不可删 | #318 | `service.go Delete` | | 按货运单号直查 SYB | #245 | `planRecord → ListByOrderNumber` | | 规格归一化 | `NormalizeInnerCodeSpecKey` | `NormalizeSpecKey`(逻辑一致) | | 原始 SKU 优先、与档口货号冲突拒绝 | #259 | `matchEvidence` | | 后台串行批量回写、检查点、只读复核、中断恢复、勾选部分重新匹配、分页 20/50/100/200 | #234/#235/#244/#246 | 已有 | ### 不一致(本单范围) 1. **档口货号匹配(高风险,#273)**:goauto `match.go strictStall` 只做子串包含:`strings.Contains(blob, stall)` 或 同时包含档口名与货号(`strings.Cut(stall, "#")` 取**第一个** #)。已知误配: - 规格中的重量数字被当作货号(货号 `50` 命中 `50公斤`); - 短数字货号命中长编号内部数字(`PDD256437`); - 前导零货号 `067` 与 `67` 不能对齐; - 档口名本身含 `#` 时拆分错误。 后果:入库码可能**回写到错误的 SYB 商品明细**。 cmautobuy 现行规则(`inner_code_match.go innerCodeStallMatches` 及相关函数):按最后一个 `#` 拆分;货号只与「连续字母/数字」令牌精确比较(标点、【】、空格为边界);纯数字货号要求候选同时包含档口名才允许前导零等价;规格仅允许以货号开头(首个空格/逗号前)。 2. **多件逐件绑定保留原绑定(中风险,#289,待测试确认)**:cmautobuy `planExistingMatchedInnerCodeItems` 在「多个单件码 ↔ 多条数量为 1 的现成明细」时,已存在目标码的明细优先保留原绑定、只补缺失;goauto `planRecord` 按明细 ID 排序顺序分配,可能把已写好的码换到另一条明细上(多余写入或覆盖)。需先用测试复现再决定是否修改。 ## 方案 - 移植 cmautobuy 的档口货号匹配规则到 `sybinnercode/match.go`(拆分、令牌化、数字货号前导零与档口名约束、规格前缀规则),`strictStall` 与无 SKU 回退路径统一使用新规则。 - 移植 cmautobuy 对应测试用例(`inner_code_match_test.go` 中 #273/#259 相关场景)为 goauto 回归测试。 - 为 #289 场景写测试:已有部分明细带目标码、顺序与 ID 顺序不同;若 goauto 会改绑,则按 cmautobuy 规则修正(保留原绑定,只补缺失);若不会,记录证据不改代码。 - 不改导入、删除、回写执行、页面与接口。 ## 非目标 - 不迁移 cmautobuy 的历史入库码数据(如需迁移另建单)。 - 不做 cmautobuy 下线操作;不改 cmautobuy 代码。 - 不改页面与 API。 ## 验收 1. 移植的 #273 测试场景全部通过:重量数字不当货号;长编号内部数字不命中;`067`/`67` 在同档口名时等价、不同档口名时不等价;档口名含 `#` 正确拆分;规格以货号开头可命中。 2. #289 场景有测试并通过(保留原绑定、只补缺失,或给出 goauto 已正确的证据)。 3. `sybinnercode` 既有测试全部通过;gofmt / build / vet 通过。 4. 使用 2026-08-22 线上试用样本(或本地数据)对新旧规则各跑一次**只读**匹配,比较结果差异并逐条说明(只读,不回写)。 ## 风险 - 匹配规则变严后,部分原本「命中」的记录会变为「跳过/需人工」——这是预期(宁可跳过不可错写),需在工单记录差异。 - 回写 SYB 属外部写操作;本单不执行任何真实回写。 ## 设计证据 - 纯后端匹配规则修正,无 UI 变化,不需原型。 ## 文档影响 - 若 Wiki 业务规则页记录了档口货号匹配规则,需更新为新规则;否则说明无长期文档影响。实施时核对。 ## 替代 cmautobuy 前的后续事项(不在本单) - 历史数据是否迁移; - 切换期间两个系统不能同时回写同一营业日期(避免重复写入 SYB); - cmautobuy 该模块的停用时间点。
Author
Owner

基线核对(2026-09-28)

  • 对比基线:goauto origin/main = abdf907(含 Codex 已合并的 #338、#339、#341、#342、#343);cmautobuy 398b1da。重新 fetch 后 main 无更新。
  • sybinnercode 模块在 main 上的全部历史仍只有 5c6655f(#119~#121)、9d23b44(#139)、57b0f15(#237);#338~#343 均未改动该模块;其他远程分支也无未合并改动。
  • #343 新增统一 SYB 会话管理 sybclient.AcquireSession;sybimport.Connect 已改为调用它,本模块 runtime.go 经 Connect 取会话,因此回写已走统一会话刷新,无需在本单处理。
  • 结论:本单的差异分析与范围在最新 main 上仍然成立。实施时基于最新 main 开分支。
## 基线核对(2026-09-28) - 对比基线:goauto **`origin/main` = `abdf907`**(含 Codex 已合并的 #338、#339、#341、#342、#343);cmautobuy `398b1da`。重新 fetch 后 main 无更新。 - `sybinnercode` 模块在 main 上的全部历史仍只有 `5c6655f`(#119~#121)、`9d23b44`(#139)、`57b0f15`(#237);#338~#343 均未改动该模块;其他远程分支也无未合并改动。 - #343 新增统一 SYB 会话管理 `sybclient.AcquireSession`;`sybimport.Connect` 已改为调用它,本模块 `runtime.go` 经 `Connect` 取会话,因此回写已走统一会话刷新,无需在本单处理。 - 结论:本单的差异分析与范围在最新 main 上仍然成立。实施时**基于最新 main 开分支**。
Author
Owner

开始实施:按已确认方案移植 cmautobuy 档口货号匹配规则(#273/#259)到 sybinnercode/match.go,并针对 #289 多件逐件绑定场景编写回归测试与(如复现)最小修正。仅修改 server/app/goauto/sybinnercode/ 下匹配/规划逻辑与其测试,不改导入、删除、执行、接口与前端。

开始实施:按已确认方案移植 cmautobuy 档口货号匹配规则(#273/#259)到 `sybinnercode/match.go`,并针对 #289 多件逐件绑定场景编写回归测试与(如复现)最小修正。仅修改 `server/app/goauto/sybinnercode/` 下匹配/规划逻辑与其测试,不改导入、删除、执行、接口与前端。
Author
Owner

实施回写(2026-09-28)

分支:fix/344-inner-code-stall-match(基于 main abdf907),提交 e99bbc2、9dace6a,已推送。未合并。仅改 server/app/goauto/sybinnercode/match.go 与 match_test.go。

1. 档口货号匹配(#273/#259)

strictStall 的子串判断改为 stallMatches,与 cmautobuy innerCodeStallMatches 及辅助函数(splitStall、isNumericArticle、textHasNumericArticle、normalizeNumericArticle、textHasExactArticle、productSpecStartsWithArticle、articleTokens)逐行一致:最后一个 # 拆分;货号只与连续字母/数字令牌比较;纯数字货号须同时命中档口名才允许前导零等价;规格仅以货号开头命中;货号为空时按档口名包含。matchEvidence 流程(原始 SKU 优先 → 冲突拒绝 → 档口 → 无 SKU 兜底)不变。

2. 多件逐件绑定(#289)——问题复现并修正

回归测试 TestRunMatchJobPreservesExistingBindingWhenCandidateOrderDiffers 在旧逻辑上失败(按 ID 顺序会把已写好的码改绑到另一明细)、新逻辑通过。新增 assignExistingBoundItems:保留已带目标码的明细绑定,只把剩余码填入空白明细;并按 cmautobuy 补齐防护:明细 ID 无效/重复拒绝、候选规格原文/SKU/变体 SKU 必须一致、档口非空时每条候选须通过 stallMatches、存在非目标码或同码多次、空白明细多或少均拒绝。数量=1 与已有采购信息排除沿用原有检查。

验证

  • 新增测试:#273/#259 场景 7 个、#289 单元 6 个 + 端到端 1 个;sybinnercode 全部 34 个测试通过(审核人复跑)。gofmt / go build ./... / go vet 干净。
  • 新旧规则夹具对比:10 组中 4 组结果变化——修正 1 处误配(长编号内部数字被当货号),修正 3 处漏配(非数字货号精确令牌、档口名含 #、规格以货号开头)。

未验证 / 遗留

  • 验收第 4 条「用 08-22 线上试用样本做只读新旧对比」未执行:需要真实读取 SYB 货运单(只读调用,但要用线上 SYB 会话),本次只做了夹具对比。是否执行待用户决定。
  • 无长期文档影响:Wiki 未记录档口货号的具体匹配算法(实施时核对 docs 无相关描述)。

状态:待验收。

## 实施回写(2026-09-28) **分支**:`fix/344-inner-code-stall-match`(基于 main `abdf907`),提交 `e99bbc2`、`9dace6a`,已推送。**未合并**。仅改 `server/app/goauto/sybinnercode/match.go` 与 `match_test.go`。 ### 1. 档口货号匹配(#273/#259) `strictStall` 的子串判断改为 `stallMatches`,与 cmautobuy `innerCodeStallMatches` 及辅助函数(`splitStall`、`isNumericArticle`、`textHasNumericArticle`、`normalizeNumericArticle`、`textHasExactArticle`、`productSpecStartsWithArticle`、`articleTokens`)逐行一致:最后一个 # 拆分;货号只与连续字母/数字令牌比较;纯数字货号须同时命中档口名才允许前导零等价;规格仅以货号开头命中;货号为空时按档口名包含。`matchEvidence` 流程(原始 SKU 优先 → 冲突拒绝 → 档口 → 无 SKU 兜底)不变。 ### 2. 多件逐件绑定(#289)——**问题复现并修正** 回归测试 `TestRunMatchJobPreservesExistingBindingWhenCandidateOrderDiffers` 在旧逻辑上失败(按 ID 顺序会把已写好的码改绑到另一明细)、新逻辑通过。新增 `assignExistingBoundItems`:保留已带目标码的明细绑定,只把剩余码填入空白明细;并按 cmautobuy 补齐防护:明细 ID 无效/重复拒绝、候选规格原文/SKU/变体 SKU 必须一致、档口非空时每条候选须通过 `stallMatches`、存在非目标码或同码多次、空白明细多或少均拒绝。数量=1 与已有采购信息排除沿用原有检查。 ### 验证 - 新增测试:#273/#259 场景 7 个、#289 单元 6 个 + 端到端 1 个;`sybinnercode` 全部 34 个测试通过(审核人复跑)。gofmt / `go build ./...` / `go vet` 干净。 - 新旧规则夹具对比:10 组中 4 组结果变化——修正 1 处误配(长编号内部数字被当货号),修正 3 处漏配(非数字货号精确令牌、档口名含 #、规格以货号开头)。 ### 未验证 / 遗留 - 验收第 4 条「用 08-22 线上试用样本做只读新旧对比」**未执行**:需要真实读取 SYB 货运单(只读调用,但要用线上 SYB 会话),本次只做了夹具对比。是否执行待用户决定。 - 无长期文档影响:Wiki 未记录档口货号的具体匹配算法(实施时核对 docs 无相关描述)。 状态:**待验收**。
Author
Owner

验收第 4 条:线上试用样本只读新旧对比(2026-09-28,用户授权「做」)

  • 方法:临时只读程序(未提交,运行后已删除)分别用 main abdf907(旧规则)与本分支 9dace6a(新规则)编译,在线上服务器以 goauto 用户、线上配置运行;按正式匹配任务相同顺序(记录 source_row,id、入库码 ordinal)对营业日期 2026-08-22 的全部记录逐条调用 planRecord。SYB 会话经 sybimport.Connect(统一会话管理)复用线上缓存会话;只调用「按订单号查货运单」「货运单明细」两个只读接口。数据库连接开启 transaction_read_only=1,任何写库都会失败;未执行任何 SYB 写操作。
  • 结果:33 条记录,两版均无错误;状态分布相同(already_filled 31、ready 1、skipped 1);逐条比较状态、主明细 ID、逐件规划 差异 0 条。其中多件记录 1 条。
  • 核对:运行后线上 syb_inner_code_record 状态分布与运行前一致(31/1/1),syb_inner_code_plan 32 条未变。
  • 结论:本批真实数据中没有触发旧规则误配的情形,新规则不改变现有结果;规则修正的效果由单元测试与夹具对比覆盖。结果只记录计数,不在工单中记录订单号、入库码等业务数据。
## 验收第 4 条:线上试用样本只读新旧对比(2026-09-28,用户授权「做」) - **方法**:临时只读程序(未提交,运行后已删除)分别用 main `abdf907`(旧规则)与本分支 `9dace6a`(新规则)编译,在线上服务器以 goauto 用户、线上配置运行;按正式匹配任务相同顺序(记录 `source_row,id`、入库码 `ordinal`)对营业日期 2026-08-22 的全部记录逐条调用 `planRecord`。SYB 会话经 `sybimport.Connect`(统一会话管理)复用线上缓存会话;只调用「按订单号查货运单」「货运单明细」两个只读接口。**数据库连接开启 `transaction_read_only=1`**,任何写库都会失败;未执行任何 SYB 写操作。 - **结果**:33 条记录,两版均无错误;状态分布相同(already_filled 31、ready 1、skipped 1);逐条比较状态、主明细 ID、逐件规划 **差异 0 条**。其中多件记录 1 条。 - **核对**:运行后线上 `syb_inner_code_record` 状态分布与运行前一致(31/1/1),`syb_inner_code_plan` 32 条未变。 - **结论**:本批真实数据中没有触发旧规则误配的情形,新规则不改变现有结果;规则修正的效果由单元测试与夹具对比覆盖。结果只记录计数,不在工单中记录订单号、入库码等业务数据。
Author
Owner

验收(2026-09-28)

用户确认:「#344通过验收,合并到main」。

  • 合并:fix/344-inner-code-stall-match 快进合并到 main(abdf907..9dace6a),无合并提交;验证沿用同一提交 9dace6a 的测试与线上只读对比结果。
  • 未部署线上。
  • 无长期文档影响(Wiki 未记录档口货号匹配算法细节)。
  • 替代 cmautobuy 前的后续事项(历史数据迁移、切换期间避免两系统同时回写、cmautobuy 模块停用时间)仍待另行决定。

状态:验收通过,关闭。

## 验收(2026-09-28) 用户确认:「#344通过验收,合并到main」。 - 合并:`fix/344-inner-code-stall-match` 快进合并到 main(`abdf907..9dace6a`),无合并提交;验证沿用同一提交 `9dace6a` 的测试与线上只读对比结果。 - **未部署线上**。 - 无长期文档影响(Wiki 未记录档口货号匹配算法细节)。 - 替代 cmautobuy 前的后续事项(历史数据迁移、切换期间避免两系统同时回写、cmautobuy 模块停用时间)仍待另行决定。 状态:验收通过,关闭。
ila closed this issue 2026-09-28 10:48:50 +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#344