feat: 完成配置与SQLite地基

- 新增 appconfig 配置读写、默认值、AI 参数和敏感字段拦截

- 新增 SQLite batches/accounts/tasks schema 与阶段写库函数

- 校准本地配置、DB、图片和登录态文件的 gitignore 规则

- 更新任务看板、模块合约、当前状态和进度记录
This commit is contained in:
chengma
2026-06-27 09:02:05 +08:00
parent 26dac2a718
commit 8ad1754946
7 changed files with 822 additions and 37 deletions
+3 -3
View File
@@ -25,9 +25,9 @@
| --- | --- | --- | --- | --- |
| T-000 | 正式代码包结构:创建 `app/`、迁入 `cdp.py` 为 `app/cdp.py`、新增 `app/__init__.py`、`app/__main__.py`、根入口 `main.py`、最小 `app/gui.py` 占位入口,并修正 prototypes 导入 | - | `python -m compileall app main.py` 通过;`python -m app`/`python main.py` 可进入入口(本机 `python` 不符合版本时用 `py -3 -m app`);GUI 未完成时给明确提示并退出;`prototypes/demo.py` 可从项目根导入 `app.cdp` | DONE |
| T-001 | `app/editor.py`:改标题/换封面/点更新/登录检测/**采集(读旧标题+旧封面下载)**/apply_task,复用 `app/cdp.py` | T-000 | 函数可调用,在测试商品跑通;与 `prototypes/demo.py` 行为一致 | DONE |
| T-002 | `app/appconfig.py` + `config.json`(含 image_dir、ai 选择/参数段、端口等默认值;不含 AI Key) | T-000 | 读写正常;不存在则写默认;AI Key 留给 `config/ai_models.json`/T-501 | TODO |
| T-003 | `app/db.py` + SQLite 建表(batches/accounts/tasks,含 Excel 行定位、状态、时间戳、重试字段) | T-000 | `init_db` 幂等;`connect` 设置 WAL/busy_timeout/foreign_keys;账号/批次/任务/各 set_* 可用;schema 同架构 5.2 | TODO |
| T-004 | `.gitignore`:排除 `config.json`、`config/ai_models.json`、`cmshopee.db`、`chrome_user_data_dir/`、`images/` | T-002, T-003 | 配置、密钥、凭证、业务数据、图片不被提交 | TODO |
| T-002 | `app/appconfig.py` + `config.json`(含 image_dir、ai 选择/参数段、端口等默认值;不含 AI Key) | T-000 | 读写正常;不存在则写默认;AI Key 留给 `config/ai_models.json`/T-501 | DONE |
| T-003 | `app/db.py` + SQLite 建表(batches/accounts/tasks,含 Excel 行定位、状态、时间戳、重试字段) | T-000 | `init_db` 幂等;`connect` 设置 WAL/busy_timeout/foreign_keys;账号/批次/任务/各 set_* 可用;schema 同架构 5.2 | DONE |
| T-004 | `.gitignore`:排除 `config.json`、`config/ai_models.json`、`cmshopee.db`、`chrome_user_data_dir/`、`images/` | T-002, T-003 | 配置、密钥、凭证、业务数据、图片不被提交 | DONE |
| T-005 | AI 模型清单后端:`config/ai_models.json` 读写 + category 过滤 + 测试连接 | T-002 | 本地明文 api_key;UI/API 打码显示;日志脱敏;至少 text/image 各一个;`get_model` 返回调用所需字段 | TODO |
| T-006 | 单元测试基座:`tests/` + appconfig/db/excel/prompts 最小测试 | T-002, T-003 | `python -m unittest discover -s tests` 可跑;不依赖真实 Shopee/AI;临时文件在测试目录清理 | TODO |
+38 -24
View File
@@ -9,22 +9,34 @@
- 凭证:登录态在 user-data-dir;密码、AI Key 本地明文存于 config/DB;`config.json`、`config/ai_models.json`、`cmshopee.db`、`chrome_user_data_dir/`、`images/` 必须 gitignore;UI 打码显示,不出现在日志/导出。
- 失败处理:抛带中文说明的异常或返回状态字段;GUI 负责提示,不静默吞错。
## appconfig 模块(`app/appconfig.py`,待建)
## appconfig 模块(`app/appconfig.py`,已建)
读写 `config.json`(schema 见 [架构 5.1](04-architecture.md))。
```python
class ConfigError(RuntimeError): ...
default_config() -> dict
load_config(path="config.json") -> dict # 不存在则写默认
chrome_path() -> str
user_data_root() -> str
image_dir() -> str
db_path() -> str
ai_config() -> dict # default_text_model/default_image_model/
save_config(config, path="config.json") -> dict
update_config(updates, path="config.json") -> dict
chrome_path(config=None) -> str
user_data_root(config=None) -> str
image_dir(config=None) -> str
db_path(config=None) -> str
default_debug_port(config=None) -> int
debug_port_range(config=None) -> tuple # (start, end)
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
response_timeout() -> int # = resolution_timeouts[resolution](返回超时,随分辨率)
response_timeout(config=None) -> int # = resolution_timeouts[resolution](返回超时,随分辨率)
```
# AI 模型清单 config/ai_models.json(含本地明文密钥;CRUD 由 ⑤ 设置)
`config.json` 不保存 AI Key;写入 `api_key` / `*_key` / `token` / `password` 等敏感字段时抛 `ConfigError`。AI Key 留给 `config/ai_models.json`。
AI 模型清单(`config/ai_models.json`,含本地明文密钥;CRUD 由 ⑤ 设置,T-005 待建):
```python
list_ai_models(category=None) -> list[dict] # category=text/image 过滤;含 connect_timeout_seconds 等
add_ai_model(model) -> None # name 唯一校验
update_ai_model(name, **fields) -> None
@@ -33,32 +45,34 @@ test_ai_model(name) -> dict # 「测试连接」:用 key/url
get_model(name) -> dict # 返回模型定义,含 api_key(调用方不得写日志)
```
## db 模块(`app/db.py`,待建)
## db 模块(`app/db.py`,已建)
SQLite 读写,表见 [架构 5.2](04-architecture.md)。
```python
class DbError(RuntimeError): ...
Batch / Account / Task # dataclass,字段同 SQLite schema
connect(path=None) -> sqlite3.Connection # 设置 foreign_keys/WAL/busy_timeout/synchronous/row_factory
init_db(path)
create_batch(file_paths, note=None) -> str
get_batch(batch_id) -> Batch|None
list_batches(status=None) -> list[Batch]
update_batch(batch_id, **fields) -> None
init_db(path=None)
create_batch(file_paths, note=None, path=None) -> str
get_batch(batch_id, path=None) -> Batch|None
list_batches(status=None, path=None) -> list[Batch]
update_batch(batch_id, **fields) -> None # 支持 status/note/source_files_json
# 账号
list_accounts() -> list[Account]
get_account_by_alias(alias) -> Account|None
list_accounts(path=None) -> list[Account]
get_account_by_alias(alias, path=None) -> Account|None
add_account(account_name, alias, region_host, debug_port, password=None, note=None) -> Account
update_account(alias, **fields) -> None
update_account(alias, **fields) -> None # 支持账号展示字段、端口、密码、note、last_login_at
delete_account(alias) -> None
# 任务 / 各阶段结果
insert_tasks(batch_id, rows) -> int # 写输入列;rows 含 source_file_abs/source_sheet/source_row/row_key
list_tasks(batch_id=None, stage=None, status=None, alias=None) -> list[Task]
insert_tasks(batch_id, rows, path=None) -> int # 写输入列;rows 含 source_file_abs/source_sheet/source_row/row_key
list_tasks(batch_id=None, stage=None, status=None, alias=None, path=None) -> list[Task]
mark_running(task_id, phase) -> None
mark_failed(task_id, phase, error) -> None
mark_skipped(task_id, reason) -> None
set_collected(task_id, old_title, old_cover_path) -> None # stage=collected,status=success, attempts+1
set_generated(task_id, new_title, new_cover_path) -> None # stage=generated,status=success, attempts+1
set_applied(task_id, committed, error=None) -> None # stage=applied,status=success/failed, attempts+1
mark_failed(task_id, phase, error) -> None # status=failed,stage 不前进,对应 attempts+1
mark_skipped(task_id, reason) -> None # status=skipped,stage 不前进
set_collected(task_id, old_title, old_cover_path) -> None # stage=collected,status=success,collect_attempts+1
set_generated(task_id, new_title, new_cover_path) -> None # stage=generated,status=success,generate_attempts+1
set_applied(task_id, committed, error=None) -> None # 成功 stage=applied;失败 status=failed 且 stage 不前进
```
`stage` 表示最后成功业务阶段(imported→collected→generated→applied);`status` 表示当前处理结果(pending/running/success/failed/skipped/cancelled)。任意步失败写 `last_error` 且 `status=failed`,`stage` 不前进。无 confirmed 阶段。
+20 -9
View File
@@ -5,12 +5,12 @@
## 当前快照
- 日期:2026-06-26
- 阶段:V0 单账号 CDP 流程已验证;V1 已完成 T-000 正式代码包结构与 T-001 `app/editor.py` 模块化,下一步开始应用配置。
- 日期:2026-06-27
- 阶段:V0 单账号 CDP 流程已验证;V1 已完成 T-000 正式代码包结构、T-001 `app/editor.py` 模块化、T-002 `app/appconfig.py` 应用配置、T-003 SQLite 持久化地基、T-004 本地数据忽略规则。
- 技术栈:Python 3.10+,自研 CDP(websocket-client + requests),SQLite(sqlite3)+ `config.json` + openpyxl + AI(服务商待定),GUI PySide6 5 Tab(已定)。
- 生产代码:已建立 `app/` 包 + 根入口 `main.py`;`app/cdp.py` 为已验证 CDP 底座;`app/editor.py` 已封装标题/封面/采集/更新按钮能力;`app/gui.py` 目前是入口占位,完整 PySide6 主窗口待 T-104。
- 生产代码:已建立 `app/` 包 + 根入口 `main.py`;`app/cdp.py` 为已验证 CDP 底座;`app/editor.py` 已封装标题/封面/采集/更新按钮能力;`app/appconfig.py` 已实现 `config.json` 默认值、读写、更新、AI 参数与端口配置读取;`app/db.py` 已实现 SQLite schema、连接 PRAGMA、批次/账号/任务与阶段写库函数;`app/gui.py` 目前是入口占位,完整 PySide6 主窗口待 T-104。
- 测试:当前以 `compileall` + 测试商品手动 CDP 验证为主;`tests/` 与 `python -m unittest discover -s tests` 由 T-006 建立,T-006 完成前不把缺少 `tests/` 视为验证失败。
- 数据:无 `config.json`、`config/ai_models.json`、`cmshopee.db`、`chrome_user_data_dir/`、`images/`(待 Phase 0/1 建立,均须 gitignore)。
- 数据:`config.json`、`config/ai_models.json`、`cmshopee.db`、`chrome_user_data_dir/`、`images/` 已由 `.gitignore` 排除;`app/appconfig.py` 首次读取缺失的 `config.json` 时会在本地写默认配置,`app/db.py` 调用 `init_db()` 时会在本地创建 SQLite DB。
## 既定设计要点(文档已定)
@@ -29,13 +29,15 @@
| --- | --- | --- |
| `docs/` | 已有 | 本 harness coding 文档集合 |
| `app/cdp.py` | 已有 | CDP 底座:连接/找 tab/开 tab/执行 JS/拖拽 |
| `prototypes/` | 已有 | 已验证原型/探查脚本(demo/set_title/set_cover/get_title/cookies/inspect_images/grab/1.py),逻辑待并入 `app/editor.py` 后清理;见 `prototypes/README.md` |
| `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 转发排查记录 |
| `app/__init__.py` / `app/__main__.py` / `main.py` | 已有 | 正式包与启动入口;`python main.py` / `python -m app` 可运行占位入口 |
| `app/gui.py` | 已有 | GUI 占位入口;完整 PySide6 主窗口待 T-104 |
| `app/editor.py` | 已有 | T-001 产出:登录检测、打开商品页、读/写标题、读/下载封面、上传拖封面、更新按钮、apply_task |
| `app/appconfig.py` / `app/db.py` / `app/excel.py` / `app/config.py` / `app/chrome.py` / `app/workers.py` | 待建 | Phase 0-3 产出 |
| `config.json` / `config/ai_models.json` / `cmshopee.db` / `chrome_user_data_dir/` / `images/` | 待建 | 含配置、密钥、业务、登录态、图片,须 gitignore |
| `app/appconfig.py` | 已有 | T-002 产出:`config.json` 默认值、读写、更新、路径/端口/AI 参数读取;拒绝敏感字段写入 |
| `app/db.py` | 已有 | T-003 产出:batches/accounts/tasks schema;WAL/busy_timeout/foreign_keys;账号/批次/任务与 set_* 阶段写库 |
| `app/excel.py` / `app/config.py` / `app/chrome.py` / `app/workers.py` | 待建 | Phase 0-3 产出 |
| `config.json` / `config/ai_models.json` / `cmshopee.db` / `chrome_user_data_dir/` / `images/` | 本地待建,已忽略 | 含配置、密钥、业务、登录态、图片,不提交版本库 |
## 已验证能力(单账号)
@@ -49,9 +51,9 @@
任务状态以 [`06-tasks.md`](06-tasks.md) 为准,历史记录见 [`../progress.md`](../progress.md)。
- 已完成:T-000(正式代码包结构)、T-001(`app/editor.py` 模块化)。
- 已完成:T-000(正式代码包结构)、T-001(`app/editor.py` 模块化)、T-002(`app/appconfig.py` + `config.json`)、T-003(`app/db.py` + SQLite 建表)、T-004(本地数据 gitignore)。
- 正在进行:无。
- 下一个可领取任务:**T-002(`app/appconfig.py` + `config.json`)**。虽然 T-003 也依赖满足,但任务领取规则固定为“取 `06-tasks.md` 中第一个 TODO 且依赖均 DONE 的任务”,因此当前不能任选。
- 下一个可领取任务:**T-005(AI 模型清单后端)**。
## 当前可运行内容
@@ -59,6 +61,15 @@
# 语法检查(T-000 后;本机优先用 py -3)
py -3 -m compileall app main.py
# appconfig 默认配置读写(写入临时目录)
py -3 -c "import os,tempfile; from app import appconfig; d=tempfile.TemporaryDirectory(dir='.'); print(appconfig.load_config(os.path.join(d.name,'config.json'))['image_dir'])"
# db 临时库初始化与 PRAGMA 检查
py -3 -c "import os,tempfile; from app import db; d=tempfile.TemporaryDirectory(dir='.'); p=os.path.join(d.name,'cmshopee.db'); db.init_db(p); c=db.connect(p); print(c.execute('PRAGMA foreign_keys').fetchone()[0], c.execute('PRAGMA journal_mode').fetchone()[0], c.execute('PRAGMA busy_timeout').fetchone()[0]); c.close()"
# gitignore 核心本地数据检查
git check-ignore -v -- config.json config/ai_models.json cmshopee.db chrome_user_data_dir/ images/
# 当前入口占位
python main.py
py -3 -m app