# 41 Admin 蝦皮数据页改为商品级列表与规格弹窗 - 类型:需求(界面重构) - 父级大工单:#14 - 所属 MVP / 版本:#15 / MVP - 关联:#38(导入)、#18(弹窗机制来源) - 状态:验收通过 - 日期:2026-08-08 - Gitea 工单:http://ilaer.eicp.net:8418/chengma/cmautobuy/issues/41 ## 背景与目标 蝦皮数据页原来是 **SKU 级一行**。问题不在行数(6092 → 5195 只少 15%, 平均每个商品仅 1.17 个 SKU),在**语义错了**: `pdd_goods_url` / `pdd_goods_id` 在 `shopee_products` 上,是**商品级**字段。 实测商品 `24420648774` 有 **62 个 SKU**——列表里 62 行显示的是**同一个 PDD 链接**,将来双击任意一行编辑的也是**同一个字段**。操作员会以为 「我改的是这一行的链接」。 ## 最终方案 ```text ☐ │ 商品 ID │ 商品名称 │ 颜色 │ 尺码 │ SKU │ 待补 │ PDD 链接 │ 采集状态 │ 更新时间 15 5 28 2 ``` ### 必须加「SKU 数」列 `[必须]` 只显示颜色数和尺码数会**严重误导**。实测: ``` 颜色数×尺码数 > 实际 SKU 数的商品:602 个(占 11.6%) goods_id 颜色数 尺码数 相乘 实际 SKU 26886533818 15 5 75 28 ← 差 2.7 倍 24773321934 10 6 60 14 27739534478 12 6 72 36 ``` 看到「颜色 15 / 尺码 5」,正常人会以为有 75 个规格要匹配,**实际只有 28 个**。 根因:蝦皮报表只含**有销售成绩的** SKU,数据本来就不全。三个数字放在一起, 操作员才看得出「这商品在报表里只有 28 条记录,不是 75 条」。 ### 必须加「待补」列并整行标黄 `[必须]` 原来 `parse_ok = 0` 的行显示 `—` 并整行标黄,一眼能看见要人工补。 聚合成数量之后这个信号会丢。实测: ``` 23 条解析失败的 SKU,分布在 6 个商品里 4 个是「部分失败」 → 显示「颜色 3」,坏的那几个直接消失 2 个是「全部失败」 → 显示「颜色 0 / 尺码 0」 ``` **「部分失败」那 4 个最危险**——数字看着正常,坏数据被吃掉,永远没人去补。 `[必须]` 颜色数 / 尺码数**只统计 `parse_ok = 1`** 的 SKU。 把解析失败的空值算进 DISTINCT 会多出一个「空颜色」。 ### 弹窗只读,待补的行显示原文 `[必须]` 待补的行必须显示 `spec_raw` 原文——不显示的话人工不知道该填什么, 保留原文这个设计就白做了。 `[必须]` 弹窗**只读**,`ShopeeSave` 仍是 501。不放一个点了没反应的保存按钮。 复用 #18 的弹窗机制(`data-detail-url` / `data-detail-slot` / `data-detail-id`), `app.js` **未改动**。 ## 与建单方案的差异 1. **详情路由用 `?id=` 而不是工单写的 `?goods_id=`。** `app.js` 里 `fetch(base + "?id=" + ...)` 的参数名是硬编码的,而「不改 `app.js`」是 更高优先级的约束。与 PDD 页的 `/pdd/detail?id=` 也保持了一致。 2. 新增 `admin/service/shopee_list_test.go`(工单预计文件表未列)。 ## 改了哪些 - `admin/repository/shopee.go`:`ListShopeeProducts` 商品级聚合查询、 `GetShopeeProductByGoodsID`、`GetShopeeCollectStatus`、 `ListShopeeSKUsByGoodsID`;删除不再使用的 SKU 级查询。 - `admin/service/shopee_list.go`:视图改商品级,新增弹窗用的 `ShopeeProductDetail`。 - `admin/service/shopee_list_test.go`:新建。 - `admin/handler/web/shopee.go`:`ShopeeList` 改写;新增只读 `ShopeeDetail`。 - `admin/handler/web/web.go`:`GET /shopee/detail` 路由。 - `admin/templates/shopee/list.html`:列重写、双击绑定、弹窗壳子。 - `admin/templates/shopee/detail_modal.html`:新建。 - `admin/static/css/app.css`:`.col-title`。 ## 验收结果 架构角色独立复跑,用真实样本导入后逐条核对: | 检查 | 实测结果 | |---|---| | 总行数 | `共 5195 个商品` | | `24420648774` | **1 行**(原 62 行),颜色10 / 尺码8 / SKU**62** / 待补0 | | `26886533818` | 颜色**15** / 尺码**5** / SKU**28**(不是 75) | | **`28431952912`(部分失败)** | 颜色4 / 尺码5 / SKU24 / 待补**4**,**整行标黄** | | `24023581128`(全部失败) | 颜色0 / 尺码0 / SKU1 / 待补1,整行标黄 | | 弹窗显示原文 | `原文:面膜安全褲3條膚色,【2xl 70-85公斤可穿】` | | PDD 链接为空 | 显示「未填写」并标红 | | `app.js` / `shopee_import.go` | 均未改动 | | `ShopeeSave` / `Delete` / `Collect` | 仍是 501(3 处) | | 五个页面 | 全部 200 | `28431952912` 那条是最危险的场景——「部分失败」,颜色尺码数字看着完全正常 (4 和 5),但里面有 4 个 SKU 解析失败。标黄之后藏不住了。 ## 测试 架构角色亲自执行,**用 `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.049s ok cmautobuy/admin/repository 0.753s ok cmautobuy/admin/service 2.081s ``` ### 变异测试(工单未要求,架构角色补做) | 变异 | 结果 | |---|---| | 颜色/尺码数不再过滤 `parse_ok = 1` | 4 个用例变红 | | 待补数恒为 0(标黄失效) | 4 个用例变红 | 这两条正是本工单加进去防信息丢失的,现在有测试守着。 **未验证到的部分:** - **浏览器里的真实交互**:双击开弹窗只验证了服务端 HTML 和 `/shopee/detail` 的返回内容,**没在真实浏览器里点过**。 - **1366×768 下的表格横向滚动**未做视觉走查。 - **`max-width` 用百分比作用在 `