# 141 Admin:商品目录导入支持无 SKU ID 和分级更新策略 - 类型:需求 - 父级大工单:#14 - 所属 MVP / 版本:#15 / 商品目录数据接入后续 - 状态:已完成 - 日期:2026-08-11 - Gitea 工单: ## 背景与目标 部分来源没有真实蝦皮 SKU ID,旧接口和数据库无法导入;多个来源重复提交时也缺少安全的更新边界。本任务允许按商品和规格身份导入,并提供只新增、补空、同来源更新三种策略。 ## 最终方案 - MySQL schema v8 保留历史 `sku_id` 作为内部主键,新增可空且唯一的 `shopee_sku_id` 作为真实外部 ID,并增加唯一 `(goods_id, spec_key)`。 - 无真实 ID 时生成明确只供系统使用的内部主键;后续取得真实 ID 时回填原记录。 - `update_policy` 支持 `insert_only`、默认 `fill_missing`、`overwrite_same_source`。 - 用 `field_sources`、`field_observed_at` 分别记录颜色、尺码、建议和 SKU 编码的来源及时间;同来源策略只更新本来源拥有且时间不旧的字段。 - `is_manual=1` 始终跳过,`spec_raw` 永不改写;同规格出现不同真实 ID 返回 409 并整批回滚。 - 导入响应、查询接口和管理页面分别统计新增、补空、同来源更新、人工跳过和旧数据跳过。 - SQLite 单向迁移在复制后补齐 v8 身份和字段来源,不改写冻结的历史 SQLite schema。 ## 改了哪些 - `admin/repository/mysql_db.go`:追加 schema v8、回填、唯一约束、形状自检和可重放迁移。 - `admin/repository/catalog_import.go`、`admin/service/catalog_import.go`:实现规格身份定位、三种策略、字段来源和结构化冲突。 - `admin/repository/sqlite_to_mysql.go`:兼容历史 SQLite 数据迁入 v8。 - `admin/handler/integration/catalog_api.go`、`admin/templates/catalog/`:输出并展示策略和分级统计。 - `admin/repository/shopee.go`、`admin/model/`:区分内部记录主键与真实外部 SKU ID。 - 对应 repository/service/handler/MySQL 集成测试及 Admin 接口、数据模型文档。 ## 验收结果 | 验收标准 | 结果 | |---|---| | 无真实 SKU ID 可按商品和规格幂等导入 | 通过 | | 后补真实 ID 复用原内部记录 | 通过 | | 同规格不同真实 ID 返回 409,整批回滚 | 通过 | | 默认只补空,不覆盖非空、人工、关联或原文 | 通过 | | 只新增和同来源更新策略边界正确 | 通过 | | 不提供强制覆盖策略 | 通过 | | 响应和记录区分各类结果 | 通过 | | v8 首建、重放、升级、冲突形状测试 | 通过(测试代码);真实 MySQL 环境未启用 | | Go 1.23 build/test/vet,Client 四接口不变 | 通过 | ## 测试 - 执行的命令:`$env:GOTOOLCHAIN='go1.23.0'; go build ./...; go test ./... -count=1; go vet ./...` - 结果:Admin 全部包通过;SQLite 目录服务测试覆盖三种策略、无 ID、补 ID、人工保护、旧时间、跨来源字段和事务回滚。 - **没验证到的部分**:未配置 `CMAUTOBUY_MYSQL_TEST=1` 和独立 MySQL 8.4 `_test` 库,因此真实 MySQL 的首次迁移、中断重放和 v7→v8 集成测试本次跳过;上线前必须按工单要求演练并备份。 ## 相关提交 - `4259be0` 支持无 SKU 目录导入策略