Files
cmshoppe/docs/api.md
T
chengmaandClaude Opus 4.8 479d02a2b8 docs: 初始化 cmshopee 文档、设计与项目骨架
- docs/ 完整 harness coding 文档集(愿景/需求/技术栈/架构/编码规则/任务/api/routes/current-state)
- 5 Tab 流水线设计 + UI 效果图 SVG(docs/ui/)
- cdp.py CDP 底座;prototypes/ 已验证原型脚本(待 editor.py 移植后清理)
- AGENTS.md/CLAUDE.md 入口、progress.md 执行流水、.gitignore(排除凭证/DB/图片)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 15:30:37 +08:00

189 lines
8.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 模块 / CLI 合约
> 本项目无后端 API,全部为**本地 Python 模块合约**。实现前可细化,但不要另起一套不兼容接口。
## 通用约定
- 形态:本地函数 + 子进程(Chrome)+ CDP(`127.0.0.1:<port>`)+ SQLite + openpyxl + AI 服务调用。
- 编码:UTF-8;传 Chrome / `setFileInputFiles` 的路径为 **Windows 绝对路径**。
- 凭证:登录态在 user-data-dir;密码、AI Key 加密存于 config/DB,不出现在日志/导出明文。
- 失败处理:抛带中文说明的异常或返回状态字段;GUI 负责提示,不静默吞错。
## appconfig 模块(`appconfig.py`,待建)
读写 `config.json`(schema 见 [架构 5.1](04-architecture.md))。
```python
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/
# title_concurrency/image_concurrency/retry/jpg_quality/
# resolution/resolution_timeouts
response_timeout() -> int # = resolution_timeouts[resolution](返回超时,随分辨率)
# AI 模型清单 config/ai_models.json(含密钥;CRUD 由 ⑤ 设置)
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
delete_ai_model(name) -> None # 至少各留一个 text+image;删到剩一禁用
test_ai_model(name) -> dict # 「测试连接」:用 key/url/model 发最小请求 -> {ok, error}
get_model(name) -> dict # 解密 api_key 供调用
```
## db 模块(`db.py`,待建)
SQLite 读写,表见 [架构 5.2](04-architecture.md)。
```python
init_db(path)
# 账号
list_accounts() -> list[Account]
get_account_by_alias(alias) -> Account|None
add_account(account_name, alias, region_host, debug_port, password_enc=None, note=None) -> Account
update_account(alias, **fields) -> None
delete_account(alias) -> None
# 任务 / 各阶段结果
insert_tasks(batch_id, rows) -> int # 写输入列
list_tasks(batch_id=None, stage=None) -> list[Task]
set_collected(task_id, old_title, old_cover_path) -> None # 采集结果,立即写
set_generated(task_id, new_title, new_cover_path) -> None # AI 结果,立即写
set_applied(task_id, committed, error=None) -> None # 更新结果,立即写
```
`stage` 随各 set_* 推进(imported→collected→generated→applied);任意步失败写 `error` 且 stage 标 failed/skipped。无 confirmed 阶段。
## excel 模块(`excel.py`,待建,依赖 openpyxl)
```python
import_tasks(file_paths: list[str]) -> dict
# 只解析【输入列】:账号名、别名、商品id(+ source_file);输出列运行时回写
# -> {"rows": [...], "stats": {"files": int, "total": int, "valid": int, "invalid": int}}
# invalid = 缺别名/商品id 或脏数据的行
match_summary(rows: list[dict], accounts: list) -> dict
# 用 accounts 的别名对 rows 做匹配统计(导入汇总栏用)
# -> {"matched": int, "unmatched": int, "by_account": {别名: 行数}, "unmatched_aliases": [..]}
write_back(batch_id, excel_path) -> str
# 把【旧标题/旧封面/新标题/新封面/更新状态】批量回写到【原 Excel】
# 原文件被占用(锁) → 抛错,调用方提示“请关闭后重试”,或改用 export_copy
export_copy(batch_id, out_path) -> str # 退路:另存新结果文件,不动原文件
```
列模板见 [架构 5.3](04-architecture.md);别名以“别名”列为权威。
## config 模块(`config.py`,待建)
```python
make_slug(alias) -> str # 别名→唯一 slug [a-z0-9_]
ensure_user_data_dir(slug) -> str # chrome_user_data_dir/<slug> 绝对路径,按需创建
```
## chrome 模块(`chrome.py`,待建)
```python
build_launch_args(account) -> list[str] # chrome + --remote-debugging-port + --remote-allow-origins=* + --user-data-dir
launch_chrome(account) -> subprocess.Popen
wait_debug_ready(port, timeout=60) -> bool
is_running(port) -> bool
create_shortcut(account, dest_dir=None) -> str # 可选 .lnk,PowerShell WScript.Shell
```
## cdp 模块(`cdp.py`,已实现)
```python
CDP_HOST: str
http_get(path); find_product_tab(item_id); create_tab(url)
class CDP: send/ev/val/object_id/drag/close # suppress_origin、trust_env=False
```
## editor 模块(`editor.py`,待建,重构自现有脚本)
```python
is_logged_in(account) -> bool # 重定向登录页或缺 SPC_ST → False
open_product(account, item_id) -> CDP # 连端口、导航商品页、等就绪
# 采集(只读)
read_title(cdp) -> str
read_cover_src(cdp) -> str # 第一张 itembox 的 img.src
download_cover(src, out_path) -> str # 下载旧封面到本地
collect(account, task) -> dict # -> {old_title, old_cover_path}
# 应用
change_title(cdp, new_title) -> dict # {ok, value, modelvalue},要求三者相等
replace_cover(cdp, image_win_path) -> dict # 上传→等CDN→拖第一位;满9张先删第一张
click_update(cdp) -> dict # {clicked, reason};禁用则记失败
apply_task(account, task) -> dict # 对已生成任务:换标题+换封面+点「更新」提交(恒提交)
# -> {committed, error}
```
## ai 模块(`ai.py`,待建,外部 AI,服务商待定)
```python
gen_title(title_prompt, old_title, retry=2) -> str
# 文本生成:提示词 + 旧标题 → 新标题
gen_cover(cover_prompt, old_cover_path, out_path, resolution, jpg_quality, retry=2) -> str
# 图像生成(image-to-image):提示词 + 旧封面 → 新封面,按 resolution 生成、jpg_quality 存盘,返回路径
generate_batch(tasks, prompts, ai_cfg, on_progress, should_stop) -> None
# 编排:先以 title_concurrency 线程池并发跑 gen_title,再以 image_concurrency 并发跑 gen_cover
# 每条完成即 db.set_generated(实时落库);should_stop() 为真则取消未开始项
# on_progress(标题完成数, 封面完成数, 失败数) 回调刷新进度
```
要点:
- 标题用 `default_text_model`、封面用 `default_image_model`(`appconfig.get_model` 取定义,含 url/key/api_type)。
- 连接超时 = 模型 `connect_timeout_seconds`;**返回超时 = `appconfig.response_timeout()`(随分辨率:512/1k/2k/4k → 180/240/360/600)**。
- 并发数/重试/分辨率/jpg 质量来自 `appconfig.ai_config()`;Key 加密存、不入日志。
- 标题快、图片慢:分两段、各用各自并发数;失败按 `retry` 重试,仍失败记 error 不阻塞其余。
- 调用有成本与失败可能:超时、限流、内容安全拒绝都要返回明确错误。
- 生成结果**直接用于 ③ 更新**(无人工确认);本地留档 + 回写 Excel 供追溯。
## prompts 模块(`prompts.py`,待建)
```python
# 标题提示词:单文件
load_title_prompt(path="title_prompt.txt") -> str # 启动回显;缺失返回 ""
save_title_prompt(text, path="title_prompt.txt") -> None # 「保存」按钮
# 封面提示词:多模板(prompts/cover/<名称>.txt)
list_cover_templates() -> list[str] # 模板名列表(下拉用)
load_cover_template(name) -> str
save_cover_template(name, text) -> None # 保存 / 另存为
rename_cover_template(old, new) -> None # 重名校验,重复则报错
delete_cover_template(name) -> None # 删除(二次确认由 GUI 负责)
# 变量替换
render_prompt(template_text, task) -> str
# 占位符 {旧标题}/{新标题}/{商品id}/{店铺} → 该任务真实值;预览与生成时调用
```
要点:
- 「插入标题」在封面提示词光标处插入 `{新标题}`;「预览」对选中任务调用 `render_prompt` 后展示。
- 生成封面时 `gen_cover` 的 prompt = `render_prompt(当前封面模板, task)`。
- 模板与 `title_prompt.txt` 均为可手改的纯文本文件。
## CLI / 触发合约(现有脚本,过渡期保留)
```bash
python prototypes/demo.py # 单账号闭环:改标题+换封面,不提交
set AUTO=1 && python prototypes/demo.py
set UPDATE=1 && python prototypes/demo.py # 走完点击「更新」提交
```
环境变量:`CDP_HOST`、`ITEM_ID`、`IMG_WIN`、`NEW_TITLE`、`AUTO`、`UPDATE`。
## 待实现时确认
- AI 服务商/模型/计费;图像 image-to-image 能力与合规。
- 密码、AI Key 加密的密钥来源(机器派生 / 主口令)。
- 满 9 张删除封面的确认框选择器(需实测)。
- Excel 缺列/脏数据容错(整文件拒绝 vs 逐行跳过)。
- 旧封面下载的图片格式/扩展名处理。