Files
cmhub/docs/current-state.md
T

166 lines
53 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.
# 当前实现状态
> 本文是可覆盖的当前快照,记录代码与任务看板的现实状态,帮助 AI coding agent 避免只看计划而忽略仓库现状。
> 历史执行流水追加到 [`../progress.md`](../progress.md),不要在本文重复维护完整执行日志。
## 职责边界
- [`06-tasks.md`](06-tasks.md):任务看板,维护任务状态、依赖和验收要点。
- [`../progress.md`](../progress.md):执行流水,只追加记录每轮执行、验证、阻塞和决策。
- `current-state.md`:当前快照,可覆盖更新当前目录、当前命令、已完成摘要和下一步。
## 当前快照
- 日期:2026-07-21
- 阶段:Phase 6 增强(MVP 后);Phase 3 对外 API 与充值已完成到 T-306,Phase 4 用户端 T-501 注册 / 登录(allauth)、T-502 API Key 自助管理页、T-503 个人中心 / 记录页、T-504 充值页与 T-505 用户端审核优化已完成,Phase 5 T-401 运营后台完善、T-402 MVP 完整验收与 T-403 部署 / 运行文档已完成,Phase 6 T-601 可用别名发现、T-602 django-admin 中文化第 1-3 层、T-603 django-admin 字段级中文化、T-604 中文敏感词本地过滤、T-605 免邮箱验证策略落地、T-606 公开首页 + 客户端下载入口、T-607 桌面端最新版本检查接口、T-608 新用户注册赠送试用点数(当前 10 点)、T-609 桌面端版本检查接口增加强制更新标记、T-610 首页导入模板下载入口、T-611 用户端品牌名统一为虾皮圈、T-612 生图同步接口止血、T-613 抽生成核心 service、T-614 生图异步任务化接口、T-615 旧同步生图接口遥测 / 弃用口径、T-616 生图失败自动重试 2 次、T-617 桌面端版本文件大小字段与 T-618 客户端发布版本后台必填校验元数据已完成;后续仍需处理真实支付回调到账闭环、客户端下载包发布和生产侧旧同步接口用量观察
- 技术栈:系统 Python 3.12.3 + Django 5.2.15 + DRF 3.16.1 + django-allauth 65.18.0 + PyMySQL 1.1.3 + cryptography 49.0.0 + requests 2.34.2 + ahocorapy 1.6.2 + wechatpayv3 2.0.2 + python-alipay-sdk 3.4.0 + django-admin;MySQL 8.4 已接入 settings,并支持 `MYSQL_CONNECT_TIMEOUT` / `MYSQL_READ_TIMEOUT` / `MYSQL_WRITE_TIMEOUT`;用户端已用 Django 模板 SSR + Bootstrap + allauth 落地注册登录;生产部署口径为 VPS / 宝塔 + Nginx + Gunicorn(gthread) + systemd;详见 `03-tech-stack.md` 与 `deployment.md`
- 生产代码:已有最小 Django 工程骨架:`manage.py`、`config/`;T-002 已创建 `apps/users|portal|billing|ai|api`;T-003 已把自定义 `User` 注册进 django-admin;T-004 已完成 email 唯一性、init 版本断言、app 顺序、`.env.example` 与 `pyproject.toml`;T-101 已新增 `apps/ai/providers/`(Provider 接口、注册表、chat/gemini/images/images_edits 适配器);T-102 已新增 `AiModel` / `ModelAlias`、Fernet 加密密钥存储、别名解析、admin 配置页、`import_ai_models` 导入命令;T-103 已新增 `AiConfigAuditLog` 审计表、admin 只读页面和后台保存/删除审计 hook;T-104/T-105 已完成录制 title/image smoke 与审核修补;T-201 已新增 `UserWallet` / `ApiKey`、`PointsLedger` / `CallRecord`、对应 admin 与迁移;T-202 已新增 `PricingRule` / `ExchangeRate`、`apps.billing.pricing` 计费计算函数、admin 配置页与迁移;T-203 已新增 `apps.billing.services`,实现并发安全预扣、成功确认与幂等失败退点;T-204 已新增 `billing.0003_pointsledger_unique_ledger_change_type_per_call`,用 MySQL 可落地的 `ref_call + change_type` 复合唯一约束兜底防重复 refund;T-301 已新增 `apps.api.authentication.ApiKeyAuthentication` 与 `ExternalApiView`;T-302 已新增生成接口编排、序列化器、图片本地存储和 `/api/v1/generate/title|image` 路由;T-303 已新增 `apps.billing.services.get_balance_snapshot()` 与 `/api/v1/balance` 余额查询接口;T-304 已新增 `RechargeOrder`、充值回调验签适配器、幂等入账服务、微信/支付宝回调路由与迁移 `billing.0004_rechargeorder_and_more`;T-305 已新增 `create_recharge_order()`、微信/支付宝扫码下单 mock/SDK 入口、`/api/v1/recharge/create` 与 `/api/v1/recharge/status`;T-306 已新增 `apps.api.throttles`、`apps.api.exceptions`、`REST_FRAMEWORK` 安全默认认证、生成/认证失败限流、`image_url` SSRF 防护与响应大小上限、充值单笔金额上限;T-501/T-608 已接入 allauth 注册登录路径,注册成功后 adapter 调用 `grant_signup_bonus()` 经 billing 一次性发放 10 点并写 `signup_bonus` 流水,新增 `SignupBonusGrant(user UNIQUE)` 幂等标记、admin 只读检索和 `billing.0007` 迁移;T-502 已新增 `/apikeys`、API Key 创建表单、列表页和删除(吊销)动作,生成后明文只显示一次,列表只显示 prefix;T-503/T-608 已扩展 `/dashboard` 为个人中心汇总,并新增 `/records/recharge` 充值记录与 `/records/usage` 点数记录,只读展示当前用户数据和注册赠点 / 消费 / 退款流水;T-504 已新增 `/recharge` 页面、`RechargeCreateForm`、充值导航入口和轮询脚本,页面创建 pending 订单、展示二维码票据、轮询 `/api/v1/recharge/status`,订单 paid 后刷新余额;T-505 已把 Bootstrap 5 CSS 与 qrcode.js vendoring 到 `apps/portal/static/portal/vendor/`,页面不再依赖 jsdelivr,并把充值记录 / 点数记录改为 Django `Paginator` 分页;T-401 已新增 `adjust_wallet_points()` 手工调点服务、钱包 admin 专用调点表单与模板,后台可管理/检索用户、钱包、API Key(脱敏)、计费规则、汇率、充值订单、点数流水、注册赠点记录和调用记录,流水/订单/调用记录保持只读;T-402 已新增 `docs/mvp-acceptance.md`,按 P0 验收矩阵记录 MVP 完整验收结论、测试证据和已知限制;T-403 已新增 `docs/deployment.md` 与 `requirements-production.txt`,并在 settings 中补齐 `STATIC_ROOT`、`CSRF_TRUSTED_ORIGINS`、共享 `CACHES`、HTTPS cookie、proxy SSL、HSTS 环境变量与 `ACCOUNT_SIGNUP_RATE_LIMIT` 注册限流配置;T-601 已新增 `apps.ai.catalog.get_public_model_catalog()`、`GET /api/v1/models` 与 portal `/models` 只读页面,只展示 active 可调用别名、能力、是否需要原图和点数单价,不解密 provider key,不暴露底层 SKU / URL / key / `extra_body`;T-604 已新增 `apps.moderation`、`SensitiveWord` 模型/admin/迁移、keyword provider、归一化管线和共享 cache 版本失效,生成接口已改为 prompt 先审再读取图片/计费/扣点/调上游;T-606 已新增公开首页 `/`、`DownloadRelease` 模型/admin/迁移、首页 SSR 模板、共享 `portal/brand.css`,并把现有 portal 页面套入同一套品牌 token;T-607/T-609 已新增 `ClientLatestReleaseView` 与 `/api/v1/client/releases/latest`,公开匿名返回当前客户端版本 JSON,`release.force_update` 表示该版本是否强制升级;`portal.0002_downloadrelease_force_update` 已给 `DownloadRelease` 增加 `force_update` 字段,admin 可编辑和筛选;T-610 已新增 `ImportTemplate` 模型/admin/迁移 `portal.0003_importtemplate`,首页读取当前模板并在“下载客户端”旁展示“下载导入模板”,本地文件 URL 转为当前站点绝对 URL,`external_url` 优先;T-611 已把用户端 portal 可见品牌名统一为“虾皮圈”,包括页面标题、顶部导航、首页 H1、用户端“虾皮圈 API Key”文案和 allauth 邮件模板;T-612 已新增 `AI_IMAGE_UPSTREAM_DEADLINE_SECONDS`,生图 Provider 上游请求和上游返回图片 URL 下载会按 `min(AiModel.timeout_seconds 或分辨率默认值, 硬截止)` 控制读取超时,超时返回 `upstream_timeout` 并走既有失败退点路径;T-613 已把旧同步生成链路抽成 `GenerationInput`、`prepare_generation()`、`precharge_generation()`、`execute_precharged_generation()` 与 `GenerationResult`,旧 view 只负责 serializer 和异常转 HTTP,后续异步 worker 可复用已预扣执行 / 确认 / 退点阶段。
- T-614/T-616 生产代码补充:已新增 `ImageGenerationTask` 与 `api.0001_initial`,新增 `POST /api/v1/generate/image/tasks`、`GET /api/v1/generate/image/tasks/{task_id}`、`apps.api.image_tasks` 任务服务、`run_image_tasks` management command 和只读 admin;异步提交支持 `Idempotency-Key` 去重 / 冲突检测,worker 使用 DB 任务表、租约、心跳与 reaper,成功返回 cmhub 托管 URL;T-616 已通过 `api.0002_imagegenerationtask_next_attempt_at_and_more` 增加 `next_attempt_at` 与 `(status, next_attempt_at)` 索引,临时性 `upstream_timeout` / `upstream_error` 默认最多重试 2 次,重试期间不退点,最终失败 / 僵任务才走计费层幂等退款。
- T-617 生产代码补充:`DownloadRelease` 已新增 `size_bytes` 可空正整数字段和迁移 `portal.0004_downloadrelease_size_bytes`,django-admin 可填写并在列表展示;`GET /api/v1/client/releases/latest` 的 `release` 对象新增 `size_bytes`,有值返回整数,未配置返回 `null`,未发布时仍只返回 `release:null`。
- T-618 生产代码补充:`DownloadReleaseAdmin` 已挂专用 `ModelForm`,后台新增 / 编辑客户端发布版本时 `sha256` 与 `size_bytes` 必填;数据库字段仍兼容历史空值,API 仍可读取历史 `size_bytes=null` 记录。
- T-624~T-627 本地代码补充:`apps.licensing` 已包含设备、会话、设备审计、套餐、权益、席位、授权事件、存量迁移资格、迁移请求和设备凭证;`licensing.0003_alter_licenseevent_action_legacymigrationgrant_and_more` 建立迁移数据与凭证 hash 存储。旧 API Key + 当前设备会话只能申请短时迁移,portal 同账号确认后才绑定席位;无会话旧客户端及现有生成/异步轮询保持不变,尚未部署生产。
- T-619 生产代码补充:`ModelAlias` / `CallRecord` 已新增 `vision` 操作并生成 `ai.0005` / `billing.0008` 迁移;`POST /api/v1/analyze/images` 已接入 API Key 鉴权、生成限流、prompt 前置审核、有序多图 URL/Base64 读取、SSRF 与大小保护、Chat/Gemini 多模态 Provider、固定单次预扣 / 成功确认 / 幂等退款及安全调用摘要。模型目录与 portal 可用模型页会展示能力匹配的 active `vision` 别名并标记 `requires_image=true`。2026-07-16 已在 `185.216.248.75` 部署 `707941a` 并应用迁移,复用 active `GPT-5.5 文本`(`text + vision`)创建默认 active `vision-standard`,默认价格为 1 点/次;Nginx 已把该同步接口分流到 `cmhub-generate` 长请求池。
- T-619 生产验收:真实提交两张图片成功返回中文文字,HTTP 200,`model_used=gpt-5.5`,`call_id=6096`;余额 234→233,调用记录为 `vision/success`,对应 consume 1 条、refund 0 条。验收临时 API Key 已吊销;`cmhub-web`、`cmhub-generate`、Nginx、MySQL 8.4 均为 active,66 个异步生图 worker 未因本次部署重启。
- 最新验证:T-619 已完成 `ai.0005_alter_modelalias_operation_type` 与 `billing.0008_alter_callrecord_operation_type_and_more` 迁移并应用到当前开发库;`manage.py check` 0 issues,`makemigrations --check --dry-run` 无变化;Provider 专项 10 tests OK,T-619 API / 别名 / 目录专项 13 tests OK,包含旧标题 / 生图的扩展回归 84 tests OK,users / billing / moderation 分组 46 tests OK。完整单命令回归在 604 秒达到执行器超时,之后 API 全量重试在测试库初始化阶段遇到远程 MySQL `43.128.3.240` 连接超时,均未产生断言失败;测试期仍有 allauth 在 MySQL 条件唯一约束上的既有 `models.W036` 警告。
- 验证补充:API 全量 88 条测试已重试两次,均在执行断言前因远程 MySQL 握手超时中断;`Test-NetConnection 43.128.3.240 -Port 3306` 同期显示 TCP 可达。收尾 `./init.ps1` 通过(Python 3.12.3,Django system check 0 issues)。
- 验证恢复:远程 MySQL 恢复后,新增“单图 + 指定 vision 别名”成功测试 1 test OK;API 非生成类拆分为鉴权 / 余额 / 模型目录 / 客户端版本 28 tests OK、充值回调 / 下单状态 13 tests OK,结合此前 `GenerateApiTests` 整类回归与新增单测,当前 API 89 条均有通过证据;`apps.portal.tests` 全量 39 tests OK。因此完整回归已通过拆分执行完成,单命令超时仅作为环境现象保留。
- 最新验证:T-618 客户端发布版本后台必填文件校验元数据已验证 `.\init.ps1` 开工前通过;`py -3.12 manage.py check` 通过,0 issues;`py -3.12 manage.py makemigrations --check --dry-run` 通过,No changes detected;`py -3.12 manage.py test apps.portal.tests.PortalAccountFlowTests.test_download_release_admin_requires_sha256_and_size_bytes apps.portal.tests.PortalAccountFlowTests.test_download_release_admin_saves_valid_release_metadata apps.api.tests.ClientLatestReleaseApiTests.test_latest_release_returns_null_size_bytes_when_not_configured apps.api.tests.ClientLatestReleaseApiTests.test_latest_release_is_public_without_api_key_and_returns_current_release --keepdb --noinput --verbosity 2` 通过,4 tests OK。测试期仍保留 allauth 在 MySQL 条件唯一约束上的既有 `models.W036` 警告。
- 最新验证:T-617 桌面端版本文件大小字段已验证 `py -3.12 manage.py makemigrations --check --dry-run` 通过,No changes detected;`py -3.12 manage.py check` 通过,0 issues;`py -3.12 manage.py test apps.api.tests.ClientLatestReleaseApiTests --keepdb --noinput --verbosity 2` 通过,12 tests OK,覆盖 `size_bytes` 有值、未配置为 `null`、未发布不返回顶层字段、响应字段白名单和 admin 字段可见;`py -3.12 manage.py migrate portal --noinput` 120 秒超时,但 `py -3.12 manage.py showmigrations portal` 确认 `portal.0004_downloadrelease_size_bytes` 已应用,且无残留 migrate 进程。测试期仍保留 allauth 在 MySQL 条件唯一约束上的既有 `models.W036` 警告。
- T-614 实现取舍补充:worker 执行前会基于任务快照复跑 `prepare_generation()`,即复审 prompt 并重解析别名 / Provider / 定价;账务使用已预扣 `CallRecord.points_cost`,不会重复扣点。短队列下这是偏安全取舍,词库变更后排队任务仍可被拦截并退款;若后续队列积压或频繁切换模型,应单独做提交时模型配置快照。异步 submit 对 `image_base64` 只解码并保存输入文件,桌面端主链路推荐继续使用;`image_url` 会在 submit 阶段完成 SSRF 校验、远程下载和大小限制,可能阻塞提交请求,属于边缘兼容路径。
- T-615 生产代码补充:已新增 `apps.api.telemetry`,旧同步 `POST /api/v1/generate/image` 与新异步提交 `POST /api/v1/generate/image/tasks` 都写 `cmhub.api.generation_usage` 结构化日志事件 `generation_route_usage`;字段白名单为 `route_type`、API Key ID/前缀、`user_id`、`X-Client-Version`、别名、状态、耗时、错误码和 HTTP 状态,不记录 API Key 明文、prompt、图片 base64 或 provider raw。
- 异步生图 worker 排障补充:`run_image_tasks` 每处理一个任务会输出 `event=image_task_processed task_id=... status=... alias=... attempt=... max_attempts=... retrying=... next_attempt_at=... duration_ms=...`,重试中 / 失败 / 过期任务额外输出 `error_code`;日志不包含 prompt、`image_base64`、provider raw 或密钥。生产扩容 worker 应按 2、4、8、16 逐级观察队列长度、耗时、错误码、MySQL 连接数和上游失败率,不建议直接扩到 100。
- 最新验证:T-616 生图失败自动重试 2 次已验证 `py -3.12 -m py_compile apps\api\generation.py apps\api\image_tasks.py apps\api\management\commands\run_image_tasks.py apps\api\models.py apps\api\admin.py apps\api\tests.py config\settings.py` 通过;`py -3.12 manage.py makemigrations api` 生成 `api.0002_imagegenerationtask_next_attempt_at_and_more`;`py -3.12 manage.py migrate api --noinput` 已把 `api.0001` / `api.0002` 应用到当前开发库;`py -3.12 manage.py check` 通过,0 issues;`py -3.12 manage.py makemigrations --check --dry-run` 通过,No changes detected;T-616 新增 5 条目标测试通过,覆盖首次超时回队列不退点、两次超时后三次成功只扣一次、连续三次超时最终只退一次、非重试错误立即退款、worker 重试日志字段和敏感信息不泄露;异步生图旧行为回归 12 tests OK;`py -3.12 manage.py test apps.api.tests.GenerateApiTests --keepdb --noinput --verbosity 2` 通过,37 tests OK;`.\init.ps1` 通过;`git diff --check` 通过,仅 Windows CRLF 提示。测试期仍保留 allauth 在 MySQL 条件唯一约束上的既有 `models.W036` 警告。
- 最新验证:T-615 旧同步生图接口遥测已验证 `py -3.12 -m py_compile apps\api\telemetry.py apps\api\views.py apps\api\tests.py` 通过;新增 3 条目标测试通过,覆盖旧同步成功日志、新异步提交成功日志、异步余额不足错误日志,确认可按 client version / api_key / route_type 查询且不泄露 prompt、base64、完整 API Key 或 provider raw;`py -3.12 manage.py check` 通过,0 issues;`py -3.12 manage.py makemigrations --check --dry-run` 通过,No changes detected;`py -3.12 manage.py test apps.api.tests.GenerateApiTests --keepdb --noinput --verbosity 2` 通过,31 tests OK;`.\init.ps1` 通过;`git diff --check` 通过,仅 Windows CRLF 提示。测试期仍保留 allauth 在 MySQL 条件唯一约束上的既有 `models.W036` 警告。
- 用户端导航:顶部导航 active 状态已修复,`portal/base.html` 基于 `request.resolver_match.url_name` 高亮当前页面入口,并用 `aria-current="page"` 标记;「充值」不再在非充值页固定深色高亮。
- 最新验证:T-614 生图异步任务化接口已验证 `py -3.12 manage.py makemigrations api` 生成 `api.0001_initial`;`py -3.12 manage.py test apps.api.tests.GenerateApiTests -v 2 --keepdb` 通过,28 tests OK,覆盖敏感词拦截不建任务/不扣点、余额不足 402、Idempotency-Key 去重和冲突、worker 成功轮询同一 URL、跨用户拒绝、失败/超时退款、reaper 僵任务退款、重复 worker 幂等和 reaper 退款后的迟到 worker 不可改回成功。首次不带 `--keepdb` 运行时因已有 `test_cmhub` 触发交互式删除确认导致 EOF 中断;重跑 `--keepdb` 通过。测试期仍保留 allauth 在 MySQL 条件唯一约束上的既有 `models.W036` 警告。
- 最新验证:T-613 抽生成核心 service 已验证 `.\init.ps1` 开工前通过;`py -3.12 -m py_compile apps\api\generation.py apps\api\storage.py apps\api\views.py apps\api\tests.py` 通过;`py -3.12 manage.py check` 通过,0 issues;`py -3.12 manage.py makemigrations --check --dry-run` 通过,No changes detected;T-613 新增核心 service 目标测试 2 tests OK;`py -3.12 manage.py test apps.api.tests.GenerateApiTests --keepdb --noinput --verbosity 1` 通过,19 tests OK;`.\init.ps1` 收尾通过;`git diff --check` 通过,仅 Windows CRLF 提示。曾尝试 `py -3.12 manage.py test apps.api --keepdb --noinput --verbosity 1`,命令 484 秒超时无断言结果;拆跑非生成 API 测试类时在测试库 setup 阶段出现远程 MySQL `43.128.3.240` 连接超时 `OperationalError(2003)`,期间 `Test-NetConnection 43.128.3.240 -Port 3306` 显示端口可达。测试期仍保留 allauth 在 MySQL 条件唯一约束上的既有 `models.W036` 警告。
- 最新验证:T-612 生图同步接口止血已验证 `py -3.12 -m py_compile config\settings.py apps\ai\providers\utils.py apps\ai\providers\openai_compatible.py apps\api\generation.py apps\ai\tests.py apps\api\tests.py` 通过;`py -3.12 manage.py check` 通过,0 issues;`py -3.12 manage.py makemigrations --check --dry-run` 通过,No changes detected;生图 Provider 目标测试 3 tests OK,确认生图上游请求与返回图片 URL 下载会被 `AI_IMAGE_UPSTREAM_DEADLINE_SECONDS` 截断,文本生成不受该硬截止影响;`GenerateApiTests.test_image_upstream_timeout_refunds_precharged_points_and_marks_call_failed` 通过;`GenerateApiTests` 整组 17 tests OK;`.\init.ps1` 收尾通过;`git diff --check` 通过,仅 Windows CRLF 提示。测试期仍保留 allauth 在 MySQL 条件唯一约束上的既有 `models.W036` 警告。
- 最新验证:T-611 用户端品牌名统一为虾皮圈已验证 `py -3.12 manage.py check` 通过,0 issues;`py -3.12 manage.py makemigrations --check --dry-run` 通过,No changes detected;`git diff --check` 通过,仅 Windows CRLF 提示;`py -3.12 manage.py test apps.portal.tests.PortalAccountFlowTests.test_homepage_is_public_and_shows_anonymous_onboarding_without_release apps.portal.tests.PortalAccountFlowTests.test_homepage_shows_current_download_release apps.portal.tests.PortalAccountFlowTests.test_portal_pages_use_shopee_circle_branding --keepdb --noinput --verbosity 2` 通过,3 tests OK。测试期仍保留 allauth 在 MySQL 条件唯一约束上的既有 `models.W036` 警告。
- 测试:T-601 已验证:`.\init.ps1` 通过(Python 3.12.3,依赖已满足,`manage.py check` 0 issues,打印启动命令);`py -3.12 -m py_compile apps\ai\catalog.py apps\api\views.py apps\api\urls.py apps\api\tests.py apps\portal\views.py apps\portal\urls.py apps\portal\tests.py` 通过;`py -3.12 manage.py check` 通过;`py -3.12 manage.py makemigrations --check --dry-run` 无变化;`git diff --check` 通过;`py -3.12 manage.py test apps.api.tests.ModelsCatalogApiTests apps.portal.tests.PortalAccountFlowTests.test_models_page_requires_session_login apps.portal.tests.PortalAccountFlowTests.test_models_page_lists_public_aliases_prices_and_unpriced_state --keepdb --noinput --verbosity 2` 首次运行时 3 条 API 测试已通过,portal `setUpClass` 阶段因远程 MySQL 连接 43.128.3.240 超时中断;随后 `Test-NetConnection 43.128.3.240 -Port 3306` 显示 `TcpTestSucceeded=True`,单独重跑两条 portal 测试通过,2 tests OK。T-403 已验证:`./init.ps1` 开工前通过;`py -3.12 manage.py check` 通过;`py -3.12 manage.py makemigrations --check --dry-run` 无变化;`py -3.12 manage.py findstatic portal/vendor/bootstrap/bootstrap.min.css portal/vendor/qrcode/qrcode.js --verbosity 1` 找到两个本地 static 文件;`py -3.12 manage.py check --deploy` 在当前开发 `.env` 下仅报预期安全配置警告,临时注入生产型安全环境变量(含 HSTS includeSubDomains/preload)后通过,0 issues;`py -3.12 manage.py collectstatic --dry-run --noinput` 通过,预期收集 169 个 static 文件;`py -3.12 manage.py createcachetable --dry-run cmhub_cache` 通过,输出 MySQL cache 表 DDL;`py -3.12 -m compileall config` 通过;尝试 `py -3.12 manage.py test apps.api --noinput --keepdb --verbosity 2` 时 25 条用例已通过,`GenerateApiTests` 14 条因远程 MySQL 连接重置/事务中断被记 ERROR,随后 `Test-NetConnection 43.128.3.240 -Port 3306` 端口可达,单独重跑 `py -3.12 manage.py test apps.api.tests.GenerateApiTests --noinput --keepdb --verbosity 2` 通过,14 tests OK。测试/迁移阶段仍有 allauth `account.EmailAddress` 条件唯一约束在 MySQL 上不可创建的 `models.W036` 警告;本项目用户账本邮箱唯一性由 `user.email` 唯一约束承担。
- 最新验证:T-608 新用户注册赠送试用点数(当前 10 点)已验证 `py -3.12 -m py_compile config\settings.py apps\billing\models.py apps\billing\services.py apps\billing\admin.py apps\portal\adapters.py apps\portal\views.py apps\billing\tests.py apps\portal\tests.py` 通过;`py -3.12 manage.py check` 通过,0 issues;`py -3.12 manage.py makemigrations --check --dry-run` 通过,No changes detected;`py -3.12 manage.py migrate billing --noinput` 通过,No migrations to apply;注册赠点服务和并发幂等目标测试 3 tests OK;注册限流配置测试 1 test OK;portal 注册赠点、首页文案、导航、dashboard 和点数记录目标测试 6 tests OK;`SignupBonusGrant` admin 注册 smoke 1 test OK;`.\init.ps1` 收尾通过(Python 3.12.3,依赖已满足,`manage.py check` 0 issues,打印启动命令);`git diff --check` 通过,仅 Windows CRLF 提示。测试期仍有 allauth 在 MySQL 条件唯一约束上的既有 `models.W036` 警告;一次 portal 目标测试启动阶段曾因远程 MySQL 连接 `43.128.3.240` 超时中断,端口探测恢复后重跑通过。
- 最新验证:T-607 桌面端最新版本检查接口已验证 `.\init.ps1` 开工前通过;`py -3.12 -m py_compile apps\api\views.py apps\api\urls.py apps\api\tests.py` 通过;`py -3.12 manage.py check` 通过,0 issues;`py -3.12 manage.py makemigrations --check --dry-run` 通过,No changes detected;`py -3.12 manage.py test apps.api.tests.ClientLatestReleaseApiTests --keepdb --noinput --verbosity 2` 通过,9 tests OK;`py -3.12 manage.py test apps.api.tests.ApiKeyAuthenticationTests apps.api.tests.BalanceApiTests apps.api.tests.ModelsCatalogApiTests apps.api.tests.ClientLatestReleaseApiTests --keepdb --noinput --verbosity 2` 通过,25 tests OK;`py -3.12 manage.py test apps.api --keepdb --noinput --verbosity 1` 通过,54 tests OK;`.\init.ps1` 收尾通过;`git diff --check` 通过,仅 Windows CRLF 提示。测试期仅保留 allauth 在 MySQL 条件唯一约束上的既有 `models.W036` 警告。
- 最新验证:T-606 公开首页 + 客户端下载入口已验证 `.\init.ps1` 通过(Python 3.12.3,依赖已满足,`manage.py check` 0 issues,打印启动命令);`py -3.12 -m py_compile apps\portal\models.py apps\portal\admin.py apps\portal\views.py apps\portal\urls.py apps\portal\tests.py` 通过;`py -3.12 manage.py check` 通过,0 issues;`py -3.12 manage.py makemigrations --check --dry-run` 通过,No changes detected;`py -3.12 manage.py migrate portal` 已应用 `portal.0001_initial` 到当前数据库;`py -3.12 manage.py shell -c "... Client().get('/') ..."` 返回 `STATUS=200`、首页标识和下载区标识均存在;`py -3.12 manage.py findstatic portal/brand.css portal/vendor/bootstrap/bootstrap.min.css portal/vendor/qrcode/qrcode.js --verbosity 1` 均找到;`py -3.12 manage.py test apps.portal.tests.PortalAccountFlowTests.test_homepage_is_public_and_shows_anonymous_onboarding_without_release apps.portal.tests.PortalAccountFlowTests.test_homepage_shows_current_download_release apps.portal.tests.PortalAccountFlowTests.test_homepage_shows_dashboard_entry_for_authenticated_user apps.portal.tests.PortalAccountFlowTests.test_download_release_only_keeps_one_current_per_platform apps.portal.tests.PortalAccountFlowTests.test_authenticated_nav_highlights_current_page_only --noinput --verbosity 2` 通过,5 tests OK。`py -3.12 manage.py test apps.portal.tests.PortalAccountFlowTests --keepdb --noinput --verbosity 2` 已重跑,因远程 MySQL `43.128.3.240` 在测试库迁移阶段连接超时(2003 / WinError 10060)中断,不是断言失败;数据库恢复后需补跑 portal 整组。
- 最新验证:T-605 免邮箱验证策略已验证 `py -3.12 -m py_compile config\settings.py apps\portal\tests.py` 通过;`py -3.12 manage.py check` 通过,0 issues;`py -3.12 manage.py makemigrations --check --dry-run` 通过,No changes detected;更新后的注册 / 登录目标测试 3 tests OK;`py -3.12 manage.py test apps.portal --keepdb --noinput --verbosity 2` 通过,26 tests OK;`git diff --check` 通过,仅 Windows CRLF 提示。全量 `py -3.12 manage.py test --keepdb --noinput --verbosity 2` 已尝试,发现 131 tests,跑到 95 tests 后远程 MySQL `43.128.3.240` 出现连接超时 / WinError 10051,最终 22 errors;随后 `Test-NetConnection 43.128.3.240 -Port 3306` 显示 `TcpTestSucceeded=True`,单独复跑失败起点的 billing 分组时 17 条业务用例 OK,并发扣点用例本体打印 `ok` 后在测试库 flush 阶段再次因 MySQL 连接超时 / WinError 10060 记 ERROR。结论:T-605 相关测试已通过,全量单次绿受远程 MySQL 稳定性阻塞。
- 最新验证:T-604 中文敏感词本地过滤已验证 `py -3.12 -m py_compile config\settings.py apps\api\generation.py apps\api\tests.py apps\moderation\apps.py apps\moderation\models.py apps\moderation\normalization.py apps\moderation\policy.py apps\moderation\services.py apps\moderation\signals.py apps\moderation\versioning.py apps\moderation\providers\base.py apps\moderation\providers\keyword.py apps\moderation\tests.py` 通过;`py -3.12 manage.py check` 通过,0 issues;`py -3.12 manage.py makemigrations --check --dry-run` 通过,No changes detected;`py -3.12 manage.py test apps.moderation --keepdb --noinput --verbosity 2` 通过,7 tests OK;新增生成接口目标测试 2 tests OK;`py -3.12 manage.py test apps.api.tests.GenerateApiTests --keepdb --noinput --verbosity 2` 通过,16 tests OK。测试期仅保留 allauth 在 MySQL 条件唯一约束上的既有 `models.W036` 警告。
- 最新验证:T-603 django-admin 字段级中文化已收尾:人工确认 `/admin/` 字段标签中文化;`py -3.12 manage.py check` 通过,0 issues;`py -3.12 manage.py makemigrations --check --dry-run` 通过,No changes detected;`py -3.12 manage.py test apps.users.tests apps.billing.tests.BillingAdminTests apps.ai.tests.AiModelAdminTests apps.ai.tests.AiConfigAuditAdminTests --keepdb --noinput --verbosity 2` 通过,15 tests OK。测试期仅保留 allauth 在 MySQL 条件唯一约束上的既有 `models.W036` 警告。
- 线上 AI 标题生成缺陷已修复:2026-07-06 用当前线上 `AI_KEY_ENCRYPTION_KEY` 重新导入 `/secure/cmhub/ai_models.json`,修复 `title-standard` 对应 `AiModel.api_key_encrypted` 无法解密的问题。验证:`python3.12 manage.py check` 通过;`python3.12 manage.py smoke_ai_generation title` 真实调用成功,`model_used=gpt-5.5`,耗时约 16.9s,返回 3 个标题;用 `api_key.txt` 第二个 Key 调 `POST https://cm.833729.com/api/v1/generate/title` 成功返回 5 个标题,`points_cost=1`,余额从 1010 扣到 1009。
- 线上热修验证:`/admin/billing/exchangerate/` 500 根因是 `ExchangeRateAdmin.date_hierarchy` 触发 MySQL `CONVERT_TZ`,而生产 MySQL 未加载时区表,Django 抛 `ValueError: Database returned an invalid datetime value`;已移除 `apps.billing.admin` 中 4 个 DateTime `date_hierarchy`,保留 `list_filter` 日期筛选。验证 `py -3.12 -m py_compile apps\billing\admin.py apps\billing\tests.py`、`py -3.12 manage.py check`、`py -3.12 manage.py test apps.billing.tests.BillingAdminTests.test_exchange_rate_changelist_renders_when_rates_exist --keepdb --noinput --verbosity 2` 均通过。
- Mock 支付提示:`PAYMENT_CALLBACK_MODE=mock` 时,用户端 `/recharge` 的待支付订单区域会显示醒目测试模式提示,说明二维码只用于联调订单创建、页面渲染和状态轮询,不能使用微信或支付宝真实付款;`sdk` 模式不显示该提示。验证 `py -3.12 -m py_compile apps\portal\views.py apps\portal\tests.py`、`py -3.12 manage.py check`、`py -3.12 manage.py test apps.portal.tests.PortalAccountFlowTests.test_recharge_page_post_creates_pending_order_without_crediting_wallet_or_ledger apps.portal.tests.PortalAccountFlowTests.test_recharge_page_does_not_show_mock_notice_in_sdk_mode --keepdb --noinput --verbosity 2` 均通过。
- 微信正式下单热修:线上微信 Native 下单失败根因是 `wechatpayv3` SDK 需要显式 `pay_type=WeChatPayType.NATIVE`,且成功响应为 `(status_code, json_string)` tuple 而非 dict;已兼容 tuple/json 响应并覆盖下单与主动查单。线上用修复后的 `_create_wechat_payment_order_with_sdk()` 创建 1 分诊断预支付请求,确认返回 `weixin://wxpay/bizpayurl` 票据。另:pending 订单主动查单异常不再让 `/api/v1/recharge/status` 返回 500,而是保持当前订单状态返回 200。
- 支付宝临时下线:线上支付宝预下单返回 `isv.illegal-client-ip`(当前调用 IP 不在支付宝可信名单中),需在支付宝开放平台把 VPS 出口 IP `43.128.3.240` 加入可信 IP 后再恢复。因此用户端 `/recharge` 表单暂时只展示并接受微信支付;底层 API、模型枚举、回调与支付宝 SDK 路径保留不删。
- 数据:AI 上游调用与模型配置参考 `D:\chengma\cmbot`(`src/services/ai_text_service.py`、`ai_image_service.py`、`config/ai_models.json`);真实 `ai_models.json` 不提交,需通过 `import_ai_models` 命令加密导入
- 标准启动路径:Windows 用 `./init.ps1`;Unix/WSL 用 `./init.sh`
- 标准验证路径:Windows 用 `py -3.12 manage.py check` / `py -3.12 manage.py test`
- 设计基线:**自助用户端 + 对外 API + 运营后台**三合一单体;用户模型 `User`(auth)/`UserWallet`(点数,锁 wallet 扣点)/`ApiKey`(1:N,哈希存储);生成标题、生成图片和多图理解均通过**能力别名 + Provider 适配器**对外提供,不暴露具体供应商模型;自助扫码充值;新用户注册成功一次性赠送 10 点试用点数,必须经 billing 写 `signup_bonus` 流水,且通过 `SignupBonusGrant(user UNIQUE)` 防重复发放。详见 `04-architecture.md` 与 2026-07-08 的 `progress.md` 决策
- 配置基线:运行环境变量集中见 `docs/env.md`;真实密钥/支付凭证不得写入代码或文档样例。用户端注册策略固定为 `ACCOUNT_EMAIL_VERIFICATION="none"`:免邮箱验证、注册即可用、邮箱仍必填且唯一;`DJANGO_EMAIL_BACKEND` / `DJANGO_DEFAULT_FROM_EMAIL` 仅用于后续密码找回、通知或恢复邮箱验证等邮件能力,不作为当前注册登录前置条件。充值订单在创建时锁定汇率与预计点数,回调入账使用订单值,不按新汇率重算。支付回调与下单由 `PAYMENT_CALLBACK_MODE` 控制:本地/测试可用 HMAC `mock`,生产应为 `sdk`;二维码本地有效期提示由 `PAYMENT_QR_EXPIRES_MINUTES` 控制。T-306 起对外 API 安全配置包含 `API_GENERATE_THROTTLE_RATE`、`API_AUTH_FAILURE_THROTTLE_RATE`、`IMAGE_URL_MAX_BYTES`、`IMAGE_URL_MAX_REDIRECTS`、`IMAGE_URL_CONNECT_TIMEOUT_SECONDS`、`IMAGE_URL_READ_TIMEOUT_SECONDS`、`RECHARGE_MAX_AMOUNT_CNY`。T-403 起生产静态、共享 cache 与 HTTPS 安全配置包含 `STATIC_URL`、`STATIC_ROOT`、`DJANGO_CACHE_BACKEND`、`DJANGO_CACHE_LOCATION`、`DJANGO_CSRF_TRUSTED_ORIGINS`、`DJANGO_SESSION_COOKIE_SECURE`、`DJANGO_CSRF_COOKIE_SECURE`、`DJANGO_SECURE_SSL_REDIRECT`、`DJANGO_SECURE_PROXY_SSL_HEADER`、`DJANGO_SECURE_HSTS_SECONDS`。T-604 起内容安全配置包含 `MODERATION_ENABLED`、`MODERATION_PROVIDER=keyword`、`MODERATION_FAIL_CLOSED`、`MODERATION_BLOCK_ON_REVIEW`、`MODERATION_CACHE_VERSION_KEY`;生产多 worker 下必须使用共享 cache 承载敏感词版本号。T-612 起生图同步接口止血配置包含 `AI_IMAGE_UPSTREAM_DEADLINE_SECONDS`,只作用于 `generate_image` 上游读取硬截止。T-614/T-616 起异步生图配置包含 `PUBLIC_BASE_URL`、`MEDIA_PUBLIC_BASE_URL`、`IMAGE_TASK_RETENTION_HOURS`、`GENERATED_IMAGE_RETENTION_HOURS`、`IMAGE_TASK_REAPER_INTERVAL_SECONDS`、`IMAGE_TASK_LEASE_SECONDS`、`IMAGE_TASK_MAX_RETRIES`、`IMAGE_TASK_RETRY_BACKOFF_SECONDS`;生产必须运行 `run_image_tasks` worker。
- 当前 blocker:微信正式下单已能返回二维码,但线上微信回调曾出现 `PaymentVerificationError`,仍需单独修复并完成“付款后自动入账”闭环验收;支付宝恢复依赖开放平台把 `43.128.3.240` 加入可信 IP。线上真实标题生成已跑通并验证扣点;图片同步旧接口已由 T-612 加上游硬截止止血,T-614 已提供异步任务化路径,T-615 已补旧同步 / 新异步提交遥测和旧路弃用口径。
## 当前目录要点
| 路径 | 状态 | 说明 |
| --- | --- | --- |
| `docs/` | 已有 | 全套 harness 文档;T-403 新增 `deployment.md` |
| `AGENTS.md` / `CLAUDE.md` | 已有 | 仓库级入口 |
| `progress.md` | 已有 | 执行流水,已记录多轮文档决策;后续任务继续追加 |
| `init.sh` / `init.ps1` | 已有 | 启动验证入口,已固定系统 Python 3.12 命令,并校验解释器版本 `>=3.12,<3.14` |
| `requirements.txt` / `requirements-production.txt` / `pyproject.toml` | 已有 | `requirements.txt` 管本地/基础运行依赖;`requirements-production.txt` 追加 Linux 生产 Gunicorn;`pyproject.toml` 落地 `requires-python`;T-101 新增 `requests`;T-102 使用既有 `cryptography` 做 Fernet 加密;T-501 新增 `django-allauth` |
| `config/`(Django 工程) | 已有 | T-001 创建,含 settings / urls / wsgi / asgi |
| `apps/`(users/portal/billing/ai/api/moderation) | 已有 | T-002 创建;`apps/users` 已定义自定义 `User`;T-003 已注册 admin 与 admin smoke test;T-004 已给 `User.email` 加唯一约束;T-101 已新增 `apps/ai/providers`;T-102 已新增 `apps/ai/security.py`、`aliases.py`、`importers.py`、management command 与 `ai.0001_initial` 迁移;T-103 已新增 `apps/ai/audit.py` 与 `ai.0002_aiconfigauditlog` 迁移;T-104/T-105 已新增 `smoke_ai_generation` 录制 title/image smoke 命令;T-201 已在 users 落 `UserWallet` / `ApiKey`,在 billing 落 `PointsLedger` / `CallRecord`;T-202 已在 billing 落 `PricingRule` / `ExchangeRate` 与 `pricing.py`;T-203/T-303/T-304/T-305 已在 `apps/billing/services.py` 落扣点/退点、余额快照、充值入账与充值下单;T-401 已在 `apps/billing/services.py` 落 `adjust_wallet_points()`,在 `apps/users/admin.py` 与 `apps/users/templates/admin/users/userwallet/adjust_points.html` 落钱包手工调点入口;T-608 已在 billing 落 `SignupBonusGrant`、`grant_signup_bonus()`、`signup_bonus` 流水类型、admin 检索和迁移;T-304/T-305 已在 `apps/billing/payment_gateways.py` 落回调验签、mock 下单与 SDK 入口;T-301~T-306 已在 api 落鉴权、生成接口编排、序列化器、图片存储、余额查询、充值回调、充值下单/状态查询、`image_url` SSRF 防护、生成/认证限流与统一 429 错误响应;T-501/T-608 已在 portal 落 allauth 注册/登录/登出路由、模板、adapter 与 dashboard,注册成功自动赠送 10 点;T-502 已在 portal 落 `/apikeys`、API Key 创建表单、列表模板与删除(吊销)动作;T-503/T-608 已在 portal 落个人中心汇总、充值记录和点数记录页;T-504 已在 portal 落 `/recharge` 充值页、充值表单、二维码票据展示和状态轮询;T-505 已在 portal 落本地 vendor static 与记录分页;T-601 已在 `apps/ai/catalog.py` 落公开模型目录,在 api 落 `/api/v1/models`,在 portal 落 `/models` 页面和导航入口;T-604 已在 moderation 落 `SensitiveWord`、keyword matcher、归一化、共享 cache 版本失效与 admin;T-606 已在 portal 落公开首页、`DownloadRelease`、下载版本 admin、`brand.css` 与首页模板;T-607/T-609 已在 api 落 `/api/v1/client/releases/latest` 公开版本检查接口,并在 portal `DownloadRelease` 落 `force_update` 字段与 admin 展示 |
| `apps/api` 异步生图 | 已有 | T-614 已落 `ImageGenerationTask`、`api.0001_initial`、异步提交/轮询路由、任务 service、worker lease / heartbeat / reaper、`run_image_tasks` 命令和 admin 只读排障;T-616 已落 `api.0002`、`next_attempt_at`、临时性失败自动重试和轮询响应新增重试字段;旧同步生图接口保留 |
| `manage.py` | 已有 | T-001 创建 |
| `tests/` | 待建 | 随各任务补充 |
## 任务看板状态
任务状态以 [`06-tasks.md`](06-tasks.md) 为准,历史执行记录见 [`../progress.md`](../progress.md)。
- 已完成:T-001 初始化 Django + DRF 项目骨架;T-002 建立 apps 目录、自定义 User 与配置;T-003 接通 django-admin 与最小测试;T-004 Phase 0 骨架审核修补;T-101 Provider 适配器层 + 移植 cmbot 调用;T-102 AiModel + ModelAlias 模型 + 别名解析;T-103 配置变更审计;T-104 跑通一次录制标题生成;T-105 Phase 1 AI 层审核修补;T-201 User / UserWallet / ApiKey / PointsLedger / CallRecord 模型;T-202 PricingRule / ExchangeRate 模型 + 计费计算;T-203 并发安全扣点 / 退点;T-204 Phase 2 计费核心审核加固;T-301 API Key 鉴权;T-302 生成标题 / 图片接口;T-303 余额查询接口;T-304 充值回调;T-305 扫码充值下单 + 轮询;T-306 Phase 3 对外 API 安全加固;T-501 注册 / 登录(allauth);T-502 API Key 自助管理页;T-503 个人中心 / 记录页;T-504 充值页(扫码 + 轮询到账);T-505 Phase 4 用户端审核优化;T-401 运营后台完善;T-402 完整验收 MVP;T-403 部署 / 运行文档;T-601 可用别名发现;T-602 django-admin 中文化(第 1-3 层);T-603 django-admin 中文化(第 4 层·字段级);T-604 中文敏感词本地过滤;T-605 免邮箱验证策略落地;T-606 公开首页 + 客户端下载入口;T-607 桌面端最新版本检查接口;T-608 新用户注册赠送试用点数(当前 10 点);T-609 桌面端版本检查接口增加强制更新标记;T-610 首页导入模板下载入口;T-611 用户端品牌名统一为虾皮圈;T-612 生图同步接口止血(上游硬截止 + 长请求池校准);T-613 抽生成核心 service(计费+审核+上游共享 core);T-614 生图异步任务化接口(提交+轮询,新增不动旧接口);T-615 旧同步生图接口用量遥测 + 弃用口径;T-616 生图失败自动重试 2 次;T-617 桌面端版本检查接口增加文件大小字段;T-618 客户端发布版本后台必填文件校验元数据。
- 已完成补充:T-619 多张图片理解并返回文字;T-620 图生图支持单图 / 多图主图与参考图;T-622 图片生成任务后台图片缩略预览;T-623 图片生成任务单图 / 多图筛选;T-624 蝦皮圈设备登记与会话观测;T-625 蝦皮圈设备使用关联与迁移观测;T-626 软件套餐、权益与设备席位基础模型;T-627 存量用户迁移权益、网页确认与设备凭证。
- 正在进行:T-629 软件套餐购买、续订订单与权益入账;先修 T-626 的凭证续期同步与撤销清理,再建设与充值隔离的软件订单、支付回调和 portal。
- 待开始:T-621 注册赠点运营后台配置继续留在 Backlog。真实支付回调到账闭环、客户端发布、生产多图理解模型配置和线上旧同步接口用量观察仍可继续拆任务。
- 当前 blocker:支付商户真实密钥/证书与生产 SDK 依赖仍待提供;微信回调到账闭环仍需真实支付验收;真实 AI 标题生成已在线上跑通,图片生成慢 / 504 / 客户端超时风险已拆为 T-612~T-616 并完成工程侧处理。
- 当前任务:T-629 软件套餐购买、续订订单与权益入账。软件订阅订单与 `RechargeOrder` / 点数账本隔离;第一版不实现自动退款回收权益,真实支付验收仍依赖微信回调到账闭环。T-621 保留在 Backlog,具体范围见 [`06-tasks.md`](06-tasks.md)。
## 当前可运行内容
```bash
# Windows PowerShell
py -3.12 -m pip install -r requirements.txt
py -3.12 manage.py check
py -3.12 manage.py test
py -3.12 manage.py runserver
py -3.12 manage.py import_ai_models path\to\ai_models.json --create-default-aliases
py -3.12 manage.py smoke_ai_generation title --recorded
py -3.12 manage.py smoke_ai_generation image --recorded
# Unix / WSL
python3.12 -m pip install -r requirements.txt
python3.12 manage.py check
python3.12 manage.py test
python3.12 manage.py runserver
python3.12 manage.py import_ai_models path/to/ai_models.json --create-default-aliases
python3.12 manage.py smoke_ai_generation title --recorded
python3.12 manage.py smoke_ai_generation image --recorded
# Linux production
python3.12 -m pip install -r requirements-production.txt
python3.12 manage.py migrate
python3.12 manage.py createcachetable cmhub_cache
python3.12 manage.py collectstatic --noinput
gunicorn config.wsgi:application --bind 127.0.0.1:8001 --workers 2 --worker-class gthread --threads 4 --timeout 120
gunicorn config.wsgi:application --bind 127.0.0.1:8002 --workers 1 --worker-class gthread --threads 4 --timeout 360
python3.12 manage.py run_image_tasks --worker-id cmhub-image-worker-1 --sleep-seconds 1
```
当前对外 API:
- `POST /api/v1/generate/title`
- `POST /api/v1/analyze/images`
- `POST /api/v1/client/devices/register`
- `POST /api/v1/client/devices/heartbeat`
- `POST /api/v1/generate/image`
- `POST /api/v1/generate/image/tasks`
- `GET /api/v1/generate/image/tasks/{task_id}`
- `GET /api/v1/balance`
- `GET /api/v1/models`
- `GET /api/v1/client/releases/latest`
- `POST /api/v1/recharge/create`(用户端 session + CSRF)
- `GET /api/v1/recharge/status`(用户端 session)
- `POST /api/v1/recharge/callback/wechat`
- `POST /api/v1/recharge/callback/alipay`
当前用户端页面:
- `GET /`
- `GET/POST /signup`
- `GET/POST /login`
- `GET/POST /logout`
- `GET /dashboard`
- `GET/POST /recharge`
- `GET/POST /apikeys`
- `POST /apikeys/<id>/delete`
- `GET /models`
- `GET /records/recharge`
- `GET /records/usage`
当前骨架可运行。T-002 已在首次迁移前创建自定义 User,并按 `env.md` 接入 MySQL 8.4 / utf8mb4;远程 MySQL 已完成 Django 初始迁移。T-003 已接通 django-admin,测试可创建/销毁 `test_cmhub` 测试库;当前远程 MySQL 对频繁建库/销库仍可能间歇超时,必要时用 `--keepdb` 且串行跑测试。T-004 已应用 `users.0002_alter_user_email`,`user.email` 已有唯一索引。T-101 的 AI provider 层只做 HTTP 调用与响应解析;T-102 已把 provider 运行配置接到数据库 `AiModel` / `ModelAlias`,`resolve_alias()` 每次查当前 active 配置并按 `text` / `image` 能力校验。T-103 已补 `AiConfigAuditLog`,admin 保存/删除 `AiModel` / `ModelAlias` 时记录 actor、action、target、changed_fields、changes、created_at,密钥只记录 empty/set 状态。T-104/T-105 已用临时回滚配置跑通录制标题和录制图片生成。T-201 已落地钱包、API Key、点数流水和调用记录:API Key 明文只在创建 helper 返回,库内只存 hash/prefix;CallRecord 只存 `result_ref`/`result_summary`,没有 provider raw 字段。T-202 已落地 `PricingRule` / `ExchangeRate`:计费按 `operation_type + alias + resolution` 查 active 规则,优先精确分辨率,再回退默认价;缺规则抛 `NoPricingRuleError(code="no_pricing_rule")`;金额换点数按当前 active 汇率向下取整。T-203 已落地 `precharge_call()` / `mark_call_success()` / `refund_call_points()`:预扣锁钱包行,余额不足不写调用/流水;失败退点锁调用记录并幂等写 refund 流水。T-204 已完成复合唯一约束加固,并取得一次完整 `manage.py test` 单次全绿。T-301 已落地 `Authorization: Bearer <API_KEY>` 鉴权:成功后 `request.user` 为所属用户、`request.auth` 为 `ApiKey`,缺失/无效 Key 返回 401,用户或 Key 禁用返回 403,外部 API 不接受 Web session。T-302/T-613 已落地生成接口与共享核心:旧同步接口仍保持原响应契约;`prepare_generation()` 执行 prompt 审核、图片输入处理、别名解析、Provider 选择和计费计算;`precharge_generation()` 调 billing 预扣;`execute_precharged_generation()` 复用已预扣 `CallRecord` 调上游并成功确认或失败退点;图片结果保存到本地 media 并通过 URL 构建器返回外部 URL。T-303 已落地余额查询接口:`GET /api/v1/balance` 继承外部 API Key 鉴权,读取 billing 余额快照并返回兼容字段 `user` / `points_balance`,以及不含邮箱的 `account.username` / `account.display_name`,测试覆盖余额与流水累加一致。T-304 已落地充值回调:`RechargeOrder` 保存下单锁定的金额/汇率/点数,微信/支付宝回调先验签再按订单幂等入账,重复回调不重复加点,金额不一致不入账;主动查单兜底可调用 `query_and_apply_recharge_payment(order_no, query_func)` 复用同一入账路径。T-305 已落地扫码下单与轮询:用户端 session 登录后可 `POST /api/v1/recharge/create` 创建 pending 订单并拿到 mock/SDK 二维码票据,`GET /api/v1/recharge/status` 只返回本人订单并在 pending 时尝试主动查单补入账;API Key 不能调用这两个用户端接口。T-306 已落地对外 API 安全加固:`image_url` 下载在扣点前做协议白名单、公网地址校验、重定向逐跳校验和响应大小上限;DRF 全局默认不再隐式启用 Session/Basic;生成接口按 Key 限流,认证失败按 IP 限流;充值下单有单笔金额上限。T-501/T-605/T-608 已落地 allauth 注册 / 登录:`ACCOUNT_EMAIL_VERIFICATION="none"`,免邮箱验证、注册即可用,邮箱仍必填且唯一;注册成功经 billing 发放 10 点并写 `signup_bonus` 流水,重复调用或并发触发由 `SignupBonusGrant` 幂等标记兜底,注册限流由 allauth signup rate limit 执行。T-502 已落地 API Key 自助管理:`/apikeys` 登录访问,生成后完整明文只显示一次,列表只显示 prefix,不显示 hash 或历史明文;删除为吊销 `revoked`,吊销后外部 API 返回 403。T-503/T-608 已落地个人中心与记录页:`/dashboard` 展示剩余点数、充值总额、获得点数、净消耗点数和最近记录;`/records/recharge` 展示当前用户充值订单;`/records/usage` 展示当前用户 signup_bonus/consume/refund 点数流水并关联调用信息;所有页面均只读且只查本人。T-504 已落地充值页:`/recharge` GET 展示余额、充值表单、当前订单和最近充值,POST 创建 pending 订单并展示二维码票据,浏览器轮询 `/api/v1/recharge/status`,paid 后刷新页面重新读取余额;页面不直接写钱包或流水。T-401 已落地运营后台完善:用户列表显示钱包余额,钱包余额只读且通过专用表单手工调点,调点必须填原因、非 0、不得扣成负数,并经 `adjust_wallet_points()` 锁钱包写 `PointsLedger(adjust)`;API Key admin 只展示 prefix 和 hash 摘要,不回显明文或完整 hash;订单、流水、注册赠点记录、调用记录继续只读并增强检索。T-402 已完成 MVP P0 验收并新增 `docs/mvp-acceptance.md`。T-403 已完成部署 / 运行文档,生产按 `deployment.md` 执行,并已补 settings 对生产静态目录、共享 cache、CSRF trusted origins、HTTPS cookie/proxy/HSTS 与注册限流的环境变量支持。T-601 已落地可用别名发现:`GET /api/v1/models` 用 API Key 鉴权返回公开别名目录,`/models` 用 session 展示只读「可用模型」页;两者均不解密 provider key,不输出底层 SKU、URL、key 或 `extra_body`。T-603 已完成 django-admin 字段级中文化并人工确认字段标签中文。T-604 已落地本地 prompt 敏感词过滤:`MODERATION_ENABLED=false` 默认 no-op,启用 `keyword` 后命中返回 `content_blocked`,并在下载 `image_url`、解析别名、计费、预扣点和上游调用前拦截。T-606 已落地公开首页:`GET /` 匿名 200、不跳登录;下载区读取 Windows 当前 `DownloadRelease`,优先 `external_url`,无当前版本显示「暂未发布」;现有 portal 页面已共用 `brand.css` 品牌 token。T-607/T-609 已落地公开版本检查接口:`GET /api/v1/client/releases/latest` 匿名 200,无需 API Key;支持 `windows`/`macos`/`linux`,返回当前版本 JSON 或 `release:null`,当前版本响应包含 `release.force_update`。真实标题上游生成已在线上跑通并验证扣点;图片生成真实耗时仍需补测。
T-614 已落地异步生图任务化:提交接口预扣后返回 `task_id`,轮询接口返回 queued/running/succeeded/failed,worker 和 reaper 复用同一套成功确认 / 失败退点路径,旧同步接口继续兼容。当前 worker 会复跑审核 / 别名解析 / 定价,但不重复扣点;`image_url` 输入会在 submit 阶段下载,桌面端主链路应优先用 `image_base64`。T-615 已落地旧同步 / 新异步提交遥测:两个入口都会写 `generation_route_usage` 结构化日志,便于按客户端版本、API Key 和 route_type 观察迁移进度;旧同步接口下线前必须继续保持响应兼容,并单独立下线任务。
T-619 已落地同步多图理解:调用方提交有序 `images` 列表,服务按 `vision + alias` 默认价固定扣点,成功返回文字,失败幂等退款;输入图片和 provider raw 不写入调用记录。代码部署并迁移后,需由运营配置视觉模型、默认别名与默认价格,才会成为可调用能力。
## 开始编码前检查
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-629。
5. T-624~T-629 按设备观测 → 套餐权益 → 存量迁移 → 专属授权影子校验 → 订阅订单顺序执行;T-629 前须保留并验证真实支付回调到账闭环。
## 维护规则
代码状态变化时同步更新本文:新增/移动入口文件、初始化框架或模块、任务状态变化、新增可运行命令、发现文档与代码不一致。
- 任务状态变化同步 [`06-tasks.md`](06-tasks.md)。
- 每轮执行记录、验证命令、阻塞、决策追加到 [`../progress.md`](../progress.md)。
- 本文件只保留当前快照,不保留完整历史。