diff --git a/app/appconfig.py b/app/appconfig.py index d2d374a..239a0a5 100644 --- a/app/appconfig.py +++ b/app/appconfig.py @@ -31,7 +31,7 @@ DEFAULT_CONFIG = { "default_text_model": "GPT-5.5 文本", "default_image_model": "Nano Banana 2", "generate_cover": False, - "backend": "direct", + "backend": "cmhub", "cmhub": { "base_url": "", "title_alias": "", @@ -321,7 +321,8 @@ def ai_config(config=None) -> dict: def ai_backend(config=None) -> str: ai = ai_config(config) - backend = str(ai.get("backend", "direct") or "direct").strip().lower() + default_backend = DEFAULT_CONFIG["ai"]["backend"] + backend = str(ai.get("backend", default_backend) or default_backend).strip().lower() if backend not in AI_BACKENDS: raise ConfigError("AI backend 必须是 direct 或 cmhub") return backend diff --git a/app/gui/tabs/settings.py b/app/gui/tabs/settings.py index 5383be3..64f2f23 100644 --- a/app/gui/tabs/settings.py +++ b/app/gui/tabs/settings.py @@ -68,6 +68,7 @@ class SettingsTab(QWidget): self.backend_combo.setObjectName("aiBackendCombo") for label, value in self.BACKEND_ITEMS: self.backend_combo.addItem(label, value) + self.backend_combo.setVisible(False) self.cmhub_base_url_edit = QLineEdit() self.cmhub_base_url_edit.setObjectName("cmhubBaseUrlEdit") self.cmhub_base_url_edit.setPlaceholderText("https://") @@ -266,8 +267,6 @@ class SettingsTab(QWidget): ] ) - backend_form = self._three_column_form([("AI 后端", self.backend_combo)]) - self.model_picker_panel = QWidget() self.model_picker_panel.setLayout(model_picker_layout) @@ -313,7 +312,7 @@ class SettingsTab(QWidget): self.settings_panel_layout = panel_layout panel_layout.setContentsMargins(13, 18, 13, 18) self.ai_model_section_title = self._section_title( - "AI 模型", + "cmhub 网关", "settingsAiModelSectionTitle", ) self.model_detail_section_title = self._section_title( @@ -333,7 +332,6 @@ class SettingsTab(QWidget): "settingsInfrastructureSectionTitle", ) panel_layout.addWidget(self.ai_model_section_title) - panel_layout.addLayout(backend_form) panel_layout.addWidget(self.model_picker_panel) panel_layout.addSpacing(14) panel_layout.addWidget(self.model_detail_section_title) @@ -437,8 +435,6 @@ class SettingsTab(QWidget): def _maybe_auto_refresh_cmhub_models(self): if self._cmhub_auto_refresh_done: return - if (self.backend_combo.currentData() or "direct") != "cmhub": - return if not self.cmhub_base_url_edit.text().strip() or not self.cmhub_api_key_edit.text(): return self._cmhub_auto_refresh_done = True @@ -454,12 +450,12 @@ class SettingsTab(QWidget): return appconfig.CMHUB_CONFIG_PATH def _on_backend_changed(self, index=None): - is_cmhub = (self.backend_combo.currentData() or "direct") == "cmhub" - self.model_picker_panel.setVisible(not is_cmhub) - self.model_detail_section_title.setVisible(not is_cmhub) - self.model_detail_panel.setVisible(not is_cmhub) - self.direct_role_panel.setVisible(not is_cmhub) - self.cmhub_panel.setVisible(is_cmhub) + self.backend_combo.setVisible(False) + self.model_picker_panel.setVisible(False) + self.model_detail_section_title.setVisible(False) + self.model_detail_panel.setVisible(False) + self.direct_role_panel.setVisible(False) + self.cmhub_panel.setVisible(True) self._set_cmhub_running(self.cmhub_thread is not None) self._update_button_state() @@ -623,12 +619,9 @@ class SettingsTab(QWidget): self._show_error("默认调试端口必须在调试端口范围内") return None ai_cfg = appconfig.ai_config(self.config) - backend = self.backend_combo.currentData() or "direct" + backend = "cmhub" text_model = self.default_text_model_combo.currentData() or ai_cfg.get("default_text_model") image_model = self.default_image_model_combo.currentData() or ai_cfg.get("default_image_model") - if backend == "direct" and (not text_model or not image_model): - self._show_error("标题大模型和图片大模型不能为空") - return None cmhub_cfg = self._cmhub_settings_values(backend) if cmhub_cfg is None: return None @@ -686,23 +679,9 @@ class SettingsTab(QWidget): "connect_timeout": self.cmhub_connect_timeout_spin.value(), "check_balance_before_batch": self.cmhub_check_balance_checkbox.isChecked(), } - if backend != "cmhub": - merged = dict(current) - merged.update(values) - return merged - missing = [] - if not values["base_url"]: - missing.append("Base URL") - if not self.cmhub_api_key_edit.text(): - missing.append("API Key") - if not values["title_alias"]: - missing.append("生文别名") - if not values["image_alias"]: - missing.append("生图别名") - if missing: - self._show_error("cmhub 配置不完整:缺少 " + "、".join(missing)) - return None - return values + merged = dict(current) + merged.update(values) + return merged def _replace_config(self, saved): internal = {} @@ -719,8 +698,7 @@ class SettingsTab(QWidget): def _populate_app_settings(self): self._populate_role_model_combos() ai_cfg = appconfig.ai_config(self.config) - backend = ai_cfg.get("backend", "direct") - self._set_combo_by_data(self.backend_combo, backend) + self._set_combo_by_data(self.backend_combo, "cmhub") cmhub_cfg = appconfig.cmhub_config(self.config) self.cmhub_base_url_edit.setText(cmhub_cfg.get("base_url", "")) self._loaded_cmhub_api_key = appconfig.get_cmhub_api_key(path=self.cmhub_config_path) diff --git a/docs/03-tech-stack.md b/docs/03-tech-stack.md index 8ad6a0c..02ee580 100644 --- a/docs/03-tech-stack.md +++ b/docs/03-tech-stack.md @@ -17,13 +17,13 @@ | 应用配置 | `config.json`(JSON,stdlib) | 已定 | 少量应用级设置:Chrome 路径、目录根、端口、DB 路径等 | | 业务数据 | SQLite(stdlib `sqlite3`,`cmshopee.db`) | 已定 | 账号、任务、结果:成行增长、要查询/统计/导出 | | Excel 读写 | `openpyxl` | 已定 | 导入任务、回写结果;stdlib 读不了 .xlsx | -| AI 模型注册 | `config/ai_models.json` direct 模型清单 + `config/cmhub.json` 网关 Key | 已接入(后端) | direct 模式保留每模型 name/category/url/model/key/api_type;cmhub 模式用 `config.json` 的 Base URL/别名 + `config/cmhub.json` 单 Key;⑤ cmhub 面板待 T-527 | -| AI 文本生成 | `app/ai.py` 支持 direct chat JSON 与 cmhub `POST /api/v1/generate/title` | 已接入 | 提示词+旧标题→新标题;返回值不变;cmhub 计费 metadata 通过事件回调上报 | -| AI 图像生成 | `app/ai.py` 支持 direct chat/images_edits 与 cmhub `POST /api/v1/generate/image` | 已接入 | 提示词+旧封面→新封面;cmhub 拿 `image_url` 后安全下载并转本地 JPEG;生图读超时不自动重发 | +| AI 模型注册 | `config/cmhub.json` 网关 Key + `config/ai_models.json` direct 兼容清单 | 已接入 | 普通产品默认 cmhub,使用 `config.json` 的 Base URL/别名 + `config/cmhub.json` 单 Key;direct 模型清单保留为内部兼容/手工回滚路径,普通⑤设置页不暴露后端切换 | +| AI 文本生成 | `app/ai.py` 默认 cmhub `POST /api/v1/generate/title`,保留 direct chat JSON 兼容分支 | 已接入 | 提示词+旧标题→新标题;返回值不变;cmhub 计费 metadata 通过事件回调上报 | +| AI 图像生成 | `app/ai.py` 默认 cmhub `POST /api/v1/generate/image`,保留 direct chat/images_edits 兼容分支 | 已接入 | 提示词+旧封面→新封面;cmhub 拿 `image_url` 后安全下载并转本地 JPEG;生图读超时不自动重发 | | 并发 | 标准库 `concurrent.futures.ThreadPoolExecutor` | 已定 | 标题/图片分别按并发数并行;③ 可按账号并行更新;可停止、可重试 | | 运行日志 | SQLite `run_logs` / `run_log_events` | 已定 | ③ dry-run 与真实更新都留痕;结构化内容走脱敏 | | 图片处理 | `requests`(下载)+ `Pillow`(按分辨率/jpg质量存盘) | 部分待定 | 下载旧封面;新封面按 resolution 生成、jpg_quality 存盘 | -| 测试 | `python -m compileall app main.py` + `unittest` + 手动 CDP/AI 验证 | 已定(分层) | 配置/DB/Excel/prompts 用单测;CDP/Shopee 与真实 AI 属集成验证或 mock | +| 测试 | `python -m compileall app main.py` + `unittest` + 手动 CDP/AI 验证 | 已定(分层) | 配置/DB/Excel/prompts 用单测;CDP/Shopee 与真实 AI 属集成验证或 mock | | 打包分发 | PyInstaller onedir(`cmshopee.spec`) | 已定 | 产出 Windows 免安装文件夹;不内置配置、DB、图片、日志、Chrome 登录态或提示词等本地数据 | ## 二、决策记录与演进 @@ -34,13 +34,13 @@ - **Excel 用 openpyxl**:运营用真实 .xlsx;stdlib 无法读写 xlsx,引入一个轻依赖比改用 CSV 更贴合用户习惯。 - **多账号隔离用独立 user-data-dir,不用 Chrome profile**:profile 共享同一 user-data-dir/进程/调试端口,无法每账号独立 CDP 与并行;独立 user-data-dir 才契合自动化。详见 [架构 3.0](04-architecture.md)。 - **快捷方式生成用 PowerShell(无额外依赖)**:用 `WScript.Shell.CreateShortcut` 生成 `.lnk`,不引入 `pywin32` 等依赖。 -- **AI 服务商不写死在代码里**:T-301 已采用 `config/ai_models.json` 的 direct 通用 HTTP 接入,支持 OpenAI-compatible chat JSON 与 images_edits multipart;T-526 已新增 `backend=cmhub` 网关后端,cmhub 模式使用 Base URL + 生文/生图别名 + `config/cmhub.json` 单 Key,直连模型清单保留用于回退。 +- **AI 服务商收口到 cmhub 网关**:T-301 的 `config/ai_models.json` direct 通用 HTTP 接入仍保留为内部兼容/手工回滚路径;T-526~T-529 后普通产品默认 `backend=cmhub`,⑤设置页只展示 cmhub Base URL + 生文/生图别名 + `config/cmhub.json` 单 Key,不再暴露 AI 后端切换。 - **AI 模型 category 是硬约束**:`config/ai_models.json` 每个模型必须有 `category=text` 或 `category=image`;启动时报 “AI 模型 category 必须是 text 或 image” 时,按 [常见问题排查](troubleshooting.md) 修复本地配置,不删除或提交含 Key 的配置文件。 - **敏感信息不加密但强提示与脱敏**:密码与 AI Key 只在本机 SQLite / `config/ai_models.json` / `config/cmhub.json` 明文保存;保存/变更时弹窗提示,UI 打码,日志/导出必须脱敏,相关本地文件必须 gitignore。 - **AI 产出无逐条审核**:生成的新标题/新封面经 ③ 批量确认后提交线上;无常驻提交开关,本地留档 + 回写 Excel 供追溯。 - **T-504 更新执行增强**:③ 支持 dry-run 预览、运行日志和按账号并行;默认 dry-run 关闭、并行关闭,不引入新依赖。 - **不引入数据库(指外部 DB)**:用 stdlib SQLite 足够;不引入 Postgres/MySQL 等。 -- **生产在 Windows 直跑**:开发期我们用过 WSL→Windows 的 `netsh portproxy`(9333→9222)连 CDP;但 GUI 与 Chrome 都在 Windows 时,直接连 `127.0.0.1:9222`,无需 portproxy。 +- **生产在 Windows 直跑**:开发期我们用过 WSL→Windows 的 `netsh portproxy`(9333→9222)连 CDP;但 GUI 与 Chrome 都在 Windows 时,直接连 `127.0.0.1:9222`,无需 portproxy。 - **PyInstaller 使用 onedir 免安装包**:T-524 先做 `dist/cmshopee/cmshopee.exe` + `_internal/` 的文件夹分发,不做自动更新器;打包版启动时工作目录切到 exe 所在目录,用户本地配置、DB、图片、日志、登录态和提示词保留在程序目录,更新时只覆盖程序文件。 ## 三、构建与运行命令 @@ -52,8 +52,8 @@ | 语法检查 | `python -m compileall app main.py` | | 启动 GUI | `python main.py` / `python -m app` | | 单元测试(T-006 后) | `python -m unittest discover -s tests` | -| CI 自动验证 | GitHub Actions `.github/workflows/tests.yml`(Windows + Python 3.11 + `QT_QPA_PLATFORM=offscreen`) | -| 安装打包依赖 | `python -m pip install -r requirements-build.txt` | +| CI 自动验证 | GitHub Actions `.github/workflows/tests.yml`(Windows + Python 3.11 + `QT_QPA_PLATFORM=offscreen`) | +| 安装打包依赖 | `python -m pip install -r requirements-build.txt` | | 打包 exe | `powershell -ExecutionPolicy Bypass -File scripts\\build_exe.ps1` | | 跑单账号演示 | `python prototypes/demo.py`(分步)/ `set AUTO=1 && python prototypes/demo.py`(自动) | | 提交更新(真改线上) | `set UPDATE=1 && python prototypes/demo.py` | @@ -74,7 +74,7 @@ set AUTO=1 && python prototypes/demo.py ## 四、依赖纪律 -- 运行时第三方依赖统一写入根目录 `requirements.txt` 并锁定版本;换机或 CI 使用 `python -m pip install -r requirements.txt` 安装。 +- 运行时第三方依赖统一写入根目录 `requirements.txt` 并锁定版本;换机或 CI 使用 `python -m pip install -r requirements.txt` 安装。 - 打包依赖只写入 `requirements-build.txt`;PyInstaller 不属于运行时依赖,不写进 `requirements.txt`。 - 当前直接依赖锁定:PySide6 6.5.3、openpyxl 3.1.3、requests 2.31.0、websocket-client 1.6.1、Pillow 9.5.0。 - 新增第三方依赖前,先在本文说明用途、替代方案和维护成本,并同步更新 `requirements.txt`。 @@ -91,4 +91,4 @@ set AUTO=1 && python prototypes/demo.py | GUI 轻测 | PySide6 主窗口可创建、Tab 数量、worker signal 基本行为 | 可用 unittest 构造 `QApplication`,不连真实 Shopee | | CI | push / pull_request 自动跑语法 + 全量 unittest | `.github/workflows/tests.yml` 安装 `requirements.txt`,运行 `python -m compileall app main.py` 与 `python -m unittest discover -s tests`;不连真实 Shopee/AI | | 集成 | CDP/editor 操作 Shopee 测试商品 | 手动跑 `prototypes/demo.py` 或后续专用集成脚本 | -| 外部 AI | 模型连接、文本/图像生成 | 默认 mock;真实调用只在手动验证时跑,避免成本和限流 | +| 外部 AI | 模型连接、文本/图像生成 | 默认 mock;真实调用只在手动验证时跑,避免成本和限流 | diff --git a/docs/04-architecture.md b/docs/04-architecture.md index 90b9c7e..06a0e7a 100644 --- a/docs/04-architecture.md +++ b/docs/04-architecture.md @@ -27,7 +27,7 @@ GUI(PySide6 QTabWidget,5 Tab) └── ai 文本生成(提示词+旧标题→新标题)/ 图像生成(提示词+旧封面→新封面) | v -Google Chrome(每账号独立 --user-data-dir + --remote-debugging-port) + AI 服务(direct 外部模型或 cmhub 网关) +Google Chrome(每账号独立 --user-data-dir + --remote-debugging-port) + AI 服务(默认 cmhub 网关;direct 仅内部兼容/回滚) | v Shopee 卖家中心页面 / 本地图片目录 @@ -38,7 +38,7 @@ Shopee 卖家中心页面 / 本地图片目录 - GUI 入口:根目录 `main.py` 调用 `app/gui/` 包(PySide6 + `QMainWindow` + `QTabWidget`,5 Tab);包入口 `app/gui/__init__.py` 提供 `main()` 并兼容 `from app import gui` / `from app.gui import MainWindow`;也支持 `python -m app`。 - 核心模块统一放在正式代码包 `app/`:`appconfig.py`、`db.py`、`excel.py`、`config.py`、`accounts.py`、`chrome.py`、`editor.py`、`workers.py`、`ai.py`、`prompts.py`;CDP 底座迁入 `app/cdp.py`(当前根目录 `cdp.py` 为已验证来源)。 - 已验证脚本(重构进模块):`prototypes/demo.py`、`prototypes/set_title.py`、`prototypes/set_cover.py`、`prototypes/get_title.py`、`prototypes/cookies.py`、`prototypes/inspect_images.py`、`prototypes/grab.py`。 -- 外部依赖:本机 Google Chrome;Shopee;AI 服务(文本+图像;direct 模式由 `config/ai_models.json` 配置,cmhub 模式由 `config.json` 的 `ai.cmhub` + `config/cmhub.json` 配置);`openpyxl`。 +- 外部依赖:本机 Google Chrome;Shopee;AI 服务(文本+图像;普通产品默认 cmhub 网关,由 `config.json` 的 `ai.cmhub` + `config/cmhub.json` 配置;direct 直连模型清单仅作为内部兼容/手工回滚路径保留);`openpyxl`。 ## 二、流水线(核心) @@ -71,12 +71,12 @@ imported → collected → generated → applied - `chrome`:拼接启动命令、启动、探测端口、(可选)生成快捷方式。 - `cdp`:连接调试端口、找/开 tab、执行 JS、拖拽、注入文件。 - `editor`:登录检测、**采集**(读旧标题、下载旧封面)、改标题、换封面、点更新。 -- `ai`:`gen_title(prompt, old_title)`、`gen_cover(prompt, old_cover_path)`(外部 AI;支持 `direct` 模型清单和 `cmhub` 网关 backend;返回值保持标题字符串/本地 JPEG 路径)。 +- `ai`:`gen_title(prompt, old_title)`、`gen_cover(prompt, old_cover_path)`(外部 AI;默认走 `cmhub` 网关,保留 `direct` 模型清单作为内部兼容/回滚 backend;返回值保持标题字符串/本地 JPEG 路径)。 **存储(同一事实只存一处)** - 应用配置(模型选择、生成参数、目录、Chrome 路径)→ `config.json`。 -- AI 模型清单(direct 模式 url/模型/密钥/类型/连接超时)→ `config/ai_models.json`(API Key 本地明文保存,必须 gitignore,UI 打码显示)。 +- AI 模型清单(direct 内部兼容模式 url/模型/密钥/类型/连接超时)→ `config/ai_models.json`(API Key 本地明文保存,必须 gitignore,UI 打码显示;普通设置页不再暴露 direct 切换入口)。 - cmhub 网关 Key → `config/cmhub.json`,schema `{ "api_key": "..." }`;`config.json` 只保存 Base URL、别名和超时,不保存 Key。 - 业务数据(账号、任务、各阶段结果)→ SQLite `cmshopee.db`。 - 图片(采集的旧封面、AI 生成的新封面)→ 本地图片目录(路径记在 DB)。 @@ -104,7 +104,7 @@ imported → collected → generated → applied "default_text_model": "GPT-5.5 文本", "default_image_model": "Nano Banana 2", "generate_cover": false, - "backend": "direct", + "backend": "cmhub", "cmhub": { "base_url": "", "title_alias": "", @@ -134,14 +134,14 @@ imported → collected → generated → applied `ai` 段只放**选择 + 全局生成参数**: -- `backend`:`direct` / `cmhub`。全新配置和旧配置缺字段时均为 `direct`,避免未配置 cmhub 时破坏既有直连生成;只有用户在⑤显式切换并配置完整后才走 cmhub。 +- `backend`:内部字段,取值仍支持 `cmhub` / `direct`;普通产品默认 `cmhub`,⑤设置页不再展示「AI 后端」label 或 direct/cmhub 下拉,保存设置固定写 `cmhub`。`direct` 仅保留为内部兼容/手工回滚路径。 - `cmhub`:cmhub 网关配置,`base_url` 为网关根地址,`title_alias` / `image_alias` 为 `GET /api/v1/models` 发现的能力别名,`connect_timeout` 为连接超时;API Key 不在此处保存。 -- `default_text_model` / `default_image_model`:direct 模式下引用 `ai_models.json` 里的模型名(标题用文本模型、封面用图像模型);cmhub 模式不读取这些模型定义。 +- `default_text_model` / `default_image_model`:仅 direct 内部兼容模式下引用 `ai_models.json` 里的模型名(标题用文本模型、封面用图像模型);普通 cmhub 模式不读取这些模型定义,⑤设置页不再展示标题/图片模型角色下拉。 - `generate_cover`:②「开始生成」时是否调用图片模型生成新封面;默认 `false`,避免用户无意产生图片生成成本。该字段只控制 AI 生成阶段,不等同于 ③ 的 `allow_cover_update`。 - `resolution`:当前分辨率,下拉 `512 / 1k / 2k / 4k`。 - `resolution_timeouts`:分辨率 → **等待大模型返回超时(秒)** 的映射;用户选分辨率即自动套用,不单独填。 -- direct 模式模型本身的定义(url/key/类型/连接超时…)在 `config/ai_models.json`,见 5.1b。 -- 密钥不在 `config.json`:direct 模式每个模型的 `api_key` 存于 `config/ai_models.json`;cmhub 模式 API Key 存于 `config/cmhub.json`。两者均本地明文保存、保存/变更时弹窗提示、UI 打码、gitignore、不入日志/导出。 +- direct 内部兼容模式模型本身的定义(url/key/类型/连接超时…)在 `config/ai_models.json`,见 5.1b。 +- 密钥不在 `config.json`:cmhub API Key 存于 `config/cmhub.json`;direct 内部兼容模式每个模型的 `api_key` 存于 `config/ai_models.json`。两者均本地明文保存、保存/变更时弹窗提示、UI 打码、gitignore、不入日志/导出。 `shopee_update` 段放**真实更新前的安全开关**: @@ -158,7 +158,7 @@ imported → collected → generated → applied ### 5.1b AI 模型清单 `config/ai_models.json` -模型定义清单("有哪些模型"),与 `config.json` 的 `ai` 段("选了哪个 + 全局参数")职责分开。该文件只用于 `backend=direct`;`backend=cmhub` 时生文/生图使用 cmhub 别名,不读取此文件。 +模型定义清单("有哪些模型"),与 `config.json` 的 `ai` 段("选了哪个 + 全局参数")职责分开。该文件只用于内部兼容 `backend=direct`;普通用户默认 `backend=cmhub`,生文/生图使用 cmhub 别名,不读取此文件。 ```json { @@ -195,7 +195,7 @@ imported → collected → generated → applied ``` - 文件必须 gitignore,不提交;UI 展示打码。 -- `appconfig.load_cmhub_config()` 缺文件时返回空 Key,不自动启用 cmhub。 +- `appconfig.load_cmhub_config()` 缺文件时返回空 Key;普通产品默认 backend 仍为 cmhub,但未配置 Key/Base URL/别名时生成阶段会给出清晰配置错误,不静默回退 direct。 - `backend=cmhub` 但 Base URL、API Key 或别名缺失时,`app/ai.py` 抛 `CMHubError(code="cmhub_not_configured")`,提示去⑤设置配置,不静默回退 direct。 ### 5.2 SQLite `cmshopee.db` @@ -395,8 +395,8 @@ images///__new. # AI 生成的新封 单个「开始生成」按钮,标题必生成,封面按本轮成本开关可选生成: -1. **并发生成标题**:线程池大小 = `title_concurrency`,调 `gen_title(标题提示词, old_title)` → new_title。`backend=direct` 时使用 `default_text_model`;`backend=cmhub` 时调用 `POST /api/v1/generate/title` 并使用 `title_alias`。 -2. **若②勾选「生成封面图片(成本较高)」**:接着并发生成图片,线程池大小 = `image_concurrency`,调 `gen_cover(封面提示词, old_cover_path, resolution, jpg_quality)` → 新图存 `images///__new.jpg`。`backend=direct` 时使用 `default_image_model`;`backend=cmhub` 时调用 `POST /api/v1/generate/image`,拿 `image_url` 后立即安全下载并转本地 JPEG。 +1. **并发生成标题**:线程池大小 = `title_concurrency`,调 `gen_title(标题提示词, old_title)` → new_title。默认 `backend=cmhub`,调用 `POST /api/v1/generate/title` 并使用 `title_alias`;`backend=direct` 仅内部兼容时使用 `default_text_model`。 +2. **若②勾选「生成封面图片(成本较高)」**:接着并发生成图片,线程池大小 = `image_concurrency`,调 `gen_cover(封面提示词, old_cover_path, resolution, jpg_quality)` → 新图存 `images///__new.jpg`。默认 `backend=cmhub`,调用 `POST /api/v1/generate/image`,拿 `image_url` 后立即安全下载并转本地 JPEG;`backend=direct` 仅内部兼容时使用 `default_image_model`。 - 连接超时取该模型 `connect_timeout_seconds`;**返回超时取 `resolution_timeouts[resolution]`**(512→180/1k→240/2k→360/4k→600)。 3. **若未勾选生成封面**:标题成功后立即写 `new_title`,`new_cover_path=NULL`,不渲染封面提示词、不调用 `gen_cover()`、不创建本地新封面文件。 diff --git a/docs/06-tasks.md b/docs/06-tasks.md index 57e6c7b..e2fc502 100644 --- a/docs/06-tasks.md +++ b/docs/06-tasks.md @@ -124,9 +124,10 @@ | ID | 任务 | 依赖 | 验收要点 | 状态 | | --- | --- | --- | --- | --- | -| T-526 | `app/ai.py` + `appconfig` 接入 cmhub backend | T-301, T-303, T-520 | 依据 `docs/cmhub-integration-design.md` v3.2。`config.json` 的 `ai` 段加 `backend`(`cmhub`/`direct`)+ `cmhub` 子段(`base_url`/`title_alias`/`image_alias`/`connect_timeout` 等);为保护既有用户,全新配置默认 `backend=direct`、加载既有配置缺 `backend` 时也补 `direct`,`cmhub` 一律由用户在⑤显式 opt-in;`backend=cmhub` 但 `base_url`/Key 缺失时须抛清晰「请去⑤配置 cmhub」错误(`CMHubError`/`AIError`),不崩溃、不静默直连。cmhub API Key 固定存 `config/cmhub.json`(schema `{ "api_key": "..." }`),新增读写/打码 helper 并把该文件加入 `.gitignore`;日志脱敏。`gen_title`/`gen_cover` **返回值不变**,内层按 backend 分流并保留 `direct`;计费元数据不塞进返回值,允许给二者**新增一个可选事件回调参数**(如 `on_meta`/`on_event`)承载,属向后兼容加参,`generate_batch` 显式传回调不受影响。cmhub 分支:生文 `POST /api/v1/generate/title` 体 `{prompt,model:别名,resolution?}`、取 `titles[0]`、空则 `AIError`;生图 `POST /api/v1/generate/image` 体 `{prompt,model:别名,image_base64:<旧封面>,resolution,aspect_ratio:"1:1"}`、拿 `image_url` 后**立即下载**再走 `_save_jpeg`;`resolution` 归一大写 `512/1K/2K/4K`。新增 `CMHubError(AIError)`,带 `code/status/retryable/retry_after`,错误按 `code` 优先分支(`insufficient_points`/`unauthorized`/`account_disabled`/`bad_request`/`model_not_allowed`/`no_pricing_rule`/`content_blocked`/`upstream_error`/`rate_limited`,未知 code 当不可重试);cmhub HTTP helper 需区分 connect/read timeout(优先用 `requests timeout=(connect, read)`),只对 502/429/连接超时重试,生图读超时绝不自动重发,读超时按分辨率封顶 600s。`points_cost`/`points_balance`/`call_id` 不改返回值,通过 `on_step`/事件回调上报;T-526 只保证 metadata 事件完整传出,T-528 再由 GUI worker 脱敏写 run_logs 和余额展示。`image_url` 下载必须限制 http/https、拒绝内网/回环地址、校验域名解析后的 IP 仍不是内网/回环/本机地址,并设置超时和大小上限。新增 `fetch_cmhub_models(base_url, api_key)` helper 调 `GET /api/v1/models` 返回别名清单(`alias/operation_type/requires_image/pricing_status/prices`)供 T-527 渲染下拉,错误脱敏。不碰 editor/cdp/chrome/accounts/excel/db,也不改 ①③④流程。`tests/test_ai.py` 加 cmhub mock(titles 列表、image_url 下载、安全下载、错误码与重试、读超时不重发、metadata 事件)、`tests/test_appconfig.py` 加 schema 和 key 文件 helper,direct 用例保持绿 | DONE | +| T-526 | `app/ai.py` + `appconfig` 接入 cmhub backend | T-301, T-303, T-520 | 依据 `docs/cmhub-integration-design.md` v3.2。`config.json` 的 `ai` 段加 `backend`(`cmhub`/`direct`)+ `cmhub` 子段(`base_url`/`title_alias`/`image_alias`/`connect_timeout` 等);T-526 实现时保留 `direct` 兼容路径;T-529 后普通产品默认 `backend=cmhub`,⑤不再暴露后端切换;`backend=cmhub` 但 `base_url`/Key 缺失时须抛清晰「请去⑤配置 cmhub」错误(`CMHubError`/`AIError`),不崩溃、不静默直连。cmhub API Key 固定存 `config/cmhub.json`(schema `{ "api_key": "..." }`),新增读写/打码 helper 并把该文件加入 `.gitignore`;日志脱敏。`gen_title`/`gen_cover` **返回值不变**,内层按 backend 分流并保留 `direct`;计费元数据不塞进返回值,允许给二者**新增一个可选事件回调参数**(如 `on_meta`/`on_event`)承载,属向后兼容加参,`generate_batch` 显式传回调不受影响。cmhub 分支:生文 `POST /api/v1/generate/title` 体 `{prompt,model:别名,resolution?}`、取 `titles[0]`、空则 `AIError`;生图 `POST /api/v1/generate/image` 体 `{prompt,model:别名,image_base64:<旧封面>,resolution,aspect_ratio:"1:1"}`、拿 `image_url` 后**立即下载**再走 `_save_jpeg`;`resolution` 归一大写 `512/1K/2K/4K`。新增 `CMHubError(AIError)`,带 `code/status/retryable/retry_after`,错误按 `code` 优先分支(`insufficient_points`/`unauthorized`/`account_disabled`/`bad_request`/`model_not_allowed`/`no_pricing_rule`/`content_blocked`/`upstream_error`/`rate_limited`,未知 code 当不可重试);cmhub HTTP helper 需区分 connect/read timeout(优先用 `requests timeout=(connect, read)`),只对 502/429/连接超时重试,生图读超时绝不自动重发,读超时按分辨率封顶 600s。`points_cost`/`points_balance`/`call_id` 不改返回值,通过 `on_step`/事件回调上报;T-526 只保证 metadata 事件完整传出,T-528 再由 GUI worker 脱敏写 run_logs 和余额展示。`image_url` 下载必须限制 http/https、拒绝内网/回环地址、校验域名解析后的 IP 仍不是内网/回环/本机地址,并设置超时和大小上限。新增 `fetch_cmhub_models(base_url, api_key)` helper 调 `GET /api/v1/models` 返回别名清单(`alias/operation_type/requires_image/pricing_status/prices`)供 T-527 渲染下拉,错误脱敏。不碰 editor/cdp/chrome/accounts/excel/db,也不改 ①③④流程。`tests/test_ai.py` 加 cmhub mock(titles 列表、image_url 下载、安全下载、错误码与重试、读超时不重发、metadata 事件)、`tests/test_appconfig.py` 加 schema 和 key 文件 helper,direct 用例保持绿 | DONE | | T-527 | ⑤设置 cmhub 网关面板 | T-526, T-517 | 依据 `docs/cmhub-integration-design.md` v3.2。⑤ AI 设置按 `backend` 切换:cmhub 模式显示「网关 Base URL + API Key(打码,提示从网页端复制、仅显示一次)+ 生文别名 + 生图别名 + 测试连接/查余额」;**别名从 `GET /api/v1/models`(T-526 的 `fetch_cmhub_models`)动态拉取渲染下拉**,按 `operation_type` 分生文/生图,过滤 `pricing_status="unpriced"` 的别名,可展示单价与 `requires_image` 提示,选中值持久化到 `ai.cmhub.title_alias/image_alias`(网关临时不可达时回退已存值);不写死别名。direct 模式保留现有 AI 模型 master-detail。保存写 `config.json` 的 `ai` 段与 `config/cmhub.json`;切换 backend 时不删除 legacy `config/ai_models.json`。测试连接/查余额经后台 worker 调 cmhub(复用 `AIModelTestWorker` 思路或新增 worker),错误必须脱敏并给用户可读提示。同步 GUI 设置测试;不改 Shopee/CDP 流程 | DONE | | T-528 | ② 计费错误提示 + 余额展示 | T-526, T-527, T-303 | 依据 `docs/cmhub-integration-design.md` v3.2。② AI生成页把 cmhub 计费失败态显式化:通过 `CMHubError.code` 识别 `insufficient_points`,弹明确提示「点数不足,请先充值」并引导去网页端充值,本轮未开始任务可提前中止,不靠中文字符串匹配、不淹没在失败计数里;用 T-526 成功响应事件里的 `points_balance` 刷新②页剩余点数显示,`/balance` 仅作手动刷新/可选批量前预检;`points_cost`/`call_id` 记入脱敏 run_logs。只改② UI、`GenerateWorker` 事件处理/文案及 GUI 单测;不改 AI HTTP 协议、DB schema、Excel、Shopee/CDP 流程 | DONE | +| T-529 | 默认 cmhub 网关并隐藏 AI 后端选择 | T-528, T-527 | 产品收口:后续普通用户只使用 cmhub 网关,不再在⑤设置页暴露「AI 后端」label 与 direct/cmhub 下拉框。方案:`DEFAULT_CONFIG.ai.backend` 改为 `cmhub`;⑤设置页默认直接展示 cmhub 网关配置(Base URL、API Key、生文/生图别名、刷新别名、测试连接/查余额),隐藏 direct 模型选择、模型详情、标题/图片模型角色下拉和「AI 后端」下拉;保存设置固定写 `ai.backend=cmhub`,允许先保存不完整 cmhub 配置,②生成时仍由 `app/ai.py` 对缺 Base URL/Key/别名给出「请去⑤配置 cmhub」错误。direct 代码、`config/ai_models.json` helper 与 direct 单测保留为内部兼容/手工回滚路径,但普通 UI 不提供切换入口。同步 `appconfig`、⑤设置页、②余额默认显示和 GUI/appconfig 单测;不改 AI HTTP 协议、DB schema、Excel、Shopee/CDP 流程 | DONE | ## Phase 8 · 工程基础设施后续(`docs/engineering-review.md`) diff --git a/docs/api.md b/docs/api.md index 60c8c53..9285505 100644 --- a/docs/api.md +++ b/docs/api.md @@ -29,13 +29,13 @@ cdp_ready_timeout(config=None) -> int ai_config(config=None) -> dict # default_text_model/default_image_model/ # title_concurrency/image_concurrency/retry/jpg_quality/ # resolution/resolution_timeouts -ai_backend(config=None) -> str # direct / cmhub +ai_backend(config=None) -> str # 默认 cmhub;direct 仅内部兼容/手工回滚 cmhub_config(config=None) -> dict # base_url/title_alias/image_alias/connect_timeout cmhub_request_url(base_url, endpoint) -> str response_timeout(config=None) -> int # = resolution_timeouts[resolution](返回超时,随分辨率) ``` -`default_config()` / `load_config()` 包含 `shopee_update` 安全配置段:历史/调试兼容测试商品 ID、是否允许真实提交、是否允许更新封面、每批最大更新条数、成功后是否关闭本轮新开编辑页、内部兼容 `dry_run`、多账号并行、最大并行账号数。普通正式更新不再用测试商品 ID 阻断当前筛选结果。`config.json` 不保存 AI Key;写入 `api_key` / `*_key` / `token` / `*_token` / `password` / `*_password` 等敏感字段时抛 `ConfigError`。AI Key 留给 `config/ai_models.json`(direct)或 `config/cmhub.json`(cmhub)。 +`default_config()` / `load_config()` 包含 `shopee_update` 安全配置段:历史/调试兼容测试商品 ID、是否允许真实提交、是否允许更新封面、每批最大更新条数、成功后是否关闭本轮新开编辑页、内部兼容 `dry_run`、多账号并行、最大并行账号数。普通正式更新不再用测试商品 ID 阻断当前筛选结果。`config.json` 不保存 AI Key;写入 `api_key` / `*_key` / `token` / `*_token` / `password` / `*_password` 等敏感字段时抛 `ConfigError`。普通产品默认 cmhub,AI Key 存 `config/cmhub.json`;`config/ai_models.json` 仅为 direct 内部兼容路径。 敏感信息展示/日志辅助: @@ -50,12 +50,12 @@ cmhub Key 文件(`config/cmhub.json`,含本地明文密钥,T-526 已建; ```python default_cmhub_config() -> dict -load_cmhub_config(path="config/cmhub.json") -> dict # 缺文件返回空 key,不自动启用 cmhub +load_cmhub_config(path="config/cmhub.json") -> dict # 缺文件返回空 key;默认 cmhub 但生成时会提示补配置 save_cmhub_config(config, path="config/cmhub.json") -> dict get_cmhub_api_key(path="config/cmhub.json", masked=False) -> str ``` -`config.json` 只保存 `ai.backend`、`ai.cmhub.base_url/title_alias/image_alias/connect_timeout` 等非密钥配置;`config/cmhub.json` 必须 gitignore,展示时打码,不写日志/导出。 +`config.json` 只保存 `ai.backend`、`ai.cmhub.base_url/title_alias/image_alias/connect_timeout` 等非密钥配置;T-529 后普通设置页固定保存 `ai.backend=cmhub`,不暴露后端切换;`config/cmhub.json` 必须 gitignore,展示时打码,不写日志/导出。 AI 模型清单(`config/ai_models.json`,含本地明文密钥,已建;UI 由 ⑤ 设置复用): ```python @@ -303,9 +303,9 @@ fetch_cmhub_models(base_url, api_key, connect_timeout=10, read_timeout=30) -> li 要点: -- `backend=direct`:标题用 `default_text_model`、封面用 `default_image_model`(`appconfig.get_model` 取定义,含 url/key/api_type)。 -- `backend=cmhub`:标题调用 `POST /api/v1/generate/title`,封面调用 `POST /api/v1/generate/image`,模型字段使用 `ai.cmhub.title_alias/image_alias`,Key 来自 `config/cmhub.json`。 -- `fetch_cmhub_models()` 调 `GET /api/v1/models` 返回别名清单,供 T-527 设置页动态下拉使用。 +- `backend=direct`:内部兼容/手工回滚路径;标题用 `default_text_model`、封面用 `default_image_model`(`appconfig.get_model` 取定义,含 url/key/api_type)。 +- `backend=cmhub`:普通产品默认路径;标题调用 `POST /api/v1/generate/title`,封面调用 `POST /api/v1/generate/image`,模型字段使用 `ai.cmhub.title_alias/image_alias`,Key 来自 `config/cmhub.json`。 +- `fetch_cmhub_models()` 调 `GET /api/v1/models` 返回别名清单,供⑤设置页动态下拉使用。 - `api_type=chat/auto` 走 OpenAI-compatible chat JSON;`api_type=images_edits` 走 multipart form。 - direct 连接超时参考模型 `connect_timeout_seconds`;**返回超时 = 模型 `timeout_seconds` 或 `appconfig.response_timeout()`(随分辨率:512/1k/2k/4k → 180/240/360/600)**。cmhub 使用 `requests timeout=(connect, read)`,connect 来自 `ai.cmhub.connect_timeout`,read 随分辨率且封顶 600s。 - 并发数/重试/分辨率/jpg 质量来自 `appconfig.ai_config()`;Key 本地明文存储,但不入日志、不导出。cmhub 响应的 `points_cost/points_balance/call_id` 不改变返回值,通过 `on_event` metadata 事件上报,GUI 余额/计费展示留给 T-528。 diff --git a/docs/cmhub-integration-design.md b/docs/cmhub-integration-design.md index 98b718b..9f72374 100644 --- a/docs/cmhub-integration-design.md +++ b/docs/cmhub-integration-design.md @@ -6,8 +6,9 @@ > > **v2 修订(2026-07-04,吸收对接文档)**:修正生图重试策略(非幂等,读超时不重发,避免重复扣点);超时按分辨率细化到 600s 上限;余额直接用响应 `points_balance`;错误处理改为按 `code` 优先分支 + 未知 code 当不可重试,补 `content_blocked`;确认 `resolution`=`512/1K/2K/4K`(大写)、`aspect_ratio` 默认 `1:1`。 > **v3 修订(2026-07-04,工程评审修正)**:补充既有配置迁移策略(缺 `backend` 先按 `direct` 处理,避免未配置 cmhub 时破坏现有生成);固定 cmhub Key 文件为 `config/cmhub.json`;新增 `CMHubError` 结构化错误要求;明确 `points_balance/points_cost/call_id` 通过回调事件传播而不是改变 `gen_title`/`gen_cover` 返回值;要求 cmhub 分支用可区分 connect/read timeout 的 HTTP 调用;把 `image_url` 下载安全校验纳入 T-526 验收;T-525 顺延,Phase 7 成为当前业务优先任务。 -> **v3.1 修订(2026-07-04,实现前澄清)**:① 全新配置默认 backend 统一为 `direct`(cmhub 显式 opt-in),且 `backend=cmhub` 但未配置时须抛清晰"去⑤配置"错误而非崩溃(见 §4.1);② 明确"返回值不变"≠"回调不变"——计费元数据须给 `gen_title`/`gen_cover` **新增可选事件回调参数**承载,是向后兼容加参(见 §4.2)。两点已同步 T-526 验收。 -> **v3.2 修订(2026-07-04,同步对接文档新增)**:cmhub 新增第 4 个接口 `GET /api/v1/models`(别名自助发现,见 §4.6),⑤设置别名由手填改为**动态下拉**(按 `operation_type` 分生文/生图、过滤 `unpriced`、展示单价、`requires_image` 提示),关闭原待确认 #1;仅 Base URL 仍待部署方提供。已同步 T-526(加 `fetch_cmhub_models` helper)与 T-527(别名下拉)。 +> **v3.1 修订(2026-07-04,实现前澄清)**:当时为保护既有直连流程,要求全新配置默认 backend 为 `direct`(cmhub 显式 opt-in),且 `backend=cmhub` 但未配置时须抛清晰"去⑤配置"错误而非崩溃(见 §4.1);② 明确"返回值不变"≠"回调不变"——计费元数据须给 `gen_title`/`gen_cover` **新增可选事件回调参数**承载,是向后兼容加参(见 §4.2)。默认策略已被 v3.3/T-529 覆盖为普通产品默认 cmhub。 +> **v3.2 修订(2026-07-04,同步对接文档新增)**:cmhub 新增第 4 个接口 `GET /api/v1/models`(别名自助发现,见 §4.6),⑤设置别名由手填改为**动态下拉**(按 `operation_type` 分生文/生图、过滤 `unpriced`、展示单价、`requires_image` 提示),关闭原待确认 #1;仅 Base URL 仍待部署方提供。 +> **v3.3 修订(2026-07-04,产品收口)**:普通用户后续默认使用 `cmhub` 网关;⑤设置页去掉「AI 后端」label 和 direct/cmhub 下拉,直接展示 cmhub 网关配置。`direct` 代码和 `config/ai_models.json` 保留为内部兼容/手工回滚路径,但普通 UI 不提供切换入口;保存设置固定写 `ai.backend=cmhub`,允许先保存不完整 cmhub 配置,②生成时再提示去⑤补齐 Base URL/API Key/别名。已同步 T-529。 ## 1. 背景与目标 @@ -39,7 +40,7 @@ - **接缝最小化**:保持 `gen_title(...)` / `gen_cover(...)` 的**返回值与现有调用兼容**(`gen_title`→标题字符串、`gen_cover`→已存 JPEG 路径),允许向后兼容新增可选事件回调参数承载计费元数据,只改内层实现。这样 `generate_batch` 编排、并发、重试、DB 写入、诊断日志、JPEG 落盘、`images///...` 路径、T-520 封面开关**全部复用、零改动**;GUI `run_logs` 记录和余额展示放到 T-528。 - **不碰高风险层**:`editor.py`/`cdp.py`/`chrome.py`/`accounts.py`/`excel.py`/`db.py`/`workers.py`,以及 ①采集/③更新/④账号全流程**完全不动**。这是本改动最重要的安全边界。 -- **适配器而非硬替换**:内层抽 `backend`(`cmhub` / `direct`),由配置选择。为保护既有用户,旧 `config.json` 缺 `ai.backend` 时先按 `direct` 迁移;用户在⑤设置明确切到 `cmhub` 并填好 Base URL / Key / 别名后才走网关。老 `ai_models.json` 不作废、测试易写、可灰度。 +- **产品默认 cmhub,direct 保留兼容**:内层仍抽 `backend`(`cmhub` / `direct`),但普通产品默认走 `cmhub`,⑤设置页不再展示后端切换。老 `ai_models.json` 和 direct 分支不删除,作为内部兼容/手工回滚路径;普通用户只配置 cmhub Base URL / Key / 别名。 ## 4. 方案 @@ -49,7 +50,7 @@ ```jsonc "ai": { - "backend": "direct", // "cmhub" | "direct";全新配置默认 direct,cmhub 为显式 opt-in + "backend": "cmhub", // 普通产品默认 cmhub;direct 仅内部兼容/手工回滚 "cmhub": { "base_url": "https://", // 网关根地址,请求时拼 /api/v1/... "title_alias": "title-standard", // 生文能力别名 @@ -64,10 +65,7 @@ - **cmhub API Key 不进 `config.json`**(避免与其它设置混放、避免误提交)。固定存到 `config/cmhub.json`,文件 schema 第一版为 `{ "api_key": "sk_cmhub_xxx" }`;新增 `CMHUB_CONFIG_PATH`、`load_cmhub_config()`、`save_cmhub_config()`、`get_cmhub_api_key(masked=False)` 等 helper;`config/cmhub.json` 必须加入 `.gitignore`。沿用现有"本地明文保存但 gitignore + UI 打码 + 日志脱敏"纪律(T-503)。 - **`ai_models.json` 去留**:`direct` 模式继续用;`cmhub` 模式不读它。文件保留但标记 legacy。 -- **默认 backend 的两条路径要分清(避免首启破坏)**:`appconfig` 里两条路径都必须落到"不破坏开箱"这一条统一规则——`backend` 默认 `direct`: - - **加载既有 `config.json`** 缺 `ai.backend` 时,补 `direct`(保护老用户,其 `ai_models.json` 仍有效)。 - - **`DEFAULT_CONFIG` / `default_config()` 造全新配置** 同样写 `direct`,不写 `cmhub`——否则全新安装 `base_url`/Key 为空时首次生成直接报错。`cmhub` 一律由用户在 ⑤ 设置显式切换并填好 Base URL / Key / 别名后才生效。 - - 兜底:即使 `backend=cmhub` 但 `base_url`/Key 缺失,也必须抛**清晰的"请去⑤配置 cmhub"错误**(`CMHubError`/`AIError`),不得崩溃或静默直连。 +- **默认 backend 与配置不完整处理(T-529 后)**:`DEFAULT_CONFIG` / `default_config()` 造全新配置时写 `backend=cmhub`;⑤设置页隐藏 direct/cmhub 下拉并固定保存 `backend=cmhub`。允许先保存不完整 cmhub 配置,便于用户先保存其它路径/安全设置;②生成真正调用时如果 `base_url`/Key/别名缺失,必须抛**清晰的"请去⑤配置 cmhub"错误**(`CMHubError`/`AIError`),不得崩溃或静默回退 direct。显式手工配置 `backend=direct` 仍作为内部回滚路径保留,但普通 UI 不提供入口。 ### 4.2 `gen_title` 改造(cmhub 分支) @@ -156,7 +154,7 @@ cmhub 返回结构化 `{error:{code}}`。映射层**按 `code` 优先分支**( - 生文:`titles` 多条取首条;空 `titles`/空串 → `AIError`。 - 生图:`image_url` → mock 下载字节 → `_save_jpeg` 落盘校验分辨率/质量;覆盖 scheme、内网/回环字符串地址、域名解析到内网 IP 的拒绝路径。 - 错误码矩阵:402/401/403/400 不重试且原因正确;502/429/连接超时按 attempts 重试;生图读超时不重发;未知 code 不重试。 - - 配置:`backend=cmhub` 走 cmhub 分支、`direct` 走旧分支;旧配置缺 `backend` 时保持 direct;缺 `base_url`/Key 时明确报错。 + - 配置:`backend=cmhub` 走 cmhub 分支、显式 `direct` 走旧分支;T-529 后缺 `backend` 的配置按 `DEFAULT_CONFIG` 补为 cmhub;缺 `base_url`/Key/别名时明确报错。 - **GUI**:⑤ cmhub 面板读写、测试连接 worker、别名下拉;② 生成在 `insufficient_points` 时的提示路径。 - **回归**:`direct` 模式现有 `test_ai.py` 用例保持绿。 - 验证命令沿用 `python -m unittest discover -s tests`。 @@ -182,6 +180,7 @@ cmhub 返回结构化 `{error:{code}}`。映射层**按 `code` 优先分支**( - **第一步**:`app/ai.py` + `app/appconfig.py` 接入 cmhub backend(mock 联调),保留 direct。 - **第二步**:⑤设置 UI cmhub 面板 + 测试连接/查余额。 - **第三步**:② 计费错误提示(`insufficient_points` 引导充值)+ 可选余额展示。 +- **第四步(T-529)**:产品默认 cmhub,⑤去掉 AI 后端选择,保存固定 `backend=cmhub`。 - 文档随每步同步。 > 当前已在 `docs/06-tasks.md` 落成 T-526~T-528;下一步按 T-526 开始代码实现,仍遵守 `docs/05-coding-rules.md` 验证清单。 diff --git a/docs/current-state.md b/docs/current-state.md index 0560228..b5fef80 100644 --- a/docs/current-state.md +++ b/docs/current-state.md @@ -6,39 +6,39 @@ ## 当前快照 - 日期:2026-07-04 -- 阶段:V0 单账号 CDP 流程已验证;V1 已完成 T-000 正式代码包结构、T-001 `app/editor.py` 模块化、T-002 `app/appconfig.py` 应用配置、T-003 SQLite 持久化地基、T-004 本地数据忽略规则、T-005 AI 模型清单后端、T-006 单元测试基座、T-101 账号 user-data-dir 工具、T-102 Chrome 启动器、T-103 登录保活与检测、T-103b 登录检测补充识别 Shopee accounts 登录页、T-104 PySide6 主窗口骨架、T-104b PySide6 worker 基座、T-105 Tab④ 账号管理、T-106 账号快捷方式、T-201 Excel 导入入库、T-202 Tab① 任务列表与导入按钮、T-202b Tab① 导入汇总栏、T-203 采集旧标题旧封面、T-204 回写旧字段到原 Excel、T-204b 采集完成自动回写、T-205 首次未配账号/Chrome 未启动/未登录引导保护、T-205b 采集后关闭自动新建商品页 tab、T-206 Tab① 删除指定批次软删除、T-301 AI 生成接口、T-302 Tab② AI 生成布局与任务列表、T-302p 提示词管理、T-303 Tab② 开始生成/停止/进度、T-303b ②/③ 商品ID筛选、T-401 Tab③ 更新列表筛选与开始更新确认、T-402 Tab③ 确认后串行更新、T-403 Tab③ 结果回写与结束汇总、T-501 Tab⑤ AI 模型管理 UI、T-501b Tab⑤ 角色与生成参数、T-501c Tab⑤ Shopee 更新安全开关、T-502 换封面删第一张再上传、T-503 敏感信息本地明文保存提示与日志脱敏、T-504 多账号并行/dry-run/运行日志、T-207 ①采集诊断日志、② AI生成图片失败诊断日志补丁、T-404a ②/③ 选中记录重置、T-404 真实 Shopee 单条更新冒烟验收、T-506 正式使用批量更新体验、T-507 正式批量更新移除普通流程测试商品 ID 限制、T-508 ③ 更新shopee生产化操作区、T-509 ② 新标题人工微调、T-510 ③ 检查本轮更新文案统一、T-505 全流程诊断日志扩展、T-511 语义色板与任务状态列上色、T-512 ③高风险按钮上色与①导入校验数字标红、T-513 登录点/③Tab危险标识/破坏性按钮上色、T-514 ①②③ 首次空状态引导卡片、T-515 批次阶段进度总览、T-516 ①筛选对齐②③、T-517 ⑤设置分区与兼容字段清理、T-518 ②左栏提示词区组件密度优化、T-519 ②AI生成长任务进度条与用户可读滚动日志、T-520 ②AI生成封面可选生成开关、T-521 依赖清单(锁版本 requirements)、T-522 CI 自动跑语法 + 单元 / GUI 测试、T-523 拆分 `app/gui.py` 为 `app/gui/` 包、T-523a ②/③ 新一轮运行前清空界面日志显示、T-404b 商品详情页加载失败 toast 自动捕获、T-524 PyInstaller 打包为免安装 exe、T-526 `app/ai.py` + `appconfig` 接入 cmhub backend、T-527 ⑤设置 cmhub 网关面板、T-528 ②计费错误提示与余额展示。 -- 技术栈:Python 3.10+,根目录 `requirements.txt` 锁定运行依赖,GitHub Actions 使用 Windows + Python 3.11 自动跑语法和单元/GUI 测试,PyInstaller onedir 打包为 Windows 免安装 exe;自研 CDP(websocket-client + requests),SQLite(sqlite3)+ `config.json` + openpyxl + AI(direct: `config/ai_models.json` 通用 HTTP;cmhub: `config.json` backend + `config/cmhub.json` Key),GUI PySide6 5 Tab(已定)。 -- 生产代码:已建立 `app/` 包 + 根入口 `main.py`;`app/cdp.py` 为已验证 CDP 底座,已区分 `CDP.close()` 断开 WebSocket 与 `close_tab()` 关闭浏览器 target;`app/editor.py` 已封装登录状态检测、标题/封面/采集/更新按钮能力,打开商品详情页时会安装 toast 捕获并在详情页加载失败时上浮商品失效/无权限等错误原因,`open_product()` 内部失败会关闭本轮自动新建的失败 tab,复用用户已有 tab 不关闭,并在采集结束后只关闭本轮自动新建的商品编辑页 tab、保留用户已有 tab,更新提交成功且设置开启时可关闭本轮自动新建商品页,确认后跳回商品列表页时关闭前等待 2 秒;`click_update()` 已补 Shopee 站点侧确认框处理,页面主「更新」后若出现 `確定您要更新商品嗎?` modal(`.eds-modal__content` / `.eds-modal__box`),只点击弹窗主按钮「更新」并避开「立即優化」,且记录确认后是否跳回商品列表页;`replace_cover()` 已实现更新封面统一先校验本地旧封面备份,再点第一张删除、可见确认框、等待图片管理器稳定和上传 input 恢复,随后先点击上传块模拟人工入口、短暂等待、重新获取 input、注入文件上传新图并等待 Shopee CDN 后拖到第一位的代码路径;已修正 2026-06-30 稳定等待回归:删除第一张前不要求上传 input 可用,只等图片列表稳定;删除后再等上传入口恢复;`重複/重复/duplicate` 上传 toast 会立即判为新封面重复错误;`app/image_paths.py` 已统一新采集/新生成图片路径为 `images///__old/new.jpg`,历史 DB 路径继续按原路径读取;`app/appconfig.py` 已实现 `config.json` 默认值/读写/更新、AI 参数、端口读取、Shopee 更新安全与执行模式默认值,`config/ai_models.json` 模型清单 CRUD/过滤/打码/测试连接与 OpenAI-compatible base URL 自动补 endpoint,`config/cmhub.json` cmhub Key 读写/打码 helper,以及 `mask_secret()`、`sanitize_for_log()`、`redact_secrets()` 敏感信息脱敏工具;`app/ai.py` 已实现 `gen_title()`/`gen_cover()`/`generate_batch()`,支持 direct 通用 HTTP 和 cmhub 网关 backend,direct 按默认文本/图片模型调用,cmhub 按 Base URL + 生文/生图别名调用 title/image/models/balance 接口,生图 `image_url` 安全下载后转本地 JPEG,计费 metadata 通过事件回调传出,且保留重试、错误脱敏、图片 URL/base64 解析、resolution resize、jpg_quality 保存、按 `ai.generate_cover` 选择只生成标题或先并发标题再并发封面、逐条 `set_generated`、失败 `mark_failed`、步骤级事件/错误回调与停止取消未开始项;`app/prompts.py` 已实现标题提示词读写、封面模板 CRUD 与变量替换;`app/db.py` 已实现 SQLite schema、连接 PRAGMA、批次/账号/任务与阶段写库函数、T-206 批次软删除标记与默认业务查询过滤、T-404a 本地生成结果/更新状态重置函数、T-509 `update_generated_title()` 本地新标题微调函数,以及 `run_logs/run_log_events` 运行日志函数;`app/diagnostics.py` 已实现 gitignore 本地诊断日志、滚动写入、结构化 payload 和自由文本脱敏;T-505 已把 Excel 导入/回写、③更新shopee、④Chrome 启动/登录检测、⑤AI模型测试连接接入 `run_logs/run_log_events` 与本地脱敏诊断日志;`app/excel.py` 已实现多 Excel 输入列解析、整文件列校验、脏行统计跳过、导入批次与任务入库、别名匹配统计、旧标题/旧封面路径回写原 Excel、更新结果回写原 Excel 与另存副本;`app/config.py` 已实现账号 slug 与 user-data-dir 创建;`app/accounts.py` 已实现账号 CRUD 服务、端口默认分配、启动登录、检测登录、生成快捷方式;`app/chrome.py` 已实现 Chrome 参数拼装、启动、CDP 端口探测、PowerShell `.lnk` 快捷方式生成;`app/gui/` 已由 T-523 拆分为 PySide6 GUI 包,包入口 `__init__.py` 兼容旧导入,`main_window.py` 放 `MainWindow`,`models.py` 放 3 个 TableModel,`widgets.py` 放色板/空状态/批次总览/helper,`workers.py` 放具体 GUI worker,`tabs/` 放 ①~⑤ Tab;整体仍实现 PySide6 `MainWindow`、五 Tab、顶部 Tab 栏防误点样式、统一语义色板、①②③任务状态列前景色、③「开始更新」warning 描边/文字色和①导入校验数字标红、④登录状态点上色、③更新shopee Tab warning 小圆点和删除类按钮 danger 样式、①②③首次空状态引导卡片、①②③批次阶段进度总览、① 导入采集的 Excel 导入按钮/导入汇总栏/批次筛选与删除批次软删除入口/QTableView 任务列表/未匹配筛选与略过标记/采集旧标题旧封面 worker/采集前账号就绪预检与④引导/采集完成自动回写/旧数据回写重试按钮与 worker/采集运行日志视图、② AI生成左右布局/标题与封面提示词管理/批次/店铺/商品ID/状态筛选/任务列表/新标题列本地微调/变量预览/生成封面图片成本开关/开始生成/停止/标题与图片双进度条/双击新旧封面预览/用户可读自动滚动AI生成运行日志/cmhub余额显示/点数不足弹窗/计费日志/重置生成结果与 `GenerateWorker`、③ 更新shopee批次/店铺/商品ID/状态筛选栏/任务列表/开始更新主按钮/重置更新状态右键菜单/更新安全开关拦截与「前往设置」跳转/「检查本轮更新」按钮/开始更新确认弹窗/确认后 `ApplyWorker` 按每批最大更新条数分批执行当前筛选全部可更新记录/账号就绪和端口冲突预检/按账号并行可选/逐条 `set_applied`/运行日志/自动回写结果到 Excel/结束汇总弹窗/手动回写重试按钮、④ 账号管理表格/弹窗/按钮/快捷方式与状态栏、密码明文保存提示、⑤ 设置按 AI backend 切换:direct 模式保留 AI 模型同列上下布局、三列详情/参数表单、API Key 明文保存提示和 `AIModelTestWorker` 后台测试连接,cmhub 模式显示网关 Base URL、API Key、动态生文/生图别名下拉、刷新别名与测试连接/查余额;通用角色/生成参数、路径/端口、Shopee 更新安全与多账号并行设置继续持久化 `config.json`,cmhub Key 单独写入 `config/cmhub.json`,保存成功后弹轻量提示框;⑤ 设置页已将「Shopee 更新安全 / 执行模式」前置、将「基础设施(路径与端口)」后置,`test_item_id` 与 `dry_run` 不再有用户可操作控件,保存时保留 `test_item_id` 兼容值并固定 `dry_run=false`;③ 普通正式更新不再用 `test_item_id` 阻断非测试商品,确认弹窗不再显示测试商品 ID;`app/workers.py` 已实现 `BaseWorker`、通用 signals、取消标记和 `QThread` 启动包装。 -- 测试:`tests/` 已建立;T-522 已新增 GitHub Actions 在 push / pull_request 自动运行语法检查与全量 unittest;T-006 后纯逻辑改动必须运行 `python -m unittest discover -s tests`,当前覆盖 appconfig/db/config/accounts/chrome 启动与快捷方式/editor 登录检测(含 Shopee accounts 登录页)与商品 tab 生命周期、商品详情页失效/隐藏 toast 捕获、更新成功后关闭本轮新开 tab、更新封面备份缺失阻断/8张与9张先删第一张/删除确认/上传入口初始不可用/重复图片 toast/物流错误不阻断上传/删图后稳定等待/上传状态诊断/拖首位 mock 路径/Shopee 更新确认框、成功跳回商品列表和残留错误 toast 不覆盖成功 mock 路径/excel 导入/旧字段与更新结果回写/ai 标题与封面 HTTP 解析/`generate_batch` 正常、失败与停止/prompts 读写与渲染/gui ① 导入采集、删除批次软删除与采集诊断日志/gui ② AI生成布局与批次/店铺/商品ID/状态筛选、提示词管理、生成 worker、图片失败诊断日志、双击预览、重置生成结果与新标题本地编辑/gui ③ 更新shopee批次/店铺/商品ID/状态筛选列表、确认弹窗、开始更新主按钮、重置更新状态右键菜单、Shopee 更新安全拦截与前往设置、`ApplyWorker` 串行/检查/分批/按账号并行/端口冲突预检、运行日志、结果回写与汇总/gui ④ 账号管理、密码打码与明文保存提示/gui ⑤ AI 模型管理、API Key 打码与明文保存提示、角色/生成参数设置、Shopee 更新安全设置、检查按钮/每批最大更新条数/多账号并行设置、测试商品 ID 限制移除/worker signal 与线程包装、T-505 全流程诊断日志(import/write_back/apply/chrome_launch/login_check/ai_model_test)与本地日志脱敏、gui T-511 语义色板和任务状态列前景色、T-512 高风险按钮与导入校验数字样式、T-513 登录状态/Tab 标识/删除按钮样式、T-514 首次空状态引导卡片、T-515 批次阶段进度总览、T-516 ①筛选对齐②③、T-517 ⑤设置分区与兼容字段清理、T-518 ②左栏提示词区组件密度优化、T-519 ②AI生成长任务进度条与用户可读滚动日志、T-520 ②AI生成封面可选生成开关、T-524 PyInstaller 打包入口/构建脚本/发布目录校验、T-526 cmhub backend mock、T-527 设置页 cmhub backend 切换/别名下拉/worker mock、T-528 ②cmhub余额显示/计费日志/点数不足中止提示;2026-07-01 已完成 5 个真实商品的 T-404 更新验收,后续 CDP/Shopee 改动仍需测试商品手动验证。 +- 阶段:V0 单账号 CDP 流程已验证;V1 已完成 T-000 正式代码包结构、T-001 `app/editor.py` 模块化、T-002 `app/appconfig.py` 应用配置、T-003 SQLite 持久化地基、T-004 本地数据忽略规则、T-005 AI 模型清单后端、T-006 单元测试基座、T-101 账号 user-data-dir 工具、T-102 Chrome 启动器、T-103 登录保活与检测、T-103b 登录检测补充识别 Shopee accounts 登录页、T-104 PySide6 主窗口骨架、T-104b PySide6 worker 基座、T-105 Tab④ 账号管理、T-106 账号快捷方式、T-201 Excel 导入入库、T-202 Tab① 任务列表与导入按钮、T-202b Tab① 导入汇总栏、T-203 采集旧标题旧封面、T-204 回写旧字段到原 Excel、T-204b 采集完成自动回写、T-205 首次未配账号/Chrome 未启动/未登录引导保护、T-205b 采集后关闭自动新建商品页 tab、T-206 Tab① 删除指定批次软删除、T-301 AI 生成接口、T-302 Tab② AI 生成布局与任务列表、T-302p 提示词管理、T-303 Tab② 开始生成/停止/进度、T-303b ②/③ 商品ID筛选、T-401 Tab③ 更新列表筛选与开始更新确认、T-402 Tab③ 确认后串行更新、T-403 Tab③ 结果回写与结束汇总、T-501 Tab⑤ AI 模型管理 UI、T-501b Tab⑤ 角色与生成参数、T-501c Tab⑤ Shopee 更新安全开关、T-502 换封面删第一张再上传、T-503 敏感信息本地明文保存提示与日志脱敏、T-504 多账号并行/dry-run/运行日志、T-207 ①采集诊断日志、② AI生成图片失败诊断日志补丁、T-404a ②/③ 选中记录重置、T-404 真实 Shopee 单条更新冒烟验收、T-506 正式使用批量更新体验、T-507 正式批量更新移除普通流程测试商品 ID 限制、T-508 ③ 更新shopee生产化操作区、T-509 ② 新标题人工微调、T-510 ③ 检查本轮更新文案统一、T-505 全流程诊断日志扩展、T-511 语义色板与任务状态列上色、T-512 ③高风险按钮上色与①导入校验数字标红、T-513 登录点/③Tab危险标识/破坏性按钮上色、T-514 ①②③ 首次空状态引导卡片、T-515 批次阶段进度总览、T-516 ①筛选对齐②③、T-517 ⑤设置分区与兼容字段清理、T-518 ②左栏提示词区组件密度优化、T-519 ②AI生成长任务进度条与用户可读滚动日志、T-520 ②AI生成封面可选生成开关、T-521 依赖清单(锁版本 requirements)、T-522 CI 自动跑语法 + 单元 / GUI 测试、T-523 拆分 `app/gui.py` 为 `app/gui/` 包、T-523a ②/③ 新一轮运行前清空界面日志显示、T-404b 商品详情页加载失败 toast 自动捕获、T-524 PyInstaller 打包为免安装 exe、T-526 `app/ai.py` + `appconfig` 接入 cmhub backend、T-527 ⑤设置 cmhub 网关面板、T-528 ②计费错误提示与余额展示、T-529 默认 cmhub 网关并隐藏 AI 后端选择。 +- 技术栈:Python 3.10+,根目录 `requirements.txt` 锁定运行依赖,GitHub Actions 使用 Windows + Python 3.11 自动跑语法和单元/GUI 测试,PyInstaller onedir 打包为 Windows 免安装 exe;自研 CDP(websocket-client + requests),SQLite(sqlite3)+ `config.json` + openpyxl + AI(默认 cmhub: `config.json` 网关配置 + `config/cmhub.json` Key;direct: `config/ai_models.json` 通用 HTTP 内部兼容),GUI PySide6 5 Tab(已定)。 +- 生产代码:已建立 `app/` 包 + 根入口 `main.py`;`app/cdp.py` 为已验证 CDP 底座,已区分 `CDP.close()` 断开 WebSocket 与 `close_tab()` 关闭浏览器 target;`app/editor.py` 已封装登录状态检测、标题/封面/采集/更新按钮能力,打开商品详情页时会安装 toast 捕获并在详情页加载失败时上浮商品失效/无权限等错误原因,`open_product()` 内部失败会关闭本轮自动新建的失败 tab,复用用户已有 tab 不关闭,并在采集结束后只关闭本轮自动新建的商品编辑页 tab、保留用户已有 tab,更新提交成功且设置开启时可关闭本轮自动新建商品页,确认后跳回商品列表页时关闭前等待 2 秒;`click_update()` 已补 Shopee 站点侧确认框处理,页面主「更新」后若出现 `確定您要更新商品嗎?` modal(`.eds-modal__content` / `.eds-modal__box`),只点击弹窗主按钮「更新」并避开「立即優化」,且记录确认后是否跳回商品列表页;`replace_cover()` 已实现更新封面统一先校验本地旧封面备份,再点第一张删除、可见确认框、等待图片管理器稳定和上传 input 恢复,随后先点击上传块模拟人工入口、短暂等待、重新获取 input、注入文件上传新图并等待 Shopee CDN 后拖到第一位的代码路径;已修正 2026-06-30 稳定等待回归:删除第一张前不要求上传 input 可用,只等图片列表稳定;删除后再等上传入口恢复;`重複/重复/duplicate` 上传 toast 会立即判为新封面重复错误;`app/image_paths.py` 已统一新采集/新生成图片路径为 `images///__old/new.jpg`,历史 DB 路径继续按原路径读取;`app/appconfig.py` 已实现 `config.json` 默认值/读写/更新、AI 参数、端口读取、Shopee 更新安全与执行模式默认值,`ai.backend` 默认 `cmhub`,`config/ai_models.json` 模型清单 CRUD/过滤/打码/测试连接与 OpenAI-compatible base URL 自动补 endpoint,`config/cmhub.json` cmhub Key 读写/打码 helper,以及 `mask_secret()`、`sanitize_for_log()`、`redact_secrets()` 敏感信息脱敏工具;`app/ai.py` 已实现 `gen_title()`/`gen_cover()`/`generate_batch()`,支持 direct 通用 HTTP 和 cmhub 网关 backend,direct 按默认文本/图片模型调用,cmhub 按 Base URL + 生文/生图别名调用 title/image/models/balance 接口,生图 `image_url` 安全下载后转本地 JPEG,计费 metadata 通过事件回调传出,且保留重试、错误脱敏、图片 URL/base64 解析、resolution resize、jpg_quality 保存、按 `ai.generate_cover` 选择只生成标题或先并发标题再并发封面、逐条 `set_generated`、失败 `mark_failed`、步骤级事件/错误回调与停止取消未开始项;`app/prompts.py` 已实现标题提示词读写、封面模板 CRUD 与变量替换;`app/db.py` 已实现 SQLite schema、连接 PRAGMA、批次/账号/任务与阶段写库函数、T-206 批次软删除标记与默认业务查询过滤、T-404a 本地生成结果/更新状态重置函数、T-509 `update_generated_title()` 本地新标题微调函数,以及 `run_logs/run_log_events` 运行日志函数;`app/diagnostics.py` 已实现 gitignore 本地诊断日志、滚动写入、结构化 payload 和自由文本脱敏;T-505 已把 Excel 导入/回写、③更新shopee、④Chrome 启动/登录检测、⑤AI模型测试连接接入 `run_logs/run_log_events` 与本地脱敏诊断日志;`app/excel.py` 已实现多 Excel 输入列解析、整文件列校验、脏行统计跳过、导入批次与任务入库、别名匹配统计、旧标题/旧封面路径回写原 Excel、更新结果回写原 Excel 与另存副本;`app/config.py` 已实现账号 slug 与 user-data-dir 创建;`app/accounts.py` 已实现账号 CRUD 服务、端口默认分配、启动登录、检测登录、生成快捷方式;`app/chrome.py` 已实现 Chrome 参数拼装、启动、CDP 端口探测、PowerShell `.lnk` 快捷方式生成;`app/gui/` 已由 T-523 拆分为 PySide6 GUI 包,包入口 `__init__.py` 兼容旧导入,`main_window.py` 放 `MainWindow`,`models.py` 放 3 个 TableModel,`widgets.py` 放色板/空状态/批次总览/helper,`workers.py` 放具体 GUI worker,`tabs/` 放 ①~⑤ Tab;整体仍实现 PySide6 `MainWindow`、五 Tab、顶部 Tab 栏防误点样式、统一语义色板、①②③任务状态列前景色、③「开始更新」warning 描边/文字色和①导入校验数字标红、④登录状态点上色、③更新shopee Tab warning 小圆点和删除类按钮 danger 样式、①②③首次空状态引导卡片、①②③批次阶段进度总览、① 导入采集的 Excel 导入按钮/导入汇总栏/批次筛选与删除批次软删除入口/QTableView 任务列表/未匹配筛选与略过标记/采集旧标题旧封面 worker/采集前账号就绪预检与④引导/采集完成自动回写/旧数据回写重试按钮与 worker/采集运行日志视图、② AI生成左右布局/标题与封面提示词管理/批次/店铺/商品ID/状态筛选/任务列表/新标题列本地微调/变量预览/生成封面图片成本开关/开始生成/停止/标题与图片双进度条/双击新旧封面预览/用户可读自动滚动AI生成运行日志/cmhub余额显示/点数不足弹窗/计费日志/重置生成结果与 `GenerateWorker`、③ 更新shopee批次/店铺/商品ID/状态筛选栏/任务列表/开始更新主按钮/重置更新状态右键菜单/更新安全开关拦截与「前往设置」跳转/「检查本轮更新」按钮/开始更新确认弹窗/确认后 `ApplyWorker` 按每批最大更新条数分批执行当前筛选全部可更新记录/账号就绪和端口冲突预检/按账号并行可选/逐条 `set_applied`/运行日志/自动回写结果到 Excel/结束汇总弹窗/手动回写重试按钮、④ 账号管理表格/弹窗/按钮/快捷方式与状态栏、密码明文保存提示、⑤ 设置页 T-529 后默认展示 cmhub 网关 Base URL、API Key、动态生文/生图别名下拉、刷新别名与测试连接/查余额,不再显示「AI 后端」label/dropdown、direct 模型选择、模型详情或标题/图片模型角色下拉;保存设置固定写 `ai.backend=cmhub`,允许先保存不完整 cmhub 配置,生成时再提示补齐;direct 模型配置、`AIModelTestWorker` 和 `config/ai_models.json` 仅作内部兼容/手工回滚;通用生成参数、路径/端口、Shopee 更新安全与多账号并行设置继续持久化 `config.json`,cmhub Key 单独写入 `config/cmhub.json`,保存成功后弹轻量提示框;⑤ 设置页已将「Shopee 更新安全 / 执行模式」前置、将「基础设施(路径与端口)」后置,`test_item_id` 与 `dry_run` 不再有用户可操作控件,保存时保留 `test_item_id` 兼容值并固定 `dry_run=false`;③ 普通正式更新不再用 `test_item_id` 阻断非测试商品,确认弹窗不再显示测试商品 ID;`app/workers.py` 已实现 `BaseWorker`、通用 signals、取消标记和 `QThread` 启动包装。 +- 测试:`tests/` 已建立;T-522 已新增 GitHub Actions 在 push / pull_request 自动运行语法检查与全量 unittest;T-006 后纯逻辑改动必须运行 `python -m unittest discover -s tests`,当前覆盖 appconfig/db/config/accounts/chrome 启动与快捷方式/editor 登录检测(含 Shopee accounts 登录页)与商品 tab 生命周期、商品详情页失效/隐藏 toast 捕获、更新成功后关闭本轮新开 tab、更新封面备份缺失阻断/8张与9张先删第一张/删除确认/上传入口初始不可用/重复图片 toast/物流错误不阻断上传/删图后稳定等待/上传状态诊断/拖首位 mock 路径/Shopee 更新确认框、成功跳回商品列表和残留错误 toast 不覆盖成功 mock 路径/excel 导入/旧字段与更新结果回写/ai 标题与封面 HTTP 解析/`generate_batch` 正常、失败与停止/prompts 读写与渲染/gui ① 导入采集、删除批次软删除与采集诊断日志/gui ② AI生成布局与批次/店铺/商品ID/状态筛选、提示词管理、生成 worker、图片失败诊断日志、双击预览、重置生成结果与新标题本地编辑/gui ③ 更新shopee批次/店铺/商品ID/状态筛选列表、确认弹窗、开始更新主按钮、重置更新状态右键菜单、Shopee 更新安全拦截与前往设置、`ApplyWorker` 串行/检查/分批/按账号并行/端口冲突预检、运行日志、结果回写与汇总/gui ④ 账号管理、密码打码与明文保存提示/gui ⑤ AI 模型管理、API Key 打码与明文保存提示、角色/生成参数设置、Shopee 更新安全设置、检查按钮/每批最大更新条数/多账号并行设置、测试商品 ID 限制移除/worker signal 与线程包装、T-505 全流程诊断日志(import/write_back/apply/chrome_launch/login_check/ai_model_test)与本地日志脱敏、gui T-511 语义色板和任务状态列前景色、T-512 高风险按钮与导入校验数字样式、T-513 登录状态/Tab 标识/删除按钮样式、T-514 首次空状态引导卡片、T-515 批次阶段进度总览、T-516 ①筛选对齐②③、T-517 ⑤设置分区与兼容字段清理、T-518 ②左栏提示词区组件密度优化、T-519 ②AI生成长任务进度条与用户可读滚动日志、T-520 ②AI生成封面可选生成开关、T-524 PyInstaller 打包入口/构建脚本/发布目录校验、T-526 cmhub backend mock、T-527 设置页 cmhub backend 切换/别名下拉/worker mock、T-528 ②cmhub余额显示/计费日志/点数不足中止提示、T-529 默认 cmhub/隐藏 AI 后端选择/保存固定 cmhub/direct 兼容;2026-07-01 已完成 5 个真实商品的 T-404 更新验收,后续 CDP/Shopee 改动仍需测试商品手动验证。 - 数据:`config.json`、`config/ai_models.json`、`config/cmhub.json`、`cmshopee.db`、`cmshopee.db-*`、`chrome_user_data_dir/`、`images/`、`logs/`、`prompts/`、`title_prompt.txt`、`build/`、`dist/` 已由 `.gitignore` 排除;密码与 API Key 本地明文保存但保存/变更时提示,UI 打码,日志/导出必须脱敏;运营填写后的 Excel 业务文件默认忽略,标准空模板 `shopee待处理任务模板.xlsx` 可提交;`app/appconfig.py` 首次读取缺失的 `config.json` 时会在本地写默认配置,`app/db.py` 调用 `init_db()` 时会在本地创建 SQLite DB。 ## 既定设计要点(文档已定) - GUI:**PySide6 5 Tab 流水线**,顺序 ① 导入采集 → ② AI生成 → ③ 更新shopee → ④ 账号管理 → ⑤ 设置;后台任务用 worker/QThread/signal。 - 流水线阶段:imported → collected(采集旧标题/旧封面+下载+回写)→ generated(AI 提示词生成新标题/新封面)→ applied(③ 弹窗批量确认后改 Shopee 并提交)。**无 confirmed、无常驻提交开关。** -- 存储:`config.json`(应用设置和 AI backend 选择)+ `config/ai_models.json`(direct AI 模型清单与本地明文 Key)+ `config/cmhub.json`(cmhub 本地明文 Key)+ SQLite `cmshopee.db`(账号/任务/各阶段结果,密码本地明文仅参考)+ openpyxl(Excel)+ 本地 `images/`(旧/新封面);密码/API Key 保存或变更时提示,展示和日志/导出必须脱敏。 +- 存储:`config.json`(应用设置和 AI backend 内部字段,普通默认 cmhub)+ `config/ai_models.json`(direct AI 模型清单与本地明文 Key)+ `config/cmhub.json`(cmhub 本地明文 Key)+ SQLite `cmshopee.db`(账号/任务/各阶段结果,密码本地明文仅参考)+ openpyxl(Excel)+ 本地 `images/`(旧/新封面);密码/API Key 保存或变更时提示,展示和日志/导出必须脱敏。 - 多账号隔离:每账号独立 user-data-dir(非 profile)。 - 账号↔任务绑定:以 Excel“别名”列为权威;未匹配略过,结束弹窗汇总。 - 执行:默认多账号串行、单条失败继续;③ 提供「检查本轮更新」按钮,检查只写运行日志和汇总,不打开 Shopee、不提交线上、不改任务状态;⑤ 可开启多账号并行(不同账号并行、同账号内串行)并设置每批最大更新条数。③ 点击「开始更新」后先按真实更新安全设置弹窗确认当前筛选范围、任务数量、每批大小和预计批次;确认后做本轮账号 Chrome/CDP/登录态预检,未启动或未登录则弹窗列出账号并中止、不自动打开 Chrome;账号就绪后按批逐条点「更新」提交线上,点击停止后不再开始下一条或下一批。 -- AI:`app/ai.py` 支持 `backend=direct` 与 `backend=cmhub`。direct 模式由 `config/ai_models.json` 配置服务商/模型/Key,支持 chat JSON 与 images_edits multipart;cmhub 模式由 `config.json` 的 Base URL/别名和 `config/cmhub.json` Key 配置,生成返回值保持不变,计费 metadata 通过事件传出。生成内容直接用于更新,本地留档+回写 Excel 供追溯。 +- AI:普通产品默认 `backend=cmhub`,由 `config.json` 的 Base URL/别名和 `config/cmhub.json` Key 配置;`app/ai.py` 仍支持 `backend=direct` 作为内部兼容/手工回滚路径,direct 由 `config/ai_models.json` 配置服务商/模型/Key。生成返回值保持不变,计费 metadata 通过事件传出。生成内容直接用于更新,本地留档+回写 Excel 供追溯。 - 登录:人工登录 + 程序检测,不自动登录;④「启动登录」只是打开对应账号浏览器供人工登录,④「检测登录」和①/③预检只验证登录态;无 Shopee tab 时检测入口为 `https:///`(默认 `https://seller.shopee.tw/`);页面跳到 `accounts.shopee.tw/seller/login` 时已明确判为未登录;首次未配账号、对应账号 Chrome 未启动或未登录时,① ③ 应禁用或执行前预检提示,并引导去④,③ 不静默启动缺失账号 Chrome。 ## 当前目录要点 | 路径 | 状态 | 说明 | | --- | --- | --- | -| `docs/` | 已有 | 本 harness coding 文档集合 | +| `docs/` | 已有 | 本 harness coding 文档集合 | | `docs/packaging.md` | 已有 | T-524 产出:PyInstaller 打包命令、发布目录排除本地数据规则、首次运行数据位置和用户手动覆盖更新步骤 | | `app/cdp.py` | 已有 | CDP 底座:连接/找 tab/开 tab/关闭 target/执行 JS/拖拽;`CDP.close()` 只断开 WebSocket,`close_tab()` 才关闭浏览器页面 | | `prototypes/` | 已有 | 已验证原型/探查脚本(demo/set_title/set_cover/get_title/cookies/inspect_images/grab/1.py),保留作人工回归与探查参考;见 `prototypes/README.md` | | `chrome-remote-debug-lan.md` | 已有 | WSL→Windows CDP 转发排查记录 | | `docs/troubleshooting.md` | 已有 | 常见问题排查;已记录 `config/ai_models.json` 缺失 `category` 导致 GUI 启动报 “AI 模型 category 必须是 text 或 image” 的原因、修复和验证步骤 | | `app/__init__.py` / `app/__main__.py` / `main.py` | 已有 | 正式包与启动入口;`python main.py` / `python -m app` 可运行占位入口 | -| `requirements.txt` | 已有 | T-521 产出:锁定运行时直接依赖 PySide6 6.5.3、openpyxl 3.1.3、requests 2.31.0、websocket-client 1.6.1、Pillow 9.5.0;换机/CI 使用 `python -m pip install -r requirements.txt` | +| `requirements.txt` | 已有 | T-521 产出:锁定运行时直接依赖 PySide6 6.5.3、openpyxl 3.1.3、requests 2.31.0、websocket-client 1.6.1、Pillow 9.5.0;换机/CI 使用 `python -m pip install -r requirements.txt` | | `requirements-build.txt` | 已有 | T-524 产出:打包环境依赖,先安装运行依赖再安装 PyInstaller 5.13.2;不作为运行时依赖文件 | -| `.github/workflows/tests.yml` | 已有 | T-522 产出:push / pull_request 自动在 `windows-latest` + Python 3.11 安装 `requirements.txt`,设置 `QT_QPA_PLATFORM=offscreen`,运行 `python -m compileall app main.py` 与 `python -m unittest discover -s tests` | +| `.github/workflows/tests.yml` | 已有 | T-522 产出:push / pull_request 自动在 `windows-latest` + Python 3.11 安装 `requirements.txt`,设置 `QT_QPA_PLATFORM=offscreen`,运行 `python -m compileall app main.py` 与 `python -m unittest discover -s tests` | | `cmshopee.spec` / `scripts/build_exe.ps1` | 已有 | T-524 产出:PyInstaller onedir 构建配置与发布目录校验脚本;`cmshopee.spec` 不声明本地数据 `datas`,脚本检查 `dist\\cmshopee` 不含配置、DB、图片、日志、Chrome 登录态或提示词 | -| `app/gui/` | 已有 | T-523 产出:由旧 `app/gui.py` 拆分的 PySide6 GUI 包,包入口 `__init__.py` 继续兼容 `from app import gui` / `from app.gui import MainWindow`;`main_window.py` 放 MainWindow,`models.py` 放 3 个 TableModel,`widgets.py` 放色板/空状态/批次总览/helper,`workers.py` 放具体 GUI workers,`tabs/` 放 ①~⑤ Tab; T-104/T-105/T-106/T-202/T-202b/T-203/T-204/T-204b/T-205/T-206/T-207/T-302/T-302p/T-303/T-303b/T-401/T-402/T-403/T-404a/T-501/T-501b/T-501c/T-503/T-504/T-506/T-507/T-508/T-509/T-510/T-505/T-511/T-512/T-513/T-514/T-515/T-516/T-517/T-518/T-519/T-520/T-527/T-528 产出:PySide6 `QMainWindow` + 五 Tab;顶部 Tab 栏防误点样式;统一语义色板、①②③状态列前景色、③开始更新 warning 样式、①导入校验数字标红、④登录状态点、③Tab warning 小圆点、删除按钮 danger 样式、①②③首次空状态引导卡片和批次阶段进度总览;① 导入采集导入按钮、导入汇总栏、批次/店铺/商品ID/状态筛选、删除批次软删除按钮、`QTableView` 任务列表、未匹配筛选与略过标记、采集旧标题旧封面 worker(旧封面路径按批次/店铺/任务细分)、采集前账号就绪预检与④引导、采集完成自动回写与手动重试、采集运行日志视图;② AI生成左右布局、提示词管理、批次/店铺/商品ID/状态筛选栏、任务列表、新标题列本地编辑、开始生成/停止/标题与图片双进度条、双击新旧封面预览、用户可读自动滚动AI生成运行日志、重置生成结果与 `GenerateWorker`;③ 更新shopee批次/店铺/商品ID/状态筛选栏、任务列表、开始更新主按钮、重置更新状态右键菜单、Shopee 更新安全拦截与「前往设置」跳转、「检查本轮更新」按钮、开始更新确认弹窗、`ApplyWorker` 检查/分批串行/按账号并行、账号与端口预检、运行日志、逐条 `set_applied`、自动回写结果到 Excel、结束汇总弹窗与手动回写重试;④ 账号管理表格、账号弹窗、密码本地明文保存提示、启动登录、检测登录、快捷方式;⑤ 设置 AI backend 选择,direct 模式保留模型下拉、新增/删除、详情编辑、密钥打码与本地明文保存提示、测试连接 worker、默认角色下拉,cmhub 模式提供 Base URL、API Key、生文/生图动态别名、刷新别名、测试连接/查余额和 `config/cmhub.json` 保存;通用生成参数、路径端口配置、三列 Shopee 更新安全与执行模式设置仍保留,高频安全/执行模式分区前置、基础设施分区后置,`test_item_id`/`dry_run` 兼容字段无用户入口且普通更新不再阻断正式更新;②封面模板「另存为/重命名/删除」低频操作已收敛进「模板操作」菜单;②AI生成底部已增加标题/图片双进度条,运行日志已改为用户可读、自动滚动、脱敏的长任务日志,且②本轮「生成封面图片(成本较高)」开关默认关闭并持久化到 `ai.generate_cover`,关闭时只生成标题并进入可更新状态;②cmhub模式会显示生成后的剩余点数,记录 points_cost/call_id 计费日志,并在点数不足时弹提示且中止未开始任务 | +| `app/gui/` | 已有 | T-523 产出:由旧 `app/gui.py` 拆分的 PySide6 GUI 包,包入口 `__init__.py` 继续兼容 `from app import gui` / `from app.gui import MainWindow`;`main_window.py` 放 MainWindow,`models.py` 放 3 个 TableModel,`widgets.py` 放色板/空状态/批次总览/helper,`workers.py` 放具体 GUI workers,`tabs/` 放 ①~⑤ Tab; T-104/T-105/T-106/T-202/T-202b/T-203/T-204/T-204b/T-205/T-206/T-207/T-302/T-302p/T-303/T-303b/T-401/T-402/T-403/T-404a/T-501/T-501b/T-501c/T-503/T-504/T-506/T-507/T-508/T-509/T-510/T-505/T-511/T-512/T-513/T-514/T-515/T-516/T-517/T-518/T-519/T-520/T-527/T-528/T-529 产出:PySide6 `QMainWindow` + 五 Tab;顶部 Tab 栏防误点样式;统一语义色板、①②③状态列前景色、③开始更新 warning 样式、①导入校验数字标红、④登录状态点、③Tab warning 小圆点、删除按钮 danger 样式、①②③首次空状态引导卡片和批次阶段进度总览;① 导入采集导入按钮、导入汇总栏、批次/店铺/商品ID/状态筛选、删除批次软删除按钮、`QTableView` 任务列表、未匹配筛选与略过标记、采集旧标题旧封面 worker(旧封面路径按批次/店铺/任务细分)、采集前账号就绪预检与④引导、采集完成自动回写与手动重试、采集运行日志视图;② AI生成左右布局、提示词管理、批次/店铺/商品ID/状态筛选栏、任务列表、新标题列本地编辑、开始生成/停止/标题与图片双进度条、双击新旧封面预览、用户可读自动滚动AI生成运行日志、重置生成结果与 `GenerateWorker`;③ 更新shopee批次/店铺/商品ID/状态筛选栏、任务列表、开始更新主按钮、重置更新状态右键菜单、Shopee 更新安全拦截与「前往设置」跳转、「检查本轮更新」按钮、开始更新确认弹窗、`ApplyWorker` 检查/分批串行/按账号并行、账号与端口预检、运行日志、逐条 `set_applied`、自动回写结果到 Excel、结束汇总弹窗与手动回写重试;④ 账号管理表格、账号弹窗、密码本地明文保存提示、启动登录、检测登录、快捷方式;⑤ 设置普通默认展示 cmhub 网关 Base URL、API Key、生文/生图动态别名、刷新别名、测试连接/查余额并保存 `config/cmhub.json` Key,不再显示 AI 后端选择、direct 模型选择/详情/角色下拉;direct 配置仅内部兼容/手工回滚;通用生成参数、路径端口配置、三列 Shopee 更新安全与执行模式设置仍保留,高频安全/执行模式分区前置、基础设施分区后置,`test_item_id`/`dry_run` 兼容字段无用户入口且普通更新不再阻断正式更新;②封面模板「另存为/重命名/删除」低频操作已收敛进「模板操作」菜单;②AI生成底部已增加标题/图片双进度条,运行日志已改为用户可读、自动滚动、脱敏的长任务日志,且②本轮「生成封面图片(成本较高)」开关默认关闭并持久化到 `ai.generate_cover`,关闭时只生成标题并进入可更新状态;②cmhub模式会显示生成后的剩余点数,记录 points_cost/call_id 计费日志,并在点数不足时弹提示且中止未开始任务 | | `app/workers.py` | 已有 | T-104b 产出:`BaseWorker` + 通用 signals + 取消标记 + `run_worker()` QThread 包装 | | `app/accounts.py` | 已有 | T-105/T-106 产出:账号 CRUD 服务、目录创建、端口分配、启动登录、检测登录、快捷方式 | | `app/editor.py` | 已有 | T-001/T-103/T-205b/T-207/T-501c/T-502/T-404b + T-404 补丁产出:登录状态检测、打开商品页、详情页加载失败 toast 捕获与商品失效归因、读/写标题、读/下载封面、采集步骤回调、更新步骤回调、采集后关闭自动新建商品页 tab、上传前等待图片管理器稳定、点击上传块后注入文件并检测 CDN 后拖封面、更新封面统一先校验旧封面备份再删线上第一张、重复图片 toast 明确失败、页面主更新按钮、Shopee 站点侧确认框主按钮、apply_task;更新成功后可按设置关闭本轮自动新建商品页 | @@ -68,8 +68,8 @@ 任务状态以 [`06-tasks.md`](06-tasks.md) 为准,历史记录见 [`../progress.md`](../progress.md)。 -- 已完成:T-000(正式代码包结构)、T-001(`app/editor.py` 模块化)、T-002(`app/appconfig.py` + `config.json`)、T-003(SQLite 建表)、T-004(本地数据 gitignore)、T-005(AI 模型清单后端)、T-006(单元测试基座)、T-101(账号 slug/user-data-dir)、T-102(Chrome 启动器)、T-103(登录保活与检测)、T-103b(登录检测补充识别 Shopee accounts 登录页)、T-104(PySide6 五 Tab 主窗口骨架)、T-104b(PySide6 worker 基座)、T-105(Tab④ 账号管理)、T-106(账号快捷方式)、T-201(Excel 导入:解析多文件输入列入库)、T-202(Tab① 任务列表 + 导入按钮 + 别名匹配标记)、T-202b(Tab① 导入汇总栏)、T-203(采集旧标题+旧封面)、T-204(回写旧字段到原 Excel)、T-204b(采集完成自动回写旧字段)、T-205(首次未配账号 / Chrome 未启动 / 未登录引导保护)、T-205b(采集后关闭自动新建商品页 tab)、T-206(Tab① 删除指定批次软删除)、T-301(AI 生成接口)、T-302(Tab② 左右布局与任务列表)、T-302p(提示词管理)、T-303(Tab② 开始生成 + 停止 + 进度)、T-303b(②/③ 商品ID筛选)、T-401(Tab③ 更新列表筛选 + 开始更新确认弹窗)、T-402(Tab③ 确认后串行更新)、T-403(Tab③ 结果回写与结束汇总)、T-501(Tab⑤ AI 模型管理 UI)、T-501b(Tab⑤ 角色与生成参数)、T-501c(Tab⑤ Shopee 更新安全开关)、T-502(换封面删第一张再上传)、T-503(敏感信息本地明文保存提示与日志脱敏)、T-504(多账号并行 / dry-run / 运行日志)、T-207(①采集诊断日志)、② AI生成图片失败诊断日志补丁、T-404a(②/③ 选中记录重置)、T-404(真实 Shopee 单条更新冒烟验收)、T-506(正式使用批量更新体验)、T-507(正式批量更新:移除普通流程测试商品 ID 限制)、T-508(③ 更新shopee生产化操作区)、T-509(② 新标题人工微调)、T-510(③ 检查本轮更新文案统一)、T-505(全流程诊断日志扩展)、T-511(语义色板 + ①②③任务状态列上色)、T-512(③高风险按钮上色 + ①导入校验数字标红)、T-513(登录点 / ③Tab危险标识 / 破坏性按钮上色)、T-514(①②③ 首次空状态引导卡片)、T-515(批次阶段进度总览)、T-516(①筛选对齐②③)、T-517(⑤设置分区 + 清理兼容字段)、T-518(②左栏提示词区组件密度优化)、T-519(②AI生成长任务进度条 + 用户可读滚动日志)、T-520(②AI生成封面可选生成开关)、T-521(依赖清单:锁版本 requirements)、T-522(CI:自动跑语法 + 单元 / GUI 测试)、T-523(拆分 `app/gui.py` 为 `app/gui/` 包)、T-523a(②/③ 新一轮运行前清空界面日志显示)、T-404b(商品详情页加载失败 toast 自动捕获)、T-524(PyInstaller 打包为免安装 exe)、T-526(`app/ai.py` + `appconfig` 接入 cmhub backend)、T-527(⑤设置 cmhub 网关面板)、T-528(②计费错误提示 + 余额展示)。 -- 下一个可领取任务:T-525(引入 ruff lint + format);Phase 7 cmhub 对接已完成。 +- 已完成:T-000(正式代码包结构)、T-001(`app/editor.py` 模块化)、T-002(`app/appconfig.py` + `config.json`)、T-003(SQLite 建表)、T-004(本地数据 gitignore)、T-005(AI 模型清单后端)、T-006(单元测试基座)、T-101(账号 slug/user-data-dir)、T-102(Chrome 启动器)、T-103(登录保活与检测)、T-103b(登录检测补充识别 Shopee accounts 登录页)、T-104(PySide6 五 Tab 主窗口骨架)、T-104b(PySide6 worker 基座)、T-105(Tab④ 账号管理)、T-106(账号快捷方式)、T-201(Excel 导入:解析多文件输入列入库)、T-202(Tab① 任务列表 + 导入按钮 + 别名匹配标记)、T-202b(Tab① 导入汇总栏)、T-203(采集旧标题+旧封面)、T-204(回写旧字段到原 Excel)、T-204b(采集完成自动回写旧字段)、T-205(首次未配账号 / Chrome 未启动 / 未登录引导保护)、T-205b(采集后关闭自动新建商品页 tab)、T-206(Tab① 删除指定批次软删除)、T-301(AI 生成接口)、T-302(Tab② 左右布局与任务列表)、T-302p(提示词管理)、T-303(Tab② 开始生成 + 停止 + 进度)、T-303b(②/③ 商品ID筛选)、T-401(Tab③ 更新列表筛选 + 开始更新确认弹窗)、T-402(Tab③ 确认后串行更新)、T-403(Tab③ 结果回写与结束汇总)、T-501(Tab⑤ AI 模型管理 UI)、T-501b(Tab⑤ 角色与生成参数)、T-501c(Tab⑤ Shopee 更新安全开关)、T-502(换封面删第一张再上传)、T-503(敏感信息本地明文保存提示与日志脱敏)、T-504(多账号并行 / dry-run / 运行日志)、T-207(①采集诊断日志)、② AI生成图片失败诊断日志补丁、T-404a(②/③ 选中记录重置)、T-404(真实 Shopee 单条更新冒烟验收)、T-506(正式使用批量更新体验)、T-507(正式批量更新:移除普通流程测试商品 ID 限制)、T-508(③ 更新shopee生产化操作区)、T-509(② 新标题人工微调)、T-510(③ 检查本轮更新文案统一)、T-505(全流程诊断日志扩展)、T-511(语义色板 + ①②③任务状态列上色)、T-512(③高风险按钮上色 + ①导入校验数字标红)、T-513(登录点 / ③Tab危险标识 / 破坏性按钮上色)、T-514(①②③ 首次空状态引导卡片)、T-515(批次阶段进度总览)、T-516(①筛选对齐②③)、T-517(⑤设置分区 + 清理兼容字段)、T-518(②左栏提示词区组件密度优化)、T-519(②AI生成长任务进度条 + 用户可读滚动日志)、T-520(②AI生成封面可选生成开关)、T-521(依赖清单:锁版本 requirements)、T-522(CI:自动跑语法 + 单元 / GUI 测试)、T-523(拆分 `app/gui.py` 为 `app/gui/` 包)、T-523a(②/③ 新一轮运行前清空界面日志显示)、T-404b(商品详情页加载失败 toast 自动捕获)、T-524(PyInstaller 打包为免安装 exe)、T-526(`app/ai.py` + `appconfig` 接入 cmhub backend)、T-527(⑤设置 cmhub 网关面板)、T-528(②计费错误提示 + 余额展示)、T-529(默认 cmhub 网关并隐藏 AI 后端选择)。 +- 下一个可领取任务:T-525(引入 ruff lint + format);Phase 7 cmhub 对接和 T-529 产品收口已完成。 ## 当前已知限制 @@ -88,16 +88,17 @@ - T-516 已完成:①导入采集补齐店铺、商品ID、状态筛选,筛选栏与②③对齐;当前筛选结果用于①表格显示和「采集旧标题/旧封面」作用范围,导入汇总栏、批次进度总览和未匹配数量仍按当前批次全量任务统计;未匹配按钮继续在当前筛选结果内筛出未匹配任务。 - T-517 已完成:⑤设置页将「Shopee 更新安全 / 执行模式」前置为独立分区,将「基础设施(路径与端口)」后置;`test_item_id`、`dry_run` 不再有用户可操作控件,保存设置仍保留 `test_item_id` 兼容字段并固定 `dry_run=false`,③「检查本轮更新」语义不变。 - T-518 已完成:②AI生成左栏保留模板选择、编辑内容、新建/保存等高频入口,把封面模板「另存为 / 重命名 / 删除」收敛进②本页「模板操作」菜单;动作仍调用原有保存/重命名/删除方法,删除二次确认、错误处理和 prompts 文件结构不变,不涉及 AI/Shopee/CDP。 -- T-519 已完成:②AI生成底部增加标题/图片两条独立进度条,现有「AI生成运行日志」改为用户可读、自动滚动、脱敏的长任务日志;日志显示本轮开始、标题/图片开始与成功、商品ID/店铺、调用失败重试、失败、停止请求和完成汇总,不显示 API Key、密码、Cookie、token、完整请求体、base64 图片或超长 prompt,不改变 AI/DB/Excel/Shopee/CDP 核心流程。 +- T-519 已完成:②AI生成底部增加标题/图片两条独立进度条,现有「AI生成运行日志」改为用户可读、自动滚动、脱敏的长任务日志;日志显示本轮开始、标题/图片开始与成功、商品ID/店铺、调用失败重试、失败、停止请求和完成汇总,不显示 API Key、密码、Cookie、token、完整请求体、base64 图片或超长 prompt,不改变 AI/DB/Excel/Shopee/CDP 核心流程。 - T-520 已完成:②AI生成页增加「生成封面图片(成本较高)」开关,默认关闭以避免无意产生图片模型成本;关闭时标题生成成功即可写库进入 generated,`new_cover_path=NULL`,③只更新标题且不受「允许更新封面」阻断;开启时保持标题后图片两段生成流程。未改 DB schema、AI HTTP 协议、Excel、Shopee/CDP 更新流程。 - T-528 已完成:②AI生成页在 cmhub backend 下显示生成后的剩余点数;GenerateWorker 读取计费 metadata,把 points_cost、points_balance、call_id 写入脱敏运行日志;遇到 CMHubError.code=insufficient_points 时弹出「点数不足,请先充值」并中止本轮未开始任务,不靠中文错误字符串匹配。未改 AI HTTP 协议、DB schema、Excel、Shopee/CDP 流程。 +- T-529 已完成:普通默认 `ai.backend=cmhub`;⑤设置页不再显示「AI 后端」label/dropdown、direct 模型选择、模型详情或标题/图片模型角色下拉,直接展示 cmhub 网关配置;保存设置固定写 `ai.backend=cmhub` 且允许先保存不完整 cmhub 配置,②生成时仍由 `app/ai.py` 提示补齐 Base URL/API Key/别名。direct 代码和 `config/ai_models.json` 保留为内部兼容/手工回滚,不改 AI HTTP 协议、DB schema、Excel、Shopee/CDP 流程。 - T-521 已完成:根目录新增 `requirements.txt` 并锁定当前运行依赖版本;`docs/03-tech-stack.md` 和 `docs/README.md` 已改为 `python -m pip install -r requirements.txt` 安装;未新增运行时依赖、未改业务代码。 - T-522 已完成:新增 `.github/workflows/tests.yml`,在 push / pull_request 上用 Windows + Python 3.11 安装 `requirements.txt`,设置 `QT_QPA_PLATFORM=offscreen`,自动运行语法检查和全量单元/GUI 测试;CI 不连接真实 Shopee 或真实 AI。 - T-523 已完成:旧 `app/gui.py` 已拆为 `app/gui/` 包,公开导入路径保持兼容;本轮为纯结构重构,未改 Shopee/CDP、AI、DB、Excel 行为。 - T-523a 已完成:②点击「开始生成」、③点击「检查本轮更新」或「开始更新」时,先清空对应界面的旧日志显示并写入本轮开始摘要;运行中只追加本轮日志。历史 `run_logs/run_log_events` 和本地 `logs/` 不删除、不自动混入当前运行界面。 -- T-404b 已完成:`open_product()` 在导航商品详情页前后安装 toast 捕获,等待详情页关键元素超时时会读取最近 `.eds-toasts`/toast/message 节点及页面缓存,把明确商品失效/商品不存在/无权限类 toast 上浮为 `商品失效:<原始toast>`;如果失败发生在 `open_product()` 内部,本轮自动新建的失败 tab 会关闭,复用用户已有 tab 不关闭;①列表“阶段”列只在这类明确失效错误时显示“商品失效”,底层仍保持 `stage=imported/status=failed`,其他打开失败仍显示“失败”。 -- T-524 已完成:新增 PyInstaller onedir 打包配置、构建脚本和发布目录校验;打包版启动时工作目录切到 `cmshopee.exe` 所在目录,首次运行在 exe 同级生成/使用本地配置、DB、图片、日志、登录态和提示词;发布包不内置这些本地数据,用户后续更新采用关闭程序后覆盖程序文件、保留本地数据的方式。 -- Phase 7 cmhub 网关对接已完成:T-526 已接 `app/ai.py`/`appconfig` backend,T-527 已完成⑤设置页 cmhub backend 切换、动态别名下拉、测试连接/查余额 worker 和 mock 单测,T-528 已完成②cmhub余额展示、计费日志和点数不足中止提示;下一步回到 T-525 ruff。 +- T-404b 已完成:`open_product()` 在导航商品详情页前后安装 toast 捕获,等待详情页关键元素超时时会读取最近 `.eds-toasts`/toast/message 节点及页面缓存,把明确商品失效/商品不存在/无权限类 toast 上浮为 `商品失效:<原始toast>`;如果失败发生在 `open_product()` 内部,本轮自动新建的失败 tab 会关闭,复用用户已有 tab 不关闭;①列表“阶段”列只在这类明确失效错误时显示“商品失效”,底层仍保持 `stage=imported/status=failed`,其他打开失败仍显示“失败”。 +- T-524 已完成:新增 PyInstaller onedir 打包配置、构建脚本和发布目录校验;打包版启动时工作目录切到 `cmshopee.exe` 所在目录,首次运行在 exe 同级生成/使用本地配置、DB、图片、日志、登录态和提示词;发布包不内置这些本地数据,用户后续更新采用关闭程序后覆盖程序文件、保留本地数据的方式。 +- Phase 7 cmhub 网关对接已完成并已做 T-529 产品收口:T-526 已接 `app/ai.py`/`appconfig` backend,T-527 已完成⑤设置页 cmhub 网关配置,T-528 已完成②cmhub余额展示、计费日志和点数不足中止提示,T-529 已把普通默认改为 cmhub 并隐藏后端选择;下一步回到 T-525 ruff。 - T-301/T-303 已完成通用 HTTP AI 接口、批量生成编排和 GUI 接入 mock 单测;真实 AI 生成还需要在 `config/ai_models.json` 填入可用 url/model/api_key 后做一次成本可控的小样本实测;URL 可填完整 endpoint 或 OpenAI-compatible base URL。 - 本地 `config/ai_models.json` 若由旧版本或手工维护,可能缺少 `category`;启动报 “AI 模型 category 必须是 text 或 image” 时,按 [`troubleshooting.md`](troubleshooting.md) 只补 `category` / `enabled` 等非密钥字段,保留 API Key,且不要提交该文件。 @@ -109,10 +110,10 @@ ```bash # 安装锁定依赖(T-521 后) -python -m pip install -r requirements.txt - -# 安装打包依赖并构建免安装 exe(T-524 后) -python -m pip install -r requirements-build.txt +python -m pip install -r requirements.txt + +# 安装打包依赖并构建免安装 exe(T-524 后) +python -m pip install -r requirements-build.txt powershell -ExecutionPolicy Bypass -File scripts\build_exe.ps1 # 语法检查(T-000 后;本机优先用 py -3) @@ -180,4 +181,4 @@ set UPDATE=1 && python prototypes/demo.py - 新建 `config/chrome/editor/gui/workers` 或改 CDP 选择器后,更新本文与 `04-architecture.md`。 - 任务状态变化同步 [`06-tasks.md`](06-tasks.md);执行记录追加 [`../progress.md`](../progress.md)。 -- 本文只保留当前快照,不保留完整历史。 +- 本文只保留当前快照,不保留完整历史。 diff --git a/docs/routes.md b/docs/routes.md index 10817fc..4cd84bf 100644 --- a/docs/routes.md +++ b/docs/routes.md @@ -14,7 +14,7 @@ | ② AI生成 | 左侧标题/封面**提示词**;右侧按批次/店铺/商品ID/状态筛选任务列表;AI 生成新标题,并按本轮开关可选生成新封面;已生成任务可本地微调新标题;双击看新旧封面 | 不触线上,中 | | ③ 更新shopee | 对**已生成**任务点击「开始更新」后弹窗确认;确认后打开编辑页换标题+封面并逐条点「更新」提交;结果回写 Excel | **写线上,高** | | ④ 账号管理 | Shopee 账号(账号名/别名/数据目录/端口/密码本地明文仅参考/登录状态);启动登录、检测登录、生成快捷方式;检测登录遇到 `accounts.shopee.tw/seller/login` 必须显示未登录 | 中 | -| ⑤ 设置 | AI 模型/API Key、本地图片目录、Chrome 路径、默认端口、Shopee 更新安全开关等 | — | +| ⑤ 设置 | cmhub 网关/API Key、生文/生图别名、生成参数、本地图片目录、Chrome 路径、默认端口、Shopee 更新安全开关等 | — | 任务的**阶段状态**贯穿各 Tab:`imported → collected → generated → applied`(或 `failed/skipped`)。② 不设逐条人工确认阶段;③ 无常驻提交开关,但点击「开始更新」后必须先通过 ⑤ 的 Shopee 更新安全开关,再弹窗确认当前筛选范围和任务数量。各 Tab 聚焦各自阶段的列与按钮,但操作同一批任务(同一 batch)。 @@ -87,7 +87,7 @@ - 右上:按导入批次 / 店铺 / 商品ID / 状态筛选任务;商品ID输入框按包含匹配 `item_id`,清空表示全部。 - 右下:任务列表(店铺名、商品id、旧标题、新标题、状态)+ AI生成运行日志;已生成、未提交线上、非运行中的任务可双击「新标题」列本地微调,写回 `tasks.new_title`,清空 `last_error` 并回到可更新;双击其他列弹窗展示旧封面 | 新封面(纯查看)。 - 底部**单个「开始生成」+「停止」**,并增加「生成封面图片(成本较高)」复选框:默认不勾选,只生成标题;勾选后才在标题完成后按 `image_concurrency` 并发生成封面。该开关状态持久化到 `config.json` 的 `ai.generate_cover`,但入口放在②,便于用户在每轮生成前做成本判断。 -- 生成参数(标题/图片并发数、失败重试、分辨率、jpg 质量、模型/Key)在 **⑤ 设置**;②只暴露“本轮是否生成封面”的成本开关。 +- 生成参数(标题/图片并发数、失败重试、分辨率、jpg 质量、cmhub 网关/Key/别名)在 **⑤ 设置**;②只暴露“本轮是否生成封面”的成本开关。 - 标题-only 模式标题成功即写库并进入 `generated`,`new_cover_path` 留空;标题+封面模式图片成功后写入本地新封面路径。两种模式都写 `run_type=generate` 的 `run_logs/run_log_events` 和用户可读滚动日志;未勾选生成封面时日志明确显示“本轮仅生成标题”。点击「开始生成」时先清空②界面可见日志并写入本轮开始摘要,运行中只追加本轮日志;不删除历史 `run_logs/run_log_events` 或本地 `logs/`。进入页面默认可显示“本轮日志会在开始运行后显示”,历史日志不自动混入当前运行界面。「停止」取消未开始项,可再次「开始生成」对剩余继续。 - 「重置生成结果」仅作用当前选中单条,运行中禁用;确认后只改本地 DB,清空 `new_title/new_cover_path/last_error` 并退回 `collected/success` 供重新生成,默认不删除本地新封面文件。若该记录已提交过线上,必须在确认框提示本地重置不回滚 Shopee。 - **无逐条人工审核环节**;新标题默认直接用 AI 输出,但允许对已生成且未提交线上的单行做本地微调;可选对单行 `重生成`。生成完即可进入 ③,③ 开始更新前会做批量确认。 @@ -143,15 +143,14 @@ ## ⑤ 设置 - 设置页整体布局:内容区居中,左右留白已从 T-506 初始实现缩短到约 40%;实现上用最大内容宽度 + 自适应 margin,而不是写死窗口像素。所有设置组默认响应式 3 列表单:短字段占 1 格,URL/API Key/路径等长字段跨 2 格或 3 格;窄窗口自动降为 2 列/1 列。点击「保存设置」成功后,状态栏显示“设置已保存”,并弹出轻量提示框。 -- AI 模型管理(T-501 已接入,待布局优化):模型详情组件组改为 3 个组件一组;启用/类别/api_type/连接超时等短字段一格,服务商名/模型 ID 可一格或两格,URL 与 API Key 跨整行;AI 模型下拉 + 新增/删除仍在“模型详情”上方。 - - 字段:启用、服务商名、类别(文本/图像)、api_type、模型ID、网址、密钥、连接超时。网址可填完整 endpoint,也可填 OpenAI-compatible base URL(如 `https://.../v1`),请求时按 `api_type` 自动补齐。 - - 密钥存在 `config/ai_models.json`,本地明文保存;保存/变更时提示;UI 使用密码框打码显示,不进入日志/导出。 - - 保存复用 `appconfig` 模型清单后端,保持 name 唯一、至少 text/image 各一个模型。 - - 「测试连接」通过后台 worker 调 `appconfig.test_ai_model()`,不阻塞主线程。 -- AI 生成参数(T-501b 已接入,待布局优化):角色与生成参数改为 3 个组件一组;标题模型、图片模型、分辨率、标题并发、图片并发、失败重试、jpg 质量等短字段按三列排列。 - - 标题模型下拉只列 text 类模型;图片模型下拉只列 image 类模型。 +- cmhub 网关配置(T-529 后普通用户唯一 AI 入口):⑤设置页不再显示「AI 后端」label 或 direct/cmhub 下拉,直接展示 cmhub 网关 Base URL、API Key、生文别名、生图别名、连接超时、刷新别名、测试连接/查余额。 + - API Key 存在 `config/cmhub.json`,本地明文保存;保存/变更时提示;UI 使用密码框打码显示,不进入日志/导出。 + - 生文/生图别名来自 `GET /api/v1/models` 动态下拉,过滤未定价别名并展示单价/需原图提示;网关临时不可达时保留已存别名。 + - 保存设置固定写 `ai.backend=cmhub`。允许先保存不完整 cmhub 配置,②真正生成时如果缺 Base URL/API Key/别名,会提示去⑤补配置,不静默回退 direct。 + - direct 模型清单和 `config/ai_models.json` 代码路径保留为内部兼容/手工回滚,不在普通 UI 暴露。 +- AI 生成参数:标题并发、图片并发、失败重试、分辨率、返回超时、jpg 质量等短字段按三列排列。 - 分辨率为 `512 / 1k / 2k / 4k`;返回超时只读展示 `resolution_timeouts[resolution]`,不单独编辑。 - - 保存写入 `config.json` 的 `ai` 段,供 ② AI生成复用。 + - 保存写入 `config.json` 的 `ai` 段,供 ② AI生成复用;标题/图片模型角色下拉随 direct UI 一起隐藏。 - 路径与端口(T-501b/T-506 已接入):组件组改为 3 个组件一组;默认端口、端口起止、CDP 就绪超时等短字段三列排列;Chrome 路径、账号数据根目录、图片目录、DB 路径等长字段跨整行或跨 2/3 列。 - Shopee 更新安全(T-501c/T-506 已接入):组件组改为 3 个组件一组;允许真实提交、允许更新封面、每批最大更新条数、成功后关闭本次新开编辑页等短字段三列排列;「多账号并行更新」与「最大并行账号数」必须合并为同一个横向组件,最大并行账号数紧跟在多账号并行更新后面,不允许换到下一行;测试商品 ID 仅作为历史/调试兼容字段保留,不参与普通正式更新安全检查,普通设置页已隐藏该入口。 - 默认关闭真实提交和封面更新,每批最大更新条数默认 1。 diff --git a/progress.md b/progress.md index 386a120..c54f436 100644 --- a/progress.md +++ b/progress.md @@ -1135,3 +1135,11 @@ - 文档:`docs/06-tasks.md` 将 T-528 标为 DONE;同步 `docs/current-state.md`,下一个可领取任务回到 T-525 ruff。 - 测试:`python -m unittest discover -s tests -p test_gui.py` 通过(80 tests);`python -m unittest discover -s tests -p test_ai.py` 通过(17 tests);`python -m compileall app main.py` 通过;`python -m unittest discover -s tests` 通过(187 tests);`git diff --check` 通过,仅有本机 LF/CRLF 提示。全量测试仍有本机 PySide6 字体目录提示,不影响结果。 - 下一步:T-525「引入 ruff(lint + format)+ 可选 pre-commit」。 +## 【2026-07-04】T-529 完成 · 默认 cmhub 网关并隐藏 AI 后端选择 + +- 状态:DONE +- 文档:同步 `docs/cmhub-integration-design.md`、`docs/03-tech-stack.md`、`docs/04-architecture.md`、`docs/api.md`、`docs/routes.md`、`docs/06-tasks.md`、`docs/current-state.md`;T-529 标为 DONE,下一步回到 T-525 ruff。 +- 代码:`DEFAULT_CONFIG.ai.backend` 改为 `cmhub`,`ai_backend()` 缺省跟随默认 backend;⑤设置页隐藏「AI 后端」下拉、direct 模型选择、模型详情和标题/图片角色下拉,普通 UI 直接展示 cmhub 网关配置;保存设置固定写 `ai.backend=cmhub`,允许先保存不完整 cmhub 配置,真正生成时仍由 `app/ai.py` 提示补齐 Base URL/API Key/别名。 +- 兼容:direct 代码、`config/ai_models.json`、AI 模型测试 worker 和 direct 单测保留为内部兼容/手工回滚路径;未改 AI HTTP 协议、DB schema、Excel、Shopee/CDP 流程。 +- 测试:更新 appconfig/GUI/AI 单测,direct 分支测试显式设置 `backend=direct`;②AI生成页默认显示 cmhub 余额标签。 +- 验证:`python -m py_compile app\appconfig.py app\gui\tabs\settings.py app\gui\tabs\generate.py tests\test_appconfig.py tests\test_gui.py tests\test_ai.py` 通过;`python -m unittest discover -s tests -p test_appconfig.py` 通过(8 tests);`python -m unittest discover -s tests -p test_gui.py` 通过(80 tests);`python -m unittest discover -s tests -p test_ai.py` 通过(17 tests);`python -m compileall app main.py` 通过;`python -m unittest discover -s tests` 通过(187 tests);`git diff --check` 通过,仅有本机 LF/CRLF 提示。全量 GUI 测试仍有本机 PySide6 字体目录提示,不影响结果。 \ No newline at end of file diff --git a/tests/test_ai.py b/tests/test_ai.py index b9d49ed..d514481 100644 --- a/tests/test_ai.py +++ b/tests/test_ai.py @@ -75,6 +75,7 @@ class AITests(TempDirMixin, unittest.TestCase): def _config(self): cfg = appconfig.default_config() + cfg["ai"]["backend"] = "direct" cfg["ai"]["default_text_model"] = "Text" cfg["ai"]["default_image_model"] = "Image" cfg["ai"]["retry"] = 1 diff --git a/tests/test_appconfig.py b/tests/test_appconfig.py index e8f4ce3..ea4fcef 100644 --- a/tests/test_appconfig.py +++ b/tests/test_appconfig.py @@ -38,8 +38,8 @@ class AppConfigTests(TempDirMixin, unittest.TestCase): config = appconfig.load_config(config_path) ai = appconfig.ai_config(config) - self.assertEqual("direct", ai["backend"]) - self.assertEqual("direct", appconfig.ai_backend(config)) + self.assertEqual("cmhub", ai["backend"]) + self.assertEqual("cmhub", appconfig.ai_backend(config)) self.assertEqual("", appconfig.cmhub_config(config)["base_url"]) self.assertFalse(os.path.exists(cmhub_path)) self.assertEqual({"api_key": ""}, appconfig.load_cmhub_config(cmhub_path)) @@ -55,9 +55,13 @@ class AppConfigTests(TempDirMixin, unittest.TestCase): with open(config_path, "w", encoding="utf-8") as fh: json.dump({"ai": {"resolution": "512"}}, fh) migrated = appconfig.load_config(config_path) - self.assertEqual("direct", appconfig.ai_config(migrated)["backend"]) + self.assertEqual("cmhub", appconfig.ai_config(migrated)["backend"]) self.assertEqual(180, appconfig.response_timeout(migrated)) + direct_cfg = appconfig.default_config() + direct_cfg["ai"]["backend"] = "direct" + self.assertEqual("direct", appconfig.ai_backend(direct_cfg)) + self.assert_removed(temp_dir) def test_config_rejects_sensitive_fields(self): diff --git a/tests/test_gui.py b/tests/test_gui.py index 1b8bfe1..dcc0463 100644 --- a/tests/test_gui.py +++ b/tests/test_gui.py @@ -814,7 +814,9 @@ class GuiTests(TempDirMixin, unittest.TestCase): self.addCleanup(tab.close) self.assertEqual("cmhub", tab.backend_combo.currentData()) + self.assertTrue(tab.backend_combo.isHidden()) self.assertTrue(tab.model_picker_panel.isHidden()) + self.assertTrue(tab.direct_role_panel.isHidden()) self.assertFalse(tab.cmhub_panel.isHidden()) self.assertEqual("https://cmhub.old", tab.cmhub_base_url_edit.text()) self.assertEqual("sk-old-secret", tab.cmhub_api_key_edit.text()) @@ -964,7 +966,7 @@ class GuiTests(TempDirMixin, unittest.TestCase): self.assertEqual("失败 0", tab.failed_progress_label.text()) self.assertEqual("generateCmhubBalanceLabel", tab.cmhub_balance_label.objectName()) self.assertEqual("cmhub余额:未获取", tab.cmhub_balance_label.text()) - self.assertTrue(tab.cmhub_balance_label.isHidden()) + self.assertFalse(tab.cmhub_balance_label.isHidden()) self.assertEqual(0, tab.title_progress_bar.value()) self.assertEqual(0, tab.cover_progress_bar.value()) self.assertEqual(1, tab.title_progress_bar.maximum())