Files
cmautobuy/docs/task/38-admin-蝦皮excel报表导入.md
T
chengmaandClaude Opus 5 10b724f62a docs: 归档 #38;改正六份归档里未经验证的 Go 版本说法
#38 归档记录了打回一次的经过(ParseSpec 两个失败分支无测试覆盖,
其中 size=="" 就是真实数据里 2 行走的分支),以及架构角色第一轮
变异打在死分支上、差点误报「测试没牙」的过程。

改正:/usr/local/go 从 2026-07-02 起一直是 1.26.5,而六份归档都写着
「架构角色亲自执行(Go 1.23.0)」——那是照项目固定版本抄的,
没有实际验证过工具链,违反 CLAUDE.md §9「说验证过必须真的跑过」。
措辞改为不宣称具体版本。已用 GOTOOLCHAIN=go1.23.0 补验,结论不变。

admin/AGENTS.md 加两条 [必须]:
- 交付前至少跑一次带 GOTOOLCHAIN=go1.23.0 的验证。开发机装的可能更新,
  Go 会默默用它编译,测过的不是要交付的那个版本。新加依赖时尤其要跑。
- 改数据库时不要只测全新库,先列出现实中存在哪些 schema 状态再一个个验(来自 #20)。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-08 09:59:34 +08:00

11 KiB
Raw Blame History

38 Admin 蝦皮 Excel 报表导入

  • 类型:需求(数据导入)
  • 父级大工单:#14
  • 所属 MVP / 版本:#15 / MVP
  • 关联:#16(pdd_products 独立成表)、#20(迁移只追加)
  • 状态:待验收
  • 日期:2026-08-08
  • Gitea 工单:#38

背景与目标

蝦皮数据模块整个是骨架,四个入口全部返回 501, service.ImportShopeeExcel 直接 return nil, ErrNotImplemented, excelize 连依赖都没装。蝦皮那 5195 个商品一直躺在 Excel 里进不了系统。

建单前确认的事实(读真实样本得出)

样本 raw_data/蝦皮数据样本.xlsx(1.9MB,gitignore 不进库):

① 工作簿有 5 个 sheet,只有一个有数据

Sheet 列数 数据行 規格ID 为 - 有規格ID
最佳表現商品 40 11287 5195 6092
新上架商品 36 1145 0 0
高潛力廣告商品 / 優化商品廣告 / 追蹤商品廣告成效 30 41 / 35 / 7 0 0

其余四个是广告报表,连 商品規格ID 列都没有。

② 规格原文的真实分布

含【】       3252   53.4%    黑色,M【建議40-50公斤】
逗号+空格    1609   26.4%    卡其色拼黑色,L 建議50-57.5kg
只有逗号     1231   20.2%    黑色,M

只认【】会漏掉 46.6%。

③ 数据比预想干净,但有两类脏数据

全部 6092 行恰好一个逗号(0 行没逗号、0 行多逗号), 所以「按第一个逗号切开」是安全的,不是碰运气。

脏数据共 23 行:

21 行  括号不配对        紅色,3XL建議80-90公斤】
 2 行  右半整个被【】包住  面膜安全褲3條膚色,【2xl 70-85公斤可穿】

最终方案

upsert 白名单式,绝不碰人工字段

INSERT INTO shopee_products (goods_id, title, shopee_status, main_sku_code, created_at, updated_at)
VALUES (?, ?, ?, ?, ?, ?)
ON CONFLICT(goods_id) DO UPDATE SET
    title = excluded.title, shopee_status = excluded.shopee_status,
    main_sku_code = excluded.main_sku_code, updated_at = excluded.updated_at

[必须] pdd_goods_url / pdd_goods_id 既不在 INSERT 列清单里, 也不在 DO UPDATE SET 里。报表没有这两列,写进去就是写空值—— 操作员攒几周的 PDD 链接会被一次导入洗光,而且不报错, 等到建采购任务才发现,那时已经找不回来。

[必须] 不写 DELETE + INSERT 的全量替换,会删掉将来手动新增的 SKU (05 §4.4 那个入口的伏笔)。

按 sheet 名字取,按列名找索引

[必须] 按名字取「最佳表現商品」,不得用第 0 个 sheet。 它现在恰好是第一个,但蝦皮调顺序时按下标会静默导入一张完全不相干的表。 找不到要报错并列出实际有哪些 sheet,不退回用第一个。

[必须] 按列名找索引。40 列里 32 列是统计指标,蝦皮加一列所有列号就错位, 而且不报错——会把「點擊率」当成「商品規格」存进去。

解析失败不猜

[必须] ok == false 时 color / size / advice 必须全为空, parse_ok 存 0,spec_raw 照存原文,界面上标出来让人工补。

括号不配对和右半整个被【】包住的,一律判整体失败,不逐段硬凑。 理由(实现写在 ParseSpec 注释里,比工单要求的更清楚): 这些脏数据的错法不统一(缺左括号 / 缺右括号 / 用「寬鬆版」代替「建議」), 针对每种错法单独写规则等于在猜,而且没有人工核对过的正确答案可验证。

与建单方案的差异

  1. 括号不配对判整体失败(工单允许实现决定,要求写清理由并有测试固定)。 实测命中 23 行,比建单时统计的 21 行多 2 行——多出的是 「右半整个被【】包住、括号外没有尺码文字」这个建单时没单独归类的格式, 同样归为解析失败。
  2. ShopeeList 无分页。工单未要求;PDD / 客户端等既有列表页也是单次全量查询, 保持风格一致。
  3. 导入结果不走 303 跳转,直接渲染页面。失败行可能几十条, 塞进跳转的查询参数既丑又有长度限制,无法满足「失败行要能全部看到」。 代价是导入后 F5 会触发浏览器「重新提交表单」确认,已在 handler 注释里写明取舍。

改了哪些

  • admin/go.mod / go.sum:加 github.com/xuri/excelize/v2 v2.9.1。
  • admin/service/shopee_import.go:新建。ParseSpec + ImportShopeeExcel + 文件安全校验。
  • admin/service/shopee_import_test.go:新建。表驱动解析测试 + 6 个导入场景, 固件用 excelize 现造(不依赖 raw_data/)。
  • admin/service/shopee_list.go:新建。列表视图组装。
  • admin/service/service.go:删掉骨架版的重复声明。
  • admin/repository/shopee.go:新建。白名单式 upsert、列表查询。
  • admin/handler/web/shopee.go:ShopeeList / ShopeeImport 真实实现; 其余三个保持 501。
  • admin/templates/shopee/list.html:表格渲染 + 失败行完整列表。
  • admin/testdata/README.md:更新为「固件现造」说明。

验收结果

验收标准 结果
三种规格格式都能解析 通过
颜色部分含【】时仍正确 通过
脏数据有确定行为 + 测试固定 + 注释写清理由 通过
ok=false 时三个字段全为空 通过(5 个失败分支变异全部被抓到)
按 sheet 名字取,找不到报错列出实际 sheet 通过
按列名找索引,缺列报错 通过
商品規格ID == "-" 归商品,其余归 SKU 通过
已有的 pdd_goods_url / pdd_goods_id 导入后仍在 通过(有独立测试 + 端到端验证)
is_manual = 1 的 SKU 不被删除 通过(无全量替换)
失败行返回行号 + 原文 + 原因,界面全部可见 通过
流式读,不是 GetRows() 通过
非 .xlsx 拒绝(看内容不只看扩展名) 通过
落盘用自己生成的文件名 通过(shopee-<hex>.xlsx)
超过 50MB 拒绝 通过
测试不依赖 raw_data/ 通过(固件现造)
ShopeeSave / ShopeeDelete / ShopeeCollect 仍是 501 通过(3 处)
go vet / gofmt / go test 全过 通过

测试

架构角色亲自执行(admin/ 目录)。本次特意用固定版本工具链复跑:

GOTOOLCHAIN=go1.23.0 go vet ./...       无输出
GOTOOLCHAIN=go1.23.0 gofmt -l .         无输出
GOTOOLCHAIN=go1.23.0 go test ./... -count=1
    ok  cmautobuy/admin            0.034s
    ok  cmautobuy/admin/repository 0.669s
    ok  cmautobuy/admin/service    1.974s

excelize v2.9.1 的 go 指令正好是 1.23.0,与项目固定版本一致。

真实样本端到端

① 第一次导入   导入完成:5195 个商品 / 6092 个 SKU,23 行解析失败
              DB 实查 shopee_products=5195  shopee_skus=6092
② 手工填链接   UPDATE shopee_products SET pdd_goods_url='https://...', pdd_goods_id='999'
③ 再导一次     DB 计数不变(5195 / 6092),无重复行
              链接还在:17197130343 | https://mobile.yangkeduo.com/... | 999
              title 正常刷新

变异测试(架构角色补做,工单未要求)

人工字段保护——往 DO UPDATE SET 里加回 pdd_goods_url = excluded.pdd_goods_url:

--- FAIL: TestImportShopeeExcel_不覆盖人工字段

ParseSpec 的 5 个失败分支,逐个改成 return "泄漏","泄漏","泄漏", false:

行 分支 结果
337 空输入 ✅ 3 个子用例变红
341 括号不配对 ✅ 3 个
347 逗号数不为 1 ✅ 3 个
354 逗号一侧为空 ✅ 3 个
378 提取后尺码为空 ✅ 2 个

未验证到的部分:

  • Windows / PowerShell 环境未实测。验证全在 WSL/Linux 完成。
  • 浏览器实机渲染未走查。只验证了 HTTP 响应和 HTML 内容, 6092 行表格在 1366×768 下的列宽、横向滚动表现未看过。
  • 打包成 exe 后的行为未验证(data/uploads/ 落盘路径)。 原有代码路径未改动,理论上不受影响,但没有为本工单重新验证。

审查过程

打回一次。

打回原因: ParseSpec 有两个失败分支没有测试覆盖。 测试文件里有「ok=false ⇒ 三个字段必须为空」的断言, 问题是没有用例能走到 color==""||right=="" 和 size=="" 这两个分支。

其中 size=="" 尤其要紧——它就是真实数据里那 2 行走的分支 (面膜安全褲3條膚色,【2xl 70-85公斤可穿】)。如果有人把它改成 return right, "", "", false(看着「更有信息量」),真实导入会给这 2 行 存进一个乱七八糟的颜色,而且没有任何测试变红,这个值会一路传到 规格匹配和采购任务。

架构角色的一次误判: 第一轮变异打在「输入为空」那个分支上, 把 return "", "", "", false 改成 return raw, ...——raw 本来就是空串, 等于没改,测试当然全绿,差点据此报告「测试没牙」。 逐行重打之后才看清是哪两个分支真的没覆盖。 探针无效和代码有问题是两回事,得先确认变异真的改变了行为。

返工结果: 补了 4 条用例(含实现自己发现的第 5 个缺口—— trimmed=="" 分支原本也没被真正覆盖,唯一的空输入用例 raw:"" 在变异后 raw 仍是 "",抓不到;补一条 raw:" " 才能暴露)。 实现代码一字未动。

遗留问题

  1. Windows 环境、浏览器实机渲染、exe 打包后的落盘路径均未验证。
  2. ShopeeSave / ShopeeDelete / ShopeeCollect 仍是 501,属后续工单。
  3. 手动新增 SKU(05 §4.4)未做。蝦皮报表只含有销售成绩的 SKU (平均每个商品仅 1.17 个),导入进来的数据本身就是不全的。

附带修正(不属于本工单范围)

审查中发现 /usr/local/go 从 2026-07-02 起一直是 1.26.5, 而 #16 / #17 / #18 / #19 / #20 / #24 六份归档都写着 「架构角色亲自执行(Go 1.23.0)」——那是照项目固定版本抄的, 没有实际验证过工具链,违反 CLAUDE.md §9。

已做两件事:

  1. 六份归档的措辞改正为不宣称具体版本。
  2. admin/AGENTS.md 的验证章节新增 [必须]:交付前至少跑一次带 GOTOOLCHAIN=go1.23.0 的验证,并写明本项目发生过什么。 同时补了一条「不要只测全新库,先列出现实中存在哪些 schema 状态」(来自 #20)。

本工单已按新规则用 1.23.0 复验,结论不变。

相关提交

  • e29d668 feat: 蝦皮 Excel 报表导入 (#38)