docs: add cmhub planning documents

This commit is contained in:
QiuSW
2026-07-01 17:42:10 +08:00
parent bd7ae0a1bc
commit eeeb45c147
26 changed files with 2129 additions and 170 deletions
+16 -169
View File
@@ -1,176 +1,23 @@
# ---> Python
# Byte-compiled / optimized / DLL files
# Local secrets and environment files
.env
.env.*
!.env.example
ai_models.json
# Python / Django local artifacts
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
.coverage
htmlcov/
db.sqlite3
db.sqlite3-journal
media/
staticfiles/
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
.pybuilder/
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
# For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# .python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# UV
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
# This is especially recommended for binary packages to ensure reproducibility, and is more
# commonly ignored for libraries.
#uv.lock
# poetry
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
# This is especially recommended for binary packages to ensure reproducibility, and is more
# commonly ignored for libraries.
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
#poetry.lock
# pdm
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
#pdm.lock
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
# in version control.
# https://pdm.fming.dev/latest/usage/project/#working-with-version-control
.pdm.toml
.pdm-python
.pdm-build/
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
# Local virtual environments
.venv/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# pytype static type analyzer
.pytype/
# Cython debug symbols
cython_debug/
# PyCharm
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/
# Ruff stuff:
.ruff_cache/
# PyPI configuration file
.pypirc
# Editor / OS noise
.DS_Store
Thumbs.db
+44
View File
@@ -0,0 +1,44 @@
# AGENTS.md
> Codex / 通用 AI coding agent 的仓库级入口。进入本仓库后,先读本文,再进入 `docs/00-ai-start-here.md`。
## 项目定位
`cmhub` 是一个**对外计费 API 服务 + 运营管理后台**。它把原桌面工具 `cmbot`(位于 `D:\chengma\cmbot`)里的 AI「生成标题」「生成图片」能力抽出来,封装成带**点数计费**的 HTTP API,供其他项目调用,并提供 django-admin 运营后台管理用户、点数与调用记录。
核心机制:**预付费点数模型**。用户在外部支付系统充值,支付成功后由支付系统回调本服务,按汇率把金额转换成点数存在本地;之后每次调用直接扣本地点数,点数不足返回「点数不足,请先充值」。本服务的点数余额是唯一权威账本,不在调用链路上实时查支付系统。
## 文档位置
项目文档集中在 `docs/`;根目录有 `progress.md`(执行流水)、`README.md`(人类视角总览)、`init.sh` / `init.ps1`(统一启动验证入口)。
agent 开始编程时,以 `docs/00-ai-start-here.md` 为工作入口;它会继续导航到愿景、需求、技术栈、架构、编码规则、任务看板、API 合约、当前状态。
## 必读顺序
每次开始工作前,按顺序读取:
1. `README.md`:了解本服务用途。
2. `docs/00-ai-start-here.md`:项目入口、阅读顺序、任务领取规则。
3. `docs/05-coding-rules.md`:硬性编码纪律(尤其涉及资金/点数的安全规则)。
4. `progress.md` 与 `docs/current-state.md`:恢复已验证状态、下一步、当前 blocker。
5. 与当前任务相关的具体文档(`api.md` / `04-architecture.md` 等)。
如果用户要求修改某类内容,优先阅读对应文件,不要只凭文件名猜内容。
## 工作规则
- 涉及**点数、金额、充值、扣费、退款**的逻辑属于高风险,必须按 `05-coding-rules.md` 第 8 节执行:显式验收、并发安全、幂等、留痕。
- 复用 `cmbot/src/services/ai_text_service.py`、`ai_image_service.py` 的上游调用逻辑,不要另写一套 AI 调用实现。
- 不把真实 API Key、密钥、支付凭证写进代码或文档样例;只用占位符或环境变量名。
- 修改数据结构必须同步更新 `docs/04-architecture.md` 和 `docs/api.md`。
- 修改导航时,必须同步检查 `README.md` 和 `docs/README.md` 的链接。
## 风格
- 文档默认使用中文,代码标识符使用英文。
- 内容面向 agent 执行:每条规则尽量落到「读取什么、修改什么、验证什么」。
## 验证
统一入口为根目录 `./init.sh`(Windows 原生 PowerShell 用 `./init.ps1`),完成安装 + 验证 + 打印启动命令。真实命令以 `docs/03-tech-stack.md` 和 `docs/current-state.md` 为准。
+13
View File
@@ -0,0 +1,13 @@
# CLAUDE.md
> Claude Code 的仓库级薄入口。进入本仓库后,先读本文,再读 [`AGENTS.md`](AGENTS.md)。
本仓库的权威 agent 规则、文档入口、工作边界和验证方式统一维护在 [`AGENTS.md`](AGENTS.md)。
Claude Code 处理本仓库任务时:
1. 先读取 [`AGENTS.md`](AGENTS.md)。
2. 再按 `AGENTS.md` 的要求进入 [`docs/00-ai-start-here.md`](docs/00-ai-start-here.md) 和相关文档。
3. 不在本文重复维护任务流程、编码规则或文档清单,避免和 `AGENTS.md` 漂移。
特别提醒:本项目涉及**资金/点数**。改动充值、扣费、退款、对账相关代码前,必须先读 [`docs/05-coding-rules.md`](docs/05-coding-rules.md) 第 8 节与 [`docs/04-architecture.md`](docs/04-architecture.md) 的计费时序。
+37 -1
View File
@@ -1,3 +1,39 @@
# cmhub
cm hub
`cmhub` 是一个**自助用户端 + 计费型 AI 能力网关 + 运营后台**三合一服务:终端用户自助注册、扫码充值、管理 API Key;用 API Key 调用把桌面工具 `cmbot` 的「生成标题」「生成图片」能力封装成的 HTTP API,按**点数计费**;运营用 django-admin 管理用户、点数和记录。
## 它做什么
- 用户端(自助):注册/登录、扫码充值、查看充值记录/剩余点数/消费记录、生成与删除 API Key(注册不送免费点数)。
- 对外提供 HTTP API:生成标题、生成图片(同步返回)、查询点数余额。
- 按「操作类型 + 模型 + 分辨率」计费,调用消耗不同点数;余额不足返回「点数不足,请先充值」。
- **预付费点数模型**:用户在外部支付系统充值,付款成功由支付系统回调本服务,按汇率把金额转成点数存在本地;之后调用直接扣本地点数,扣费与上游解耦、低延迟。
- django-admin 运营后台:管理注册用户、点数余额、计费规则、充值订单、点数流水、调用记录。
## 技术栈
Python 3.12 / Django 5.2 LTS + DRF / django-admin / 用户端 Django 模板 SSR + Bootstrap 5 + allauth / MySQL 8.4 LTS(cmhub 专用独立实例,不复用 VPS 已有的 MySQL 5.7)。AI 上游调用复用 `cmbot/src/services/` 的实现。详见 [`docs/03-tech-stack.md`](docs/03-tech-stack.md)。
## 文档入口
本仓库采用 harness coding 文档约定。AI coding agent 与开发者请从这里进入:
- 人类总览:本文件。
- agent 工作入口:[`docs/00-ai-start-here.md`](docs/00-ai-start-here.md)。
- 文档导航:[`docs/README.md`](docs/README.md)。
- 仓库级规则:[`AGENTS.md`](AGENTS.md) / [`CLAUDE.md`](CLAUDE.md)。
## 当前状态
MVP 起步:目前仅有文档,尚无代码。下一步是 T-001 初始化 Django 骨架。详见 [`docs/current-state.md`](docs/current-state.md)。
> ⚠️ 涉及资金/点数。改动充值、扣费、退款、对账相关代码前,先读 [`docs/05-coding-rules.md`](docs/05-coding-rules.md) 第 8 节与 [`docs/04-architecture.md`](docs/04-architecture.md) 第四节计费时序。
## 启动
```bash
./init.sh # WSL / Git Bash / macOS / Linux
./init.ps1 # Windows 原生 PowerShell
```
脚本完成依赖安装 + 验证 + 打印启动命令。骨架落地(T-001)后即可使用。
+127
View File
@@ -0,0 +1,127 @@
# AI 开发入口
> 给 AI coding agent 的项目入口。这里负责导航和流程,硬性编码规则见 [`05-coding-rules.md`](05-coding-rules.md)。
## 一句话定位
`cmhub` 是「**自助用户端 + 计费 API + 运营后台**」三合一服务:终端用户在用户端自助注册、扫码充值、管理 API Key,用 API Key 调用把 `cmbot` 的「生成标题」「生成图片」能力封装成的 HTTP 接口,按**点数计费**;运营用 django-admin 管理用户、点数与记录。
第一版 MVP 做:**用户自助注册登录 → 扫码充值转点数 → 自助生成/删除 API Key → 带 Key 调用按规则算点数 → 点数足够则调上游生成并扣点 / 不足则报错 → 写调用与消费记录 → django-admin 运营后台**。**注册不送免费点数,必须充值才有点数。**
## 必读顺序
每次开始写代码前,按这个顺序建立上下文:
1. [`01-vision.md`](01-vision.md):为什么做、为谁做、什么不做。
2. [`02-requirements.md`](02-requirements.md):MVP 要什么、怎么算达成。
3. [`03-tech-stack.md`](03-tech-stack.md):既定技术选型(Django + DRF + django-admin)。
4. [`04-architecture.md`](04-architecture.md):系统结构、计费时序、数据模型和关键难点。
5. [`05-coding-rules.md`](05-coding-rules.md):写代码前必须遵守的规则(含资金安全)。
6. [`api.md`](api.md):对外接口与支付回调合约。
7. [`06-tasks.md`](06-tasks.md):领取本轮唯一任务。
8. [`../progress.md`](../progress.md):历史执行记录、验证结果、阻塞点和关键决策。
9. [`current-state.md`](current-state.md):当前代码现实、可运行命令、下一步任务。
仓库根目录 `AGENTS.md`、`CLAUDE.md` 必须先读。仓库级规则优先于项目局部建议。
## 固定开工流程
读完上述文档后,每轮会话按这个机械顺序进入,恢复持久状态再动手:
1. `pwd`:确认在 `cmhub` 仓库根目录。
2. 读 [`../progress.md`](../progress.md) 和 [`current-state.md`](current-state.md):恢复已验证状态、下一步和当前 blocker。
3. `git log --oneline -5`:看清最近发生了什么(仓库初始化后)。
4. 运行 `./init.sh`(Windows 原生 PowerShell 用 `./init.ps1`):统一安装、验证、打印启动命令;如脚本提示命令未替换,先配置脚本顶部三个命令。
5. 跑一条基础 smoke / 端到端路径,确认基线没坏。
6. **如果基线已坏,先修基线**,不要在坏的起点上叠新功能。
7. 基线绿了,再从 [`06-tasks.md`](06-tasks.md) 领取唯一任务。
## 当前阶段
当前项目处于:**MVP 起步**(`cmhub` 目录目前只有文档,尚无代码)。
优先路径:
1. Phase 0:Django 骨架可运行、**自定义 User 模型在首次迁移前定好**、django-admin 可登录。
2. Phase 1:最高风险功能原型 —— 移植 `cmbot` 的 AI 调用并在服务端跑通一次标题/图片生成。
3. Phase 2:计费核心 —— User/UserWallet/ApiKey 模型 + 点数扣减(并发安全,锁 Wallet 行)+ 计费规则 + 调用记录。
4. Phase 3:对外 API 与充值 —— Key 鉴权、生成接口、余额查询、充值回调、扫码下单与轮询。
5. Phase 4:用户端(Django 模板 SSR)—— 注册登录、API Key 管理、个人中心/记录页、充值页。
6. Phase 5:后台与发布 —— 运营后台完善、完整验收、部署 / 运行文档。
## 领取任务规则
从 [`06-tasks.md`](06-tasks.md) 领取任务时:
- 只领取第一个状态为 `TODO` 且依赖均为 `DONE` 的任务。
- 开始前把该任务状态改为 `DOING`。
- 本轮只完成这一个任务。
- 验收通过后把状态改为 `DONE`。
- 完成后把执行记录追加到 [`../progress.md`](../progress.md),并覆盖更新 [`current-state.md`](current-state.md)。
- 结束会话前过一遍 [`clean-state-checklist.md`](clean-state-checklist.md)。
- 做完即停,汇报验证结果,等待下一步指令。
如果代码实际状态和任务看板冲突,先说明冲突,不要擅自跳步或重排。
## MVP 边界
MVP 只做:
- **用户端(Django 模板 SSR + Bootstrap)**:自助注册/登录、扫码充值、查看充值记录/充值总额/剩余点数/消费记录、自助生成与删除 API Key。
- 对外两个生成接口:`POST /api/v1/generate/title`、`POST /api/v1/generate/image`(同步返回结果)。
- API Key 鉴权,识别所属注册用户(API 只认 Key,不认 Web session)。
- 点数计费:按「操作类型 + 能力别名(+ 可选分辨率)」查计费规则得点数;本地余额原子扣减;不足报错。
- 支付充值回调:支付系统服务端回调 → 验签 + 幂等 → 按汇率把金额转点数入账。
- 调用记录与点数流水落库。
- django-admin 运营后台:管理账号、点数余额、计费规则、充值订单、点数流水、调用记录。
MVP 不做:
- 异步任务队列(图片生成第一版走**同步等待**返回,不引入 Celery)。
- 自建支付/收银台(充值走外部支付系统,本服务发起扫码下单并接收回调,不碰资金清算)。
- 面向终端消费者的「内容生成产品页」(用户端只做账户自助,生成结果仍只经 API 输出)。
- 注册赠送免费点数(必须充值才有点数)。
- 多币种、分账、自动退款对账。
## 事实来源
项目事实只信:
- 本 `docs/` 目录的需求、架构、API 合约。
- `cmbot/src/services/ai_text_service.py`、`ai_image_service.py`、`cmbot/config/ai_models.json`:AI 上游调用的权威实现与模型配置形状。
- 支付协议以 `api.md` 与 `04-architecture.md` 为准:微信 V3 native + 支付宝当面付的下单、回调、验签、应答已按同系统实现明确;仅真实商户密钥/证书/notify_url 待提供。
不要把以下内容当事实来源:历史备份、旧导出文档、临时实验目录、未被任务或需求引用的草稿。
## 常见任务该看哪里
做对外 API / 支付回调:先看 `api.md` 合约,再看 `04-architecture.md` 的鉴权与计费时序。
做计费 / 扣点 / 充值:先看 `04-architecture.md` 第四节计费时序与并发安全,再看 `05-coding-rules.md` 第 8 节。
做数据模型:先看 `04-architecture.md` 第三节;schema 变化必须同步 `api.md`、`current-state.md` 和相关任务验收。
做运营后台:先看 `routes.md`(admin 入口与页面职责),再看 `04-architecture.md` 数据模型。
做部署 / 运行:先看 `03-tech-stack.md` 的运行命令、`env.md` 的环境变量,再看 `current-state.md` 的当前真实命令。
## 验证命令
统一启动与验证入口收敛到根目录 `./init.sh`(Windows 原生 PowerShell 用 `./init.ps1`)。骨架落地后,真实命令大致为:
```bash
# 安装依赖(建议 uv 或 venv + pip)
uv sync # 或 pip install -r requirements.txt
# 数据库迁移
python manage.py migrate
# 基础验证 / 测试
python manage.py test
# 本地开发启动
python manage.py runserver
```
说明:
- 改后端逻辑后跑:`python manage.py test`。
- 改数据模型后跑:`python manage.py makemigrations && python manage.py migrate && python manage.py test`。
- 如果命令当前不可运行(骨架未建),必须在回复里如实说明原因。
+45
View File
@@ -0,0 +1,45 @@
# 项目愿景
## 一、核心目标
`cmhub` 要解决:`cmbot` 的 AI 生成能力(标题、图片)目前锁在单机桌面工具里,无法被其他项目复用,也没有计费和用量管控。
> 让**其他业务项目**能够**通过 HTTP API 调用 AI 生成标题和图片**,并获得**按点数计费、用量可控、调用可追溯**的能力。
它面向两类使用者:**终端用户**在自助用户端注册、扫码充值、管理自己的 API Key;**其他业务项目/程序**用这些 API Key 调用生成接口。本服务是**自助用户端 + 计费型 AI 能力网关 + 运营后台**三合一。用户端不做面向消费者的「内容生成产品页」,只做账户自助(注册/充值/Key 管理/记录),生成结果仍只经 API 输出。
## 二、目标用户
- **注册用户(终端用户)**:在用户端自助注册登录、扫码充值、查看自己的充值记录/充值总额/剩余点数/消费记录、自助生成与删除 API Key。点数余额挂在用户账户上。
- **接入方(用户的程序 / 其他项目)**:拿用户名下的 API Key 调用生成接口,关心稳定、点数够不够、报错清晰;扣的是所属用户的点数。
- **运营人员**:在 django-admin 管理注册用户、点数余额、计费规则,查看充值和调用记录,处理点数不足/异常。
## 三、产品原则
遇到取舍时,以这些原则为准:
- **核心流程优先**:先把「鉴权 → 计费 → 生成 → 扣点 → 记录」这条主链路做顺。
- **真实数据优先**:点数、金额、扣费、充值不造假;不用演示逻辑冒充计费逻辑。
- **账目必须对得上**:每一笔点数变动都有流水、可追溯到充值订单或调用记录。
- **小步交付**:每一步都能运行、能验证、能回退。
- **少即是稳**:MVP 不追求完整,只追求最小闭环可靠;先同步、先单机、先后台手工开通。
- **可维护**:复用 `cmbot` 现有 AI 调用代码,不另起一套。
## 四、核心价值主张
| 价值点 | 说明 |
| --- | --- |
| 能力复用 | 把 `cmbot` 的标题/图片生成开放给任意项目,无需各自重复对接上游 |
| 按量计费 | 不同操作/模型消耗不同点数,用量与成本可控 |
| 充值即点数 | 外部支付系统付款后自动转点数,扣费与上游解耦、低延迟 |
| 可观测可运营 | django-admin 一处管理账号、点数、计费规则、充值与调用记录 |
## 五、不做什么(非目标)
- 不做自建支付/收银台,付款在外部支付系统完成,本服务发起扫码下单并接收回调,不碰资金清算。
- 不做异步任务队列(第一版图片生成走同步等待)。
- 不做面向终端消费者的「内容生成产品页」;用户端只做账户自助(注册/充值/API Key 管理/记录查询)。
- **注册不赠送免费点数**:必须充值才有点数,降低薅羊毛/滥用风险。
- 不做多币种、分账、自动退款对账等容易膨胀、不属于 MVP 的能力。
> MVP 的具体功能范围与验收标准,见 [需求](02-requirements.md)。
+100
View File
@@ -0,0 +1,100 @@
# 需求
> 本文只描述**要什么**与**怎么算达成**,用产品 / 用户语言表达,**不涉及技术实现**。
> 技术方案、数据结构、字段定义见 [架构设计](04-architecture.md)。
## 一、业务现状
| 项 | 状态 |
| --- | --- |
| 用户 | 其他项目想复用 `cmbot` 的标题/图片生成,但能力锁在单机桌面端,且无计费 |
| 数据 | AI 上游调用逻辑与模型配置已存在于 `cmbot`(`ai_text_service.py` / `ai_image_service.py` / `config/ai_models.json`),可复用 |
| 现有系统 | `cmbot` 桌面工具继续运行,不改动;外部已有支付系统,支付协议已明确为微信 V3 native + 支付宝当面付,真实商户配置上线前提供 |
| 约束 | 上游 AI(vectorengine.ai)按次产生真实成本;图片生成单次可能耗时几十秒到 1-2 分钟;涉及资金/点数,必须账目准确、防超扣、防重复入账 |
## 二、用户角色
- **注册用户(终端用户)**:在用户端自助注册登录、扫码充值、查看自己的充值记录/充值总额/剩余点数/消费记录、自助生成与删除 API Key。点数余额挂在用户账户上。
- **接入方(用户的程序)**:持有该用户名下的 API Key,调用生成接口、查询余额;扣的是所属用户的点数。
- **运营人员(后台管理员)**:登录 django-admin,管理注册用户、启用/禁用账号、配置计费规则与汇率、查看充值订单/点数流水/调用记录、必要时手工调整点数。
- **游客 / 未登录 / 未携带有效 API Key**:用户端页面需登录访问;生成/余额接口未带有效 Key 一律拒绝(401)。
## 三、功能清单
### 第一版 MVP(最小闭环)
| 功能 | 用户能做什么 | 优先级 |
| --- | --- | --- |
| 用户注册/登录 | 终端用户在用户端自助注册、登录(邮箱验证);**注册不送免费点数** | P0 |
| API Key 自助管理 | 登录后自助生成/删除 API Key;Key 只在生成时显示一次(库内哈希存储) | P0 |
| 扫码充值 | 用户端发起充值→展示支付二维码→扫码付款→回调到账;金额按汇率转点数 | P0 |
| 个人中心 | 查看剩余点数、充值总额、充值记录、点数使用(消费)记录 | P0 |
| 生成标题 API | 带 API Key + prompt(+可选商品图)调用,拿到标题文字 | P0 |
| 生成图片 API | 带 API Key + prompt + 图 + 模型/分辨率调用,**同步**拿到生成图片 | P0 |
| 点数计费与扣减 | 按「操作类型+能力别名+分辨率」算点数,余额足够则扣点放行,不足则报错 | P0 |
| 余额查询 API | 查询当前用户点数余额 | P0 |
| 调用记录 | 每次调用落库:用户、Key、时间、操作、模型、消耗点数、成功/失败、错误 | P0 |
| 点数流水 | 每次点数变动(充值/消费/运营调整)落库,可对账 | P0 |
| 运营后台 | django-admin 管理用户、点数、计费规则、充值订单、流水、调用记录 | P0 |
### 后续迭代
| 功能 | 描述 | 阶段 |
| --- | --- | --- |
| 异步生成 | 图片生成改任务队列 + 轮询/回调,提升并发 | V2 |
| 用量统计报表 | 按用户/时间/模型聚合消耗与成本 | V2 |
| 注册赠点 / 试用额度 | 若开放,需配套邮箱/图形验证码 + 限流防薅羊毛 | V2 |
| API Key 轮换 / 授权 | Key 到期轮换、按 Key 限授权别名 | V2 |
| 退款对账自动化 | 外部退款联动本地点数冲正 | V3 |
## 四、核心用户故事(MVP)
1. 作为新用户,我在用户端自助注册登录,扫码充值一笔钱,看到对应点数按汇率到账。
2. 作为已登录用户,我自助生成一把 API Key(明文只显示一次),也能删除不用的 Key。
3. 作为接入方,我带着 API Key 调用生成标题接口,传入 prompt,能拿到生成的标题。
4. 作为接入方,我调用生成图片接口,等待若干秒后在同一个响应里拿到图片结果。
5. 当我的点数不足以支付本次调用时,系统拒绝调用并返回「点数不足,请先充值」,且不调用上游、不扣点。
6. 作为已登录用户,我在个人中心看到剩余点数、充值总额、充值记录和消费(调用)记录。
7. 作为运营,我在后台能管理用户与点数(手工调点带原因),配置某操作的点数单价,查看某用户的调用记录和点数流水。
## 五、验收标准(MVP)
每条 P0 功能对应可验证的判据:
- **生成标题 API**:携带有效 API Key + 合法 prompt 调用,返回 200 且响应含标题文本;无效/缺失 Key 返回 401。
- **生成图片 API**:携带有效 Key 调用,在超时上限内返回 200 且含图片(URL 或 base64);上游失败时返回明确错误码,且**不扣点**(已扣则冲正)。
- **点数计费与扣减**:调用前按规则算出点数 N;余额 ≥ N 才放行并精确扣 N;余额 < N 返回点数不足错误码且不调上游。并发同时多次调用,最终扣点总额正确,**不出现超扣或扣成负数**。
- **余额查询 API**:返回的余额等于该账号点数流水累加结果。
- **充值转点数**:模拟一次支付系统回调(含订单号、金额、签名),验签通过后按汇率加点;**同一订单号重复回调只入账一次**(幂等);验签失败不入账。
- **调用记录 / 点数流水**:每次成功/失败调用、每次充值/调整都各生成一条记录,字段完整、可在后台检索。
- **用户注册/登录**:新用户能自助注册(邮箱验证)、登录;注册后点数为 0(不送免费点数)。
- **API Key 自助管理**:登录用户能生成 API Key(明文只显示一次,库内存哈希)、能删除;删除后该 Key 调用返回 401。
- **扫码充值**:用户端发起充值→展示二维码→(mock 或真实)支付成功回调后点数按汇率到账,充值记录与流水各一条;同一订单号重复回调只入账一次。
- **个人中心**:剩余点数 = 点数流水累加;充值总额、充值记录、消费记录与实际一致。
- **运营后台**:运营能登录 django-admin,管理用户与点数(手工调点带原因写流水)、配置计费规则、检索并查看充值订单/流水/调用记录。
- **可插拔模型**:运营在后台维护具体上游模型并配置「能力别名 → 模型」映射;调用方只用别名调用。改某别名的指向后,调用方无需改动即用上新模型;切换前后该别名的计费规则不变。
## 六、范围边界与决策
| 问题 | 决策 |
| --- | --- |
| 第一版形态 | 自助用户端(Django 模板 SSR + Bootstrap)+ 对外 HTTP API(DRF)+ Web 运营后台(django-admin),单体部署 |
| 是否需要账号 | 是。终端用户**自助注册**(Django auth / allauth)→ 自助生成 API Key;运营用 Django 后台账号 |
| 计费模型 | **预付费点数**:充值时按汇率把金额转点数存本地,调用直接扣本地点数 |
| 余额是否实时查支付系统 | 否。本地点数余额为唯一权威账本,调用链路不查支付系统 |
| 生成返回方式 | **同步**等待返回结果(图片接口需把网关/服务超时调到 ≥ 300s) |
| 模型选择方式 | 对外用**能力别名**(如 `image-hd`),不暴露具体模型名;后台配置别名→模型映射 |
| 充值入口 | 用户端自助发起扫码充值(本服务向支付系统下单取二维码),付款成功由支付系统**服务端回调**入账 |
| 注册是否送点数 | 否。注册不送免费点数,必须充值才有点数 |
| 暂不支持 | 异步队列、注册赠点、多币种、分账、自动退款对账 |
## 七、待确认 / 风险点
- **支付商户配置**:协议字段、签名/验签方式、成功应答已明确;上线前仍需提供微信/支付宝真实商户密钥、证书、公网 `notify_url`。配置到位前,先以 mock 支付客户端开发和测试。
- **汇率与点数单价**:1 元 = 多少点?标题/图片/各模型各分辨率分别多少点?由运营/产品确认,配置在后台。
- **资金风险**:充值入账必须幂等防重复加点;扣点必须并发安全防超扣;上游失败必须不扣点或冲正。规则由产品确认,实现按 `05-coding-rules.md` 第 8 节。
- **凭证风险**:上游 AI 的 api_key、支付系统密钥属敏感信息,只能放环境变量/后台配置,不入代码与文档样例。
- **第三方平台风险**:上游 vectorengine 可能限流、超时、变更;需处理超时、错误透传与失败不扣点。
- **耗时风险**:图片同步生成耗时长,需确认网关/反向代理/客户端超时上限,避免连接中断导致已扣点但结果丢失。
- **退款 / 纠纷**:外部退款后本地点数如何冲正?MVP 不自动化,但数据结构需保留可冲正能力。
- **待确认决策**:上述由谁、何时决策需明确,尤其商户配置交付时间与计费规则。
+63
View File
@@ -0,0 +1,63 @@
# 技术栈(Tech Stack)
> “用什么”的统一速查表。选型与理由在此集中维护;“怎么把它们搭起来”见 [架构设计](04-architecture.md)。
> 未定项必须标为待定,不要让 agent 在代码里自行决定。
## 一、技术栈一览
| 维度 | 选型 | 状态 | 理由 / 说明 |
| --- | --- | --- | --- |
| 语言 | Python 3.12(锁定 `>=3.12,<3.14`) | 已定 | 复用 `cmbot` 的 Python AI 调用代码(cmbot 为 3.11+,向上兼容);3.12 稳定、库全、性能好,落在 Django 5.2 支持窗口内 |
| Web 框架 | Django 5.2 LTS | 已定 | 自带 ORM、迁移、admin,适合「API + 运营后台」;选 LTS 维护到 2028,安全更新窗口最长。**禁用已 EOL 的 4.0/4.1**(无安全补丁,资金服务不可用) |
| API 框架 | Django REST Framework (DRF) | 已定 | 鉴权、序列化、参数校验、限流现成 |
| 运营后台 | django-admin | 已定 | 近零代码即得用户/点数/记录的增删改查与检索,省 80% 后台工作量 |
| 用户端 | Django 模板 SSR + Bootstrap 5 + django-allauth + crispy-forms | 已定 | 自助注册/登录/充值/API Key 管理/记录页;allauth 出注册登录邮箱验证,crispy + 现成 Bootstrap 模板出页面,单体不引前端框架 |
| 后台美化 | django-unfold 或 simpleui | 待定 | 仅外观,MVP 可先用原生 admin,后期按需引入 |
| AI 上游对接 | **Provider 适配器层**(按 `api_type` 注册)+ **能力别名** 映射 | 已定 | 对外只暴露 `generate text/image` 两接口与别名;换供应商改后台映射,不动对外契约。移植 `cmbot` 的调用逻辑到各适配器。当前 3 模型机制不同:文本 chat、`nano-banana2` chat 多模态返图、`gpt-image-2` images/edits 改图(详见 `04` 3.1) |
| 供应商密钥存储 | 应用层对称加密(如 `cryptography` Fernet)或 KMS | 待定 | `AiModel.api_key` 加密入库、admin 脱敏不回显;加密主密钥走环境变量,配置清单见 `env.md` |
| 图片结果存储 | 对象存储(S3 兼容 / 本地存储)返回 URL | 待定 | 同步响应默认返回 `image_url`,避免大 base64 进响应体 |
| 配置变更审计 | django-admin LogEntry 或自建审计表 | 待定 | 模型/别名/密钥变更留痕,与「账目对得上」一致 |
| 数据库 | MySQL 8.4 LTS(cmhub 专用独立实例) | 已定 | 满足 Django 5.2 的 MySQL ≥8.0.11;引擎 InnoDB + 字符集 utf8mb4;行锁 `select_for_update` / 条件更新保并发扣点。**不复用 VPS 已有的 MySQL 5.7**(跑不了 Django 5.2、无 CHECK 约束)。开发亦用 MySQL,勿用 SQLite(不支持 `select_for_update`) |
| 对外鉴权 | API Key(DRF 自定义 Authentication,哈希存储比对) | 已定 | 用户自助生成 Key;**API 只认 Key、不挂 SessionAuthentication**,防浏览器 cookie 绕过计费 |
| 用户端鉴权 | Django Session(+ allauth 注册登录邮箱验证) | 已定 | 用户端页面与 `recharge/create` 走 session + CSRF;后台账号也用 Session 登录 |
| 充值对接 | 自助扫码:微信 V3 native + 支付宝当面付;下单取二维码 + 服务端回调(验签 + 幂等) | 已定 | 库 `wechatpayv3` / `python-alipay-sdk`;协议对齐同支付系统 PHP 实现(见 `api.md`);仅商户密钥/证书待提供 |
| 生成返回方式 | 同步 HTTP(无任务队列) | 已定 | MVP 简化;图片接口需调大网关/服务超时 |
| 任务队列 | 暂不引入(Celery/RQ) | 待定 | V2 异步化时再评估 |
| 部署方式 | Docker + Gunicorn(gthread) + Nginx,单体 | 待定 | MVP 先 `runserver`;生产 Nginx 按路径把 `/api/generate/*`(图片长请求)与用户端页面**分流到不同 gunicorn/worker 池**,避免图片阻塞拖慢页面(见 `04-architecture.md` 5.1) |
| 测试 | Django 自带 `manage.py test`(unittest)/ 可选 pytest-django | 已定 | 先用内置 test runner,重点覆盖计费与回调 |
| 依赖管理 | uv 或 venv + pip(`requirements.txt`) | 待定 | 二选一,骨架任务确定后写死并同步 init 脚本 |
## 二、决策记录与演进
- **Django 而非 FastAPI**:核心收益是 django-admin 直接满足「运营后台」需求;FastAPI 需自建后台。代价是异步生态较弱,但 MVP 同步返回,不受影响。
- **锁定 Django 5.2 LTS + Python 3.12**:① 版本红线属安全而非性能——生文/图生图瓶颈在「等上游 + worker 并发 + 超时」,不在框架版本(详见 [架构设计](04-architecture.md) 5.1),故版本选择只按安全与维护窗口定;② Django 4.0/4.1 已 EOL、无安全补丁,涉资金服务禁用;4.2 LTS 支持窗口临近尾声,不从其起步;5.2 LTS 维护到 2028,窗口最长。③ Django 5.2 支持 Python 3.10–3.13,锁 3.12 取「稳定 + 库全 + 性能」的平衡,避开 3.10(临近 EOL)与 3.14(不在 5.2 官方矩阵)。骨架任务须在 `pyproject.toml` 写死 `requires-python = ">=3.12,<3.14"`,并在 `init.sh` / `init.ps1` 校验解释器版本。
- **预付费点数而非实时查支付余额**:充值时按汇率把金额转点数存本地,解耦支付系统、降低调用延迟、并发扣减用本地数据库事务即可保证。代价是需处理充值幂等与对账。
- **稳定接口 + 可插拔供应商**:对外只 `generate text/image` 两接口 + 能力别名;具体模型在后台配置并经适配器调用。收益是换供应商对调用方零改动、计费按别名稳定、可加授权与故障转移;代价是需维护适配器层与别名映射。**调用方不绑具体模型 SKU**。
- **同步生成而非任务队列**:MVP 不引入 Celery/Redis,降低复杂度;图片耗时长,靠调大超时支撑,V2 再异步化。
- **桌面端可不改、全同步接入**:cmbot 现有批处理引擎(后台线程池 + 进度/心跳/重试/停止)只需把 service 层 URL/密钥从直连中转站换成 cmhub 的 `base_url + API Key`,即可正常使用;`桌面端 → cmhub → 中转站` 多一跳不影响可用性,点数一致性反而更简单(一次请求闭环:预扣→同步调→成功/失败退点)。
- **同步可用的前提是「超时链路 + worker 容量」配对**,否则会「小量正常、上量假死」:① 每模型 `timeout_seconds` 设有限值(`ai_models.json` 现为 `0`,迁入须改);② Gunicorn `--timeout` 与网关 `proxy_read_timeout` 按最慢图片放大(别用默认 30s / 60s);③ worker/线程数按**峰值总并发**预留。详见 [架构设计](04-architecture.md) 第五节结论。
- **何时转 V2 异步**:worker 被长连接占满拖慢快接口/后台、接入方总并发明显上涨、或需要「关窗重连/任务持久化」体验——在此之前保持同步。
- **数据库:MySQL 8.4 LTS,cmhub 专用独立实例**:① 部署环境的 VPS 已装 MySQL 5.7 供其他服务用,但 5.7 跑不了 Django 5.2(需 ≥8.0.11)、已 EOL、且不支持 CHECK 约束,故**不复用**它;② 机器内存宽裕(`available` 7.4G),给 cmhub **单开一个 MySQL 8.4 LTS 实例**(独立端口/容器),与已有 5.7 完全隔离、互不影响;③ 选 8.4 LTS 取长维护窗口 + 完整 CHECK 约束(CHECK 需 MySQL ≥8.0.16 才真正生效);④ 强制 InnoDB + utf8mb4(5.7/老配置默认非 utf8mb4,prompt 的 emoji/生僻字会写失败);⑤ MySQL 默认隔离级别 REPEATABLE READ(不同于 PostgreSQL 的 READ COMMITTED),`select_for_update` 扣点仍安全,但计费实现按此语义验证;⑥ 开发环境同用 MySQL,不要用 SQLite——SQLite 会静默忽略 `FOR UPDATE`,并发扣点逻辑测不出来;⑦ 不在代码里写死只适配某一种库的 SQL。
- **用户端用 Django 模板 SSR 单体,不引前端框架**:需求含终端用户自助(注册/充值/API Key/记录),选 Django 模板 + Bootstrap + allauth 与后端同工程单体部署,复用 Django auth/session,开发部署最快、最契合单机 MVP;代价是交互不如 SPA,可后续加 HTMX。放弃 Vue/React 前后端分离(两套项目/部署,与单体 MVP 调性冲突)。用户模型:`User`(auth) 持登录态、`UserWallet` 持点数(扣点锁 wallet、与 auth 解耦)、`ApiKey`(User 1:N,哈希存储)。注册不送免费点数。
## 三、构建与运行命令
> 以下为骨架落地后的目标命令;初始化任务(T-001)完成后须用真实命令替换并同步到 `init.sh` / `init.ps1` 与 `current-state.md`。
| 用途 | 命令 |
| --- | --- |
| 安装依赖 | `uv sync` 或 `pip install -r requirements.txt` |
| 数据库迁移 | `python manage.py makemigrations && python manage.py migrate` |
| 创建后台管理员 | `python manage.py createsuperuser` |
| 本地开发 | `python manage.py runserver` |
| 测试 | `python manage.py test` |
| 格式化 / 静态检查 | `ruff check .`(待定,确定后写死) |
Windows PowerShell 命令与上相同(Django 跨平台),差异仅在依赖安装与虚拟环境激活方式,由 `init.ps1` 统一封装。
## 四、依赖纪律
- 新增第三方依赖前,先说明用途、替代方案和维护成本。
- 不确定的技术选型先更新本文,再进入代码(如 Celery、unfold、pytest)。
- 不允许同一职责并存两套方案(如两套鉴权、两套 HTTP 客户端)。
- 敏感配置(上游 api_key、支付密钥、SECRET_KEY、数据库密码)只走环境变量或后台配置,不写进代码与文档样例;变量名集中维护在 [`env.md`](env.md)。
+350
View File
@@ -0,0 +1,350 @@
# 架构设计
> 本文讲“怎么把技术栈搭起来”:系统结构、职责划分、数据模型、计费时序、技术难点、开发顺序。
> 具体用了哪些框架 / 库 / 数据库 / 部署方式,见 [技术栈](03-tech-stack.md)。
## 一、系统结构
```text
终端用户 ──浏览器/session──> [用户端 Web · Django模板SSR] 注册/登录/扫码充值/管API Key/看记录
用户的程序 ──API Key──────> [对外 API · DRF] 鉴权 → 计费 → 扣点 → 调上游 → 记录
│
[cmhub · Django 单体] ──下单/回调(验签)──> 外部支付系统 <──扫码付款── 终端用户
├──> 上游 AI(vectorengine.ai:标题/图片)
└──> MySQL(用户/钱包/APIKey/规则/订单/流水/调用记录)
│
[django-admin] <──── 运营人员(管理用户、点数、规则、记录)
```
组件落位:
- **用户端层(Django 模板 SSR)**:注册/登录(Django auth / allauth)、个人中心(余额/充值总额/充值记录/消费记录)、API Key 自助管理、发起扫码充值。入口 `apps/portal/`,用 session 鉴权。
- **用户与账号层**:注册用户 `User`、点数钱包 `UserWallet`、`ApiKey`(一用户多把、哈希存储)。入口 `apps/users/`。
- **API 层(DRF)**:对外生成接口、余额查询、支付回调接收、扫码下单。入口 `apps/api/`。生成/余额这类对外业务 API **只认 API Key,不接受 Web session**;充值下单/状态查询属于用户端流程,走 Web session + CSRF;支付回调走平台验签。
- **计费层**:点数计算、原子扣减(锁 `UserWallet` 行)、退点、充值入账、流水记账。入口 `apps/billing/`。
- **AI 调用层(Provider Adapter 架构)**:对外只暴露稳定能力,内部用「能力别名 → 具体供应商适配器」解耦。入口 `apps/ai/`,适配器在 `apps/ai/providers/`。
- **运营后台**:django-admin,注册各模型的 Admin。入口各 app 的 `admin.py`。
- **外部服务**:上游 AI(vectorengine)、外部支付系统(跳转 + 回调)。
- **数据库**:MySQL 8.4 LTS(cmhub **专用独立实例**,不复用 VPS 已有的 MySQL 5.7);引擎必须 InnoDB、字符集 utf8mb4。开发与生产同用 MySQL,勿用 SQLite(SQLite 会静默忽略 `select_for_update`,测不出并发扣点)。
## 二、职责划分
**API 层(`apps/api`)**
- API Key 鉴权(自定义 DRF Authentication,哈希比对),把请求绑定到 Key 所属的注册用户。
- 参数校验、请求/响应序列化、错误码统一。
- 编排单次调用:调用计费层预扣 → 调 AI 层 → 成功确认 / 失败退点 → 写调用记录。
- 不直接写点数余额字段,必须走计费层提供的方法。
**计费层(`apps/billing`)**
- 计费规则查询:按「操作类型 + 能力别名(+ 可选分辨率)」算出本次点数 N。**按别名定价,不按具体供应商 SKU 定价**,这样后台换底层模型时计费不变。
- 点数原子扣减与退回:数据库事务 + 行锁,保证并发不超扣、不为负。
- 充值入账:接收已验签的支付回调数据,按汇率换算点数,幂等入账,写流水。
- 点数流水记账:所有点数变动(充值/消费/调整/冲正)都生成一条流水。
- 是点数余额的唯一写入方。
**AI 调用层(`apps/ai`)**
- **别名解析**:把对外的能力别名(如 `title-standard` / `image-hd`)解析成当前映射的具体 `AiModel`,调用方不感知供应商 SKU。
- **Provider 适配器**:每个供应商一个适配器(`apps/ai/providers/`),实现统一接口 `generate_text()` / `generate_image()` / `capabilities()`;用注册表按 `api_type` 选适配器,取代一长串 `if api_type == ...`。移植 `cmbot/src/services/ai_text_service.py`、`ai_image_service.py` 的上游调用与解析逻辑到对应适配器内。**注意当前 3 个模型调用机制不同(见 3.1):文本走标准 chat,`nano-banana2` 走 chat 多模态返图、`gpt-image-2` 走 images/edits 改图,两图片模型非标准生成、需分别解析返回。**
- **能力校验前置**:按目标模型声明的 `capabilities` 校验请求(如图片模型不能生成文字),在扣点和调上游**之前**拒掉非法组合。
- **配置热生效**:模型配置从数据库读取,后台修改后运行时及时生效(每次查库或带缓存失效),不在进程内缓存到失效。
- 输入:prompt、可选图片、能力别名、分辨率、`parameters` 透传;输出:文字或图片,或明确错误。
- 处理超时、错误,向上层抛出可分类的异常(区分「上游失败」与「参数错误」)。不在本层写点数逻辑。
**数据库 / 存储**
- 持久化:账号、API Key、点数余额、计费规则、汇率、充值订单、点数流水、调用记录、模型配置。
- 点数余额是权威账本;不持久化上游返回的大图原文时,调用记录只存引用/路径/摘要(大对象存储策略 MVP 待定)。
- 点数余额可变但只能经计费层修改;流水与调用记录只追加、不可改。
## 三、数据模型
> 以下为目标 schema 形状;以 Django models 实现,字段名以英文为准。schema 变化必须同步本节与 `api.md`。
### 3.1 配置 / 规则数据(运营在后台维护)
| 数据 | 来源 | 说明 |
| --- | --- | --- |
| AiModel | 迁移自 `cmbot/config/ai_models.json` | name、url、model、**api_key(加密存储)**、api_type、timeout、**capabilities**(text/image/vision、支持的分辨率) |
| ModelAlias | 运营配置 | 对外**能力别名** → 映射到一个具体 AiModel;调用方只见别名,不见 SKU |
| PricingRule | 运营配置 | 操作类型 × **能力别名**(+ 可选分辨率)→ 点数单价 |
| ExchangeRate | 运营配置 | 金额 → 点数 的汇率(如 1 元 = N 点),可带生效时间 |
关键事实:
- **对外契约绑能力别名,不绑具体模型名**:调用方传 `model = "title-standard"` 这类别名;后台把别名映射到具体 `AiModel`,换供应商只改后台映射,调用方零改动。具体模型名只在后台可见。
- `api_type` 取值沿用 cmbot:`chat` / `gemini` / `images` / `images_edits` / `auto`,用于选择 Provider 适配器。**当前启用 3 个模型(中转站 `api.vectorengine.ai`),调用机制各不相同,适配器须分别实现**:
- **GPT-5.5 文本**(`api_type=chat`,model `gpt-5.5`):标准 `chat/completions`。
- **Nano Banana 2**(`api_type=auto`,model `gemini-3.1-flash-image-preview`):走 `chat/completions` **多模态返图**,图片在返回消息里(URL 或 base64),需**自定义解析**;可纯文生图,也可带原图图生图。
- **GPT Image 2**(`api_type=images_edits`,model `gpt-image-2`):走 `images/edits` **改图**,**必须传入原图 + prompt**(契合「传商品图→生成主图」)。
- ⚠️ 两个图片模型**都不是**标准 `images/generations`,不可套用通用图片生成 SDK 写法;每个模型**独立 url + key**,不共享 base_url。
- ⚠️ 图片返回结构(URL / base64、在返回中的位置)**首次对接须抓一次真实响应,再定适配器解析代码**。
- `capabilities` 显式声明每个模型能做什么;标题接口只允许声明 `text` 的模型,图片接口只允许声明 `image` 的模型。
- `api_key` **加密存储**,admin 列表脱敏、不回显明文;模型/密钥/别名映射的变更需审计留痕。
- 汇率在**充值下单创建订单时**锁定并记录到订单,同时计算预计到账点数;后续改汇率不影响已创建订单。支付回调入账时使用订单上的 `exchange_rate` / `points_granted`,不重新读取当前汇率。
#### 配置表目标 schema
```sql
-- 上游模型(具体 SKU,后台维护,密钥加密)
CREATE TABLE ai_model (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL, -- 后台展示名
url TEXT NOT NULL,
model TEXT NOT NULL, -- 供应商模型标识
api_key_enc TEXT NOT NULL, -- 加密后的密钥,不存明文
api_type TEXT NOT NULL, -- chat/gemini/images/images_edits/auto
capabilities TEXT NOT NULL, -- JSON: {text,image,vision, resolutions:[...]}
timeout_seconds INTEGER,
connect_timeout_seconds INTEGER,
status TEXT NOT NULL DEFAULT 'active',
updated_at TEXT NOT NULL
);
-- 能力别名(对外稳定标识 → 当前映射的具体模型)
CREATE TABLE model_alias (
id INTEGER PRIMARY KEY,
alias TEXT UNIQUE NOT NULL, -- 如 title-standard / image-hd,对外用
operation_type TEXT NOT NULL, -- title / image
ai_model_id INTEGER NOT NULL REFERENCES ai_model(id), -- 可随时改指向
status TEXT NOT NULL DEFAULT 'active',
updated_at TEXT NOT NULL
);
-- 计费规则(按别名定价,换底层模型不影响计费)
CREATE TABLE pricing_rule (
id INTEGER PRIMARY KEY,
operation_type TEXT NOT NULL, -- title / image
alias TEXT NOT NULL REFERENCES model_alias(alias),
resolution TEXT, -- 可空:按档位区分价格时使用
points_cost BIGINT NOT NULL,
UNIQUE(alias, resolution)
);
```
> 可选增强(接口预留、MVP 不实现):`account_alias_permission`(按账号授权可用别名,防止调用方点用未授权/昂贵模型);别名按比例分流到多个模型(灰度/AB/故障转移)。适配器接口需为此留口子。
### 3.2 业务动态数据
```sql
-- 注册用户(扩展 Django AbstractUser,登录态)
CREATE TABLE "user" (
id INTEGER PRIMARY KEY,
username TEXT UNIQUE NOT NULL,
email TEXT NOT NULL, -- 注册邮箱,需验证
password TEXT NOT NULL, -- Django 哈希存储
payment_user_id TEXT, -- 对应外部支付系统的用户标识
status TEXT NOT NULL DEFAULT 'active', -- active / disabled
created_at TEXT NOT NULL
);
-- 点数钱包(点数余额为权威账本;与 auth 表解耦,扣点只锁本行)
CREATE TABLE user_wallet (
id INTEGER PRIMARY KEY,
user_id INTEGER UNIQUE NOT NULL REFERENCES "user"(id),
points_balance BIGINT NOT NULL DEFAULT 0, -- >=0,仅计费层可改
updated_at TEXT NOT NULL
);
-- API Key(一个用户可有多把;哈希存储,明文只在创建时显示一次)
CREATE TABLE api_key (
id INTEGER PRIMARY KEY,
user_id INTEGER NOT NULL REFERENCES "user"(id),
name TEXT, -- 用户自定义备注
key_hash TEXT UNIQUE NOT NULL, -- sha256(key),不存明文
key_prefix TEXT NOT NULL, -- 如 sk_live_abc…,列表展示/定位用
status TEXT NOT NULL DEFAULT 'active', -- active / revoked
last_used_at TEXT,
created_at TEXT NOT NULL
);
-- 充值订单(支付回调入账依据,订单号幂等)
CREATE TABLE recharge_order (
id INTEGER PRIMARY KEY,
order_no TEXT UNIQUE NOT NULL, -- 幂等键,重复回调只入账一次
user_id INTEGER NOT NULL REFERENCES "user"(id), -- 发起充值的用户,绑定防充错账户
amount_money DECIMAL NOT NULL, -- 付款金额
pay_method TEXT NOT NULL, -- weixin / alipay
exchange_rate DECIMAL NOT NULL, -- 下单时锁定的汇率
points_granted BIGINT NOT NULL, -- 下单时按汇率计算的预计/实际入账点数
status TEXT NOT NULL DEFAULT 'pending', -- pending / paid / failed / expired
code_url TEXT, -- 支付二维码票据(微信 code_url / 支付宝 qr_code)
expires_at TEXT, -- 二维码过期时间
payment_txn_no TEXT, -- 支付系统流水号
created_at TEXT NOT NULL,
paid_at TEXT
);
-- 点数流水(只追加,对账与审计核心)
CREATE TABLE points_ledger (
id INTEGER PRIMARY KEY,
user_id INTEGER NOT NULL REFERENCES "user"(id),
change_type TEXT NOT NULL, -- recharge / consume / adjust / refund
points_delta BIGINT NOT NULL, -- 正为加、负为减
balance_after BIGINT NOT NULL, -- 变动后余额,便于对账
ref_order_id INTEGER REFERENCES recharge_order(id),
ref_call_id INTEGER REFERENCES call_record(id),
reason TEXT, -- 运营手工调整必填原因
created_at TEXT NOT NULL
);
-- 调用记录(只追加)
CREATE TABLE call_record (
id INTEGER PRIMARY KEY,
user_id INTEGER NOT NULL REFERENCES "user"(id),
api_key_id INTEGER REFERENCES api_key(id), -- 本次调用所用的 Key
operation_type TEXT NOT NULL, -- title / image
alias TEXT, -- 调用方请求的能力别名(对外稳定标识)
model_name TEXT, -- 实际服务该次请求的具体模型(解析后,便于排障/对账)
resolution TEXT,
prompt TEXT,
points_cost BIGINT NOT NULL DEFAULT 0,
status TEXT NOT NULL, -- pending / success / failed
upstream_latency_ms INTEGER,
error_message TEXT,
result_ref TEXT, -- 结果引用(URL/路径/摘要)
created_at TEXT NOT NULL
);
```
需要说明:
- 主键自增;`api_key.key_hash`、`recharge_order.order_no`、`user.username` 唯一。
- 重要索引:`call_record(user_id, created_at)`、`points_ledger(user_id, created_at)`、`recharge_order(order_no)`、`api_key(key_hash)`。
- 不软删除业务流水;用户/账号可标记 `disabled` 而非物理删;API Key 用 `revoked` 状态而非物理删。
- 服务端生成字段:`api_key.key_hash`/`key_prefix`、`points_balance`、`balance_after`、各 `created_at`。
- `payment_user_id`、`payment_txn_no` 为对账预留,字段先建。
- `recharge_order.exchange_rate` 与 `points_granted` 在下单时写入,状态为 `pending` 时也必须有值;支付回调金额必须与订单金额一致,入账时不得按新的汇率重算。
- `call_record.status` 状态机为 `pending -> success / failed`。上游失败退点后仍保持 `failed`,退款流水通过 `points_ledger(change_type=refund, ref_call_id=call_record.id)` 关联,不单独增加 `refunded` 状态,避免调用结果与账务动作混在一个字段里。
## 四、计费时序(核心,务必照此实现)
### 4.1 调用扣点(同步生成)
```text
1. API Key 鉴权(对请求 Key 做哈希比对)→ 定位 ApiKey → 所属 User(user/key 任一 disabled 直接拒绝)
2. 别名解析:alias → ModelAlias → 具体 AiModel;按 capabilities 校验请求合法(非法返回 model_not_allowed)
3. 查 PricingRule(operation_type, alias, resolution) → 本次点数 N(缺规则返回 no_pricing_rule)
4. 事务 A:select_for_update 锁该用户的 user_wallet 行
若 points_balance < N → 回滚,返回「点数不足,请先充值」(不调上游、不扣点)
否则 points_balance -= N,写一条 points_ledger(consume, -N, user),建 call_record(status=pending, user, api_key, alias, model_name)
提交事务 A(点数已预扣)
5. 调 AI 上游生成(同步等待,经 Provider 适配器)
成功 → 更新 call_record(success, result_ref, latency)
失败 → 事务 B:退点 points_balance += N,写 points_ledger(refund, +N),
更新 call_record(failed, error)
6. 返回结果或错误
```
要点:
- 扣点锁 `user_wallet` 行(`select_for_update()`)或 `F('points_balance') - N` 原子更新 + DB 约束 `points_balance >= 0`(MySQL 的 CHECK 需 ≥8.0.16),**杜绝并发超扣 / 扣成负数**;钱包与 auth `user` 表分离,扣点不与登录/资料更新抢锁。
- **先扣后调、失败必退**:保证不会“调用成功但没扣到”或“失败还扣钱”。
- 余额不足在调上游**之前**拦截。
### 4.2 充值入账(自助扫码 + 支付回调)
```text
0. 用户在用户端发起充值(选金额与支付方式)→ 后端读取当前 ExchangeRate,
计算 points_granted = floor(amount × rate),建 recharge_order(status=pending, user, amount, pay_method, exchange_rate, points_granted)
→ 调支付系统下单接口取支付二维码 → 前端展示二维码 + 轮询订单状态
(二维码有临时有效期,过期可重新下单;订单绑定发起的 user,防充错账户;页面可展示预计到账点数)
1. 用户扫码付款成功 → 支付系统服务端回调(含 order_no、amount、txn_no、签名)
2. 验签失败 → 拒绝,不入账(记录可疑回调)
3. 事务:按 order_no 查 recharge_order
若该订单已是 paid → 幂等返回成功,不重复加点
否则:校验回调金额与订单 amount_money 一致
使用订单已锁定的 points_granted 入账
锁 user_wallet 行,points_balance += points_granted(计费层方法)
写 points_ledger(recharge, +points_granted, ref_order)
order.status = paid,记 txn_no、paid_at
4. 返回支付系统约定的成功应答
```
要点:
- **幂等**:同一 `order_no` 重复回调只入账一次。
- **验签**:回调必须验签,防伪造刷点;微信 V3 用 SDK 验签解密,支付宝用 SDK `verify`。
- 汇率下单时锁定写入订单,后续改汇率不影响 `pending` 或已支付订单;回调不重新读取当前汇率。
- 订单状态机 `pending / paid / failed / expired`;二维码有有效期,过期重新下单。发起充值的 `user` 必须绑定到订单,回调按 `order_no` 定位订单→其 `user` 入账,防充错账户。
- **通道协议**(对齐同支付系统 PHP 实现):微信 V3 `native`(库 `wechatpayv3`,金额**分**,回调 SDK 验签解密、`TRANSACTION.SUCCESS`、应答 `{code:SUCCESS}`);支付宝当面付 `trade.precreate`(库 `python-alipay-sdk`,金额**元**,回调 `verify` 验签、`TRADE_SUCCESS/FINISHED`、应答纯文本 `success`)。两个回调端点必须 `@csrf_exempt`。
- **兜底**:回调可能丢失,须提供按 `order_no` 主动查单补入账;前端每 ~1s 轮询订单状态仅作 UX。
- 仅商户密钥/证书为真实值待提供(微信 appid/mchid/apiv3_key/证书、支付宝 appid/公私钥/RSA2、notify_url 域名),协议已明确,可先 mock。
## 五、关键技术难点
| 难点 | 说明 | 应对 |
| --- | --- | --- |
| 并发扣点超扣 | 多请求同时扣同一账号 | 行锁 / 原子更新 + DB 约束 `>=0`(MySQL 的 CHECK 需 ≥8.0.16 才生效,不能只靠它;主防线是 `select_for_update` 或 `UPDATE ... WHERE balance>=N`),并发测试覆盖 |
| 充值重复入账 | 回调可能重发 | `order_no` 唯一 + 状态机幂等 |
| 回调伪造 | 不验签会被刷点 | 强制验签,验签失败不入账并留痕 |
| 图片生成耗时长 | 同步等待几十秒到分钟级;后台可换上慢模型拖垮 worker | 每模型 timeout 必须生效并设上限;worker 数/网关超时按最慢模型预留;V2 异步化 |
| 上游错误分类 | 区分参数错误与上游故障 | AI 层抛分类异常;上游故障退点 |
| 凭证安全 | 上游 api_key、支付密钥 | 应用层加密存储,admin 脱敏不回显;其余密钥走环境变量,不入代码与样例 |
| 抽象泄漏 | 各供应商入参出参不一致(resolution/aspect/vision/返回形态) | Provider 适配器 + capabilities 声明 + `parameters` 透传,核心字段稳定不取交集 |
| 供应商耦合 | 调用方绑具体 SKU 则换模型要通知所有接入方 | 对外绑能力别名,后台改别名→模型映射即可换供应商 |
| 配置热生效 | 后台改模型/密钥后运行时仍用旧值 | 不在进程内长缓存;每次查库或保存时失效缓存 |
| 配置变更审计 | 改密钥/模型/别名映射无痕 | 后台变更留痕(谁、何时、改了什么),与「账目对得上」原则一致 |
| API Key 泄露 | 明文存库一旦泄露全泄 | Key **哈希存储**(sha256),明文只在创建时显示一次,库存 `key_hash`+`key_prefix`,鉴权做哈希比对 |
| 注册滥用 | 自助注册被批量刷 | 邮箱验证 + 生成接口限流(DRF throttle);**注册不送点数**,无免费额度可薅 |
| Web/API 抢 worker | 图片同步长请求占满 worker、拖慢用户端页面 | 单体下按路径把 `/api/generate/*` 与用户端页面**分流到不同 gunicorn/worker 池**(见 5.1) |
| 充错账户 | 扫码订单未绑定发起用户 | 订单创建即绑定 `user`;回调按 `order_no` 定位订单→其 user 入账 |
高风险功能(计费扣点、充值回调)应先做最小原型并写并发/幂等测试,再接入完整流程。
### 5.1 同步方案可用性结论(桌面端不改、全同步接入)
桌面端 cmbot 可以**不改交互骨架**,继续用同步方式调 cmhub、cmhub 再同步转中转站,正常使用——决定成败的不是同步/异步本身,而是下面两件事有没有配对好;配好就稳,配不好会「小量正常、上量假死」:
1. **超时链路层层放大且对齐**(外层 ≥ 内层):桌面端读超时 ≥ Gunicorn `--timeout` / 网关 `proxy_read_timeout` ≥ cmhub→中转站读超时。三个默认值是雷:`AiModel.timeout_seconds`(`ai_models.json` 现为 `0`=无限等,迁入须改有限值)、Gunicorn 默认 `30s`(会直接杀掉跑图片的 worker)、Nginx 默认 `60s`。图片链路建议统一放到 `300s` 量级。
2. **worker/线程数按峰值总并发预留**:同步下每个在飞请求占住一个 worker 整个生成周期(几十秒~分钟)。用 Gunicorn `gthread`(`--threads`)或多 sync worker,数量 ≥ 预期峰值总并发 + 余量,避免慢图片把快的生文接口和后台挤死。
适用边界:**单接入方、小并发批量**(桌面端 `concurrency` 1~4)完全够用,点数一致性也更简单。当出现「worker 被长连接占满拖慢快接口/后台」「接入方总并发明显上涨」「需要关窗重连/任务持久化」任一信号时,才转 V2 异步(队列),在此之前保持同步;但适配器接口与 `call_record` 的 `pending/success/failed` 三态需为异步预留口子(见 4.1、七、八)。
## 六、推荐开发顺序
1. Django + DRF 骨架可运行,django-admin 可登录(Phase 0)。
2. 移植并跑通一次 AI 调用(标题 / 图片)原型(Phase 1)。
3. 计费:点数扣减(并发安全)+ 计费规则 + 调用记录(Phase 2)。
4. 对外 API 鉴权 + 余额查询 + 充值回调(验签、幂等)入账(Phase 3)。
5. 运营后台完善、完整验收、部署(Phase 4)。
## 七、项目结构建议
```text
cmhub/
├── docs/
├── manage.py
├── config/ # Django 工程:settings / urls / wsgi
├── apps/
│ ├── users/ # 注册用户 User、UserWallet 钱包、ApiKey(模型 + admin)
│ ├── portal/ # 用户端页面(Django 模板 SSR):注册/登录/个人中心/充值/API Key 管理
│ ├── billing/ # 计费规则、汇率、充值订单、点数流水、扣点/入账逻辑(点数唯一写入方)
│ ├── ai/ # 别名解析 + Provider 适配器层
│ │ └── providers/ # 各供应商适配器(chat/gemini/images_edits…),移植自 cmbot
│ └── api/ # DRF 对外接口:生成、余额、支付回调、扫码下单
├── tests/ # 计费/回调/接口测试(或各 app 内 tests.py)
├── requirements.txt # 或 pyproject.toml(uv)
└── README.md
```
- `apps/ai/`:别名解析与适配器注册表;`apps/ai/providers/` 放移植自 `cmbot/src/services/` 的供应商实现,仅依赖 `requests`。
- `apps/billing/`:点数与资金的唯一权威逻辑,其他层只能调它的方法。
- 路径为模板建议,骨架落地后按真实结构同步 `current-state.md`。
## 八、架构纪律
- 业务事实和 schema 变化必须同步本文与 `api.md`。
- 点数余额只能经 `apps/billing` 修改,其他层不得直接写 `points_balance`。
- **对外契约只暴露能力别名,不暴露具体模型名**;新增供应商 = 加一个适配器,不改对外接口。
- 计费按别名定价;后台换别名→模型映射不影响对外契约与计费。
- 供应商密钥加密存储、不回显;模型/别名/密钥变更留痕。
- 不在代码里发明文档没有的接口、字段、状态码、错误码。
- 充值入账必须幂等,扣点必须并发安全,上游失败必须退点——这三条是硬约束。
- 不把配置数据、点数余额、流水混进同一个模型。
- 点数余额存 `UserWallet`(与 auth `User` 表分离);扣点锁 wallet 行,不与登录/资料更新抢锁。
- **API Key 哈希存储**(sha256),明文只在创建时返回一次;库内存 `key_hash`+`key_prefix`,任何页面不回显明文。
- **对外 API 只接受 API Key 认证,不挂 SessionAuthentication**,防止浏览器带 cookie 绕过 Key 与计费归属。
- 用户端页面用 session + CSRF;自助注册须邮箱验证,生成接口须限流。
+97
View File
@@ -0,0 +1,97 @@
# 编码规则(Coding Rules)
> 每次写代码前先读完本文件。这是让 AI 不跑偏、代码质量稳定的硬约束。
> 与技术细节冲突时,以 [技术栈](03-tech-stack.md) / [架构设计](04-architecture.md) 的事实为准;与“该不该做”冲突时,以 [需求](02-requirements.md) 为准。
## 0. 黄金法则
1. **不臆造**:数据字段、文件、接口、依赖、支付回调字段,不确定就查证或询问。
2. **守范围**:只做当前任务要求的事,不顺手加后续功能(如异步队列、报表、注册赠点)。
3. **照架构**:使用既定技术栈(Django+DRF+admin)和模块边界,不擅自引入新框架。
4. **小步改**:一次只解决一个问题,不夹带无关重构。
5. **可验证**:改完必须能迁移、能测试、对得上验收标准。
## 1. 动手前
- 按链路确认:`vision` → `requirements` → `tech-stack` → `architecture` → `api` → `tasks`。
- 找到本任务对应的验收标准,写之前就知道“怎么算做对”。
- 复用优先:AI 上游调用复用 `cmbot/src/services/ai_text_service.py`、`ai_image_service.py`,不另写一套。
- 如果需求含糊,或改动会偏离原则/架构,先问。
## 2. 事实来源纪律
- 只相信文档指定的权威源:本 `docs/`、`cmbot` 的 AI 服务代码与 `ai_models.json`、支付系统官方文档。
- 不从备份、草稿、旧导出文件里推断当前事实。
- 不虚构字段、接口、状态码、配置项、支付回调签名规则。
- 数据结构变化必须同步更新 `04-architecture.md`、`api.md` 和相关任务。
## 3. 范围纪律
- MVP 只做 `02-requirements.md` 中列为 P0 的功能。
- V2 / V3 功能(异步队列、报表、注册赠点、自动退款对账)只记录,不实现。(自助注册/扫码充值/API Key 管理已纳入 MVP)
- 需求明确排除的非目标不得实现(自建收银台、多币种、分账等)。
- 不为“将来可能用到”提前抽象。
## 4. 架构纪律
- 技术栈以 `03-tech-stack.md` 为准;新增依赖前先说明理由,未经确认不引入重量级依赖(Celery、Redis 等)。
- 模块职责以 `04-architecture.md` 为准,不跨层偷写逻辑。
- **点数余额只能经 `apps/billing` 修改**:余额存 `UserWallet`(与 auth `User` 分离),扣点锁 wallet 行;API 层/Admin/用户端都不得直接写 `points_balance`。
- **API Key 哈希存储**(sha256),明文只在生成时返回一次;不存明文、任何页面不回显。
- **对外 API 只挂 API Key 认证,不挂 SessionAuthentication**;用户端页面用 session + CSRF,自助注册须邮箱验证、生成接口须限流。
- **对外接口只接受能力别名,不接受具体模型名**;新增供应商必须走 `apps/ai/providers/` 的适配器,不在 API 层写供应商分支逻辑。
- 计费按别名定价,不按具体模型 SKU;不要把模型名写进对外契约或调用方文档。
- 供应商 `api_key` 必须加密存储、使用时解密,不落明文、不在 admin 回显。
- 接口形状以 `api.md` 为准,运营后台路由以 `routes.md` 为准。
## 5. 代码规范
- 标识符使用英文;UI 文案、注释、文档使用中文,保持一致。
- 错误必须处理,不吞错;上游/支付异常要分类并向上抛清晰错误。
- 注释解释“为什么”,不复述“做了什么”。
- 遵守项目格式化工具,不手工制造风格分裂。
## 6. 测试与验证
完成前至少检查:
- [ ] 迁移成功(`makemigrations` + `migrate` 无误)。
- [ ] 相关测试通过(`python manage.py test`)。
- [ ] 对得上需求验收标准。
- [ ] 没有夹带无关改动。
- [ ] 涉及文档事实变化时,文档已同步。
- [ ] 已在 `../progress.md` 记录跑过的命令和结果作为证据,不靠“代码已写”判定完成。
- [ ] 回复里如实说明跑了什么命令、结果如何。
真实命令:
```bash
python manage.py makemigrations && python manage.py migrate
python manage.py test
python manage.py runserver
```
## 7. 绝不
- 绝不把上游 api_key、支付密钥、SECRET_KEY、数据库密码等真实值写进代码或文档样例。
- 绝不为了让测试通过而删除断言、降低验收标准。
- 绝不擅自删除用户已有文件或重置工作区(尤其 `cmbot` 目录)。
- 绝不在没说明的情况下改公共接口、迁移数据结构或升级依赖。
## 8. 安全与合规(资金/点数专项,最高优先级)
涉及点数、金额、充值、扣费、退款时,必须全部满足:
- **并发安全扣点**:扣减点数必须用数据库事务 + `select_for_update()` 锁 `UserWallet` 行或 `F()` 原子更新,并配 DB 约束 `points_balance >= 0`(MySQL 的 CHECK 需 ≥8.0.16 才生效,不能只靠它),杜绝并发超扣或扣成负数。
- **充值幂等**:支付回调入账必须以 `order_no` 幂等,重复回调只入账一次。
- **回调验签**:支付回调必须验签,验签失败一律不入账并留痕;微信 V3 用 SDK 验签解密、支付宝用 SDK verify;缺商户密钥时按 mock 契约实现并标注待替换。
- **回调防护与兜底**:支付回调端点必须 `@csrf_exempt`(网关调用无 CSRF token);回调可能丢失,须提供按 `order_no` 主动查单兜底,避免已付款却一直 pending。
- **失败必退**:调用上游失败时,已预扣的点数必须冲正退回。
- **全程留痕**:每笔点数变动写 `points_ledger`,每次调用写 `call_record`,运营手工调整必须填原因。
- **敏感信息**:密钥/凭证只用环境变量或后台配置,不写真实值进代码与文档。
- 资金相关改动必须有对应测试(并发扣点、重复回调、上游失败退点至少各一条)。
- 不绕过支付系统的鉴权、验签或风控。
## 9. 拿不准就问
问题要具体:说明卡在哪里、有哪些选项、倾向哪个选项及原因。尤其支付接口字段/签名、汇率与点数单价,缺信息时先问或先用标注清楚的 mock,不要凭空假设。
+88
View File
@@ -0,0 +1,88 @@
# 任务看板(Tasks)
> 把 MVP 拆成小步、可独立交付的任务,让 AI 一步一步开发,避免一次生成整个项目。
## 使用规则
1. **一次只做一个任务**:每轮只领取一个状态为 `TODO`、且依赖均已 `DONE` 的任务,取最靠前的那个。
2. **做完即停**:完成该任务、自测通过、把状态改成 `DONE` 后,停下来汇报。
3. **不跳步**:依赖未完成的任务不能开工。
4. **完成定义**:以 [编码规则](05-coding-rules.md) 的验证清单为准。
5. **passing 需证据**:标记 `DONE` 前,必须在 [`../progress.md`](../progress.md) 记录跑过的验证命令和结果。
6. **动手前**先读 `00-ai-start-here.md`、`05-coding-rules.md` 和 `current-state.md`。
7. **完成后**同步任务状态到本文,执行记录追加到 `../progress.md`,覆盖更新 `current-state.md`,过一遍 `clean-state-checklist.md`。
## 状态图例
`TODO` 待开始 · `DOING` 进行中(同一时间最多 1 个)· `DONE` 已完成并验收 · `BLOCKED` 受阻(注明原因)
---
## Phase 0 · 地基
| ID | 任务 | 依赖 | 验收要点 | 状态 |
| --- | --- | --- | --- | --- |
| T-001 | 初始化 Django + DRF 项目骨架 | - | `manage.py` 可运行;`runserver` 起得来;用真实命令替换 `init.sh`/`init.ps1` 与 `00-ai-start-here.md`/`03-tech-stack.md`/`current-state.md` 的占位命令 | TODO |
| T-002 | 建立 apps 目录、自定义 User 与配置 | T-001 | 按 `04-architecture.md` 建 `apps/users|portal|billing|ai|api`;**首次迁移前定义自定义 `User` 模型(设 `AUTH_USER_MODEL`)**;settings 用环境变量读密钥、配 MySQL(utf8mb4),无明文密钥 | TODO |
| T-003 | 接通 django-admin 与最小测试 | T-001 | `createsuperuser` 后能登录 `/admin/`;`manage.py test` 可运行(至少 1 条占位测试通过) | TODO |
## Phase 1 · 最高风险验证(AI 调用 + 可插拔供应商)
| ID | 任务 | 依赖 | 验收要点 | 状态 |
| --- | --- | --- | --- | --- |
| T-101 | Provider 适配器层 + 移植 cmbot 调用 | T-002 | 定义 `Provider` 接口(`capabilities`/`generate_text`/`generate_image`),按 `api_type` 注册;把 `ai_text_service.py`/`ai_image_service.py` 搬进 `apps/ai/providers/` 并去除桌面依赖;**注意 3 模型机制不同(chat / chat 多模态返图 nano-banana2 / images_edits 改图 gpt-image-2,见 `04` 3.1),两图片模型非标准生成需分别解析、首次对接抓真实响应**;mock 上游单测验证解析与适配器选取 | TODO |
| T-102 | AiModel + ModelAlias 模型 + 别名解析 | T-101 | AiModel 含 `capabilities`、`api_key` **加密存储**(admin 脱敏不回显);ModelAlias 映射别名→模型;`resolve_alias()` 能解析并按能力校验;配置迁移自 `ai_models.json`;后台改配置运行时热生效 | TODO |
| T-103 | 配置变更审计 | T-102 | AiModel/ModelAlias/密钥的后台变更留痕(谁、何时、改了什么);可在 admin 查看 | TODO |
| T-104 | 跑通一次真实/录制的标题或图片生成 | T-102 | 用别名 + 最小输入跑通一次生成,结论写入 `progress.md`(含耗时,验证图片同步可行性与超时配置) | TODO |
## Phase 2 · 计费核心
| ID | 任务 | 依赖 | 验收要点 | 状态 |
| --- | --- | --- | --- | --- |
| T-201 | User / UserWallet / ApiKey / PointsLedger / CallRecord 模型 | T-002 | 表结构符合 `04-architecture.md`;`UserWallet.points_balance>=0` 约束;ApiKey **哈希存储**(key_hash+key_prefix,明文只创建时返回);CallRecord 含 `user`/`api_key`/`alias`/`model_used`;admin 注册 | TODO |
| T-202 | PricingRule / ExchangeRate 模型 + 计费计算 | T-201, T-102 | **按「操作 + 能力别名(+ 可选分辨率)」定价**;换底层模型不影响计费;缺规则返回 `no_pricing_rule` | TODO |
| T-203 | 并发安全扣点 / 退点(billing 层) | T-201 | 锁 `UserWallet` 行或 F() 原子扣减;并发测试不超扣、不为负;失败退点写流水;含测试 | TODO |
## Phase 3 · 对外 API 与充值
| ID | 任务 | 依赖 | 验收要点 | 状态 |
| --- | --- | --- | --- | --- |
| T-301 | API Key 鉴权(DRF Authentication) | T-201 | 对请求 Key 哈希比对定位 ApiKey→User;**只挂 Key 认证、不挂 Session**;无效/缺失 401;用户或 Key 禁用 403 | TODO |
| T-302 | 生成标题 / 图片接口 | T-104, T-203, T-301 | 按 `api.md` 实现;请求传**能力别名**+ `parameters` 透传;编排「别名解析+能力校验→预扣→调上游→成功确认/失败退点→写记录」;点数不足返回 402;含测试 | TODO |
| T-303 | 余额查询接口 | T-301 | 返回余额等于流水累加;含测试 | TODO |
| T-304 | 充值回调(微信/支付宝验签 + 幂等入账) | T-202 | 两端点 `@csrf_exempt`;微信 SDK 验签解密、支付宝 SDK verify;验签失败不入账;同一 order_no 重复回调只入账一次;校验回调金额与订单金额一致;使用订单创建时锁定的 `points_granted` 锁 wallet 入账写流水;补主动查单兜底;含幂等测试 | TODO |
| T-305 | 扫码充值下单 + 轮询(create/status) | T-304 | 支持 weixin(native,金额分)/alipay(precreate,金额元);建 pending 订单绑定 user,并在下单时锁定汇率/预计点数→取 code_url/qr_code→前端渲染 + 轮询 status;缺商户密钥时 mock | TODO |
## Phase 4 · 用户端(Django 模板 SSR)
| ID | 任务 | 依赖 | 验收要点 | 状态 |
| --- | --- | --- | --- | --- |
| T-501 | 注册 / 登录(allauth) | T-201 | 自助注册(邮箱验证)、登录、登出;注册后钱包点数为 0(不送点数);session + CSRF | TODO |
| T-502 | API Key 自助管理页 | T-501, T-301 | 登录用户生成/删除 Key;明文只显示一次、库存哈希;列表只显示 prefix;删除后该 Key 调用 401 | TODO |
| T-503 | 个人中心 / 记录页 | T-501, T-203 | 剩余点数、充值总额、充值记录、消费(调用)记录;数据与流水一致;仅见本人 | TODO |
| T-504 | 充值页(扫码 + 轮询到账) | T-501, T-305 | 发起充值→展示二维码→轮询订单状态→到账后余额刷新;到账以回调为权威 | TODO |
## Phase 5 · 后台与发布
| ID | 任务 | 依赖 | 验收要点 | 状态 |
| --- | --- | --- | --- | --- |
| T-401 | 运营后台完善 | T-302, T-304 | admin 可管理用户/钱包/ApiKey(脱敏)、配规则/汇率、检索充值订单/流水/调用记录;手工调点带原因并经计费层写流水 | TODO |
| T-402 | 完整验收 MVP | T-401, T-504 | `02-requirements.md` 的 P0 验收全部通过(含用户端注册/充值/API Key/记录) | TODO |
| T-403 | 部署 / 运行文档 | T-402 | 新环境可按文档运行;明确图片接口超时配置 | TODO |
## 里程碑
- M1:Django + admin 骨架可运行、自定义 User 就位(T-003)。
- M2:服务端跑通一次 AI 生成(T-104)。
- M3:计费 + 对外接口 + 充值闭环(T-305)。
- M4:用户端(注册/充值/API Key/记录)可用(T-504)。
- M5:运营后台 + MVP 验收 + 可部署(T-403)。
## 待办池(Backlog)
- 异步生成(任务队列 + 轮询/回调)。
- 按账号授权可用别名(`account_alias_permission`),防止调用未授权/昂贵模型。
- 别名按比例分流到多个模型(灰度 / A/B / 故障转移);供应商 A 故障自动切 B。
- 注册赠点 / 试用额度(需邮箱/图形验证码 + 限流防薅羊毛)。
- 用量统计报表。
- 退款对账自动化、API Key 轮换、限流细化。
+46
View File
@@ -0,0 +1,46 @@
# 项目文档导航
> `cmhub` 项目文档总览。agent 真正开始编程时,以 [`00-ai-start-here.md`](00-ai-start-here.md) 为工作入口。
## 一句话定位
`cmhub` 是一个**自助用户端 + 计费型 AI 能力网关 + 运营后台**三合一服务:终端用户自助注册、扫码充值、管理 API Key;用户的程序用 API Key 调用把 `cmbot` 的「生成标题」「生成图片」能力封装成的 HTTP API,按点数计费;运营用 django-admin 管理用户、点数与记录。
## 文档导航
- [`../AGENTS.md`](../AGENTS.md):Codex / 通用 AI coding agent 的仓库级入口。
- [`../CLAUDE.md`](../CLAUDE.md):Claude Code 的薄入口,具体规则以 `AGENTS.md` 为准。
- [`../progress.md`](../progress.md):执行历史流水,只追加记录任务执行、验证、阻塞和决策。
- [一页汇报版 / 电梯陈述](project-onepager.md):最短版本,用于口头汇报或投影。
- [项目介绍(给管理层)](project-brief.md):面向决策与汇报的业务视角概览,非技术执行文档。
- [AI 开发入口](00-ai-start-here.md):每次开始工作的入口、阅读顺序和任务领取规则。
- [项目愿景](01-vision.md):为什么做、为谁做、产品原则、非目标。
- [需求](02-requirements.md):要什么、用户故事、验收标准(含资金风险)。
- [技术栈](03-tech-stack.md):Django + DRF + django-admin 及运行命令。
- [架构设计](04-architecture.md):系统结构、计费时序、数据模型、关键风险和开发顺序。
- [编码规则](05-coding-rules.md):硬性约束,含资金/点数安全专项(第 8 节)。
- [任务看板](06-tasks.md):按依赖拆分的小任务,agent 每轮只做一个。
- [API 合约](api.md):对外接口、支付回调、AI 调用模块合约、错误码。
- [路由与页面结构](routes.md):API 路由与 django-admin 后台职责。
- [环境变量与配置](env.md):Django、数据库、AI 密钥加密、支付、对象存储等配置项。
- [当前实现状态](current-state.md):可覆盖的当前快照、可运行命令、下一步任务。
- [已有项目接入清单](adoption-checklist.md):把模板补进已有代码库时的迁移步骤。
- [收尾检查清单](clean-state-checklist.md):会话结束前逐项检查。
- [方法对照表](method-map.md):失败模式 → 首要修复 → 工件。
- [评审评分表](evaluator-rubric.md):单次会话输出的结构化评审。
- [质量文档](quality-document.md):代码库长期健康度追踪。
- [`../init.sh`](../init.sh) / [`../init.ps1`](../init.ps1):标准启动与验证入口(按操作系统二选一);换命令只改脚本顶部三个变量。
## 任务 / 进度 / 当前状态
- `06-tasks.md` 维护任务看板:任务 ID、依赖、验收要点和状态。
- `../progress.md` 维护执行进度:每轮做了什么、跑了什么验证、遇到什么阻塞、做了什么决策。
- `current-state.md` 维护当前快照:当前目录、可运行命令、已完成摘要和下一个可领取任务。
## 维护原则
- 需求变化先改文档,再改代码。
- 代码现实变化后同步 `current-state.md` 和 `06-tasks.md`,执行过程追加到 `../progress.md`。
- API、数据模型、计费时序、错误码一旦在文档中定稿,代码不得另起一套。
- 涉及点数/资金的改动,先读 `05-coding-rules.md` 第 8 节与 `04-architecture.md` 第四节。
- agent 开始新任务前,必须从 `00-ai-start-here.md` 进入。
+64
View File
@@ -0,0 +1,64 @@
# 已有项目接入清单
> 用于把本模板补进一个已经存在的项目。目标是先建立 agent 可读的事实来源,再继续开发新功能。
## 适用场景
- 项目已经有代码,但缺少清晰的 agent 入口、任务看板、当前状态和验证路径。
- 项目被多轮 AI 修改过,文档、代码和真实可运行状态已经不一致。
- 想从“靠聊天记录推进”切换到“靠仓库内工件推进”。
如果是全新空项目,优先按根目录 `README.md` 的“空项目接入”流程复制模板。
## 最小接入文件
先复制或建立这些文件:
| 文件 | 作用 |
| --- | --- |
| `AGENTS.md` | 仓库级 agent 入口和总规则 |
| `CLAUDE.md` | Claude Code 薄入口,指向 `AGENTS.md` |
| `docs/00-ai-start-here.md` | 每轮开工流程 |
| `docs/05-coding-rules.md` | 编码纪律和验证底线 |
| `docs/06-tasks.md` | 任务看板 |
| `docs/current-state.md` | 当前实现状态快照 |
| `progress.md` | 只追加的执行流水 |
| `init.sh` 或 `init.ps1` | 标准启动与验证入口,按操作系统二选一 |
推荐随后补齐:`docs/01-vision.md`、`docs/02-requirements.md`、`docs/03-tech-stack.md`、`docs/04-architecture.md`、`docs/api.md`、`docs/routes.md`、`docs/clean-state-checklist.md`。
## 接入步骤
1. 确认仓库根目录,读取已有 `README`、运行脚本、测试配置和主要入口文件。
2. 复制最小接入文件,并把所有 `【占位符】` 替换成当前项目事实。
3. 在 `docs/current-state.md` 写清真实目录、真实启动命令、真实验证命令、当前 blocker。
4. 在 `docs/03-tech-stack.md` 固定已经实际使用的技术栈,不确定项标为“待定”,不要让 agent 自行选择。
5. 在 `docs/04-architecture.md` 记录当前代码的真实模块边界;不清楚的地方标为“待确认”。
6. 在 `docs/06-tasks.md` 只放下一阶段能小步交付的任务,不要把历史愿望清单全部搬进去。
7. 配置 `init.sh` 或 `init.ps1` 顶部三个命令,让它能安装依赖、运行基础验证、打印启动命令。
8. 运行标准验证;如果失败,第一轮任务应先修基线,不做新功能。
9. 把接入过程、验证结果和遗留 blocker 追加到 `progress.md`。
## 第一轮 agent 任务建议
已有项目接入后的第一轮,不建议直接做新功能。推荐任务是:
| ID | 任务 | 验收要点 |
| --- | --- | --- |
| T-000 | 建立当前状态基线 | `current-state.md` 写清真实状态;`init` 脚本已配置;基础验证结果已记录到 `progress.md` |
| T-001 | 修复启动 / 验证基线 | 标准启动路径和标准验证路径可运行;失败原因已消除或记录为 blocker |
| T-002 | 对齐任务看板 | `06-tasks.md` 只保留可执行的小任务;第一个 TODO 依赖清楚、验收可观察 |
## 代码现实与文档冲突时
- 以当前可运行代码和真实验证结果为事实起点。
- 文档描述旧功能但代码不存在时,先把差异记录到 `current-state.md`,不要直接补实现。
- 代码已有行为但文档没写时,先补 `02-requirements.md`、`04-architecture.md` 或 `api.md`,再继续修改代码。
- 命令不可运行时,不要标记任务完成;在 `progress.md` 记录失败命令和错误摘要。
## 不建议做的事
- 不要一次性把所有模板都填满;先让入口、当前状态、任务和验证路径可用。
- 不要把聊天记录当事实来源。
- 不要为了让验证通过而降低测试或验收标准。
- 不要在接入 harness 的同一轮顺手重构业务代码,除非是修复启动或验证基线所必需。
+219
View File
@@ -0,0 +1,219 @@
# API / 模块合约
> 本文定义 `cmhub` 对外 HTTP 接口、支付回调合约与 AI 调用模块合约的目标形状。
> 实现前可细化,但不要在代码里另起一套不兼容接口。schema 变化须同步 `04-architecture.md`。
## 通用约定
- 传输:JSON over HTTPS。
- 对外鉴权:请求头 `Authorization: Bearer <API_KEY>`(用户在用户端自助生成的 Key)。
- 后台鉴权:django-admin 用 Django Session 登录。
- 编码:UTF-8 JSON。时间:ISO 8601。
- 未携带有效 API Key 调用生成/余额接口:返回 `401`。
- 用户或 Key 被禁用/吊销(disabled/revoked):返回 `403`。
- **对外 API 只接受 API Key 认证,不接受 Web session**(浏览器带 cookie 也不能调 API,防绕过计费归属)。
- 图片生成为**同步**接口,可能耗时较长,调用方与网关需设置足够超时(≥ 300s)。
通用错误响应:
```json
{
"error": {
"code": "insufficient_points",
"message": "点数不足,请先充值"
}
}
```
错误码枚举(MVP):
| code | 含义 | HTTP |
| --- | --- | --- |
| `unauthorized` | 缺失/无效 API Key | 401 |
| `account_disabled` | 账号被禁用 | 403 |
| `bad_request` | 参数错误 | 400 |
| `insufficient_points` | 点数不足,请先充值 | 402 |
| `no_pricing_rule` | 未配置对应计费规则 | 400 |
| `model_not_allowed` | 该模型不支持此操作(如图片模型生成标题) | 400 |
| `upstream_error` | 上游 AI 失败(已退点) | 502 |
| `signature_invalid` | 支付回调验签失败 | 400 |
| `amount_mismatch` | 支付回调金额与本地订单金额不一致 | 400 |
## 对外接口
> **重要约定**:`model` 字段传的是**能力别名**(如 `title-standard` / `image-hd`),不是具体供应商模型名。后台把别名映射到当前的具体模型,换供应商时调用方零改动。供应商特有参数放 `parameters` 透传对象,核心字段保持稳定。
### `POST /api/v1/generate/title`
生成标题。请求:
```json
{
"prompt": "为这件女装生成5个吸睛标题",
"model": "title-standard", // 能力别名,非具体模型名;可选,缺省用该操作的默认别名
"image_url": "https://...", // 可选,看图生成时传商品图(或 image_base64)
"resolution": "1K", // 可选,影响计费规则匹配
"parameters": {} // 可选,供应商特有参数透传,未知字段忽略
}
```
成功响应:
```json
{
"titles": ["标题一", "标题二", "标题三"],
"alias": "title-standard",
"model_used": "gpt-5.5", // 实际服务的具体模型,便于排障;不构成稳定契约
"points_cost": 2,
"points_balance": 98,
"call_id": 12345
}
```
要点:别名必须映射到声明 `text` 能力的模型;映射到图片模型时返回 `model_not_allowed`;别名无计费规则返回 `no_pricing_rule`。
### `POST /api/v1/generate/image`
生成图片(同步等待)。请求:
```json
{
"prompt": "把这件衣服换成模特上身的穿搭图",
"model": "image-hd", // 能力别名,非具体模型名
"image_base64": "data:image/png;base64,...", // 或 image_url;改图类模型(如映射到 gpt-image-2)原图必传
"resolution": "1K",
"aspect_ratio": "1:1",
"parameters": {} // 可选,供应商特有参数透传
}
```
成功响应:
```json
{
"image_url": "https://.../result.jpg", // 默认返回 URL(结果存对象存储);大 base64 不进同步响应体
"alias": "image-hd",
"model_used": "nano_banana_2",
"points_cost": 10,
"points_balance": 88,
"call_id": 12346
}
```
要点:上游失败返回 `upstream_error` 且**不扣点**(已预扣则退回)。结果默认存对象存储返回 `image_url`,避免同步响应体过大。
### `GET /api/v1/balance`
查询当前账号点数余额。成功响应:
```json
{
"user": "demo-client",
"points_balance": 88
}
```
## 支付充值(自助扫码:微信 V3 native + 支付宝当面付)
> 协议对齐同支付系统的既有 PHP 实现(微信库 `wechatpayv3` / 支付宝库 `python-alipay-sdk`)。二维码是平台不透明票据、**不含业务数据**,靠 `out_trade_no`(=本地 `order_no`) 在回调关联,付款人由平台识别。**协议已明确,仅商户密钥/证书为真实值待提供。**
> 金额单位陷阱:**微信传「分」(元×100 取整),支付宝传「元」(两位小数字符串)**。
### 轮询 / 主动查单
前端展示二维码后每 ~1s 轮询 `GET /api/v1/recharge/status?order_no=...`(session)返回订单 `status`;到账以异步回调为权威。回调可能丢失,服务端应提供**主动查单**兜底(按 `order_no` 向平台查单后补入账)。
### `POST /api/v1/recharge/callback/wechat`
微信 V3 异步回调(支付网关服务端调,**必须 `@csrf_exempt`**)。处理合约:
1. SDK `callback(headers, body)` **验签 + 解密**;失败 → `signature_invalid`,不入账、记可疑回调。
2. `event_type == TRANSACTION.SUCCESS` 且 `resource.trade_state == SUCCESS`:按 `out_trade_no` 定位订单。
3. 幂等:`select_for_update` 锁订单,`status != pending` 直接返回成功、不重复加点。
4. 入账:校验回调金额与本地订单金额一致;使用订单创建时锁定的 `exchange_rate` / `points_granted`,锁 `user_wallet` 加点、写 `points_ledger(recharge)`、订单置 `paid`(记 `transaction_id`、`success_time`)。
5. 返回 `{"code":"SUCCESS","message":"成功"}`(微信要求 HTTP 200)。
### `POST /api/v1/recharge/callback/alipay`
支付宝当面付异步回调(**必须 `@csrf_exempt`**;原 PHP 未实现,本项目须补全)。处理合约:
1. SDK `verify(data, sign)` 验签;失败 → 不入账、记可疑回调。
2. `trade_status ∈ {TRADE_SUCCESS, TRADE_FINISHED}`:按 `out_trade_no` 定位订单,同上幂等 + 入账(记 `trade_no`、`gmt_payment`)。
3. 返回纯文本 `success`(支付宝要求)。
> 订单状态机 `pending → paid`(成功)/ `failed` / `expired`。**待提供的仅是商户配置真实值**:微信 `appid/mchid/apiv3_key/cert_serial/private_key(证书)`、支付宝 `appid/app_private_key/alipay_public_key/RSA2`、各 `notify_url`(公网)。到位前用 mock 联调并标注待替换。
### `POST /api/v1/recharge/create`
由**已登录用户**在用户端发起充值(MVP 必做)。创建 `recharge_order(pending, 绑定 user)` 时锁定当前汇率并计算预计到账点数,再向支付平台下单,返回**支付二维码**。请求:
```json
{
"amount": "100.00",
"pay_method": "weixin" // weixin | alipay
}
```
成功响应:
```json
{
"order_no": "T202606291230001234",
"amount": "100.00",
"exchange_rate": "10.00",
"points_granted": 1000,
"pay_method": "weixin",
"code_url": "weixin://wxpay/bizpayurl?pr=abc123", // 微信 native 返回;支付宝为 qr_code(https://qr.alipay.com/...)
"expires_at": "2026-06-29T12:10:00Z" // 二维码有效期
}
```
- 下单字段:`out_trade_no`(=order_no)、`amount`(微信**分**=元×100 取整 / 支付宝**元**字符串)、`description`、`notify_url`。
- `exchange_rate` 与 `points_granted` 以订单创建时的配置为准;回调入账使用订单值,不因后台后续改汇率而变化。
- 微信走 `pay/transactions/native` 取 `code_url`;支付宝走 `trade.precreate` 取 `qr_code`;前端用 qrcode.js 渲染。
- 走 **Web session** 鉴权(用户端流程),不同于对外 API Key;订单绑定发起用户,防充错账户。
## AI 调用模块合约(`apps/ai`)
分两层:**别名解析** + **Provider 适配器**。API 层只传别名,由本模块解析到具体模型并选适配器。
### 别名解析
```python
# alias -> 具体 AiModel(含 capabilities、解密后的 key);找不到/能力不符抛业务异常
resolve_alias(operation_type: str, alias: str | None) -> ResolvedModel
```
### Provider 适配器接口
每个供应商一个适配器,按 `api_type` 注册;移植自 cmbot 的调用逻辑落到各适配器内:
```python
class Provider(Protocol):
def capabilities(self) -> set[str]: ... # {"text","image","vision"}
def generate_text(self, prompt: str, model: ResolvedModel,
image: bytes | None = None,
resolution: str = "1K",
parameters: dict | None = None) -> list[str]: ...
def generate_image(self, prompt: str, model: ResolvedModel, image: bytes,
resolution: str = "1K", aspect_ratio: str = "1:1",
parameters: dict | None = None) -> bytes | str: ...
```
要点:
- `ResolvedModel` 来自数据库 AiModel(形状同 `cmbot/config/ai_models.json`,外加 `capabilities`;`api_key` 在库中加密,使用时解密,不落明文)。
- 适配器按 `api_type`(`chat`/`gemini`/`images`/`images_edits`/`auto`)从注册表选取,新增供应商 = 新增一个适配器,不改对外接口。
- `parameters` 为供应商特有参数透传;适配器负责把统一入参翻译成各家上游格式。
- 配置热生效:每次调用读当前 AiModel/ModelAlias,后台改动及时反映(或带缓存失效)。
- 区分异常:别名/能力不匹配 → 业务错误(400 类,如 `model_not_allowed`);上游网络/超时/服务错误 → `upstream_error`(502,触发退点)。
- 不在本模块写点数逻辑,只负责解析、调上游与解析返回。
## 待实现时确认
- 支付商户真实配置:微信 `appid/mchid/apiv3_key/证书/notify_url`,支付宝 `appid/应用私钥/支付宝公钥/notify_url`。协议字段、验签方式与成功应答已按同系统实现明确;缺真实配置时用 mock。
- 汇率与各操作/别名(+ 分辨率档)的点数单价。
- `AiModel.api_key` 加密方案(应用层 Fernet / KMS)与后台脱敏展示方式;运行配置见 `env.md`。
- 图片结果存储:对象存储选型与 `image_url` 生成(默认走存储返回 URL)。
- **图片模型调用机制**:`gpt-image-2` 走 `images/edits`(改图,原图必传)、`nano-banana2` 走 `chat/completions` 多模态返图(需自定义解析),两者非标准 `images/generations`。图片返回结构(URL / base64 / 位置)**首次对接抓真实响应确认**后再定适配器解析。
- 别名命名规范、默认别名、是否按账号授权可用别名(`account_alias_permission`)。
- 参数校验细则、限流策略、API Key 轮换机制。
+16
View File
@@ -0,0 +1,16 @@
# 干净收尾检查清单
> 每轮会话结束前逐项过一遍,确保仓库处于"下一轮无需人工修复即可直接开工"的状态。
> 这是把"提前宣告完成"挡在门外的最后一道关卡,配合 [`05-coding-rules.md`](05-coding-rules.md) 的验证清单使用。
收尾前确认:
- [ ] 标准启动路径仍可用(`./init.sh` 或本项目等价命令能跑通)。
- [ ] 标准验证 / smoke 仍可运行,结果如实。
- [ ] 本轮执行记录已追加到 [`../progress.md`](../progress.md)(含跑过的命令和结果作为证据)。
- [ ] [`06-tasks.md`](06-tasks.md) 任务状态真实反映 `DONE` 与未验证的边界,没有"假 DONE"。
- [ ] [`current-state.md`](current-state.md) 已覆盖更新到当前快照(目录、命令、下一步、blocker)。
- [ ] 没有半成品改动处于未记录状态;如有,已写明 `BLOCKED` / `PARTIAL` 和原因。
- [ ] 代码处于可安全恢复的状态(必要时已提交,提交信息清晰)。
任意一项不满足,就先补到满足,再结束会话。
+75
View File
@@ -0,0 +1,75 @@
# 当前实现状态
> 本文是可覆盖的当前快照,记录代码与任务看板的现实状态,帮助 AI coding agent 避免只看计划而忽略仓库现状。
> 历史执行流水追加到 [`../progress.md`](../progress.md),不要在本文重复维护完整执行日志。
## 职责边界
- [`06-tasks.md`](06-tasks.md):任务看板,维护任务状态、依赖和验收要点。
- [`../progress.md`](../progress.md):执行流水,只追加记录每轮执行、验证、阻塞和决策。
- `current-state.md`:当前快照,可覆盖更新当前目录、当前命令、已完成摘要和下一步。
## 当前快照
- 日期:2026-07-01
- 阶段:MVP 起步(仅文档,无代码)
- 技术栈:Django 5.2 LTS + Python 3.12 + DRF + django-admin + 用户端(模板 SSR/Bootstrap/allauth) + MySQL 8.4(独立实例)(计划,未落地);详见 `03-tech-stack.md`
- 生产代码:尚无。`cmhub/` 目前只有 `docs/` 与根级入口文档
- 测试:尚无
- 数据:AI 上游调用与模型配置参考 `D:\chengma\cmbot`(`src/services/ai_text_service.py`、`ai_image_service.py`、`config/ai_models.json`)
- 标准启动路径:`./init.sh`(待 T-001 填入真实命令后生效)
- 标准验证路径:`python manage.py test`(待骨架落地后生效)
- 设计基线:**自助用户端 + 对外 API + 运营后台**三合一单体;用户模型 `User`(auth)/`UserWallet`(点数,锁 wallet 扣点)/`ApiKey`(1:N,哈希存储);对外两接口 + **能力别名 + Provider 适配器**(可插拔供应商);自助扫码充值;注册不送点数。详见 `04-architecture.md` 与 2026-06-29 / 2026-07-01 的 `progress.md` 决策
- 配置基线:运行环境变量集中见 `docs/env.md`;真实密钥/支付凭证不得写入代码或文档样例。充值订单在创建时锁定汇率与预计点数,回调入账使用订单值,不按新汇率重算
- 当前 blocker(已降级):**支付协议已明确**(微信 V3 native + 支付宝当面付,见 `api.md` / `04` 4.2,参考同支付系统 PHP 实现);**仅缺商户密钥/证书真实值**(微信 appid/mchid/apiv3_key/证书、支付宝 appid/公私钥、notify_url 域名)。不阻塞开发,T-304/T-305 可先 mock 联调,上线前填真实商户配置
## 当前目录要点
| 路径 | 状态 | 说明 |
| --- | --- | --- |
| `docs/` | 已有 | 全套 harness 文档 |
| `AGENTS.md` / `CLAUDE.md` | 已有 | 仓库级入口 |
| `progress.md` | 已有 | 执行流水,已记录多轮文档决策;后续任务继续追加 |
| `init.sh` / `init.ps1` | 已有 | 启动验证入口(三命令占位待 T-001 替换) |
| `config/`(Django 工程) | 待建 | T-001 创建 |
| `apps/`(users/portal/billing/ai/api) | 待建 | T-002 创建(含自定义 User,首迁移前定) |
| `manage.py` | 待建 | T-001 创建 |
| `tests/` | 待建 | 随各任务补充 |
## 任务看板状态
任务状态以 [`06-tasks.md`](06-tasks.md) 为准,历史执行记录见 [`../progress.md`](../progress.md)。
- 已完成:无。
- 正在进行:无。
- 下一个可领取任务:**T-001 初始化 Django + DRF 项目骨架**。
## 当前可运行内容
```bash
# 骨架未建,以下命令在 T-001 完成后才可用:
# 本地开发
python manage.py runserver
# 测试
python manage.py test
# 迁移
python manage.py makemigrations && python manage.py migrate
```
当前仓库尚无可运行代码,只有文档。
## 开始编码前检查
1. 读仓库级 `AGENTS.md` / `CLAUDE.md`。
2. 读 `docs/00-ai-start-here.md`。
3. 读 `docs/05-coding-rules.md`(尤其第 8 节资金安全)。
4. 在 `docs/06-tasks.md` 取第一个 `TODO` 且依赖均 `DONE` 的任务(当前为 T-001)。
5. 将该任务状态改为 `DOING`。
## 维护规则
代码状态变化时同步更新本文:新增/移动入口文件、初始化框架或模块、任务状态变化、新增可运行命令、发现文档与代码不一致。
- 任务状态变化同步 [`06-tasks.md`](06-tasks.md)。
- 每轮执行记录、验证命令、阻塞、决策追加到 [`../progress.md`](../progress.md)。
- 本文件只保留当前快照,不保留完整历史。
+90
View File
@@ -0,0 +1,90 @@
# 环境变量与配置
> 本文集中约定运行 `cmhub` 所需配置项。真实密钥、数据库密码、支付凭证不得写入代码、文档样例或提交记录;本文件只写变量名、用途和占位示例。
## 一、配置来源
- Django 运行级配置走环境变量或 `.env`(`.env` 不提交)。
- 上游 AI 模型的 `api_key` 存入数据库前必须用 `AI_KEY_ENCRYPTION_KEY` 加密,admin 脱敏展示且不回显明文。
- 微信、支付宝商户密钥/证书走环境变量或部署机安全文件路径,不写入数据库明文字段。
- 本地开发、测试、生产使用同一套变量名;差异只在变量值。
## 二、Django 基础配置
| 变量 | 必填 | 示例 | 说明 |
| --- | --- | --- | --- |
| `DJANGO_SECRET_KEY` | 是 | `change-me` | Django SECRET_KEY;生产必须使用高强度随机值 |
| `DJANGO_DEBUG` | 是 | `false` | 生产必须为 `false` |
| `DJANGO_ALLOWED_HOSTS` | 是 | `cmhub.example.com,127.0.0.1` | 逗号分隔 |
| `DJANGO_CSRF_TRUSTED_ORIGINS` | 生产是 | `https://cmhub.example.com` | 用户端表单、admin、充值页需要 |
| `DJANGO_TIME_ZONE` | 否 | `Asia/Shanghai` | 默认按中国业务时区 |
## 三、数据库配置
开发和生产都使用 MySQL 8.4 LTS 独立实例,不使用 SQLite。
| 变量 | 必填 | 示例 | 说明 |
| --- | --- | --- | --- |
| `MYSQL_HOST` | 是 | `127.0.0.1` | cmhub 专用 MySQL 实例地址 |
| `MYSQL_PORT` | 是 | `3307` | 独立端口,避免复用已有 MySQL 5.7 |
| `MYSQL_DATABASE` | 是 | `cmhub` | 数据库名 |
| `MYSQL_USER` | 是 | `cmhub` | 应用账号 |
| `MYSQL_PASSWORD` | 是 | `change-me` | 数据库密码 |
| `MYSQL_CHARSET` | 是 | `utf8mb4` | 必须为 `utf8mb4` |
## 四、AI 与加密配置
| 变量 | 必填 | 示例 | 说明 |
| --- | --- | --- | --- |
| `AI_KEY_ENCRYPTION_KEY` | 是 | `base64-fernet-key` | 用于加密 `AiModel.api_key`;生产不可更换,除非完成密钥轮换 |
| `AI_DEFAULT_CONNECT_TIMEOUT_SECONDS` | 否 | `10` | 上游连接超时默认值 |
| `AI_DEFAULT_READ_TIMEOUT_SECONDS` | 否 | `300` | 图片同步生成链路建议 300 秒量级 |
`AiModel.url`、`AiModel.model`、`AiModel.api_type`、`AiModel.capabilities` 与加密后的 `api_key` 由后台或数据迁移维护,不通过环境变量硬编码具体模型。
## 五、支付配置
### 微信 V3 native
| 变量 | 必填 | 示例 | 说明 |
| --- | --- | --- | --- |
| `WECHAT_PAY_APPID` | 真实支付是 | `wx...` | 微信 appid |
| `WECHAT_PAY_MCHID` | 真实支付是 | `1900000001` | 商户号 |
| `WECHAT_PAY_API_V3_KEY` | 真实支付是 | `change-me` | API v3 key |
| `WECHAT_PAY_CERT_SERIAL_NO` | 真实支付是 | `ABC...` | 商户证书序列号 |
| `WECHAT_PAY_PRIVATE_KEY_PATH` | 真实支付是 | `/secure/wechat/apiclient_key.pem` | 私钥文件路径 |
| `WECHAT_PAY_NOTIFY_URL` | 真实支付是 | `https://cmhub.example.com/api/v1/recharge/callback/wechat` | 公网回调地址 |
### 支付宝当面付
| 变量 | 必填 | 示例 | 说明 |
| --- | --- | --- | --- |
| `ALIPAY_APPID` | 真实支付是 | `202100...` | 支付宝 appid |
| `ALIPAY_APP_PRIVATE_KEY_PATH` | 真实支付是 | `/secure/alipay/app_private_key.pem` | 应用私钥路径 |
| `ALIPAY_PUBLIC_KEY_PATH` | 真实支付是 | `/secure/alipay/alipay_public_key.pem` | 支付宝公钥路径 |
| `ALIPAY_NOTIFY_URL` | 真实支付是 | `https://cmhub.example.com/api/v1/recharge/callback/alipay` | 公网回调地址 |
| `ALIPAY_DEBUG` | 否 | `false` | 生产必须为 `false` |
缺少真实商户配置时,充值任务只能使用 mock 支付客户端;mock 必须在代码和测试中标明,不得伪装成真实支付。
## 六、对象存储配置
图片结果默认返回 URL,避免大 base64 进入同步响应体。对象存储选型未最终落地前,可使用本地开发存储,但生产必须给出可公开访问或可签名访问的 URL。
| 变量 | 必填 | 示例 | 说明 |
| --- | --- | --- | --- |
| `STORAGE_BACKEND` | 否 | `local` | `local` / `s3`;MVP 可先 `local` |
| `MEDIA_ROOT` | local 是 | `D:\chengma\cmhub\media` | 本地媒体文件目录 |
| `MEDIA_URL` | local 是 | `/media/` | 本地媒体 URL 前缀 |
| `S3_ENDPOINT_URL` | s3 是 | `https://s3.example.com` | S3 兼容 endpoint |
| `S3_BUCKET_NAME` | s3 是 | `cmhub-media` | bucket |
| `S3_ACCESS_KEY_ID` | s3 是 | `change-me` | access key |
| `S3_SECRET_ACCESS_KEY` | s3 是 | `change-me` | secret key |
## 七、上线前检查
- `DJANGO_DEBUG=false`。
- `DJANGO_SECRET_KEY`、`AI_KEY_ENCRYPTION_KEY`、数据库密码、支付密钥均已使用生产值。
- `ALLOWED_HOSTS`、`CSRF_TRUSTED_ORIGINS`、支付 `notify_url` 使用同一公网域名。
- MySQL 为 8.4 LTS / InnoDB / `utf8mb4`,不是 SQLite 或已有 MySQL 5.7。
- 图片同步链路的客户端、Nginx、Gunicorn、上游 read timeout 均按最慢图片模型放大到同一量级。
+43
View File
@@ -0,0 +1,43 @@
# 评审评分表
> 在一轮(或几轮)会话实现完成后、正式验收前,用这张表做一次结构化评审,回答"这轮 agent 做得好不好"。
> 它评的是**单次输出质量**;代码库长期健康度见 [`quality-document.md`](quality-document.md)。
## 评分维度
六个维度,每个 0-2 分(0 不满足 · 1 部分满足 · 2 满足)。
| 维度 | 问题 | 分数 (0-2) | 备注 |
| --- | --- | --- | --- |
| 正确性 | 实现出来的行为是否符合目标功能 / 验收标准? | | |
| 验证 | 要求的检查是否真的跑过,并在 `../progress.md` 留下证据? | | |
| 范围纪律 | 这一轮是否基本保持在选定的单个任务范围内? | | |
| 可靠性 | 结果是否能在重启或重跑后继续工作? | | |
| 可维护性 | 代码和文档是否清楚到足以交给下一轮会话? | | |
| 交接准备度 | 新会话是否能只靠仓库内文件继续推进? | | |
## 结论
从下面三选一:
- **Accept** — 达标,可验收。
- **Revise** — 需要修补才能接受(列出必须补的修复)。
- **Block** — 有根本性问题,需要先解决(列出阻塞项)。
## 后续动作
- 缺失的证据:
- 必须补的修复:
- 下次复审触发条件:
## 关于校准(重要)
开箱即用的 agent 做评审很弱——它会发现问题,然后把自己说服到通过。所以这张表的通过/失败标准需要反复校准到和人工判断一致:
1. 用本表给一个已完成的任务打分。
2. 把它的分数和你自己的人工判断对比。
3. 有分歧的地方,把对应维度的"什么算 2 分 / 什么算 0 分"写得更具体,落到本项目的真实验收标准上。
4. 对同一个输出重新打分,看是否对齐。
5. 重复直到评审判断和人工评审基本一致。
预计需要 3-5 轮校准。每轮在 `../progress.md` 记录改了什么、为什么改。
+31
View File
@@ -0,0 +1,31 @@
# 方法对照表
> 把最常见的长时 coding-agent 失败模式,对应到本仓库里最该先补的工件或规则。
> 出问题时先查这张表,对症补对应工件,不要把更多规则一股脑堆进一个超长入口文件。
## 失败模式 → 首要修复 → 工件
| 失败模式 | 实际表现 | 首要修复 | 主要工件 |
| --- | --- | --- | --- |
| 新会话摸黑 | 新会话花大量时间重新摸索状态和启动方式 | 让仓库成为唯一事实来源 | [`current-state.md`](current-state.md) + [`../progress.md`](../progress.md) |
| 启动脆弱 | 每轮会话都要重新学怎么启动、装依赖、跑测试 | 统一启动与验证路径 | [`../init.sh`](../init.sh) / [`../init.ps1`](../init.ps1) |
| 范围蔓延 | 一次启动多个任务,最后没有一个完整收尾 | 限制当前活跃范围,一轮只做一个任务 | [`06-tasks.md`](06-tasks.md) |
| 提前宣布完成 | 代码改了就说"完成了",但没有可运行证据 | 把完成绑定到验证证据 | [`06-tasks.md`](06-tasks.md)(passing 需证据)+ [`clean-state-checklist.md`](clean-state-checklist.md) |
| 交接薄弱 | 下一轮看不出哪里可用、哪里坏了、接下来做什么 | 每轮留下明确的当前快照和下一步 | [`current-state.md`](current-state.md) |
| 评审主观 | 质量判断靠个人记忆和感觉,agent 容易自我说服通过 | 用固定维度做评分 | [`evaluator-rubric.md`](evaluator-rubric.md) |
| 代码库悄悄退化 | 速度上去了,但几轮会话后代码越来越难审、边界越来越糊 | 定期给代码库健康度打分 | [`quality-document.md`](quality-document.md) |
| 文档堆叠失控 | 入口文件越来越长,"每次失败加一句" | 渐进披露,入口保持薄 | [`00-ai-start-here.md`](00-ai-start-here.md) + 拆分到具体文档 |
## 使用原则
- 优先补最能直接消除当前失败模式的那**一个**工件,不要一次铺开全部。
- 工件之间用链接互相引用,让全新 agent 不问人也能从一个文件跳到相关规则。
- 同一个事实只维护一份,避免多个文件互相打架。
- 修复落地的同一轮会话里,就把对应工件更新掉。
## 评审 vs 健康度:两个不同的问题
- [`evaluator-rubric.md`](evaluator-rubric.md) 回答:**"这轮 agent 做得好不好?"**(单次输出质量)
- [`quality-document.md`](quality-document.md) 回答:**"这个项目在变强还是变弱?"**(代码库本身质量)
两者配合:rubric 守住每轮交付,quality document 守住长期趋势。
+108
View File
@@ -0,0 +1,108 @@
# cmhub 项目介绍(给管理层)
> 面向决策与汇报的项目概览。技术细节见同目录架构与需求文档。
> 日期:2026-06-29 | 阶段:MVP 起步(文档就绪,待开工)
## 一句话概括
把现有桌面工具 `cmbot` 里的「AI 生成标题、生成图片」能力,做成一个**自助用户端 + 对外计费 API 服务 + 运营后台**,让用户可自助注册充值和管理 API Key,让公司其他项目可按点数计费调用,做到用量可控、账目可查。
## 一、为什么做
- 现在 AI 生成能力**锁在单机桌面工具里**,别的项目想用就得各自重复对接上游 AI,成本高、不统一。
- 上游 AI 调用**按次产生真实成本**,目前没有计费和用量管控,谁用了多少、花了多少不清楚。
- 缺一个统一入口去**管账号、管用量、管成本、留记录**。
## 二、做成什么
一个**自助用户端 + 计费型 AI 能力网关 + 运营后台**:
- 用户可自助注册、扫码充值、查看点数和记录、管理 API Key。
- 对外只提供两个稳定接口:**生成标题**、**生成图片**。
- 谁能调、调多少,用**点数**计费:不同操作、不同模型消耗不同点数。
- 点数不够时直接提示「点数不足,请先充值」。
- 配一个**运营后台**,运营人员可视化管理用户、点数、计费规则和所有调用记录。
> 它不是面向消费者的「内容生成网站」;终端用户有一个**自助用户端**(注册 / 扫码充值 / 管理 API Key / 查记录),生成能力则通过 **API** 提供给用户的程序调用。
## 三、给业务带来什么价值
| 价值 | 说明 |
| --- | --- |
| 能力复用 | 一处建设,多个项目共享,无需各自重复对接 AI |
| 成本可控 | 按点数计费,用量与成本透明、可量化 |
| 收入闭环 | 用户充值 → 转点数 → 调用扣点,形成可计量的变现链路 |
| 可运营 | 后台一处管理账号、点数、计费规则、充值与调用记录 |
| 可演进 | 后台可随时切换/新增底层 AI 模型,调用方无感知 |
## 四、它怎么运转(业务流程)
**充值**:用户到公司**已有支付系统**付款 → 付款成功后系统按汇率自动把金额**转成点数**记到账户。
**调用**:其他项目带密钥调用接口 → 系统检查点数是否足够 → 足够就生成内容并扣点、记一条账 → 不足就提示充值。
```
用户付款 ──> 支付系统 ──回调──> 点数到账
其他项目 ──调用接口──> 够点? ──是──> 生成 + 扣点 + 记录
└──否──> 提示充值
```
关键原则:**账目必须对得上**——每一笔点数变动都有流水,可追溯到充值订单或具体调用。
## 五、商业 / 计费模式
- **预付费点数**:先充值、后消费,类似话费/云服务的预付费。
- 充值金额按**可配置汇率**换算成点数(如 1 元 = N 点,由运营在后台设定)。
- 不同接口、不同模型、不同清晰度可设**不同点数单价**,灵活定价。
- 付款在公司**已有支付系统**完成,本项目不碰收银,只接收付款结果——合规、风险低。
## 六、第一版范围(MVP)
**第一版做**:
- 用户端:自助注册登录、扫码充值、查看点数/充值/消费记录、自助生成与删除 API Key(注册不送免费点数)。
- 生成标题、生成图片两个接口(同步返回结果)。
- 点数计费、扣点、余额查询。
- 充值到账(对接已有支付系统)。
- 调用记录与点数流水。
- 运营后台(账号、点数、计费规则、充值、调用记录)。
**第一版先不做**(留待后续迭代):
- 高并发异步处理、用量统计报表、自动退款对账、注册赠点等(自助注册/扫码充值/API Key 管理已纳入第一版)。
> 取舍原则:先把「充值 → 调用 → 计费 → 记录」最小闭环做稳,再扩展。
## 七、设计亮点(用业务语言)
- **换模型不影响客户**:对外只暴露「能力档位」(如"高清出图"),底层用哪家 AI 模型由后台配置,**随时可换、可比价、可降本**,调用方完全无感。
- **资金安全内建**:防止并发超扣、防止重复充值入账、生成失败自动退点、全程留痕——保证不会算错账、不会乱扣费。
## 八、关键依赖与风险(需要拍板的事)
| 事项 | 说明 | 需谁确认 |
| --- | --- | --- |
| 支付商户配置 | 协议已明确;上线前需提供微信/支付宝商户密钥、证书、公网回调域名 | 支付系统负责人 |
| 计费定价 | 1 元换多少点、各接口/模型多少点 | 产品 / 运营 |
| 上游 AI 成本 | 上游按次收费,需关注成本与定价的利润空间 | 财务 / 产品 |
> 这些不阻塞开发起步;前期可用模拟方式推进,定价与真实商户配置到位后接入真实环境。
## 九、计划与里程碑
| 里程碑 | 内容 |
| --- | --- |
| M1 | Django + admin 骨架可运行,自定义 User 就位 |
| M2 | 服务端跑通一次 AI 生成(验证可行性) |
| M3 | 计费 + 对外接口 + 充值闭环打通 |
| M4 | 用户端(注册 / 充值 / API Key / 记录)可用 |
| M5 | 运营后台完善 + MVP 验收 + 可部署上线 |
## 十、当前状态
- 需求、架构、接口、任务规划等**全套文档已就绪**。
- 代码尚未开始,下一步进入**第一阶段(搭建骨架)**。
- 技术选型已定(成熟开源方案,开发后台几乎零额外成本),风险可控。
---
*更多细节:愿景 `01-vision.md` | 需求与验收 `02-requirements.md` | 架构 `04-architecture.md` | 任务计划 `06-tasks.md`。*
+46
View File
@@ -0,0 +1,46 @@
# cmhub · 一页汇报版
> 自助用户端 + 计费型 AI 能力网关 + 运营后台 | 2026-06-29 | MVP 起步
## 电梯陈述(30 秒)
> 我们把现有工具 `cmbot` 的「AI 生成标题、生成图片」能力,做成一个**自助用户端 + 对外计费 API 服务 + 运营后台**。用户自助注册、充值换点数、管理 API Key,其他项目按调用扣点,**用量可控、成本透明、账目可查**。
## 痛点 → 方案
- **痛点**:AI 能力锁在单机工具里,别的项目用不了;上游按次花钱却无计费、无管控。
- **方案**:自助用户端 + 统一接口 + 点数计费 + 运营后台。一处建设,多项目复用,花了多少一目了然。
## 怎么运转
```
用户付款 → 已有支付系统 → 自动转点数到账
其他项目 → 调接口 → 点数够? → 生成内容 + 扣点 + 记账
└ 不够 → 提示充值
```
## 价值(一句话各一条)
- **能力复用**:一次建设,多项目共享。
- **成本可控**:按点数计费,用量成本透明。
- **收入闭环**:充值→点数→消费,可计量变现。
- **不碰收银**:付款走已有支付系统,合规低风险。
- **可演进**:后台随时换底层 AI 模型,客户无感、可降本。
## 第一版范围
做:自助用户端(注册/扫码充值/API Key 管理/记录)、两个生成接口、点数计费、充值到账、调用记录、运营后台。
先不做:异步高并发、报表、注册赠点(后续迭代;自助注册/充值/API Key 已在第一版)。
## 需要拍板
- 微信/支付宝真实商户配置与公网回调域名(支付负责人)
- 点数定价:1 元=多少点、各接口多少点(产品/运营)
- 上游成本与定价的利润空间(财务/产品)
## 进度
文档全套就绪 → 下一步搭骨架。里程碑:M1 骨架可跑 · M2 跑通生成 · M3 计费充值闭环 · M4 用户端可用 · M5 验收上线。
---
*详见 `project-brief.md`(完整介绍)。*
+64
View File
@@ -0,0 +1,64 @@
# 质量文档
> 给项目的每个产品领域和架构层打分,跟踪代码库随时间是变强还是变弱,回答"这个项目在变强还是变弱"。
> 它评的是**代码库本身的质量**;单次 agent 输出质量见 [`evaluator-rubric.md`](evaluator-rubric.md)。
## 使用时机
- **开始会话前**:读它,了解代码库当前哪里最弱,优先处理。
- **会话结束后**:更新评级。
- **长期**:对比不同时间点的快照,看哪些改动真正改善了代码库健康度。
- 做基准对比、清理简化、或换新 agent / 新模型时,也更新一次。
## 评级标准
- **A**:验证全部通过,结构干净,agent 能读懂,测试稳定。
- **B**:验证通过,基本干净,可读性或测试覆盖有少量缺口。
- **C**:部分可用,有已知缺口,部分代码 agent 不容易理解。
- **D**:不可用,或存在重大结构问题。
---
## 产品领域
> 把下面的占位领域换成本项目 `02-requirements.md` 里的真实功能域。
| 领域 | 评级 | 验证状态 | Agent 可读性 | 测试稳定性 | 关键缺口 | 上次更新 |
|------|------|---------|-------------|-----------|---------|---------|
| 【核心流程 1】 | - | - | - | - | - | - |
| 【核心流程 2】 | - | - | - | - | - | - |
| 【数据 / 持久化】 | - | - | - | - | - | - |
| 【账号 / 鉴权】 | - | - | - | - | - | - |
## 架构层
> 把下面的占位层换成本项目 `04-architecture.md` 里的真实分层 / 模块边界。
| 层级 | 评级 | 边界执行 | Agent 可读性 | 关键缺口 | 上次更新 |
|------|------|---------|-------------|---------|---------|
| 【前端 / 客户端 / CLI】 | - | - | - | - | - |
| 【后端 API / 核心模块】 | - | - | - | - | - |
| 【数据 / 存储层】 | - | - | - | - | - |
| 【外部服务适配】 | - | - | - | - | - |
## 变更历史
### YYYY-MM-DD
- 变更内容:
- 提升:
- 下降:
- 新发现的缺口:
- 已关闭的缺口:
---
## 进阶:验证 harness 是否可以简化
Harness 里的每个组件(规则、脚本、检查清单)都编码了一个假设——"模型做不到这件事"。模型变强后,这些假设可能过时。用本文档检查某个组件是否还有必要:
1. 拍一份本文档快照。
2. 移除一个 harness 组件。
3. 跑一轮基准任务。
4. 再拍一份快照。
5. 对比——评级没降,说明那个组件多余,可以去掉;降了,就恢复。
+72
View File
@@ -0,0 +1,72 @@
# 路由与页面结构
> 本项目含三类界面:**用户端页面**(Django 模板 SSR,终端用户自助)、**对外 HTTP API**(DRF,程序调用)、**运营后台**(django-admin)。本文约定各自路由与页面职责。
> 接口请求/响应合约以 [`api.md`](api.md) 为准。
## 用户端页面(Django 模板 SSR,session 登录)
| 路由 | 方法 | 职责 | 鉴权 |
| --- | --- | --- | --- |
| `/signup` `/login` `/logout` | GET/POST | 自助注册(邮箱验证)/登录/登出(Django auth/allauth) | 公开 |
| `/dashboard` | GET | 个人中心:剩余点数、充值总额、快捷入口 | session |
| `/recharge` | GET/POST | 发起充值:选金额→展示支付二维码→轮询到账 | session |
| `/records/recharge` | GET | 充值记录 | session |
| `/records/usage` | GET | 点数使用(消费/调用)记录 | session |
| `/apikeys` | GET/POST | API Key 管理:列表 / 生成 / 删除(明文只显示一次) | session |
## API 路由(对外,DRF)
| 路由 | 方法 | 职责 | 鉴权 |
| --- | --- | --- | --- |
| `/api/v1/generate/title` | POST | 生成标题 | API Key |
| `/api/v1/generate/image` | POST | 生成图片(同步) | API Key |
| `/api/v1/balance` | GET | 查询点数余额 | API Key |
| `/api/v1/recharge/create` | POST | 用户端发起充值(weixin/alipay),下单取二维码 | Session(用户端) |
| `/api/v1/recharge/status` | GET | 轮询订单状态(前端每秒) | Session(用户端) |
| `/api/v1/recharge/callback/wechat` | POST | 微信 V3 异步回调 | 验签(`@csrf_exempt`) |
| `/api/v1/recharge/callback/alipay` | POST | 支付宝异步回调 | 验签(`@csrf_exempt`) |
## 运营后台(django-admin,`/admin/`)
后台用 Django Session 登录,按模型注册 Admin:
| 管理项 | 对应模型 | 运营能做什么 |
| --- | --- | --- |
| 注册用户 | User | 查看/禁用注册用户;查看其钱包点数余额 |
| 点数钱包 | UserWallet | 查看余额;手工调整点数(必填原因,经计费层写流水,不直接改字段) |
| API Key | ApiKey | 查看 / 吊销用户的 Key(脱敏显示 prefix,不回显明文) |
| 计费规则 | PricingRule | 配置「操作类型 × 能力别名(+ 可选分辨率)→ 点数单价」 |
| 汇率 | ExchangeRate | 配置金额→点数汇率 |
| 充值订单 | RechargeOrder | 检索订单、查看状态/金额/入账点数/支付流水号 |
| 点数流水 | PointsLedger | 检索充值/消费/调整/冲正流水(只读,对账用) |
| 调用记录 | CallRecord | 按账号/时间/状态检索调用、查看别名/实际模型/消耗/错误(只读) |
| 模型配置 | AiModel | 维护上游模型(url/model/api_key[加密脱敏]/api_type/capabilities/timeout) |
| 能力别名 | ModelAlias | 维护对外别名 → 具体模型的映射;换供应商在此改指向 |
## 后台职责约定
### 用户与账号管理
- 用户自助注册;API Key 由用户在用户端自助生成(服务端生成、哈希存储、明文只显示一次),运营侧只能查看 prefix / 吊销,不回显明文。
- 禁用用户或吊销 Key 后,相关调用一律 403 / 401。
- 手工调整点数必须经计费层方法(写 `PointsLedger`、锁 `UserWallet`),不允许直接编辑 `points_balance` 字段。
### 模型与别名管理
- AiModel 的 `api_key` 加密存储,列表/详情脱敏显示,不回显明文。
- 换供应商:改 `ModelAlias` 的指向即可,对外别名与计费规则不变。
- AiModel / ModelAlias / 密钥的变更需留痕(谁、何时、改了什么)。
### 流水 / 调用记录
- 只读列表,支持按账号、时间范围、类型/状态筛选与搜索。
- 不允许在后台修改或删除流水与调用记录(账目不可篡改)。
### 列表与检索
- 充值订单、流水、调用记录默认按时间倒序,提供账号与状态筛选。
## 导航规则
- 对外只暴露 `/api/v1/*`;`/admin/` 仅限运营,生产环境建议限制来源或加额外保护。
- 未授权访问 admin 跳转登录;API 未授权返回 401,不静默失败。
+59
View File
@@ -0,0 +1,59 @@
#!/usr/bin/env pwsh
# 标准启动与验证入口(Windows PowerShell 版),与 init.sh 等价,二选一:
# - Windows 原生 PowerShell:用本文件 ./init.ps1
# - WSL / Git Bash / macOS / Linux:用 ./init.sh
# 一条命令完成:依赖安装 -> 基础验证 -> 打印启动命令。
# 命令已按 Django 技术栈预填;T-001 骨架落地后如有调整(如改用 uv),同步更新
# docs/03-tech-stack.md、docs/00-ai-start-here.md 和 docs/current-state.md。
$ErrorActionPreference = "Stop"
Set-Location -Path $PSScriptRoot
# 依赖管理 uv / pip 二选一(03-tech-stack 标为待定);默认 pip + requirements.txt。
$InstallCmd = "pip install -r requirements.txt" # 或 uv sync
$VerifyCmd = "python manage.py test" # 基础验证 / 测试
$StartCmd = "python manage.py runserver" # 开发启动
function Assert-Configured {
param(
[string]$Name,
[string]$Value
)
if ($Value -like "__REPLACE_*") {
Write-Error "请先在 init.ps1 中替换 $Name。同步更新 docs/03-tech-stack.md、docs/00-ai-start-here.md 和 docs/current-state.md 中的命令。"
exit 2
}
}
Assert-Configured -Name "InstallCmd" -Value $InstallCmd
Assert-Configured -Name "VerifyCmd" -Value $VerifyCmd
Assert-Configured -Name "StartCmd" -Value $StartCmd
Write-Host "==> 当前目录: $($PWD.Path)"
if (-not (Test-Path "manage.py")) {
Write-Error "未找到 manage.py。Django 骨架尚未初始化(见 docs/06-tasks.md 的 T-001)。完成 T-001 后本脚本即可正常运行。"
exit 1
}
Write-Host "==> 同步依赖"
Invoke-Expression $InstallCmd
Write-Host "==> 数据库迁移"
Invoke-Expression "python manage.py migrate"
Write-Host "==> 运行基础验证"
Invoke-Expression $VerifyCmd
Write-Host "==> 启动命令"
Write-Host " $StartCmd"
if ($env:RUN_START_COMMAND -eq "1") {
Write-Host "==> 启动应用"
Invoke-Expression $StartCmd
} else {
Write-Host "如果希望 init.ps1 直接启动应用,请设置环境变量 RUN_START_COMMAND=1。"
Write-Host "如果基础验证失败,先修复基线状态,不要在坏的起点上继续叠新功能。"
}
+61
View File
@@ -0,0 +1,61 @@
#!/usr/bin/env bash
# 标准启动与验证入口(Unix shell 版),与 init.ps1 等价,二选一:
# - WSL / Git Bash / macOS / Linux:用本文件 ./init.sh
# - Windows 原生 PowerShell:用 ./init.ps1
# 一条命令完成:依赖安装 -> 基础验证 -> 打印启动命令。
# 命令已按 Django 技术栈预填;T-001 骨架落地后如有调整(如改用 uv),同步更新
# docs/03-tech-stack.md、docs/00-ai-start-here.md 和 docs/current-state.md。
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "$ROOT_DIR"
# 依赖管理 uv / pip 二选一(03-tech-stack 标为待定);默认 pip + requirements.txt。
INSTALL_CMD=(pip install -r requirements.txt) # 或 uv sync
VERIFY_CMD=(python manage.py test) # 基础验证 / 测试
START_CMD=(python manage.py runserver) # 开发启动
ensure_configured() {
local name="$1"
local first="$2"
if [[ "$first" == __REPLACE_* ]]; then
echo "ERROR: 请先在 init.sh 中替换 ${name}。"
echo " 同步更新 docs/03-tech-stack.md、docs/00-ai-start-here.md 和 docs/current-state.md 中的命令。"
exit 2
fi
}
ensure_configured "INSTALL_CMD" "${INSTALL_CMD[0]}"
ensure_configured "VERIFY_CMD" "${VERIFY_CMD[0]}"
ensure_configured "START_CMD" "${START_CMD[0]}"
echo "==> 当前目录: $PWD"
if [ ! -f "manage.py" ]; then
echo "ERROR: 未找到 manage.py。Django 骨架尚未初始化(见 docs/06-tasks.md 的 T-001)。"
echo " 完成 T-001 后本脚本即可正常运行。"
exit 1
fi
echo "==> 同步依赖"
"${INSTALL_CMD[@]}"
echo "==> 数据库迁移"
python manage.py migrate
echo "==> 运行基础验证"
"${VERIFY_CMD[@]}"
echo "==> 启动命令"
printf ' %q' "${START_CMD[@]}"
printf '\n'
if [ "${RUN_START_COMMAND:-0}" = "1" ]; then
echo "==> 启动应用"
exec "${START_CMD[@]}"
fi
echo "如果希望 init.sh 直接启动应用,请设置 RUN_START_COMMAND=1。"
echo "如果基础验证失败,先修复基线状态,不要在坏的起点上继续叠新功能。"
+155
View File
@@ -0,0 +1,155 @@
# 执行进度记录
> 本文件是只追加的历史流水,用来记录任务执行过程、验证命令、阻塞点和关键决策。
> 当前目录、当前命令、下一个可领取任务等可覆盖快照,写入 [`docs/current-state.md`](docs/current-state.md)。
## 职责边界
- `docs/06-tasks.md`:任务看板,维护任务状态、依赖和验收要点。
- `progress.md`:历史流水,只追加记录每轮执行发生了什么。
- `docs/current-state.md`:当前快照,可覆盖更新仓库现实、可运行命令和下一步。
不要在本文重复维护当前目录结构、当前运行命令或下一个任务;这些信息以 `docs/current-state.md` 为准。
## 记录格式
每完成或中断一轮任务,在文件末尾追加一条记录:
```markdown
## 【YYYY-MM-DD】T-【编号】 【任务名】
- 状态:【DONE / BLOCKED / PARTIAL】
- 变更:【修改了哪些文件或模块】
- 验证:【运行的真实命令和结果】
- 阻塞:【如有,写明原因和需要谁决策】
- 决策:【如有,记录本轮确定的关键取舍】
- 下一步:【建议下一个任务 ID 或待确认事项】
```
## 执行记录
## 2026-06-29 文档初始化(非任务)
- 状态:DONE
- 变更:基于 `D:\github\harness_coding_docs` 模板,结合需求讨论生成 `cmhub` 全套 harness 文档(AGENTS/CLAUDE、docs/00–06、api、routes、current-state、README、progress、init 脚本);通用流程文档(adoption/clean-state/method-map/evaluator/quality)从模板复制。
- 验证:仅文档,无代码可跑。
- 决策:技术栈 Django+DRF+django-admin;预付费点数模型(充值按汇率转点存本地,调用扣本地点数,不实时查支付系统);生成接口同步返回;充值由支付系统服务端回调入账(验签+幂等)。
- 下一步:领取 T-001 初始化 Django + DRF 骨架。支付系统接口文档待提供(T-304 前需要)。
## 2026-06-29 设计优化:可插拔供应商(别名 + 适配器)(非任务)
- 状态:DONE
- 变更:把「两个接口 + 后台配模型」的设计从简化版抬到可治理版,更新到多个文档:
- `04-architecture.md`:AI 调用层改为 Provider 适配器架构;新增 ModelAlias 表与 AiModel 的 capabilities/加密 key;PricingRule 改为按别名定价;CallRecord 增 alias + model_used;计费时序加入别名解析与能力校验;难点表补充抽象泄漏/供应商耦合/配置热生效/审计;项目结构加 `apps/ai/providers/`。
- `api.md`:对外 `model` 字段明确为能力别名;新增 `parameters` 透传;响应加 `model_used`;图片默认返回 URL;AI 模块合约改为别名解析 + Provider 适配器接口。
- `03-tech-stack.md` / `02-requirements.md` / `05-coding-rules.md` / `routes.md` / `06-tasks.md`:同步别名机制、密钥加密、配置审计、对象存储等决策与任务。
- 验证:仅文档;自检文档间链接与任务依赖一致(T-302 依赖更新为 T-104)。
- 决策:① 对外绑能力别名而非具体模型 SKU;② `api_type` 升级为显式 Provider 适配器层 + capabilities 声明 + parameters 透传;③ 供应商密钥加密存储 + 配置热生效 + 后台变更审计。按账号授权别名、按比例分流/故障转移列入 Backlog(接口预留,MVP 不实现)。
- 下一步:领取 T-001 初始化 Django + DRF 骨架(Phase 1 任务已重排为 T-101 适配器层 / T-102 别名解析 / T-103 审计 / T-104 跑通)。
## 2026-07-01 设计决策:桌面端全同步接入 + V2 异步预研(非任务)
- 状态:DONE
- 变更:把「桌面端不改、全同步接入 cmhub」的可用性结论与前提写入两个每轮必读文档:
- `03-tech-stack.md` 第二节「同步生成而非任务队列」决策下补:桌面端可不改交互骨架、只换 service 层 URL/密钥即可接入;同步可用的三前提;何时转 V2 异步的触发信号。
- `04-architecture.md` 第五节新增 `5.1 同步方案可用性结论`:超时链路层层放大对齐(点名 `timeout_seconds:0`、Gunicorn 30s、Nginx 60s 三个默认值雷,建议统一 300s)、worker 数按峰值总并发预留、适用边界与 V2 触发条件。
- 验证:仅文档;`ai_models.json` 现存 `timeout_seconds: 0`,迁入服务端时须改有限值(关联约束已写入 04 第五节)。
- 决策:① MVP 桌面端不改、`桌面端 → cmhub → 中转站` 全同步,多一跳不影响可用性,点数一致性更简单(一次请求闭环:预扣→同步调→成功/失败退点);② 同步可用的硬前提是「超时链路 + worker 容量」配对,否则「小量正常、上量假死」;③ 适用边界为单接入方小并发批量,V2 异步(队列)延后,但适配器接口与 `call_record` 三态需为异步预留口子。
- 安全提醒:`ai_models.json` 内三把 `sk-` 为明文真实密钥,视为已泄露,迁入时须加密存储(Fernet/KMS)并轮换;密钥不进桌面端。
- 下一步:不改变任务看板顺序,仍从 T-001 起步;V2 异步化触发条件见 03/04,暂不排期。
## 2026-07-01 设计决策:版本锁定 + 数据库选型(MySQL 8.4 独立实例)(非任务)
- 状态:DONE
- 变更:结合实际部署环境(一台 VPS,实测 15G 内存 / available 7.4G / 无 swap,已装 MySQL 5.7 供其他服务用),定稿版本与数据库选型,更新 `03-tech-stack.md`(技术栈表语言/框架/数据库三行 + 决策记录三条)、`04-architecture.md`(数据库条目 + 第五节并发扣点难点补 CHECK 版本注意)、`README.md` 技术栈行。
- 验证:仅文档。
- 决策:
- ① 框架/语言锁定 **Django 5.2 LTS + Python 3.12**(`requires-python ">=3.12,<3.14"`);禁用已 EOL 的 Django 4.0/4.1;理由是安全/维护窗口,非性能(性能瓶颈在等上游+worker,见 5.1)。
- ② 数据库定 **MySQL 8.4 LTS,cmhub 专用独立实例**。放弃复用 VPS 已有的 MySQL 5.7:5.7 跑不了 Django 5.2(需 ≥8.0.11)、已 EOL、不支持 CHECK 约束;曾评估「坚持 5.7」会连锁把 Django 拖回 4.0(EOL)+Python≤3.10+cmbot 兼容风险,被否。内存宽裕(7.4G),单开独立实例与已有 5.7 隔离、互不影响。
- ③ 硬性约束:InnoDB + utf8mb4;CHECK 需 MySQL ≥8.0.16 才生效,扣点主防线是 `select_for_update` / `UPDATE ... WHERE balance>=N`,不能只靠 CHECK;MySQL 默认隔离级别 REPEATABLE READ,计费按此语义验证;开发亦用 MySQL,不用 SQLite(会忽略 FOR UPDATE,测不出并发扣点)。
- 待办提醒:T-001 骨架落地时须选 MySQL 驱动(`mysqlclient` 或 `PyMySQL`)、`DATABASES` 配 `charset=utf8mb4`、连接指向独立实例端口;机器建议补 2–4G swap;`03` 部署维度仍为「待定」,部署基线待后续定稿。
- 下一步:不改变任务看板顺序,仍从 T-001 起步(Django+DRF 骨架,按上述版本/数据库落地)。
## 2026-07-01 定位扩展:新增自助用户端(B2B → B2B+B2C)(非任务)
- 状态:DONE
- 变更:按用户新增需求(终端用户自助注册/扫码充值/API Key 管理/查记录),把项目从纯 B2B API 网关扩展为「自助用户端 + 计费 API + 运营后台」三合一,系统性更新文档:
- 定位/需求:`01-vision`、`00-ai-start-here`、`02-requirements`、`project-brief`、`project-onepager`(用户角色加注册用户;MVP 加用户端;移除"自助注册"非目标;注册不送点数)。
- 架构:`04-architecture`(系统结构加用户端;数据模型 Account→`User`+`UserWallet`+`ApiKey`;两套认证;4.2 加自助扫码下单时序;难点加 API Key 哈希/注册滥用/Web-API worker 隔离/充错账户;项目结构 apps;架构纪律)。
- 合约/路由/规则:`api.md`(认证主体 User、`recharge/create` 转正扫码、API 只认 Key)、`routes.md`(用户端页面路由、admin 改 User/Wallet/ApiKey)、`03-tech-stack`(用户端形态 Django SSR+Bootstrap+allauth、鉴权、部署 worker 隔离)、`05-coding-rules`(范围、API Key 哈希、API 只认 Key、锁 wallet)。
- 任务:`06-tasks`(T-002 首迁移前定自定义 User;T-201 改 User/Wallet/ApiKey;T-203 锁 wallet;T-305 扫码下单;新增 Phase 4 用户端 T-501~504;里程碑加 M4 用户端)。
- `README`、`current-state` 同步。
- 验证:仅文档。
- 决策:
- ① 前端形态选 **Django 模板 SSR 单体**(+Bootstrap/allauth/crispy),不引前后端分离框架。
- ② 用户模型:`User`(auth 登录态) / `UserWallet`(点数余额,扣点锁 wallet、与 auth 解耦) / `ApiKey`(User 1:N,sha256 哈希存储、明文只显示一次)。
- ③ 认证分两套认同一 User:用户端 session+CSRF,对外 API 只挂 API Key(不挂 Session,防绕过计费)。
- ④ 充值:`recharge/create` 转正,用户端自助扫码下单 + 回调入账,订单绑定 user 防充错账户,金额 Decimal 向下取整。
- ⑤ **注册不送免费点数**(必须充值才有点数,降低薅羊毛);注册须邮箱验证、生成接口须限流。
- ⑥ 单体部署按路径把图片 API 与用户端页面分流到不同 worker 池。
- 待办提醒:**支付系统扫码下单 + 回调接口文档仍未提供**,是 T-304/T-305 充值的硬阻塞;自定义 User 必须在首次 migrate 前定义(Django 硬约束)。
- 下一步:任务仍从 T-001 起步;用户端任务见 Phase 4(T-501~504)。
## 2026-07-01 补全支付协议(参考同系统 PHP 实现,解除充值 blocker)(非任务)
- 状态:DONE
- 变更:从 Obsidian 笔记(虎观虾皮一键采购 `扫码支付购买流程-技术文档` + `扫码支付迁移到Django-实施指南`)提取同一支付系统的扫码支付协议,补进当前 Django 仓库文档:
- `api.md`:充值段重写为微信 V3 native + 支付宝当面付双回调(`/recharge/callback/wechat`、`/alipay`)+ `create`(pay_method、code_url/qr_code)+ `status` 轮询;金额单位、验签、幂等、应答格式明确。
- `04-architecture.md` 4.2:补通道协议、库、金额单位、回调应答、`@csrf_exempt`、主动查单兜底。
- `03-tech-stack.md`:充值对接行加库 `wechatpayv3`/`python-alipay-sdk`。
- `routes.md`:拆微信/支付宝回调端点 + status。
- `06-tasks.md`:T-304 拆双回调+主动查单,T-305 加 weixin/alipay+轮询。
- `05-coding-rules.md`:回调 `@csrf_exempt` + 主动查单兜底。
- `current-state.md`:blocker 降级。
- 验证:仅文档。
- 决策:
- ① 路线确认:**继续当前 Django 仓库**,Obsidian 的 Go(Gin+GoAdmin) cmhub 方案仅作支付逻辑参考(用户拍板)。
- ② 支付通道:微信 V3 native(`wechatpayv3`,金额**分**,回调 SDK 验签解密、`TRANSACTION.SUCCESS`、应答 `{code:SUCCESS}`);支付宝当面付 `trade.precreate`(`python-alipay-sdk`,金额**元**,`verify`、`TRADE_SUCCESS/FINISHED`、应答 `success`)。
- ③ 二维码不含业务数据,靠 `out_trade_no`(=order_no) 在回调关联;回调 `@csrf_exempt`;幂等 `select_for_update`+`status!=pending`;须主动查单兜底。
- ④ 只取支付层,不引入 Obsidian 源里的套餐/会员有效期/邀请返佣业务(那是虎观助手专有);充值入账走本项目 `UserWallet`+`points_ledger`+`ExchangeRate`。
- 待办提醒:**blocker 从「协议缺失」降级为「仅缺商户密钥/证书真实值」**,不阻塞开发,可先 mock。原 PHP 遗留坑(测试后门号、H5 金额写死、支付宝 debug 沙箱、明文密钥)不迁入。
- 下一步:任务仍从 T-001 起步;充值见 T-304/T-305(可 mock 先行)。
## 2026-07-01 吸收 AI 模型调用机制(来自 Go 方案 §16.6)(非任务)
- 状态:DONE
- 变更:从 Obsidian `Golang-Gin-GoAdmin-技术方案` §16.6 提取三个上游模型的真实调用机制(业务规则/上游契约,非技术栈),补进 `04-architecture.md`(3.1 + AI 层职责)、`api.md`(待确认 + image 改图必传)、`03-tech-stack.md`、`06-tasks.md`(T-101 验收)。
- 验证:仅文档。
- 决策:
- ① GPT-5.5(`chat`)标准 chat/completions;Nano Banana 2(`api_type=auto`)走 chat/completions 多模态返图、需自定义解析、可文/图生图;GPT Image 2(`images_edits`)走 images/edits 改图、原图必传。
- ② 两个图片模型**非标准** `images/generations`,每模型独立 url+key;图片返回结构(URL/base64/位置)**首次对接抓真实响应再定解析**。
- ③ 只吸收 AI 模型机制,**不吸收** Go 方案的会员套餐/续期/折扣/邀请返佣/多端 JWT(属虎观采购业务或 Go 栈,非 cmhub 定位);鉴权继续用 API Key(比桌面端 JWT 更安全,不在桌面端存密码)。
- 下一步:T-101/T-102 实现适配器时按此机制,首次对接 `api.vectorengine.ai` 抓真实响应确认图片返回结构。
<!-- 后续从这里向下继续追加记录。 -->
## 2026-07-01 文档优化:定位一致性 + 充值 schema + 调用状态 + 环境配置(非任务)
- 状态:DONE
- 变更:按全栈落地前优先级修正文档:
- `docs/README.md` / `project-brief.md` / `project-onepager.md`:定位同步为「自助用户端 + 计费型 AI 能力网关 + 运营后台」三合一,并加入 `env.md` 导航。
- `docs/04-architecture.md` / `docs/api.md` / `docs/06-tasks.md`:充值订单改为下单时锁定 `exchange_rate` 与 `points_granted`,回调入账使用订单值并校验金额;`call_record.status` 明确为 `pending -> success / failed`,失败退点通过 `points_ledger(refund, ref_call_id)` 关联。
- `docs/env.md`:新增环境变量与配置清单,覆盖 Django、MySQL、AI 密钥加密、微信/支付宝、对象存储与上线检查。
- `docs/03-tech-stack.md` / `docs/current-state.md` / `docs/00-ai-start-here.md` / `docs/02-requirements.md` / 汇报文档:同步环境配置入口、当前快照与支付 blocker 降级口径。
- 验证:文档修改;用 `rg` 定位相关段落,未运行代码测试(仓库当前尚无代码)。
- 阻塞:无。
- 决策:充值汇率采用「下单锁定」而非「回调时读取当前汇率」,便于页面展示预计到账点数,也避免用户扫码后后台改价导致入账变化;调用记录不增加 `refunded` 状态,账务冲正由点数流水表达。
- 下一步:仍从 T-001 初始化 Django + DRF 骨架开始。
## 2026-07-01 文档优化:里程碑口径同步(非任务)
- 状态:DONE
- 变更:同步汇报文档里程碑到 `06-tasks.md` 的 M1-M5 口径:`project-brief.md` 增加 M4 用户端可用、M5 验收上线;`project-onepager.md` 进度行同步 M4/M5。
- 验证:文档修改;用 `rg` 定位里程碑旧口径。
- 阻塞:无。
- 决策:以 `06-tasks.md` 的里程碑为权威,汇报文档只做同口径摘要。
- 下一步:仍从 T-001 初始化 Django + DRF 骨架开始。
## 2026-07-01 文档优化:Phase 顺序口径同步(非任务)
- 状态:DONE
- 变更:更新 `docs/00-ai-start-here.md` 的优先路径,Phase 3/4/5 与 `docs/06-tasks.md` 对齐:Phase 3 为对外 API 与充值,Phase 4 为用户端,Phase 5 为后台与发布。
- 验证:文档修改;用 `rg` 对照 `00-ai-start-here.md` 与 `06-tasks.md` 的 Phase 标题。
- 阻塞:无。
- 决策:任务阶段顺序以 `06-tasks.md` 为权威,入口文档只做一致的导航摘要。
- 下一步:仍从 T-001 初始化 Django + DRF 骨架开始。