Admin:蝦皮 Excel 报表导入 #38

Closed
opened 2026-08-08 09:29:05 +08:00 by ila · 2 comments
Owner

基本信息

  • 类型:需求(数据导入)
  • 父级大工单:#14
  • 所属 MVP / 版本:#15 / MVP
  • 关联:#16(pdd_products 独立成表)、#20(迁移只追加)

要解决什么

蝦皮数据模块整个是骨架,四个入口全部返回 501:

功能 现状
列表 TODO(骨架),不查库
导入 Excel 501「导入功能尚未实现」
保存 / 删除 / 采集 501

service.ImportShopeeExcel 直接 return nil, ErrNotImplemented,
excelize 连依赖都没装(go.mod 里只有 gin 和 sqlite,它只出现在注释里)。

蝦皮那 5195 个商品现在还躺在 Excel 里,进不了系统。

已经确认的事实(读真实样本得出,不是猜的)

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

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

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

[必须] 按 sheet 名字取「最佳表現商品」,不得用第 0 个 sheet。
其余 4 个是广告报表,连 商品規格ID 列都没有。虽然它现在恰好是第 0 个,
但蝦皮调整报表顺序时,按下标取会静默导入一张完全不相干的表。

[必须] 找不到这个 sheet 要报错并说清楚,列出实际有哪些 sheet,
不要退回用第一个。

② 分行规则:商品規格ID == "-" 是商品汇总行

实测 5195 汇总行 + 6092 SKU 行 = 11287,两边都零重复
(商品ID 去重后 5195,商品規格ID 去重后 6092)。

③ 40 列,必须按列名找索引

前 8 列是业务列,后 32 列全是销售/广告统计(曝光、点击率、转换率……):

[0] 商品ID      [1] 商品名稱      [2] 商品當前狀態   [3] 商品規格ID
[4] 商品規格    [5] 規格當前狀態  [6] 商品選項貨號   [7] 主商品貨號

[必须] 按列名定位,不要写死列号。报表 40 列,蝦皮加一列统计指标
所有列号就全错位了,而且不会报错——会把「點擊率」当成「商品規格」存进去。

[必须] 缺少必需列时报错并说清缺哪一列,不要用零值继续。

④ 规格原文的真实分布

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

[必须] 只认【】会漏掉 46.6%。

关键实测结论:全部 6092 行恰好有一个逗号——0 行没有逗号,0 行有多个逗号。
所以「按第一个逗号切开」是安全的,左边是颜色、右边是尺码+建议。

[注意] 颜色部分自己也可能带【】:
黑色 【夏裝單件T恤】,L 【57.5/70公斤】。按逗号切仍然正确。

⑤ 21 行(0.3%)括号不配对

紅色,3XL建議80-90公斤】     ← 有右括号没左括号
綠色,3XL建議80-90公斤】
黑色,3XL建議80-90公斤】

[必须] 这是真实的脏数据,必须明确处理:不许因此崩溃,也不许猜。

做什么 / 不做什么

做:

  1. 装 excelize 依赖
  2. 实现 ParseSpec
  3. 实现 ImportShopeeExcel(流式读、upsert、统计)
  4. 实现 ShopeeImport handler(文件校验、落盘、调 service、显示结果)
  5. 实现 ShopeeList(列表能看到导入结果)

不做(各自独立工单):

  • 不实现 ShopeeSave / ShopeeDelete / ShopeeCollect(保持 501)
  • 不做「手动新增 SKU」(05 §4.4)
  • 不做规格匹配
  • 不改 pdd_products 及采集相关任何代码

怎么做

upsert 绝不清空人工字段 —— 本工单最要命的一条

shopee_products 当前 8 列(v3 之后):

goods_id  title  shopee_status  main_sku_code
pdd_goods_url  pdd_goods_id          ← 人工维护,报表里没有
created_at  updated_at

[必须] ON CONFLICT DO UPDATE SET 里绝不允许出现 pdd_goods_url
和 pdd_goods_id
。

理由:报表里没有这两列,写进去就是写空值。操作员可能攒了几周的 PDD 链接,
导入一次全洗掉,而且不报错——等到建采购任务时才发现,那时已经找不回来了。

