- main_window: wrap workflow in a QStackedWidget (page 0 = print, page 1 = AI outfit); enable tab 2「AI 穿搭」, switch pages on tab change. - app/widgets/ai_outfit_panel.py: three-column page per docs/11 §10 — left settings (Excel/output/model/prompt editor+save+insert+preview dialog/batch options), center (recent-results thumbnails + detail table), right (progress/stats/start/stop/export failures/log). - Threading: QThread + _OutfitWorker(QObject) wraps OutfitBatchRunner; queued signals refresh UI, each row written back to Excel on the worker thread; finish summary + failure-list CSV export. - config_service: load_ai_models()/load_outfit_prompt()/save_outfit_prompt() + outfit_* keys in app_config; panel persists via config_changed signal. - tests/test_config_service.py: 8 cases for the new helpers. Full suite (12 files) green on Python 3.7; offscreen MainWindow smoke test passes. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
43 KiB
开发任务清单
任务文档说明
本文档用于跟踪自动合成印花服饰效果图工具的整体开发任务。后续 AI 或开发者领取任务前,必须先阅读对应任务的前置文档,并严格遵守 docs/04-development-rules.md。
任务状态说明:
[x]已完成。[ ]未开始。[~]进行中或部分完成。
任务执行顺序
任务编号即建议开发顺序,后续任务依赖前序任务的产出:
1(基础服务)→ 2(数据模型)→ 3/4(文件/模板服务)
→ 5(图片合成核心)→ 6(批量任务核心)→ 6.5(低对比组合筛选核心)→ 7(主界面布局)→ 8-13(各 UI 面板)→ 14(微调状态)→ 15(测试)→ 16(打包)
任务 1-6 完成前不应开始任务 7 及以后的 UI 任务,否则 UI 层将缺少可依赖的数据模型和服务层。
执行每个「完善 xxx.py」类任务前,必须先读取该文件的当前内容,了解骨架代码现状,再决定新增或修改哪些内容,避免覆盖已有实现。
0. 已完成基础工作
- 编写项目愿景文档:
docs/01-product-vision.md - 编写 PRD:
docs/02-prd.md - 编写技术栈文档:
docs/03-technical-stack.md - 编写开发规则文档:
docs/04-development-rules.md - 编写项目架构文档:
docs/05-project-architecture.md - 编写 UI 效果图提示词:
docs/06-ui-mockup-prompt.md - 整理 Claude UI 提示词版本:
docs/06-ui-mockup-prompt-claude.md - 保存 UI 效果图设计稿:
docs/ui-v1.html - 保存 UI 效果图截图:
docs/ui-v1.png - 编写 UI 设计文档:
docs/07-ui-design.md - 编写图像编辑器设计文档:
docs/08-image-editor-design.md - 编写本地打包发布文档:
docs/09-packaging-release.md - 创建
requirements.txt - 创建
src/version.py,统一维护APP_NAME和APP_VERSION - 创建
src/main.py最小 PySide6 入口 - 创建
src/app/main_window.py最小主窗口 - 创建
src/app/widgets/占位模块 - 创建
src/core/占位模块 - 创建
src/services/占位模块 - 创建
src/resources/资源目录 - 创建
tests/测试目录 - 验证本机 Python 版本为
3.7.9 - 验证 PySide6 版本为
6.5.3 - 通过
python -m py_compile语法检查 - 验证最小 PySide6 窗口可以创建并关闭
- 已提交文档规划代码
- 已提交 PySide6 工程骨架代码
1. 工程基础完善
1.1 日志服务
前置阅读:
docs/04-development-rules.mddocs/05-project-architecture.md
任务:
- 先读取
src/services/log_service.py现有内容 - 完善
src/services/log_service.py - 启动时创建
logs/目录 - 日志文件按启动时间或日期命名
- 日志格式包含时间、模块、级别、消息
- 在
src/main.py中初始化日志
验收:
- 启动程序后生成日志文件
- 程序启动信息写入日志
- 不使用
print作为正式日志
1.2 路径与资源服务
前置阅读:
docs/04-development-rules.mddocs/05-project-architecture.mddocs/09-packaging-release.md
任务:
- 在
src/services/file_service.py中新增路径辅助函数(不单独建模块) - 实现
get_app_dir():返回程序根目录,兼容开发环境和 PyInstaller 打包环境 - 实现
get_resource_path(relative_path):返回资源文件绝对路径 - 实现
get_config_path(relative_path):返回配置文件绝对路径 - 实现
get_log_dir():返回日志目录路径 - 实现
get_output_dir():返回默认输出目录路径 - 保证 Windows 中文路径可用
验收:
- 路径函数不使用开发机绝对路径
- 程序目录下缺少
logs/或output/时可自动创建
1.3 配置服务
前置阅读:
docs/04-development-rules.mddocs/05-project-architecture.md
任务:
- 先读取
src/services/config_service.py现有内容 - 完善
src/services/config_service.py - 定义默认配置
- 支持读取 JSON 配置
- 支持保存 JSON 配置
- 配置损坏时使用安全默认值并记录日志
验收:
- 缺少配置文件时程序可启动
- 配置文件损坏时程序可启动并记录日志
- 不会无提示清空用户配置
2. 核心数据模型
前置阅读:
docs/02-prd.mddocs/04-development-rules.mddocs/05-project-architecture.mddocs/08-image-editor-design.md
任务:
- 先读取
src/core/models.py现有内容 - 完善
src/core/models.py - 定义
ImageAsset - 定义
TransformState - 定义
Template - 定义
ExportOptions - 定义
BatchOptions - 定义
ComposeResult - 定义
BatchResult
验收:
models.py不依赖 PySide6 UI 控件- 变换状态包含
x/y/width/height/rotation/keep_aspect_ratio - 模型可以被 core、services 和 UI 层复用
3. 文件扫描与素材导入
前置阅读:
docs/02-prd.mddocs/04-development-rules.mddocs/05-project-architecture.mddocs/07-ui-design.md
任务:
- 先读取
src/services/file_service.py现有内容 - 完善
src/services/file_service.py - 支持递归扫描衣服图片文件夹
- 支持递归扫描印花图片文件夹
- 支持格式:PNG、JPG、JPEG、WEBP
- 忽略不支持格式并记录日志
- 保留来源子文件夹信息
- 生成安全输出文件名
验收:
- 可以扫描包含子文件夹的中文路径
- 不修改原始素材文件
- 不支持文件格式不会导致扫描失败
4. 模板服务
前置阅读:
docs/02-prd.mddocs/04-development-rules.mddocs/05-project-architecture.md
任务:
- 先读取
src/services/template_service.py现有内容 - 完善
src/services/template_service.py - 定义内置模板
- 支持读取自定义模板 JSON
- 支持保存自定义模板 JSON
- 支持新增、重命名、删除自定义模板
- 校验模板字段
验收:
- 内置模板和自定义模板可区分
- 单个模板损坏不影响全部模板加载
- UI 不直接操作模板 JSON 文件
5. 图片合成核心
前置阅读:
docs/02-prd.mddocs/04-development-rules.mddocs/05-project-architecture.mddocs/08-image-editor-design.md
任务:
- 先读取
src/core/composer.py现有内容 - 完善
src/core/composer.py - 使用 Pillow 读取衣服底图和印花图
- 支持透明 PNG alpha 合成
- 支持按
TransformState缩放印花 - 支持按
TransformState旋转印花 - 支持旋转后按中心点对齐粘贴
- 支持 PNG 导出
- 支持 JPG 导出
- 输出文件名避免默认覆盖
验收:
- 合成逻辑不依赖 PySide6 UI 控件
- 输出尺寸默认与衣服底图一致
- 透明 PNG 不出现黑底或白底
- 印花部分超出画布时不会报错
6. 批量任务核心
前置阅读:
docs/02-prd.mddocs/04-development-rules.mddocs/05-project-architecture.md
任务:
- 先读取
src/core/batch.py现有内容 - 完善
src/core/batch.py - 支持多衣服 × 单印花
- 支持单衣服 × 多印花
- 支持一一匹配
- 支持全组合
- 复用
core/composer.py - 单个任务失败时继续处理剩余任务
- 汇总成功数量、失败数量和失败原因
验收:
- 不复制第二套合成算法
- 单张失败不会中断整个批量任务
- 失败原因可供 UI 和日志使用
6.5 低对比组合筛选核心
前置阅读:
docs/02-prd.mddocs/04-development-rules.mddocs/05-project-architecture.mddocs/08-image-editor-design.md
任务:
- 定义可见度状态:
正常、偏低、不明显、无法判断 - 新增低对比分析核心函数,位置应符合架构分层,不能写进 UI 事件
- 基于当前模板或
TransformState获取衣服目标区域 - 只分析衣服目标区域颜色,不使用整张衣服图判断
- 只统计印花 alpha 有效像素,例如
alpha > 20 - 计算 RGB 颜色距离
- 计算亮度差
- 根据阈值输出可见度状态和评分
- 单个组合分析失败时返回
无法判断,不得中断整个队列 - 为后续队列项保存可见度结果预留字段或结果结构
验收:
- 白色衣服配浅色印花时可标记为
偏低或不明显 - 深色衣服配深色印花时可标记为
偏低或不明显 - 透明 PNG 的透明区域不参与印花颜色判断
- 低对比分析不修改原始衣服图片和原始印花图片
- 分析逻辑不依赖 PySide6 UI 控件
- 失败项不会中断批量队列生成或导出流程
7. 主界面布局
前置阅读:
docs/04-development-rules.mddocs/05-project-architecture.mddocs/07-ui-design.md
任务:
- 先读取
src/app/main_window.py现有内容 - 将
src/app/main_window.py从最小窗口扩展为主布局 实现顶部标题和版本号(后移除内容区自绘标题栏:与系统窗口标题重复,名称/版本改由系统标题栏显示,见 17.7)- 实现流程页签区域
- 实现左侧素材栏容器
- 实现中间预览区容器
- 实现右侧参数栏容器
- 实现底部合成队列容器
- 实现底部状态栏
验收:
- 窗口标题显示
APP_NAME和APP_VERSION - UI 布局接近
docs/ui-v1.png - 不把所有业务逻辑塞进
main_window.py
8. 素材列表 UI
前置阅读:
docs/02-prd.mddocs/05-project-architecture.mddocs/07-ui-design.md
任务:
- 先读取
src/app/widgets/image_list_panel.py现有内容 - 实现衣服图片列表
- 实现印花图片列表
- 实现打开文件夹按钮
- 实现全选和取消全选
- 实现单张勾选和取消勾选
- 显示已选数量和总数量(例如:
含子文件夹 · 共 12) - 显示来源子文件夹标签和文件名
- 实现排序选择器(默认按文件夹+名称排序)
- 点击素材后更新当前预览选择
验收:
- 支持递归导入素材
- 当前预览选中态和批量勾选态可区分
- 不在 UI 层重复实现文件扫描规则
9. 图像预览编辑器
前置阅读:
docs/04-development-rules.mddocs/05-project-architecture.mddocs/07-ui-design.mddocs/08-image-editor-design.md
任务:
- 先读取
src/app/widgets/image_canvas.py现有内容 - 使用
QGraphicsView / QGraphicsScene - 显示衣服底图(scene 坐标与原图像素坐标保持一致)
- 显示印花图层
- 实现印花拖动
- 实现缩放控制点(至少四角)
- 实现旋转控制点(顶部中心外侧)
- 实现预览缩放(通过
QGraphicsViewview transform,不修改 scene 内容) - 实现预览顶部栏:显示当前底图文件名、印花文件名、操作模式按钮(移动/缩放/旋转)、缩放比例控制
- 实现画布操作提示 Overlay(拖动移动 / 四角缩放 / 顶部旋转 / Shift 锁比例)
- 将交互结果同步为
TransformState
验收:
- 预览缩放不改变真实合成参数
- 拖动后坐标为衣服原图像素坐标
- 缩放后宽高同步
- 旋转后角度同步
- 不使用截图作为导出结果
10. 参数面板 UI
前置阅读:
docs/05-project-architecture.mddocs/07-ui-design.mddocs/08-image-editor-design.md
任务:
- 先读取
src/app/widgets/transform_panel.py现有内容 - 完善
src/app/widgets/transform_panel.py - 实现 X/Y 坐标输入
- 实现宽度/高度输入
- 实现锁定宽高比例
- 实现角度输入
- 实现左旋 90 度和右旋 90 度按钮
- 与
ImageCanvas双向同步TransformState
验收:
- 参数输入后预览同步更新
- 预览交互后参数输入框同步更新
- 程序化更新输入框不会造成信号循环
11. 模板面板 UI
前置阅读:
docs/02-prd.mddocs/05-project-architecture.mddocs/07-ui-design.md
任务:
- 先读取
src/app/widgets/template_panel.py现有内容 - 完善
src/app/widgets/template_panel.py - 显示模板下拉框
- 支持选择模板
- 支持保存模板
- 支持另存为模板
- 支持重置为模板
- 通过
template_service操作模板
验收:
- 选择模板后更新预览和参数面板
- UI 不直接修改模板 JSON
- 自定义模板重启后仍可用
12. 导出面板 UI
前置阅读:
docs/05-project-architecture.mddocs/07-ui-design.mddocs/09-packaging-release.md
任务:
- 先读取
src/app/widgets/export_panel.py现有内容 - 完善
src/app/widgets/export_panel.py - 实现输出目录选择
- 实现输出格式选择
- 实现质量选择
- 实现导出当前单张
实现应用为模板入口(后移除:与模板区域「另存为」重复,见 17.3)实现重置为模板入口(后移除:与模板区域「重置为模板」重复,见 17.3)- 显示当前操作影响范围提示
验收:
- 缺少衣服或印花时导出按钮禁用
- 输出目录不可写时提示用户并记录日志
- 单张导出调用
core/composer.py
13. 合成队列 UI
前置阅读:
docs/02-prd.mddocs/05-project-architecture.mddocs/07-ui-design.md
任务:
- 实现底部合成队列表格(建议使用
QTableView+ 模型视图结构) - 实现队列头部批量模式切换:多衣服×单印花 / 单衣服×多印花 / 一一匹配 / 全组合(矩阵)
- 实现队列统计显示(共 N 项 · 完成 · 进行 · 失败 · 待导出)
- 实现「重置全部」按钮
- 实现「导出选中」按钮
- 实现「开始批量导出」按钮(导出中切换为停止/暂停)
- 实现队列折叠/展开
- 显示任务状态(已完成 / 待导出 / 导出中 / 失败)
- 导出中任务显示进度条
- 显示失败原因
- 支持选中队列项并更新预览区和参数面板
验收:
- 队列统计显示总数、完成、进行、失败、待导出
- 导出中任务显示进度条
- 失败行显示可理解原因
- 选中队列项可以加载对应状态
14. 微调状态
前置阅读:
docs/07-ui-design.mddocs/08-image-editor-design.md
任务:
- 定义队列项微调状态
- 手动调整队列项后标记为
已微调 - 微调项保存独立
TransformState - 批量导出时微调项使用自己的参数
- 未微调项使用模板参数
验收:
- 已微调项不会被无提示覆盖
- 队列表格能显示
模板和已微调
15. 测试
前置阅读:
docs/04-development-rules.mddocs/05-project-architecture.mddocs/08-image-editor-design.md
任务:
- 编写
tests/test_composer.py - 编写
tests/test_templates.py - 测试透明 PNG 合成
- 测试缩放参数
- 测试旋转中心
- 测试文件扫描
- 测试模板读写
- 测试批量任务生成
验收:
- 核心逻辑测试不依赖 GUI
- 测试可在 Python 3.7 环境运行
16. 打包
前置阅读:
docs/03-technical-stack.mddocs/04-development-rules.mddocs/09-packaging-release.md
任务:
- 创建打包脚本
- 验证 PyInstaller
onedir打包 - 打包资源文件
- 打包默认配置和模板
- 验证打包后程序启动
- 验证标题栏版本号
- 验证单张导出
验收:
- 打包产物可在无 Python 环境的 Windows 电脑启动
- 发布包不包含源代码、缓存、测试输出和私人配置
17. 体验优化(持久化与独立重置)
第二阶段根据实际使用反馈新增。
17.1 用户偏好持久化
前置阅读:
docs/02-prd.md(6.6 模板选择、6.9 批量合成导出、9 数据与文件结构)docs/05-project-architecture.md(4.12 config_service)
任务:
- 先读取
src/services/config_service.py与src/app/main_window.py现有内容 - 在
main_window启动时调用load_config()并把初值分发给各面板(集中管理,不让控件各自读写) - 在
config_service默认配置中新增last_template、last_batch_mode - 记住并在启动时恢复上次选择的模板;自定义模板已删除时回退到第一个内置模板
- 记住并在启动时恢复上次选择的批量模式;持久化值非法时回退默认(全组合)
- 面板选择变化时「改一次存一次」回写配置
template_panel增加模板选择变化信号(携带模板名)queue_panel增加set_batch_mode()与批量模式变化信号
验收:
- 重启后自动恢复上次选择的模板和批量模式
- 自定义模板被删除或批量模式值非法时安全回退,不报错
- UI 控件不直接读写配置文件
17.2 位置 / 尺寸 / 旋转独立重置
前置阅读:
docs/07-ui-design.md(7 右侧参数栏、10 视觉规范)docs/08-image-editor-design.md
任务:
- 先读取
src/app/widgets/transform_panel.py现有内容 - 在
位置/尺寸/旋转三个分区标题右侧各加一个独立重置按钮 - 各重置只把对应参数还原为当前选中模板的值,互不影响(位置→仅 X/Y;尺寸→仅宽/高,位置不变;旋转→仅角度)
- 移除合并的
重置位置 / 尺寸 / 角度按钮与角度旁的归零按钮 - 重置按钮采用幽灵样式(静止灰、悬停蓝、按下深蓝、禁用更灰),不使用红色
- 未选模板或未加载衣服/印花时三个按钮置灰
- 由
main_window取模板换算值并按项合并进当前TransformState
验收:
- 位置、尺寸、旋转可分别独立重置,互不影响
- 尺寸重置只改变宽 / 高,不改变位置 X / Y
- 按钮视觉层级符合
docs/07-ui-design.md第 10 节
17.3 按钮去重(导出面板模板入口)
前置阅读:
docs/07-ui-design.md(7.1 模板区域、7.7 操作按钮)
任务:
- 移除导出面板的
应用为模板与重置为模板按钮(与模板区域的另存为/重置为模板是同一动作的重复入口) - 模板相关操作统一留在模板区域,导出面板只负责输出与导出
验收:
- 同一个动作在界面上只出现一次,命名一致
- 导出面板仅保留输出设置与
导出当前单张
17.4 「重置为模板」归位并更名为「位置尺寸旋转重置为当前模板」
前置阅读:
docs/07-ui-design.md(7 右侧参数栏、10 视觉规范)
任务:
- 把模板区域的
重置为模板按钮移出,移到参数栏底部(旋转分区下方) - 更名为
位置尺寸旋转重置为当前模板,与三个分项↺ 重置构成「全部 + 分项」两级 - 还原逻辑改为把位置/尺寸/旋转整体设为模板值,并与分项重置同走
_apply_preview_transform - 与三个分项重置一起受「未加载图片时置灰」控制
- 采用安静、全宽、悬停出强调色的样式(不使用红色)
验收:
- 模板区域只剩
保存/另存为,不含整体重置 位置尺寸旋转重置为当前模板在旋转分区下方,未加载图片时置灰- 点击后位置、尺寸、旋转一并还原为当前选中模板
17.5 模板删除按钮
前置阅读:
docs/07-ui-design.md(7.1 模板区域)
任务:
- 在模板区域增加
删除按钮(复用template_service.delete_template) 删除仅对自定义模板可用,选中内置模板时隐藏(与保存一致)- 删除前弹二次确认对话框;确认后才删除
- 删除后刷新下拉框并回选第一个内置模板,同步持久化选择
删除用红色文字提示危险但保持安静样式
验收:
- 可在界面上删除自定义模板,无需手改
templates.json - 内置模板不显示
删除 - 删除有二次确认,取消则不删
17.6 参数/导出面板紧凑化与命名
前置阅读:
docs/07-ui-design.md(7.2 位置区域、7.3 尺寸区域、7.5 输出设置区域)
任务:
- 「位置」的 X、Y 输入框放在同一行(标签简化为
X/Y) - 「尺寸」的宽、高输入框放在同一行(标签简化为
宽/高),锁定宽高比例在其下方 - 导出面板「格式」「质量」放在同一行
- 底部整体重置按钮更名为
位置尺寸旋转重置为当前模板 - 仅改布局/文案,不动同步与导出逻辑
验收:
- X/Y 一行、宽/高一行、格式/质量一行,界面更紧凑
- 拖动/缩放预览后输入框仍正常同步
- 质量仍仅在 JPG 时可用
17.7 移除重复的自绘标题栏与无效「设置」按钮
前置阅读:
docs/07-ui-design.md(3 整体布局、4.1 标题栏)
任务:
- 移除标题栏里点了无反应的占位「设置」按钮
- 移除内容区自绘标题栏(与系统窗口标题重复),名称/版本由系统标题栏(
setWindowTitle)显示 - 同步清理相关样式与未用常量
验收:
- 界面不再出现重复的软件名称/版本号
- 系统窗口标题仍显示
APP_NAME与APP_VERSION - 工作区因移除标题栏而获得更多纵向空间
17.8 加载文件夹后默认预览第一张
前置阅读:
docs/07-ui-design.md(5.1 衣服图片面板)
任务:
- 衣服、印花面板加载文件夹后,自动选中并预览第一张(按当前排序)
- 空文件夹不选;把选中逻辑抽成按行选择以复用
- 与现有「模板粘性」衔接:自动选印花后按当前模板落位
验收:
- 加载文件夹后画布不再为空、无需手动点选
- 两个文件夹加载顺序无关,最终都能得到合成预览
17.9 输出按「时间戳 → 印花」分组到子文件夹
前置阅读:
docs/02-prd.md(6.8 单张合成导出、9 数据与文件结构)
任务:
make_safe_output_path输出到<输出目录>/<印花文件名>/<衣服文件名>.<扩展名>- 新增
timestamped_run_dir(base),每次导出运行在输出目录下新建时间戳文件夹 - 一次导出运行(批量/单张)共用同一个时间戳;批量在开始时计算一次
- 单张导出、队列导出、
core.batch.run_batch均接入时间戳层 - 子文件夹由
composer.compose保存时自动创建;重名仍追加_1/_2
验收:
- 路径形如
输出目录/<时间戳>/<印花文件名>/<衣服文件名>.<扩展名> - 重复运行落在不同时间戳文件夹,互不覆盖
- 一次批量运行内所有图片共用同一个时间戳文件夹
17.10 记住上次的衣服/印花文件夹
前置阅读:
docs/02-prd.md(6.1 图片加载、9 数据与文件结构)docs/05-project-architecture.md(4.12 config_service)
任务:
_AssetPanel打开对话框时定位到上次目录(set_start_dir),并在选中后发folder_opened信号ImageListPanel暴露set_garment_start_dir/set_print_start_dir与garment_folder_opened/print_folder_openedmain_window启动恢复last_garment_dir/last_print_dir,并在打开文件夹时「改一次存一次」回写- UI 不直接读写配置,统一经主窗口
验收:
- 重启后打开「打开文件夹」对话框定位到上次的衣服/印花目录
- 目录已不存在时安全回退,不报错
17.11 修复:批量导出忽略所选输出格式/质量/目录
前置阅读:
docs/07-ui-design.md(7.5 输出设置区域、8 底部合成队列)
问题:
queue_panel.set_export_options()从未被调用,导出面板也无「选项变化」信号,导致队列始终使用默认ExportOptions(PNG、默认目录),批量导出无视 UI 所选格式/质量/目录。
任务:
- 导出面板新增
export_options_changed信号与公开current_options() - 格式/质量/输出目录任一变化即发出当前
ExportOptions main_window接到queue_panel.set_export_options,并在启动时同步一次- 单张导出
_do_export改用同一current_options()来源
验收:
- 选 JPG 后批量导出的文件为
.jpg且按 JPEG 保存 - 批量导出跟随所选输出目录与质量
17.12 合并图文件名加上印花名
前置阅读:
docs/02-prd.md(6.8 单张合成导出、9 数据与文件结构)
任务:
make_safe_output_path的文件名由<衣服文件名>改为<衣服文件名>_<印花文件名>- 目录分组结构不变(仍
输出目录/<时间戳>/<印花文件名>/) - 重名仍追加
_1/_2避免覆盖
验收:
- 输出文件形如
输出目录/<时间戳>/TY030/1_TY030.png - 单张导出与批量导出一致
17.13 隐藏「导出当前单张」按钮
前置阅读:
docs/07-ui-design.md(7.7 操作按钮、10 视觉规范)
任务:
- 隐藏导出面板的
导出当前单张按钮(setVisible(False)) - 保留按钮与
_do_export逻辑,便于后续需要时再显示 - 导出统一经底部合成队列完成
验收:
- 导出面板不再显示
导出当前单张,只剩输出设置 - 队列的批量/选中导出不受影响
17.14 修复:选中未微调队列项时预览未按模板落位
前置阅读:
docs/02-prd.md(6.6 模板选择)docs/07-ui-design.md(12.4 选中队列项)
问题:
_on_queue_item_activated仅在item.transform存在(已微调)时覆盖画布变换;未微调项保留load_print的写死默认(印花≈衣服宽 40%、居中),预览尺寸与选中模板不一致——「模板粘性」在队列项激活路径的遗漏分支。
任务:
- 选中队列项且
item.transform为空时,调用template_panel.apply_current()按当前模板落位 - 仍在
_loading_queue_item保护下,避免被误标为「已微调」 - 与预览流程(
_on_print_preview)行为一致
验收:
- 点击未微调队列项,预览印花为选中模板要求的大小/位置
- 已微调项仍显示其自身参数,不被覆盖
17.15 修复:批量导出对未微调项复用单个像素变换
前置阅读:
docs/02-prd.md(6.6 模板选择)docs/08-image-editor-design.md(变换状态)
问题:
QueuePanel用单个像素级TransformState(_template_transform,来自template_applied,按「当时画布那张衣服/印花尺寸」算出)作为所有未微调项的回退变换。TransformState是「衣服原图像素坐标」,对尺寸不同的衣服/印花原样复用会错位、错尺寸(如 1000×1000 算出的 x=400/w=250 套到 2000×3000 上,印花跑到左上、尺寸只剩一半)。- 影响批量导出与「导出选中」中所有未微调项。
任务:
- 队列改存模板对象(比例坐标
Template)而非单个像素变换:set_template()取代set_transform() - 导出时按每项自身衣服/印花尺寸现算:抽纯函数
core.composer.resolve_transform()(已微调项用自身变换,否则template.to_transform_state(gw,gh,pw,ph)) - widget 仅负责读图尺寸(
core.composer.image_size(),只读 header)+ 单次运行内缓存 main_window在template_changed/template_applied时把模板推给队列,并在启动时初始同步- 新增 4 个纯函数单测覆盖:已微调优先、不同尺寸各自换算、空模板/空尺寸返回 None
验收:
- 一个批次内含不同尺寸的衣服/印花时,未微调项各自按模板正确落位
- GUI 实测:混合尺寸批量导出,结果图印花位置/大小均正确
17.16 局域网更新 · 阶段②:启动时检测并通知
前置阅读:
docs/10-lan-update.md(§7 更新源、§8 流程、§16 阶段②)docs/02-prd.md(app_config 的update_source)
说明:
- 阶段①(数据目录分离)已在 commit
76f2c6d完成。本任务实现阶段②「只读通知」,不自动安装。
任务:
- 配置项
update_source(空 = 不检查)写入DEFAULT_CONFIG services/update_service.py:parse_version/is_newer/check_for_update,读取<source>/manifest.json,任何不可达/损坏/非更新一律返回 None(不抛错、不阻塞)- 主窗口顶部通知横幅(默认隐藏):「发现新版本 vX.Y.Z」+「打开更新目录」+ 关闭
- 检查在后台守护线程进行,经 Qt 队列信号回主线程显示横幅(更新源不可达不卡启动)
- 「打开更新目录」用
QDesktopServices打开manifest.source - 15 个纯函数单测覆盖版本比较与
check_for_update各分支
验收:
- 单测通过;更新源为空/不可达时静默跳过
- GUI 实测:配置可达更新源 + 高版本 manifest → 启动后显示横幅,点击打开目录
- GUI 实测:更新源不可达 → 正常启动、无横幅、无卡顿
17.17 局域网更新 · 阶段③:自动安装启动器(脚本)
前置阅读:
docs/10-lan-update.md(§3 架构、§8 流程、§16 阶段③)
说明:
- 实现 PowerShell 启动器
scripts/update.ps1,跑通「检查 → staging → 校验 → 原子切换 → 启动 → 降级」整条链路。先以脚本验证流程,后续再决定是否编译为Launcher.exe。
任务:
scripts/update.ps1:读current.txt与data\config\app_config.json的update_source- 读远端
manifest.json、语义化版本比较,仅当远端更高才更新 - robocopy 拷到
staging\<ver>.tmp→ 校验marker→ 重命名进versions\<ver>→ 原子写current.txt(ascii 无 BOM) - 启动
versions\<current>\CMBot.exe并设CMBOT_DATA_DIR - 降级:源不可达 / robocopy 失败 / marker 缺失 / 坏 manifest 一律启动本地现版本
- 假版本目录验证 6 个用例(更新、幂等、源不可达、未配置源、下载损坏、坏 manifest)全过
- 接入真实安装结构:新增
scripts/install_local.ps1产出%LOCALAPPDATA%\CMBot\app/data/staging启动器布局 - 真实双机实测
17.18 更新传输统一改为 HTTP
前置阅读:
docs/10-lan-update.md(已整体改为 HTTP:§7 源、§8 流程、§14 安全、§16 阶段)docs/02-prd.md(update_source/update_user/update_pass)
背景:
- 更新源改为 HTTP(S) 文件服务(已部署 gohttpserver + nginx,HTTP Basic Auth,示例
http://cm.xiapi.com/)。§17.16/§17.17 的本地文件版与 UNC/robocopy 版作为历史保留,本任务把传输统一到 HTTP。
任务:
- 文档:
docs/10由 UNC/robocopy 全面改为 HTTP(源/清单/流程/安全/阶段);docs/02、docs/05同步配置项 - 配置项
update_user/update_pass写入DEFAULT_CONFIG update_service.check_for_update支持http(s)://源 + Basic Auth(当前仅open()本地文件,填 URL 静默返回 None),补单测- 启动器
update.ps1改 HTTP:带凭据下载 zip → 校验 SHA-256 → 解压到staging\app.new→app/app.old切换回滚 - manifest 字段由
source/files/marker改为url/sha256/size - 发布流程脚本:build → 打 zip → 算 SHA-256 → 写 manifest → 上传
- BOM 兼容:
check_for_update用utf-8-sig解码,build.ps1写 manifest 不带 BOM(否则 PS5.1 的 UTF8 BOM 会让 app 内json.loads静默失效);补 2 个 BOM 单测 update.ps1HTTP 版以本地 HTTP server 假发布包端到端验证(拉清单→下载→SHA-256→app/app.old切换)- 生产前将更新源切到 HTTPS、客户端改用只读账号
- 真实环境(cm.xiapi.com)端到端实测
17.19 启动器改 Launcher.exe + 数据移到 ~/.cmbot(便携模型)
前置阅读:
docs/10-lan-update.md(§3 架构、§4 布局、§5 数据根三级回退、§9 权限、§11、§16 阶段④)
背景:
- 改用编译的
Launcher.exe(Python + PyInstaller onefile)取代update.ps1,对终端用户更友好。 - 程序采用便携布局:解压任意可写目录即用,安装根 =
Launcher.exe所在目录,不限定%LOCALAPPDATA%。 - 用户数据移到
~/.cmbot(%USERPROFILE%\.cmbot):始终可写、按用户隔离、不随程序更新丢失。
任务:
- 文档:
docs/10改为便携 +Launcher.exe+~/.cmbot模型(§3/§4/§5/§8/§9/§11/§16) get_data_dir()三级回退:CMBOT_DATA_DIR→ 打包态~/.cmbot→ 开发态项目根;tests/test_file_service.py5 个单测src/launcher.py:复用update_service,下载 zip→SHA-256→解压→app/app.old切换→启动;安装根可写性检测;首次把app\config\默认模板播种到~/.cmbot;update_service扩展(UpdateInfo.sha256/size/min_supported、绝对 url 解析、make_auth_header/download);tests/test_launcher.py11 个单测 + 本地 HTTP server 真实端到端验证build.ps1增产Launcher.exe(PyInstaller onefile,console);发布目录改为便携布局Launcher.exe+app\;产出两个 zip——自更新载荷CMBot-<ver>.zip(app\ 内容,manifest.url 指向它)与便携安装包CMBot-<ver>-portable.zip;launcher.py日志初始化健壮化(--windowed/只读根不崩)。语法校验通过;实际 PyInstaller 构建需在 Windows 跑- 退休
scripts/update.ps1与scripts/install_local.ps1(已删除,git 历史可查) - 端到端实测(解压到 D 盘运行、自更新、回滚)
17.20 设置对话框(更新配置入口)
前置阅读:
docs/07-ui-design.md(4.3 设置入口、4.4 设置对话框)docs/02-prd.md(update_source/update_user/update_pass)docs/05-project-architecture.md(4.12 config 集中管理)
背景:
- 更新地址/账号/密码目前只能手改
~/.cmbot/config/app_config.json,加一个应用内设置入口,管理员配一次,launcher 与 app 内通知都跟着用。
任务:
- 文档:
docs/07§3 布局、§4.1 去掉「暂不提供设置入口」、新增 §4.3 入口 + §4.4 对话框 - 页签栏右端加低调
⚙ 配置按钮(addStretch与编号页签隔开,非编号页签) src/app/widgets/settings_dialog.py(QDialog,纯 UI):更新地址 / 账号 / 密码(掩码+显示) / 测试连接 / 当前版本 + 安全提示测试连接:后台线程调用update_service.load_manifest(区分「连不上」与「已是最新」),反馈最新/发现新版/连接失败main_window:打开时注入当前 config,保存时集中save_config,UI 不直接写配置- 保存后重新触发一次在线更新检查(横幅刷新;
_update_found改为只连一次避免重复) - 取消不改动配置
验收:
- 单测:
load_manifest返回 dict / 缺失抛错(2 个);对话框 Qt 符号导入校验通过 - GUI 实测:改更新地址/账号/密码并持久化到
~/.cmbot/config/app_config.json - GUI 实测:测试连接正确反馈三种结果
- GUI 实测:改完无需重启 launcher,app 内横幅按新配置刷新
17.21 更新改为非阻塞:app 内下载 + 启动器只应用
前置阅读:
docs/10-lan-update.md(§3 两阶段架构、§6 时机、§8 流程、§12 体验)docs/07-ui-design.md(4.3 有新版本提示、4.4 设置对话框「检查并更新」)
背景:
- 原启动器在启动时下载更新、阻塞进主界面(57MB 包看着像卡死)。改为「下载在 app 内、切换在启动器」,启动永不阻塞。
任务:
services/installer.py:download_and_stage()(app 运行时下载→SHA-256→解压→staging\app.new)、apply_staged()(启动器秒切app/app.old,含「不比当前新则丢弃」「移动失败回滚」)、staged_version()、is_writablelauncher.py瘦身:seed +apply_staged+ 启动,不联网;tests/test_launcher.py重写main_window:发现新版在⚙ 配置左侧显示蓝色提示文字(删除旧阻塞横幅与「打开更新目录」)settings_dialog:加「检查并更新」→ 下载暂存 → 「下次启动生效」tests/test_installer.py11 个单测 + 本地 HTTP server 真实端到端(下载暂存→app 不动→应用切换)- 文档:
docs/10§3/§6/§8/§12/§16/§18、docs/07§4.3/§4.4 对齐 - GUI 实测:提示文字出现 → 设置里更新 → 重启生效(真机)
17.22 默认导出目录改为程序旁的「合并后的图片」
前置阅读:
docs/10-lan-update.md(§4 布局、§5 路径规则)docs/07-ui-design.md(7.5 输出设置)
背景:
- 默认导出在
~/.cmbot/output,藏在用户目录深处不好找。改为放在安装根(Launcher.exe旁)的合并后的图片\:好找、不随app\更新替换。不放app\(每次更新会被替换、导出会丢)。
任务:
- 文档:
docs/10§4/§5、docs/02配置说明、docs/07§7.5 对齐 file_service.get_output_dir():打包态优先<安装根>\合并后的图片(安装根 =get_app_dir().parent),不可写回退get_data_dir()/output;开发态用项目目录;tests/test_file_service.py3 个单测- 导出面板默认值随之变化(沿用
get_output_dir(),无需单独改) - GUI 实测:打包运行后默认导出到
<安装根>\合并后的图片,且更新后仍在
18. 后续暂缓任务
以下任务第一阶段暂不做,后续需要时再新增设计文档:
- 按电脑名控制版本
- 强制更新与版本保留策略(
mandatory/min_supported生效、app.old保留策略;docs/10 §16 阶段④) - [~] AI 穿搭(已转正、进入设计/实现,见 §19)
- 导出上架流程
- 自动抠图
- 自动识别衣服区域
- 移动端应用
已交付(原属暂缓,本季完成,见 §17.16–§17.20、docs/10-lan-update.md):
- 在线更新(HTTP 检测 + 通知 +
Launcher.exe自动安装 + 回滚) - 局域网/在线分发(便携包 + HTTP 文件服务 + manifest)
多版本启动器(改为app/app.old单版本切换,不再做多版本并排)
19. AI 穿搭模块
前置阅读:
docs/11-ai-outfit.md(本模块完整设计)docs/旧ai穿搭项目.md(同事旧项目分析,可复用件)docs/04-development-rules.md、docs/05-project-architecture.md(分层)
一句话目标:以 Excel 为数据源,按行读取「衣服图 + 标题/货号」,套提示词调 AI 图像 API 生成「人物穿着该衣服」的效果图,保存 JPG 并把结果路径写回 Excel。
19.0 设计与效果图
- 分析旧项目 →
docs/旧ai穿搭项目.md - 模块设计文档
docs/11-ai-outfit.md(架构/Excel 适配/数据模型/AI 服务/提示词/并发/输出/界面/配置/阶段/验收) - UI 效果图
docs/ui-ai-outfit.html+docs/ui-ai-outfit.png(三栏:左设置 / 中「最近结果缩略图条 + 处理明细表」/ 右运行日志;与印花页同款外壳、纯蓝统一) - 确认取舍:不做实时单图大预览(批量工具,并发会有「显示哪行」歧义);预览=按钮弹
QDialog;保留「保存话术」按钮;并发由 Python 线程负责、Qt 仅 signal 回主线程
19.1 核心与服务(无 GUI,可单测)— docs/11 §14 阶段 1
前置阅读:docs/11-ai-outfit.md(§3–§9)
- 前置依赖:
requirements.txt增加 Python 3.7 兼容的requests/urllib3/openpyxl锁定版本,并同步docs/03-technical-stack.md core/models.py新增OutfitTask/OutfitResult(纯 dataclass,Python 3.7 兼容,不依赖 PySide6)services/excel_service.py:读行 →List[OutfitTask]、写回 D/E/F、占用检测、跳过「完成」/按设置重试「失败」/空字段安全跳过、每行即存services/ai_image_service.py:移植旧项目ImageApiClient(多模型、多请求格式chat/gemini/images/images_edits、传图 data-url、递归取图、URL 归一化、字段校验);注意 PEP585 类型注解改 Python 3.7 写法core/ai_outfit.py:单行生成纯逻辑编排(提示词渲染 + 调用 + 保存 JPG + 产出OutfitResult);§8「超时按分辨率动态决定(512/1K/2K/4K→180/240/360/600,可被timeout_seconds覆盖)」由ai_image_service.resolution_timeout实现- 单测:
test_excel_service/test_ai_image_service/test_ai_outfit/test_outfit_batch共 40+ 用例(Excel 读写、提示词渲染、取图、命名去重、超时映射;API 用 mock);Python 3.7 通过、不依赖 GUI
19.2 批量编排 — docs/11 §14 阶段 2
前置阅读:docs/11-ai-outfit.md(§8)
core/outfit_batch.py纯逻辑OutfitBatchRunner:ThreadPoolExecutor(并发数)+RateLimiter(请求间隔)+ 单任务冷却 + 阶梯重试 + 温和停止 + >30s 心跳;全可测(QObject包装放 §19.3 UI 接线)- 子线程只经回调/signal 回主线程,不直接碰控件(runner 用 callback,UI 层转 signal)
19.3 UI 页签 — docs/11 §14 阶段 3
前置阅读:docs/11-ai-outfit.md(§10)、docs/07-ui-design.md(§4.2)、docs/ui-ai-outfit.png
- 页签栏接
QStackedWidget(页 0=添加印花工作区+队列,页 1=AI 穿搭面板);启用页签 1(导出上架仍禁用) app/widgets/ai_outfit_panel.py:左设置(Excel/输出/模型/话术编辑+保存+插入占位符+预览弹窗/生成设置)、中(最近结果缩略图 + 处理明细表)、右(进度+统计+开始/停止+导出失败清单+打开输出目录+实时日志)- 接线后台线程:
QThread+_OutfitWorker(QObject)包OutfitBatchRunner,signal 回主线程刷日志/进度/明细/缩略图;每行写回 Excel;结束摘要弹窗;失败清单导出 CSV - GUI 实测(真机):选 Excel/模型跑通、停止生效、失败清单正确(离屏冒烟已过,待真机)
19.4 配置与提示词 — docs/11 §14 阶段 4
前置阅读:docs/11-ai-outfit.md(§7、§11)
config_service增load_ai_models()/load_outfit_prompt()/save_outfit_prompt();ai_models.json(密钥明文、不入库、BOM 容错)、outfit_prompt.txt(无 BOM)、app_config.json并入outfit_*(上次 Excel/输出/模型 + 批量设置),UI 经config_changed信号回主窗口集中存;tests/test_config_service.py8 用例requirements.txt已锁requests 2.31/openpyxl 3.1.3/urllib3 1.26;docs/03增列依赖- 可选:应用内 AI 模型编辑界面(当前由管理员预置
ai_models.json,暂不做)
19.5 真机联调 — docs/11 §14 阶段 5
- 真实中转 API + 小批量 Excel 端到端跑通,调穿搭提示词
- 验收对照
docs/11§16