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 的话真实导入会存进乱码颜色且无测试变红。
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
基本信息
pdd_products独立成表)、#20(迁移只追加)要解决什么
蝦皮数据模块整个是骨架,四个入口全部返回 501:
TODO(骨架),不查库service.ImportShopeeExcel直接return nil, ErrNotImplemented,excelize连依赖都没装(go.mod里只有 gin 和 sqlite,它只出现在注释里)。蝦皮那 5195 个商品现在还躺在 Excel 里,进不了系统。
已经确认的事实(读真实样本得出,不是猜的)
样本:
raw_data/蝦皮数据样本.xlsx(1.9MB,gitignore 不进库)。① 工作簿有 5 个 sheet,只有一个有数据
-[必须]按 sheet 名字取「最佳表現商品」,不得用第 0 个 sheet。其余 4 个是广告报表,连
商品規格ID列都没有。虽然它现在恰好是第 0 个,但蝦皮调整报表顺序时,按下标取会静默导入一张完全不相干的表。
[必须]找不到这个 sheet 要报错并说清楚,列出实际有哪些 sheet,不要退回用第一个。
② 分行规则:
商品規格ID == "-"是商品汇总行实测 5195 汇总行 + 6092 SKU 行 = 11287,两边都零重复
(
商品ID去重后 5195,商品規格ID去重后 6092)。③ 40 列,必须按列名找索引
前 8 列是业务列,后 32 列全是销售/广告统计(曝光、点击率、转换率……):
[必须]按列名定位,不要写死列号。报表 40 列,蝦皮加一列统计指标所有列号就全错位了,而且不会报错——会把「點擊率」当成「商品規格」存进去。
[必须]缺少必需列时报错并说清缺哪一列,不要用零值继续。④ 规格原文的真实分布
[必须]只认【】会漏掉 46.6%。关键实测结论:全部 6092 行恰好有一个逗号——0 行没有逗号,0 行有多个逗号。
所以「按第一个逗号切开」是安全的,左边是颜色、右边是尺码+建议。
[注意]颜色部分自己也可能带【】:黑色 【夏裝單件T恤】,L 【57.5/70公斤】。按逗号切仍然正确。⑤ 21 行(0.3%)括号不配对
[必须]这是真实的脏数据,必须明确处理:不许因此崩溃,也不许猜。做什么 / 不做什么
做:
excelize依赖ParseSpecImportShopeeExcel(流式读、upsert、统计)ShopeeImporthandler(文件校验、落盘、调 service、显示结果)ShopeeList(列表能看到导入结果)不做(各自独立工单):
ShopeeSave/ShopeeDelete/ShopeeCollect(保持 501)05§4.4)pdd_products及采集相关任何代码怎么做
upsert 绝不清空人工字段 —— 本工单最要命的一条
shopee_products当前 8 列(v3 之后):[必须]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时前三个必须为空,parse_ok存 0,spec_raw照存原文,界面上标出来让人工补。[必须]不要猜。猜出来的颜色尺码会一路传到采购任务,最后买错东西,而且没人知道这个值是猜的。
[必须]那 21 行括号不配对的,按「能提取多少算多少」还是「整条判失败」由实现决定,但必须写清理由并有测试固定住行为。两种都可接受,
不可接受的是「碰巧能跑通但没人知道它怎么处理的」。
失败行不许静默跳过
[必须]返回结构里带失败行号和原因,界面上列出来。[必须]界面显示时,失败行多的话要能全部看到(滚动或折叠),不要只显示"失败 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.sumgithub.com/xuri/excelize/v2admin/service/shopee_import.goParseSpec+ImportShopeeExceladmin/service/shopee_import_test.goadmin/repository/shopee.goadmin/handler/web/shopee.goShopeeImport/ShopeeList真实实现admin/templates/shopee/list.htmladmin/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 全为空导入
商品規格ID == "-"归为商品,其余归为 SKUpdd_goods_url/pdd_goods_id导入后仍在(有独立测试)is_manual = 1的 SKU 不被导入删除GetRows()文件安全
.xlsx拒绝(看内容不只看扩展名)../之类的文件名不会穿越其他
raw_data/,固件在admin/testdata/ShopeeSave/ShopeeDelete/ShopeeCollect仍是 501(本工单不做)go vet/gofmt -l ./go test ./...全过怎么验证
真实样本导入(本机有
raw_data/蝦皮数据样本.xlsx):打开 http://127.0.0.1:8080/shopee 导入它。
[必须]期望并贴出实际输出:人工字段保护(最关键的一条):
再导入一次同一个文件,然后查这条记录 ——
pdd_goods_url必须还在。[必须]这一步的实际 SQL 输出要贴出来。风险和回退
ok=false时三个字段必须为空回退:
git revert。已导入的数据可以清表重来(本工单不产生外部副作用)。实施完成,待验收
e29d668feat: 蝦皮 Excel 报表导入 (#38)10b724fdocs/task/38-admin-蝦皮excel报表导入.md验证结果(架构角色亲自复跑,用
GOTOOLCHAIN=go1.23.0固定工具链)excelize v2.9.1的go指令正好是1.23.0,与项目固定版本一致。真实样本端到端
「23 行」核对准确:21 行括号不配对 + 2 行右半整个被【】包住。
变异测试(工单未要求,架构角色补做)
人工字段保护 —— 往
DO UPDATE SET里加回pdd_goods_url = excluded.pdd_goods_url→TestImportShopeeExcel_不覆盖人工字段立刻变红 ✅ParseSpec5 个失败分支 逐个改成返回泄漏值 → 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的验证。未验证到的部分
data/uploads/的落盘路径未重新验证验收通过,关闭
用户实机确认通过。归档状态已更新(
6d40c1e)。e29d668docs/task/38-admin-蝦皮excel报表导入.md遗留
Windows 环境、浏览器实机渲染、exe 打包后的落盘路径均未验证。
ShopeeSave/ShopeeDelete/ShopeeCollect仍是 501。手动新增 SKU(05§4.4)未做——蝦皮报表只含有销售成绩的 SKU(平均每商品 1.17 个),导入进来的数据本身就不全。