[必须] 这条要写进代码注释,并且有对应测试:
先造一条带 pdd_goods_url 的记录,导入同一个 goods_id,
断言链接还在。

[建议] 同理,shopee_skus 的 is_manual = 1 的行导入时不得删除
(05 §4.4)。本工单不做手动新增,但删除逻辑现在就要留出口子——
不要写 "先 DELETE 再 INSERT" 这种全量替换。

解析失败不要猜

// 返回 ok=false 时,color/size/advice 必须全为空。
func ParseSpec(raw string) (color, size, advice string, ok bool)

[必须] ok == false 时前三个必须为空,parse_ok 存 0,
spec_raw 照存原文,界面上标出来让人工补。

[必须] 不要猜。猜出来的颜色尺码会一路传到采购任务,
最后买错东西,而且没人知道这个值是猜的。

[必须] 那 21 行括号不配对的,按「能提取多少算多少」还是「整条判失败」
由实现决定,但必须写清理由并有测试固定住行为。两种都可接受,
不可接受的是「碰巧能跑通但没人知道它怎么处理的」。

失败行不许静默跳过

[必须] 返回结构里带失败行号和原因,界面上列出来。

type ImportResult struct {
    ProductCount int
    SKUCount     int
    Failures     []ImportFailure  // 行号 + 原文 + 原因
}

[必须] 界面显示时,失败行多的话要能全部看到(滚动或折叠),
不要只显示"失败 N 行"——操作员没法据此定位是哪些商品出了问题。

文件安全

[必须] 只允许 .xlsx,按扩展名和实际内容判断(xlsx 是 zip,
可以校验魔数),不要只看文件名。

[必须] 落盘时用自己生成的文件名,不得用 file.Filename 拼路径——
路径穿越(../../etc/passwd)。存到 data/uploads/。

[必须] 限制大小。样本 1.9MB,上限设 50MB 并写清理由。

流式读,不要全读进内存

[必须] 11287 行 × 40 列。用 excelize 的 Rows() 流式迭代,
不要 GetRows() 一次性读出来。

[建议] 整个导入放一个事务里,失败整体回滚——导入一半的数据比没导入更难收拾。

测试固件不能依赖 raw_data/

[必须] raw_data/ 在 .gitignore 里,测试不得依赖它。

在 admin/testdata/ 下生成一个小的 .xlsx 固件(几十行足够),
覆盖:正常行、三种规格格式、括号不配对、缺列、错 sheet 名、
已有人工填的 pdd_goods_url。

[建议] 固件可以用 excelize 在测试里现造,也可以提交一个小 xlsx 文件。
现造更好——文件二进制不好 review。

预计修改文件

文件 改什么
admin/go.mod / go.sum 加 github.com/xuri/excelize/v2
admin/service/shopee_import.go 新建:ParseSpec + ImportShopeeExcel
admin/service/shopee_import_test.go 新建
admin/repository/shopee.go 新建:upsert(不碰人工字段)、列表查询
admin/handler/web/shopee.go ShopeeImport / ShopeeList 真实实现
admin/templates/shopee/list.html 列表渲染、导入结果显示
admin/testdata/ 测试固件
docs/admin/01-requirements.md / 05-ui-specification.md 同步已实现状态

[注意] excelize 需要的 Go 版本:本项目固定 Go 1.23.0。
最新版 excelize 可能要求更高,装能在 1.23.0 上编译的版本
(参考 docs/admin/00-getting-started.md 里已固定的版本策略)。
装不上就停下来说明,不要升 Go。

验收标准

解析

  • 黑色,M【建議40-50公斤】 → 黑色 / M / 40-50公斤 / ok
  • 卡其色拼黑色,L 建議50-57.5kg → 卡其色拼黑色 / L / 50-57.5kg / ok
  • 黑色,M → 黑色 / M / 空 / ok
  • 黑色 【夏裝單件T恤】,L 【57.5/70公斤】 → 颜色含【】仍正确
  • 紅色,3XL建議80-90公斤】 有确定行为 + 测试固定住 + 注释写清理由
  • 完全认不出的 → ok=false 且 color/size/advice 全为空

