fix(server): SYB 空格分隔的颜色尺码被整条当成颜色(#274 回归) #284

Open
opened 2026-09-15 14:21:25 +08:00 by ila · 1 comment
Owner

原始需求

来源:2026-09-15 部署 #275~#280 前的线上核对。用户要求「按 1→2→3 走,然后部署」,其中 1 即本工单。

缺陷

sybimport.Parse 把空格分隔的「颜色 尺码」整条当成颜色,尺码为空,且状态判为 success。

线上实测(be03972,2026-09-14 已部署):

"黑色 XL"  ->  status=success  color="黑色 XL"  size=""  note="仅识别到颜色"
"紅色 M"   ->  status=success  color="紅色 M"   size=""  note="仅识别到颜色"

解析器自己在 note 里写了「仅识别到颜色」,却给了 success。

影响

success 的行永远不会进 AI 解析队列——ai_parse_batch.go 的候选查询是:

Where("parse_status IN ?", []string{models.SYBParseStatusUncertain, models.SYBParseStatusFailed})

因此这批数据不会被任何机制纠正,带着错误的颜色和空尺码直接进入采购规格匹配。

2026-09-15 线上统计:

SELECT COUNT(*) FROM syb_product WHERE parse_status='success' AND target_size='';
-> 152

152 条中并非全部误判(部分商品本来就是单维度),具体构成待另行核查。

根因

b83f20c(#274 accept descriptive color specs)删除了 ambiguousPattern 这道闸:

-var ambiguousPattern = regexp.MustCompile(`[++\s]`)
-		if only != "" && !ambiguousPattern.MatchString(only) {
-	if ambiguousPattern.MatchString(colorPart) {
-		return ParseResult{... Status: uncertain, Note: "颜色部分含备注文本或多个分隔符,拆分结果可能不准确"}
-	}

#274 的意图是让描述性颜色(黑色+白色 簡約親膚)被接受而不是被拒绝,这个意图是对的;副作用是 黑色 XL 也一并放行。parse.go 的文档注释仍停留在「no comma -> uncertain」的旧描述,未随 #274 更新。

方案

不恢复被删的旧闸(那会退回 #274 之前、重新拒绝描述性颜色)。改为把空格当作与逗号同等的分隔符,复用逗号分支已有的规则:恰好一个空格分隔 token 带明确尺码特征时才拆分。这不构成猜测,与既有契约一致。

新增 splitOnWhitespace:

  • token 数 < 2 -> 不拆
  • 多于一个 token 命中 explicitSizePattern -> 不拆(对齐逗号分支「两侧均像尺码则无法安全识别颜色」)
  • 恰好一个命中 -> 该 token 为尺码,其余合并为颜色

接入 Parse 的两处单维度返回点(无逗号分支、逗号存在但一侧为空分支),并修正过期的文档注释。

行为对照

输入 修改前(线上) 修改后
黑色 XL color=黑色 XL size=`` color=黑色 size=XL
XL 黑色 color=XL 黑色 size=`` color=黑色 size=XL
黑色+白色 簡約親膚 color 原样 不变(#274 意图保留)
均碼 size=均碼 不变
黑色 color=黑色 不变
S M L color=S M L 不变(多尺码 token 不拆)
黑色,XL color=黑色 size=XL 不变

设计证据

后端解析逻辑,无界面变化,不强制 UI 原型。

验收

  • 黑色 XL / 紅色 M / XL 黑色 解析出颜色与尺码两个维度。
  • 黑色+白色 簡約親膚 行为与 #274 一致,未被退回拒绝。
  • 均碼、黑色、黑色,XL 行为不变。
  • 多个尺码 token(S M L)不拆分。
  • 新增测试覆盖以上全部对照项。

验证

go build ./... 与 go test ./app/goauto/sybimport/...。

[必须] 该包存在 12 个先于本工单的失败用例,来自 #272/#274 改变契约后未更新的旧断言,已另行建单记录。本工单前后失败集合必须完全一致,不得新增。

风险

  • 解析输出变化只影响之后新解析的数据,不改写既有行行。
  • 线上已写入的 152 条不会自动修正;存量重解析属于另一件事,需单独确认后再决定是否执行 #282 的批量重解析脚本。
  • S M L 这类多尺码 token 按最保守处理(不拆)。实际数据中该形态的占比与是否应进 AI 兜底尚无证据,如后续有数据可再调整。

文档影响

解析规则属于业务规则。parse.go 的文档注释随本次改动更新;若 Wiki 业务规则页面记录了旧的 uncertain 描述,需一并更新。

## 原始需求 来源:2026-09-15 部署 #275~#280 前的线上核对。用户要求「按 1→2→3 走,然后部署」,其中 1 即本工单。 ## 缺陷 `sybimport.Parse` 把空格分隔的「颜色 尺码」整条当成颜色,尺码为空,且状态判为 `success`。 线上实测(`be03972`,2026-09-14 已部署): ``` "黑色 XL" -> status=success color="黑色 XL" size="" note="仅识别到颜色" "紅色 M" -> status=success color="紅色 M" size="" note="仅识别到颜色" ``` 解析器自己在 `note` 里写了「仅识别到颜色」,却给了 `success`。 ## 影响 `success` 的行**永远不会进 AI 解析队列**——`ai_parse_batch.go` 的候选查询是: ```go Where("parse_status IN ?", []string{models.SYBParseStatusUncertain, models.SYBParseStatusFailed}) ``` 因此这批数据不会被任何机制纠正,带着错误的颜色和空尺码直接进入采购规格匹配。 2026-09-15 线上统计: ```sql SELECT COUNT(*) FROM syb_product WHERE parse_status='success' AND target_size=''; -> 152 ``` 152 条中并非全部误判(部分商品本来就是单维度),具体构成待另行核查。 ## 根因 `b83f20c`(#274 accept descriptive color specs)删除了 `ambiguousPattern` 这道闸: ```go -var ambiguousPattern = regexp.MustCompile(`[++\s]`) - if only != "" && !ambiguousPattern.MatchString(only) { - if ambiguousPattern.MatchString(colorPart) { - return ParseResult{... Status: uncertain, Note: "颜色部分含备注文本或多个分隔符,拆分结果可能不准确"} - } ``` #274 的意图是让描述性颜色(`黑色+白色 簡約親膚`)被接受而不是被拒绝,这个意图是对的;副作用是 `黑色 XL` 也一并放行。`parse.go` 的文档注释仍停留在「no comma -> uncertain」的旧描述,未随 #274 更新。 ## 方案 **不恢复被删的旧闸**(那会退回 #274 之前、重新拒绝描述性颜色)。改为把空格当作与逗号同等的分隔符,复用逗号分支已有的规则:**恰好一个空格分隔 token 带明确尺码特征时才拆分**。这不构成猜测,与既有契约一致。 新增 `splitOnWhitespace`: - token 数 < 2 -> 不拆 - 多于一个 token 命中 `explicitSizePattern` -> 不拆(对齐逗号分支「两侧均像尺码则无法安全识别颜色」) - 恰好一个命中 -> 该 token 为尺码,其余合并为颜色 接入 `Parse` 的两处单维度返回点(无逗号分支、逗号存在但一侧为空分支),并修正过期的文档注释。 ## 行为对照 | 输入 | 修改前(线上) | 修改后 | |---|---|---| | `黑色 XL` | color=`黑色 XL` size=`` | color=`黑色` size=`XL` | | `XL 黑色` | color=`XL 黑色` size=`` | color=`黑色` size=`XL` | | `黑色+白色 簡約親膚` | color 原样 | **不变**(#274 意图保留) | | `均碼` | size=`均碼` | **不变** | | `黑色` | color=`黑色` | **不变** | | `S M L` | color=`S M L` | **不变**(多尺码 token 不拆) | | `黑色,XL` | color=`黑色` size=`XL` | **不变** | ## 设计证据 后端解析逻辑,无界面变化,不强制 UI 原型。 ## 验收 - [ ] `黑色 XL` / `紅色 M` / `XL 黑色` 解析出颜色与尺码两个维度。 - [ ] `黑色+白色 簡約親膚` 行为与 #274 一致,未被退回拒绝。 - [ ] `均碼`、`黑色`、`黑色,XL` 行为不变。 - [ ] 多个尺码 token(`S M L`)不拆分。 - [ ] 新增测试覆盖以上全部对照项。 ## 验证 `go build ./...` 与 `go test ./app/goauto/sybimport/...`。 `[必须]` 该包存在 12 个**先于本工单**的失败用例,来自 #272/#274 改变契约后未更新的旧断言,已另行建单记录。本工单前后失败集合必须完全一致,不得新增。 ## 风险 - 解析输出变化只影响之后新解析的数据,不改写既有行行。 - 线上已写入的 152 条不会自动修正;存量重解析属于另一件事,需单独确认后再决定是否执行 #282 的批量重解析脚本。 - `S M L` 这类多尺码 token 按最保守处理(不拆)。实际数据中该形态的占比与是否应进 AI 兜底尚无证据,如后续有数据可再调整。 ## 文档影响 解析规则属于业务规则。`parse.go` 的文档注释随本次改动更新;若 Wiki 业务规则页面记录了旧的 uncertain 描述,需一并更新。
Author
Owner

上线信息(共用)

  • 合并提交:1d01186,已推送 be03972..1d01186 到 main。
  • 发布目录:/home/goauto/releases/20260915-1d01186,二进制 SHA-256 前缀 65e404c4721d592a(本地与服务器一致)。
  • systemctl is-active goauto.service = active,监听 127.0.0.1:8010。
  • 数据库迁移:新执行 2 个,跳过 45 个已应用。迁移前已做全库逻辑备份(66 张表,gzip 校验通过),路径在运维备份目录,不在此列出。
  • 迁移前后 collection_task 222 行、shopee_product 26901 行,逐一相符。

外部入口验证:

检查 结果
GET / HTTP 200
GET /api/admin/v1/collection-tasks {"code":401,"msg":"cookie token is empty"}
POST /api/admin/v1/collection-tasks/image-search/batch {"code":401,...}
不存在路由对照 HTTP 404

最后两行是关键对照:图搜接口返回 401 而非 404,证明新路由已注册。

回退方式:把 current 软链切回 20260914-be03972-r2 后重启。两条迁移均为加列与放宽约束,不阻碍回退到旧二进制。

实施

提交:93a175d。改动 server/app/goauto/sybimport/parse.go 51 行,新增 parse_whitespace_split_test.go 80 行。

新增 splitOnWhitespace:token 数 < 2 不拆;多于一个 token 命中 explicitSizePattern 不拆(对齐逗号分支「两侧均像尺码则无法安全识别颜色」);恰好一个命中时该 token 为尺码、其余合并为颜色。接入 Parse 的两处单维度返回点,并修正停留在旧 uncertain 描述的文档注释。

行为核对(实测)

输入 修改前(线上) 修改后
黑色 XL color=黑色 XL size=`` color=黑色 size=XL
紅色 M color=紅色 M size=`` color=紅色 size=M
XL 黑色 color=XL 黑色 size=`` color=黑色 size=XL
黑色 XL 加厚 color 整条 color=黑色 加厚 size=XL
黑色+白色 簡約親膚 color 原样 不变(#274 意图保留)
均碼 / 黑色 / 黑色,XL — 不变
S M L color=S M L 不变(多尺码 token 不拆)

验证

go build ./... 通过;新增 5 个测试全部通过(含 #274 描述性颜色不被退回的回归)。

[必须] 该包的 12 个失败用例先于本工单存在(#285)。本次提交前后失败集合逐条 diff 完全一致,数量 12 → 12,未新增。

存量数据未处理

线上 parse_status='success' AND target_size='' 共 152 条(2026-09-15 统计)。其中并非全部误判——部分商品本来就是单维度。修复只影响之后新解析的数据,存量不会自动修正。是否执行 #282 的批量重解析脚本需单独确认,建议先核查这 152 条的实际构成。

状态

已上线,保持待验收。

## 上线信息(共用) - 合并提交:`1d01186`,已推送 `be03972..1d01186` 到 `main`。 - 发布目录:`/home/goauto/releases/20260915-1d01186`,二进制 SHA-256 前缀 `65e404c4721d592a`(本地与服务器一致)。 - `systemctl is-active goauto.service` = `active`,监听 `127.0.0.1:8010`。 - 数据库迁移:新执行 2 个,跳过 45 个已应用。迁移前已做全库逻辑备份(66 张表,gzip 校验通过),路径在运维备份目录,不在此列出。 - 迁移前后 `collection_task` 222 行、`shopee_product` 26901 行,逐一相符。 外部入口验证: | 检查 | 结果 | |---|---| | `GET /` | HTTP 200 | | `GET /api/admin/v1/collection-tasks` | `{"code":401,"msg":"cookie token is empty"}` | | `POST /api/admin/v1/collection-tasks/image-search/batch` | `{"code":401,...}` | | 不存在路由对照 | HTTP 404 | 最后两行是关键对照:图搜接口返回 401 而非 404,证明新路由已注册。 回退方式:把 `current` 软链切回 `20260914-be03972-r2` 后重启。两条迁移均为加列与放宽约束,不阻碍回退到旧二进制。 ## 实施 提交:`93a175d`。改动 `server/app/goauto/sybimport/parse.go` 51 行,新增 `parse_whitespace_split_test.go` 80 行。 新增 `splitOnWhitespace`:token 数 < 2 不拆;多于一个 token 命中 `explicitSizePattern` 不拆(对齐逗号分支「两侧均像尺码则无法安全识别颜色」);恰好一个命中时该 token 为尺码、其余合并为颜色。接入 `Parse` 的两处单维度返回点,并修正停留在旧 uncertain 描述的文档注释。 ## 行为核对(实测) | 输入 | 修改前(线上) | 修改后 | |---|---|---| | `黑色 XL` | color=`黑色 XL` size=`` | color=`黑色` size=`XL` | | `紅色 M` | color=`紅色 M` size=`` | color=`紅色` size=`M` | | `XL 黑色` | color=`XL 黑色` size=`` | color=`黑色` size=`XL` | | `黑色 XL 加厚` | color 整条 | color=`黑色 加厚` size=`XL` | | `黑色+白色 簡約親膚` | color 原样 | **不变**(#274 意图保留) | | `均碼` / `黑色` / `黑色,XL` | — | **不变** | | `S M L` | color=`S M L` | **不变**(多尺码 token 不拆) | ## 验证 `go build ./...` 通过;新增 5 个测试全部通过(含 #274 描述性颜色不被退回的回归)。 `[必须]` 该包的 12 个失败用例先于本工单存在(#285)。本次提交前后失败集合**逐条 diff 完全一致**,数量 12 → 12,未新增。 ## 存量数据未处理 线上 `parse_status='success' AND target_size=''` 共 152 条(2026-09-15 统计)。其中并非全部误判——部分商品本来就是单维度。修复只影响之后新解析的数据,**存量不会自动修正**。是否执行 #282 的批量重解析脚本需单独确认,建议先核查这 152 条的实际构成。 ## 状态 已上线,保持待验收。
Sign in to join this conversation.
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: OPC/goauto#284