Files
cmshoppe/docs/tasks/T-693.md
T

5.5 KiB
Raw Blame History

id, title, phase, deps, status, created
id title phase deps status created
T-693 商品套图连续拖放丢失显示修复 7
T-692
DONE 2026-07-23

问题 / 背景

T-692 已避免 Qt 默认内部投放复制同一图片项,但用户连续把参考1、参考2、参考3、参考4等图片拖到第一位时,商品原图列表仍可能突然看不到“参考1”,后续拖放也不能稳定写入排序。

现场 SQLite 检查确认相关项目的原图记录、状态和本地文件路径仍完整,未执行删除;异常属于 GUI 列表状态失配。当前 ProductOriginalList._move_dragged_item_to() 在 dropEvent() 尚未退出时同步发送 orderChanged,槽函数 ProductSuiteTab.reorder_originals() 随即写库并调用 _refresh_originals(),通过 clear() 删除全部旧 QListWidgetItem 后重新创建。此时 Qt 的 QDrag 仍持有本轮拖拽源和模型状态,连续拖动会让旧拖拽对象与新列表项发生重入,出现缩略图临时消失、标签缺位或后续排序未提交。

“主图 / 参考1 / 参考2”仍是当前位置标签,不是图片永久名称;每次排序后应连续重编号。只要原图不少于两张,界面刷新后必须存在“参考1”。

方案

1. 拖拽结束后再提交顺序

  • startDrag() 开始前保存唯一的拖拽项和排序快照,并清空上一轮待提交状态。
  • dropEvent() 只用 T-692 的受控 takeItem() + insertItem() 完成当前列表内移动;不在该事件内发送 orderChanged,也不触发列表刷新。
  • 移动成功后仅保存待提交的唯一 asset ID 顺序;原地释放、无效目标、跨列表、锁定状态或重复 ID 不建立待提交顺序。
  • 等 super().startDrag() 返回,确认 Qt 拖拽事务完全结束后,再清理拖拽上下文并发送一次 orderChanged。不使用固定毫秒延时猜测拖拽完成。

2. 保存、失败恢复与显示一致性

  • ProductSuiteTab.reorder_originals() 继续复用 image_studio.reorder_original_assets() 的 SQLite 单事务写入和唯一性校验;成功或失败后的完整刷新发生在拖拽事务之外。
  • 写库成功后按数据库顺序重建列表,并连续显示“主图、参考1、参考2……”;保留勾选状态、图片 ID 和缩略图。
  • 写库失败时重新读取数据库恢复原顺序并显示中文错误,不保留仅存在于 GUI 的临时顺序。
  • 不删除、复制或新增原图资产,不修改本地文件;现有删除确认、生成/拉取锁定及历史 job 快照保持不变。

3. 回归测试

  • 扩展 ProductOriginalList Qt 测试,验证 dropEvent() 阶段只记录待提交顺序,不发送 orderChanged;拖拽结束后只发送一次。
  • 连续模拟把不同参考图移到第一位,接收排序后重建列表,验证每轮图片数量不变、asset ID 唯一且集合不变,标签始终连续包含“主图、参考1……”;最终顺序与 SQLite 一致。
  • 覆盖原地释放、重复 ID、无拖拽源和被锁定时不提交;外部文件 URL 拖入仍只走 filesDropped。

验收要点

  • 对同一商品连续把参考1、参考2、参考3、参考4拖到第一位,每次均只改变顺序,不丢失或复制任何缩略图。
  • 原图不少于两张时,每轮刷新后都显示“参考1”,其余参考图按位置连续编号。
  • 每次有效拖拽只写库一次;无效拖拽不写库、不显示成功。
  • 切换商品或重启程序后,SQLite 中原图数量、图片 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、图片文件、原图删除规则、生成任务、提示词、cmhub、蝦皮 CDP 或①至⑤模块。
  • 不改变“主图 / 参考N”按当前位置动态编号的产品语义,不给图片增加永久参考编号。
  • 不放宽服务层对重复 ID、缺失 ID 和跨项目 ID 的校验,不实现跨列表或多项拖放。

执行记录

  • 2026-07-23 完成。
  • ProductOriginalList 新增单轮拖拽生命周期状态:开始时冻结原图 ID 集合,dropEvent() 仅以受控移动更新列表并保存待提交顺序,无效投放直接拒绝;super().startDrag() 返回后先清除旧拖拽项引用,再发送一次 orderChanged。因此页面写库和 _refresh_originals() 清空重建列表时,Qt 已不再持有上一轮拖拽源。
  • 提交前同时校验当前列表、待提交顺序和拖拽开始快照的数量、唯一性与 ID 集合;原地释放、无拖拽源、重复 ID、列表中途变化和排序锁定均不提交。SQLite 服务层的完整集合与重复 ID 校验保持不变。
  • 扩展列表级测试,确认移动发生时不会提前发信号,拖拽结束才发送一次;新增项目级连续换主图回归,依次把4张参考图移到第一位,每轮确认5张原图全部保留、标签连续、数据库在结束拖拽前不变且结束后与界面一致。同步更新 docs/04-architecture.md 与 docs/routes.md。
  • 验证通过:py -3.10 -m unittest tests.test_product_suite_gui tests.test_image_studio tests.test_product_suite(109项)、py -3.10 -m unittest discover -s tests(676项)、py -3.10 -m ruff check app tests main.py、py -3.10 -m compileall app main.py。离屏 PySide6 仍有字体目录和插件能力告警,但无测试失败。