导入

  • 按 sheet 名字取「最佳表現商品」
  • sheet 不存在 → 报错并列出实际有哪些 sheet,不退回第一个
  • 按列名找索引;缺列 → 报错说清缺哪列
  • 商品規格ID == "-" 归为商品,其余归为 SKU
  • 已有的 pdd_goods_url / pdd_goods_id 导入后仍在(有独立测试)
  • is_manual = 1 的 SKU 不被导入删除
  • 失败行返回行号 + 原文 + 原因,界面能全部看到
  • 流式读,不是 GetRows()

文件安全

  • 非 .xlsx 拒绝(看内容不只看扩展名)
  • 落盘用自己生成的文件名,../ 之类的文件名不会穿越
  • 超过 50MB 拒绝

其他

  • 测试不依赖 raw_data/,固件在 admin/testdata/
  • 列表页能看到导入的数据
  • ShopeeSave / ShopeeDelete / ShopeeCollect 仍是 501(本工单不做)
  • go vet / gofmt -l . / go test ./... 全过
  • 五个页面均 200

怎么验证

cd D:\chengma\cmautobuy\admin
go vet ./...; gofmt -l .; go test ./... -count=1
go run .

真实样本导入(本机有 raw_data/蝦皮数据样本.xlsx):

打开 http://127.0.0.1:8080/shopee 导入它。

[必须] 期望并贴出实际输出:

  • 导入结果显示 5195 个商品 / 6092 个 SKU
  • 失败行数和行号列出来(预计约 21 行括号不配对,视你的处理方式而定)
  • 列表页能看到数据
  • 再导入一次:数量不翻倍(upsert 生效)

人工字段保护(最关键的一条):

-- 导入后手工填一个链接
UPDATE shopee_products SET pdd_goods_url = 'https://x/1'
 WHERE goods_id = '<任选一个>';

再导入一次同一个文件,然后查这条记录 —— pdd_goods_url 必须还在。

[必须] 这一步的实际 SQL 输出要贴出来。

风险和回退

风险 应对
导入洗掉人工填的 PDD 链接 upsert 白名单 + 独立测试 + 验证步骤单列一条
按下标取 sheet,蝦皮调顺序后导错表 按名字取,找不到报错
写死列号,蝦皮加列后静默错位 按列名找,缺列报错
解析失败时猜值,脏数据流到采购 ok=false 时三个字段必须为空
路径穿越 自己生成文件名
全量替换删掉手动新增的 SKU 禁止 DELETE + INSERT

回退:git revert。已导入的数据可以清表重来(本工单不产生外部副作用)。

