From 9c22ec6a699d00849cf3f39251e00eb2840ae31a Mon Sep 17 00:00:00 2001 From: chengma Date: Fri, 21 Aug 2026 09:16:45 +0800 Subject: [PATCH] =?UTF-8?q?docs:=20=E5=BD=92=E6=A1=A3=E4=BB=BB=E5=8A=A1=20?= =?UTF-8?q?#283?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/task/283-恢复并发SKU规格唯一键冲突.md | 47 ++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 docs/task/283-恢复并发SKU规格唯一键冲突.md diff --git a/docs/task/283-恢复并发SKU规格唯一键冲突.md b/docs/task/283-恢复并发SKU规格唯一键冲突.md new file mode 100644 index 0000000..7aa4acc --- /dev/null +++ b/docs/task/283-恢复并发SKU规格唯一键冲突.md @@ -0,0 +1,47 @@ +# 283 恢复并发 SKU 规格唯一键冲突 + +- 类型:缺陷 +- 父级大工单:#14 +- 所属 MVP / 版本:#15 / MVP 后续修复 +- 状态:待验收 +- 日期:2026-08-21 +- Gitea 工单:http://ilaer.eicp.net:8418/chengma/cmautobuy/issues/283 + +## 背景与目标 + +顺运宝同步与 `xlsx-processed` 商品目录处理同时写入同一个蝦皮商品规格时,SYB 的“先查询、后新增”路径可能在新增时遇到 `shopee_skus.uq_shopee_skus_spec` 重复键。生产记录确认:目录 SKU 在失败同步运行期间创建,后续重跑同步成功。 + +目标是把这类已经由唯一键确认的同一规格并发写入恢复为正常的幂等写入,同时保留已有的人工优先和来源优先级规则。 + +## 最终方案 + +仅在 MySQL 错误码为 `1062` 且冲突索引明确是 `uq_shopee_skus_spec` 时恢复。代码使用 `SELECT ... FOR UPDATE` 当前读重新取得竞争方已提交的 SKU,再继续执行原有的 SKU 更新分支。 + +当前读避免 MySQL `REPEATABLE READ` 事务继续使用首次查询快照。目录来源或人工来源已有的非空字段仍由原有 `fill_missing` 和人工保护规则决定,SYB 不会覆盖它们。其他唯一键冲突和其他数据库错误仍保持失败,不会被吞掉。 + +本任务没有数据库迁移、数据删除或数据回写。 + +## 改了哪些文件 + +- `admin/repository/catalog_import.go`:增加规格唯一键冲突识别和当前读重读恢复路径。 +- `admin/repository/catalog_import_test.go`:模拟目录竞争方插入后的 1062,验证安全重读和非目标唯一键仍失败。 + +## 验收结果 + +| 验收标准 | 结果 | +|---|---| +| `uq_shopee_skus_spec` 并发撞键可重读并继续处理 | 通过 | +| 目录来源 SKU 的记录、来源和非空颜色字段不被 SYB 覆盖 | 通过 | +| 非规格唯一键 1062 仍原样失败 | 通过 | +| 现有 Admin 全量测试、静态检查和构建通过 | 通过 | + +## 测试 + +- 执行的命令:`$env:GOTOOLCHAIN="go1.23.0"; go test ./repository ./service -count=1; go build ./...` +- 执行的命令:`$env:GOTOOLCHAIN="go1.23.0"; go vet ./...; go test ./... -count=1` +- 结果:全部通过;`repository`、`service` 和 Admin 全部包测试通过。 +- **没验证到的部分**:未对生产数据库执行写操作;未在独立 MySQL `_test` 数据库中制造真实并发事务。单元测试已模拟首查未命中、竞争方提交、1062 和当前读重读过程。 + +## 相关提交 + +- `05b04e0` `fix: 恢复并发 SKU 规格唯一键冲突 (#283)`