From de8dbc77549a05a67ea2ef29782639b708cad69e Mon Sep 17 00:00:00 2001 From: chengma Date: Thu, 23 Jul 2026 09:12:38 +0800 Subject: [PATCH] docs(tasks): add product suite drag duplicate fix --- docs/tasks/T-692.md | 57 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 docs/tasks/T-692.md diff --git a/docs/tasks/T-692.md b/docs/tasks/T-692.md new file mode 100644 index 0000000..60511a1 --- /dev/null +++ b/docs/tasks/T-692.md @@ -0,0 +1,57 @@ +--- +id: T-692 +title: 商品套图原图拖放重复项修复 +phase: 7 +deps: [T-691] +status: TODO +created: 2026-07-23 +--- + +## 问题 / 背景 + +T-691 为使参考图可以拖到第一位,给每个 `QListWidgetItem` 添加了 `Qt.ItemIsDropEnabled` 并继续调用 `QListWidget.InternalMove` 的默认 `dropEvent`。在图标列表中,拖到已有图片上会变成“投放到该项”的模型插入/复制语义,而非单纯的行移动;同一商品原图 ID 因此短暂出现两次。 + +`ProductOriginalList` 随后把重复的 `asset_ids()` 传给 `image_studio.reorder_original_assets()`。服务层的唯一性校验正确阻止了 SQLite 写入,显示“商品原图排序不能包含重复图片”,但用户无法完成主图调整。 + +## 方案 + +### 1. 受控单项内部移动 + +- 真实原图项恢复为可选择、可勾选、可拖动,不再用 `ItemIsDropEnabled` 把“拖到图片上”交给 Qt 默认复制式模型处理。 +- `ProductOriginalList` 自行管理内部拖拽源:开始拖拽时记录当前原图项及其 asset ID;拖放时只接受来源为当前列表、且排序未锁定的单项移动。 +- 根据 drop 坐标和目标缩略图中心计算插入行;使用 `takeItem()` + `insertItem()` 移动同一个 `QListWidgetItem`,不调用 Qt 默认 `super().dropEvent()` 的内部复制路径。 +- 只在移动后最终 `asset_ids` 与移动前不同、ID 数量与集合数量一致时发出一次 `orderChanged`;异常、无效位置、跨列表、锁定状态或重复 ID 一律不写库。 + +### 2. 既有行为保持 + +- 外部文件 URL 拖入继续发送 `filesDropped` 并导入图片;剪贴板、勾选、预览、删除、悬停删除和主图样式不变。 +- 生成中、拉取 URL 或图片下载未完成时继续拒绝内部排序;完成后可重新拖放。 +- 成功排序仍复用 `reorder_originals()` 与 SQLite 单事务服务,刷新后首项显示“主图”;服务层的重复 ID 校验保留为最后防线。 + +### 3. 自动回归 + +- 为列表内部移动增加无需系统原生拖放平台的 Qt 单元回归:第二项移动到首项前后,列表顺序唯一且只发一次 `orderChanged`。 +- 覆盖向下移动、原地 drop、无拖拽源、锁定状态、外部 URL 拖入和重复 ID 防御;项目级回归验证最终排序同步到 SQLite,首项更新为主图。 + +## 验收要点 + +- 将任意参考图拖到第一张位置后,列表仅保留每张图片一次,参考图成为“主图”,不再出现重复图片错误。 +- 向其它位置拖动同样只移动原项,不复制项;原地释放不写库、不提示成功。 +- `orderChanged` 只携带唯一 asset ID 顺序;服务层仍拒绝任何异常重复输入。 +- 外部图片拖入、生成锁定和拉取锁定语义不变。 +- 自动验证: + - `py -3.10 -m unittest tests.test_product_suite_gui tests.test_image_studio tests.test_product_suite` + - `py -3.10 -m unittest discover -s tests` + - `py -3.10 -m ruff check app tests main.py` + - `py -3.10 -m compileall app main.py` + - `git diff --check` + +## 边界(不改什么) + +- 不改 SQLite schema、图片文件、已生成 job 的主图/参考图快照、提示词、cmhub、Shopee CDP 或①至⑤模块。 +- 不放宽 `reorder_original_assets()` 的重复 ID 校验,不改变外部文件导入来源和原图上限。 +- 不实现跨商品、跨任务或多项拖放;本任务只处理当前列表单张原图的可靠排序。 + +## 执行记录 + +- 待实现。