## 基本信息 - 类型:需求(数据导入) - 父级大工单:#14 - 所属 MVP / 版本:#15 / MVP - 关联:#16(`pdd_products` 独立成表)、#20(迁移只追加) ## 要解决什么 蝦皮数据模块**整个是骨架**,四个入口全部返回 501: | 功能 | 现状 | |---|---| | 列表 | `TODO(骨架)`,不查库 | | **导入 Excel** | **501「导入功能尚未实现」** | | 保存 / 删除 / 采集 | 501 | `service.ImportShopeeExcel` 直接 `return nil, ErrNotImplemented`, `excelize` **连依赖都没装**(`go.mod` 里只有 gin 和 sqlite,它只出现在注释里)。 蝦皮那 5195 个商品现在还躺在 Excel 里,进不了系统。 ## 已经确认的事实(读真实样本得出,不是猜的) 样本:`raw_data/蝦皮数据样本.xlsx`(1.9MB,**gitignore 不进库**)。 ### ① 工作簿有 5 个 sheet,只有一个有数据 | Sheet | 列数 | 数据行 | 規格ID 为 `-` | 有規格ID | |---|---|---|---|---| | **最佳表現商品** | **40** | **11287** | **5195** | **6092** | | 新上架商品 | 36 | 1145 | 0 | 0 | | 高潛力廣告商品 | 30 | 41 | 0 | 0 | | 優化商品廣告 | 30 | 35 | 0 | 0 | | 追蹤商品廣告成效 | 30 | 7 | 0 | 0 | `[必须]` **按 sheet 名字取「最佳表現商品」,不得用第 0 个 sheet。** 其余 4 个是广告报表,连 `商品規格ID` 列都没有。虽然它现在恰好是第 0 个, 但蝦皮调整报表顺序时,按下标取会静默导入一张完全不相干的表。 `[必须]` 找不到这个 sheet 要**报错并说清楚**,列出实际有哪些 sheet, 不要退回用第一个。 ### ② 分行规则:`商品規格ID == "-"` 是商品汇总行 实测 5195 汇总行 + 6092 SKU 行 = 11287,两边**都零重复** (`商品ID` 去重后 5195,`商品規格ID` 去重后 6092)。 ### ③ 40 列,必须按列名找索引 前 8 列是业务列,后 32 列全是销售/广告统计(曝光、点击率、转换率……): ``` [0] 商品ID [1] 商品名稱 [2] 商品當前狀態 [3] 商品規格ID [4] 商品規格 [5] 規格當前狀態 [6] 商品選項貨號 [7] 主商品貨號 ``` `[必须]` 按列名定位,**不要写死列号**。报表 40 列,蝦皮加一列统计指标 所有列号就全错位了,而且不会报错——会把「點擊率」当成「商品規格」存进去。 `[必须]` 缺少必需列时报错并说清缺哪一列,不要用零值继续。 ### ④ 规格原文的真实分布 ``` 含【】 3252 53.4% 黑色,M【建議40-50公斤】 逗号+空格 1609 26.4% 卡其色拼黑色,L 建議50-57.5kg 只有逗号 1231 20.2% 黑色,M ``` `[必须]` **只认【】会漏掉 46.6%**。 **关键实测结论:全部 6092 行恰好有一个逗号**——0 行没有逗号,0 行有多个逗号。 所以「按第一个逗号切开」是安全的,左边是颜色、右边是尺码+建议。 `[注意]` 颜色部分自己也可能带【】: `黑色 【夏裝單件T恤】,L 【57.5/70公斤】`。按逗号切仍然正确。 ### ⑤ 21 行(0.3%)括号不配对 ``` 紅色,3XL建議80-90公斤】 ← 有右括号没左括号 綠色,3XL建議80-90公斤】 黑色,3XL建議80-90公斤】 ``` `[必须]` 这是**真实的脏数据**,必须明确处理:不许因此崩溃,也不许猜。 ## 做什么 / 不做什么 做: 1. 装 `excelize` 依赖 2. 实现 `ParseSpec` 3. 实现 `ImportShopeeExcel`(流式读、upsert、统计) 4. 实现 `ShopeeImport` handler(文件校验、落盘、调 service、显示结果) 5. 实现 `ShopeeList`(列表能看到导入结果) 不做(各自独立工单): - **不实现 `ShopeeSave` / `ShopeeDelete` / `ShopeeCollect`**(保持 501) - 不做「手动新增 SKU」(`05` §4.4) - 不做规格匹配 - 不改 `pdd_products` 及采集相关任何代码 ## 怎么做 ### upsert 绝不清空人工字段 —— 本工单最要命的一条 `shopee_products` 当前 8 列(v3 之后): ``` goods_id title shopee_status main_sku_code pdd_goods_url pdd_goods_id ← 人工维护,报表里没有 created_at updated_at ``` `[必须]` `ON CONFLICT DO UPDATE SET` 里**绝不允许出现 `pdd_goods_url` 和 `pdd_goods_id`**。 理由:报表里没有这两列,写进去就是写空值。操作员可能攒了几周的 PDD 链接, **导入一次全洗掉,而且不报错**——等到建采购任务时才发现,那时已经找不回来了。 `[必须]` 这条要写进代码注释,并且**有对应测试**: 先造一条带 `pdd_goods_url` 的记录,导入同一个 `goods_id`, 断言链接**还在**。 `[建议]` 同理,`shopee_skus` 的 `is_manual = 1` 的行导入时不得删除 (`05` §4.4)。本工单不做手动新增,但删除逻辑现在就要留出口子—— **不要写 "先 DELETE 再 INSERT" 这种全量替换**。 ### 解析失败不要猜 ```go // 返回 ok=false 时,color/size/advice 必须全为空。 func ParseSpec(raw string) (color, size, advice string, ok bool) ``` `[必须]` `ok == false` 时**前三个必须为空**,`parse_ok` 存 0, `spec_raw` 照存原文,界面上标出来让人工补。 `[必须]` **不要猜**。猜出来的颜色尺码会一路传到采购任务, 最后买错东西,而且没人知道这个值是猜的。 `[必须]` 那 21 行括号不配对的,按「能提取多少算多少」还是「整条判失败」 **由实现决定,但必须写清理由并有测试固定住行为**。两种都可接受, 不可接受的是「碰巧能跑通但没人知道它怎么处理的」。 ### 失败行不许静默跳过 `[必须]` 返回结构里带**失败行号和原因**,界面上列出来。 ```go type ImportResult struct { ProductCount int SKUCount int Failures []ImportFailure // 行号 + 原文 + 原因 } ``` `[必须]` 界面显示时,失败行多的话要能全部看到(滚动或折叠), **不要只显示"失败 N 行"**——操作员没法据此定位是哪些商品出了问题。 ### 文件安全 `[必须]` 只允许 `.xlsx`,按扩展名**和**实际内容判断(xlsx 是 zip, 可以校验魔数),不要只看文件名。 `[必须]` 落盘时用**自己生成的文件名**,不得用 `file.Filename` 拼路径—— 路径穿越(`../../etc/passwd`)。存到 `data/uploads/`。 `[必须]` 限制大小。样本 1.9MB,上限设 **50MB** 并写清理由。 ### 流式读,不要全读进内存 `[必须]` 11287 行 × 40 列。用 `excelize` 的 `Rows()` 流式迭代, 不要 `GetRows()` 一次性读出来。 `[建议]` 整个导入放一个事务里,失败整体回滚——导入一半的数据比没导入更难收拾。 ### 测试固件不能依赖 raw_data/ `[必须]` `raw_data/` 在 `.gitignore` 里,**测试不得依赖它**。 在 `admin/testdata/` 下生成一个**小的 .xlsx 固件**(几十行足够), 覆盖:正常行、三种规格格式、括号不配对、缺列、错 sheet 名、 已有人工填的 `pdd_goods_url`。 `[建议]` 固件可以用 `excelize` 在测试里现造,也可以提交一个小 xlsx 文件。 现造更好——文件二进制不好 review。 ## 预计修改文件 | 文件 | 改什么 | |---|---| | `admin/go.mod` / `go.sum` | 加 `github.com/xuri/excelize/v2` | | `admin/service/shopee_import.go` | 新建:`ParseSpec` + `ImportShopeeExcel` | | `admin/service/shopee_import_test.go` | 新建 | | `admin/repository/shopee.go` | 新建:upsert(**不碰人工字段**)、列表查询 | | `admin/handler/web/shopee.go` | `ShopeeImport` / `ShopeeList` 真实实现 | | `admin/templates/shopee/list.html` | 列表渲染、导入结果显示 | | `admin/testdata/` | 测试固件 | | `docs/admin/01-requirements.md` / `05-ui-specification.md` | 同步已实现状态 | `[注意]` `excelize` 需要的 Go 版本:本项目固定 **Go 1.23.0**。 最新版 excelize 可能要求更高,**装能在 1.23.0 上编译的版本** (参考 `docs/admin/00-getting-started.md` 里已固定的版本策略)。 装不上就停下来说明,不要升 Go。 ## 验收标准 **解析** - [ ] `黑色,M【建議40-50公斤】` → 黑色 / M / 40-50公斤 / ok - [ ] `卡其色拼黑色,L 建議50-57.5kg` → 卡其色拼黑色 / L / 50-57.5kg / ok - [ ] `黑色,M` → 黑色 / M / 空 / ok - [ ] `黑色 【夏裝單件T恤】,L 【57.5/70公斤】` → 颜色含【】仍正确 - [ ] `紅色,3XL建議80-90公斤】` 有确定行为 + 测试固定住 + 注释写清理由 - [ ] 完全认不出的 → `ok=false` 且 **color/size/advice 全为空** **导入** - [ ] 按 sheet **名字**取「最佳表現商品」 - [ ] sheet 不存在 → 报错并列出实际有哪些 sheet,不退回第一个 - [ ] 按**列名**找索引;缺列 → 报错说清缺哪列 - [ ] `商品規格ID == "-"` 归为商品,其余归为 SKU - [ ] **已有的 `pdd_goods_url` / `pdd_goods_id` 导入后仍在**(有独立测试) - [ ] `is_manual = 1` 的 SKU 不被导入删除 - [ ] 失败行返回行号 + 原文 + 原因,界面能全部看到 - [ ] 流式读,不是 `GetRows()` **文件安全** - [ ] 非 `.xlsx` 拒绝(看内容不只看扩展名) - [ ] 落盘用自己生成的文件名,`../` 之类的文件名不会穿越 - [ ] 超过 50MB 拒绝 **其他** - [ ] 测试**不依赖 `raw_data/`**,固件在 `admin/testdata/` - [ ] 列表页能看到导入的数据 - [ ] `ShopeeSave` / `ShopeeDelete` / `ShopeeCollect` **仍是 501**(本工单不做) - [ ] `go vet` / `gofmt -l .` / `go test ./...` 全过 - [ ] 五个页面均 200 ## 怎么验证 ```powershell cd D:\chengma\cmautobuy\admin go vet ./...; gofmt -l .; go test ./... -count=1 go run . ``` **真实样本导入**(本机有 `raw_data/蝦皮数据样本.xlsx`): 打开 http://127.0.0.1:8080/shopee 导入它。 `[必须]` 期望并贴出实际输出: - 导入结果显示 **5195 个商品 / 6092 个 SKU** - 失败行数和行号列出来(预计约 21 行括号不配对,视你的处理方式而定) - 列表页能看到数据 - **再导入一次**:数量不翻倍(upsert 生效) **人工字段保护**(最关键的一条): ```sql -- 导入后手工填一个链接 UPDATE shopee_products SET pdd_goods_url = 'https://x/1' WHERE goods_id = '<任选一个>'; ``` 再导入一次同一个文件,然后查这条记录 —— **`pdd_goods_url` 必须还在**。 `[必须]` 这一步的实际 SQL 输出要贴出来。 ## 风险和回退 | 风险 | 应对 | |---|---| | **导入洗掉人工填的 PDD 链接** | upsert 白名单 + 独立测试 + 验证步骤单列一条 | | 按下标取 sheet,蝦皮调顺序后导错表 | 按名字取,找不到报错 | | 写死列号,蝦皮加列后静默错位 | 按列名找,缺列报错 | | 解析失败时猜值,脏数据流到采购 | `ok=false` 时三个字段必须为空 | | 路径穿越 | 自己生成文件名 | | 全量替换删掉手动新增的 SKU | 禁止 DELETE + INSERT | 回退:`git revert`。已导入的数据可以清表重来(本工单不产生外部副作用)。
Author
Owner

实施完成,待验收

  • 实现提交: e29d668 feat: 蝦皮 Excel 报表导入 (#38)
  • 归档提交: 10b724f
  • 归档: docs/task/38-admin-蝦皮excel报表导入.md

验证结果(架构角色亲自复跑,用 GOTOOLCHAIN=go1.23.0 固定工具链)

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 ... SET pdd_goods_url='https://...', pdd_goods_id='999'
③ 再导一次     计数不变(无重复行),**链接还在**,title 正常刷新

「23 行」核对准确:21 行括号不配对 + 2 行右半整个被【】包住。

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

人工字段保护 —— 往 DO UPDATE SET 里加回 pdd_goods_url = excluded.pdd_goods_url → TestImportShopeeExcel_不覆盖人工字段 立刻变红 ✅

ParseSpec 5 个失败分支 逐个改成返回泄漏值 → 5/5 全部变红 ✅

审查打回一次

ParseSpec 有两个失败分支没有测试覆盖。断言本身是有的,是没有用例走到它们。其中 size=="" 就是真实数据里那 2 行(面膜安全褲3條膚色,【2xl 70-85公斤可穿】)实际走的分支——改成返回 right 的话真实导入会存进乱码颜色且无测试变红。

架构角色的一次误判也记进归档了: 第一轮变异打在「输入为空」分支上(raw 本来就是空串,等于没改),差点据此误报「测试没牙」。

附带修正

发现 /usr/local/go 一直是 1.26.5,而 #16/#17/#18/#19/#20/#24 六份归档都写着「Go 1.23.0 验证通过」——是照项目固定版本抄的,没验证过工具链。已改正措辞,并在 admin/AGENTS.md 加了 [必须]:交付前跑一次带 GOTOOLCHAIN=go1.23.0 的验证。

未验证到的部分

  • Windows / PowerShell 环境未实测
  • 浏览器实机渲染未走查(6092 行表格在 1366×768 下的列宽和横向滚动)
  • 打包成 exe 后 data/uploads/ 的落盘路径未重新验证
## 实施完成,待验收 - **实现提交:** `e29d668` feat: 蝦皮 Excel 报表导入 (#38) - **归档提交:** `10b724f` - **归档:** `docs/task/38-admin-蝦皮excel报表导入.md` ### 验证结果(架构角色亲自复跑,**用 `GOTOOLCHAIN=go1.23.0` 固定工具链**) ``` 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 ... SET pdd_goods_url='https://...', pdd_goods_id='999' ③ 再导一次 计数不变(无重复行),**链接还在**,title 正常刷新 ``` 「23 行」核对准确:21 行括号不配对 + 2 行右半整个被【】包住。 ### 变异测试(工单未要求,架构角色补做) **人工字段保护** —— 往 `DO UPDATE SET` 里加回 `pdd_goods_url = excluded.pdd_goods_url` → `TestImportShopeeExcel_不覆盖人工字段` **立刻变红** ✅ **`ParseSpec` 5 个失败分支** 逐个改成返回泄漏值 → **5/5 全部变红** ✅ ### 审查打回一次 `ParseSpec` 有两个失败分支没有测试覆盖。断言本身是有的,是没有用例走到它们。其中 `size==""` 就是真实数据里那 2 行(`面膜安全褲3條膚色,【2xl 70-85公斤可穿】`)实际走的分支——改成返回 `right` 的话真实导入会存进乱码颜色且无测试变红。 **架构角色的一次误判也记进归档了:** 第一轮变异打在「输入为空」分支上(`raw` 本来就是空串,等于没改),差点据此误报「测试没牙」。 ### 附带修正 发现 `/usr/local/go` 一直是 **1.26.5**,而 #16/#17/#18/#19/#20/#24 六份归档都写着「Go 1.23.0 验证通过」——是照项目固定版本抄的,没验证过工具链。已改正措辞,并在 `admin/AGENTS.md` 加了 `[必须]`:交付前跑一次带 `GOTOOLCHAIN=go1.23.0` 的验证。 ### 未验证到的部分 - Windows / PowerShell 环境未实测 - 浏览器实机渲染未走查(6092 行表格在 1366×768 下的列宽和横向滚动) - 打包成 exe 后 `data/uploads/` 的落盘路径未重新验证
Author
Owner

验收通过,关闭

用户实机确认通过。归档状态已更新(6d40c1e)。

  • 实现提交: e29d668
  • 归档: docs/task/38-admin-蝦皮excel报表导入.md

遗留

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

## 验收通过,关闭 用户实机确认通过。归档状态已更新(`6d40c1e`)。 - **实现提交:** `e29d668` - **归档:** `docs/task/38-admin-蝦皮excel报表导入.md` ### 遗留 Windows 环境、浏览器实机渲染、exe 打包后的落盘路径均未验证。`ShopeeSave` / `ShopeeDelete` / `ShopeeCollect` 仍是 501。手动新增 SKU(`05` §4.4)未做——蝦皮报表只含有销售成绩的 SKU(平均每商品 1.17 个),导入进来的数据本身就不全。
ila closed this issue 2026-08-09 10:06:44 +08:00
Sign in to join this conversation.
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: chengma/cmautobuy#38