Files
cmhub/docs/06-tasks.md
T

20 KiB
Raw Blame History

任务看板(Tasks)

把 MVP 拆成小步、可独立交付的任务,让 AI 一步一步开发,避免一次生成整个项目。

使用规则

  1. 一次只做一个任务:每轮只领取一个状态为 TODO、且依赖均已 DONE 的任务,取最靠前的那个。
  2. 做完即停:完成该任务、自测通过、把状态改成 DONE 后,停下来汇报。
  3. 不跳步:依赖未完成的任务不能开工。
  4. 完成定义:以 编码规则 的验证清单为准。
  5. passing 需证据:标记 DONE 前,必须在 ../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 的占位命令 DONE
T-002 建立 apps 目录、自定义 User 与配置 T-001 按 04-architecture.md 建 `apps/users portal
T-003 接通 django-admin 与最小测试 T-001 createsuperuser 后能登录 /admin/;manage.py test 可运行(至少 1 条占位测试通过) DONE
T-004 Phase 0 骨架审核修补 T-003 按 phase-0-review.md 修 P1(User.email 加 unique、requires-python 落地 + init 校验解释器版本)与 P2(INSTALLED_APPS 顺序、补 .env.example);同步修正根 README.md 当前状态;check/test/init 重新全绿并在 ../progress.md 留证据。建议先于 T-101 完成(P1-1 趁 user 表空成本最低) DONE

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 上游单测验证解析与适配器选取 DONE
T-102 AiModel + ModelAlias 模型 + 别名解析 T-101 AiModel 含 capabilities、api_key 加密存储(admin 脱敏不回显);ModelAlias 映射别名→模型;resolve_alias() 能解析并按能力校验;配置迁移自 ai_models.json;后台改配置运行时热生效 DONE
T-103 配置变更审计 T-102 AiModel/ModelAlias/密钥的后台变更留痕(谁、何时、改了什么);可在 admin 查看 DONE
T-104 跑通一次真实/录制的标题或图片生成 T-102 用别名 + 最小输入跑通一次生成,结论写入 progress.md(含耗时,验证图片同步可行性与超时配置) DONE(管路已验;图片同步耗时风险未退,已挂到 T-302/T-403,见 phase-1-review P1-2)
T-105 Phase 1 AI 层审核修补 T-104 按 phase-1-review.md 修 P1(parameters/extra_body 透传白名单化,核心/计费字段不可被调用方覆盖,含单测;T-104 结论改写为「图片同步风险未退」并把该未决风险显式登记到 T-302/T-403)与 P2(.env.example 的 AI_DEFAULT_* 超时键接上或删除、resolution_to_size 大小写归一、默认别名口径对齐);check/test/init 全绿并在 ../progress.md 留证据。P1-1 建议先于 T-302 完成(否则易顺手 parameters=request.data 造成越权计费) DONE

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;调用结果只存 result_ref/摘要,不 dump provider raw、base64 图片或敏感上游字段;admin 注册 DONE
T-202 PricingRule / ExchangeRate 模型 + 计费计算 T-201, T-102 按「操作 + 能力别名(+ 可选分辨率)」定价;换底层模型不影响计费;缺规则返回 no_pricing_rule DONE
T-203 并发安全扣点 / 退点(billing 层) T-201 锁 UserWallet 行或 F() 原子扣减;并发测试不超扣、不为负;失败退点写流水;含测试 DONE
T-204 Phase 2 计费核心审核加固 T-203 按 phase-2-review.md 处理 P2(points_ledger 加 MySQL 可落地的 ref_call + change_type 复合唯一约束,作为「每 ref_call 最多一条 REFUND」DB 兜底,不要使用 MySQL 不支持的 partial unique / 条件唯一约束;补直写 IntegrityError 测试,并确认同一调用的 CONSUME 与 REFUND 可共存;在稳定 MySQL 上跑一次完整 test 全绿留证,并在测试文档标注「并发测试须在 MySQL 上跑,SQLite 会假绿」);P3 已挂到 T-401/T-501 或备忘。无 P1,不阻塞 T-301;check/test/init 全绿并在 ../progress.md 留证据 DONE

Phase 3 · 对外 API 与充值

ID 任务 依赖 验收要点 状态
T-301 API Key 鉴权(DRF Authentication) T-201 对请求 Key 哈希比对定位 ApiKey→User;只挂 Key 认证、不挂 Session;无效/缺失 401;用户或 Key 禁用 403 DONE
T-302 生成标题 / 图片接口 T-104, T-203, T-301 按 api.md 实现;请求传能力别名+ parameters,但 parameters 只能经 Provider 白名单透传,核心/计费字段不可被覆盖;编排「别名解析+能力校验→预扣→调上游→成功确认/失败退点→写记录」;评估 Provider.capabilities() 与模型声明能力的二次校验;images_edits 缺原图 / AiCapabilityError 翻译为 400,不落 500;调用记录不保存 provider raw / base64;点数不足返回 402;含测试;建议随本任务或 T-403 前跑一次真实图片生成并记录真实耗时 DONE
T-303 余额查询接口 T-301 返回余额等于流水累加;含测试 DONE
T-304 充值回调(微信/支付宝验签 + 幂等入账) T-202 两端点 @csrf_exempt;微信 SDK 验签解密、支付宝 SDK verify;验签失败不入账;同一 order_no 重复回调只入账一次;校验回调金额与订单金额一致;使用订单创建时锁定的 points_granted 锁 wallet 入账写流水;补主动查单兜底;含幂等测试 DONE
T-305 扫码充值下单 + 轮询(create/status) T-304 支持 weixin(native,金额分)/alipay(precreate,金额元);建 pending 订单绑定 user,并在下单时锁定汇率/预计点数→取 code_url/qr_code→前端渲染 + 轮询 status;缺商户密钥时 mock DONE
T-306 Phase 3 对外 API 安全加固 T-305 按 phase-3-review.md 处理 P1(download_image_input SSRF:协议白名单 + 私有/回环/链路本地网段拦截含重定向后地址 + 响应大小上限,含内网/元数据 URL 被拒测试)与 P2(全局 REST_FRAMEWORK 安全默认认证 + 生成/认证端点限流 + 充值金额上限);P3 仅登记媒体访问控制、完整测试补跑和过期订单口径,充值流水 DB 兜底已在 T-304 完成不要重复迁移。P1-1 属上线前必须;check/test/init 全绿并在 ../progress.md 留证据 DONE

Phase 4 · 用户端(Django 模板 SSR)

ID 任务 依赖 验收要点 状态
T-501 注册 / 登录(allauth) T-201 自助注册(免邮箱验证、注册即可用;邮箱仍必填且唯一)、登录、登出;注册后钱包点数为 0(不送点数);session + CSRF。策略变更见 T-605 与 2026-07-06 决策 DONE
T-502 API Key 自助管理页 T-501, T-301 登录用户生成/删除 Key;明文只显示一次、库内只存哈希;列表只显示 prefix;删除即吊销,吊销后该 Key 调用 403(无效/不存在 Key 仍为 401) DONE
T-503 个人中心 / 记录页 T-501, T-203 剩余点数、充值总额、充值记录、消费(调用)记录;数据与流水一致;仅见本人 DONE
T-504 充值页(扫码 + 轮询到账) T-501, T-305 发起充值→展示二维码→轮询订单状态→到账后余额刷新;到账以回调为权威 DONE
T-505 Phase 4 用户端审核优化 T-504 按 phase-4-review.md 处理 P2(P2-1 Bootstrap/qrcode 自托管或加 SRI+crossorigin,断网 jsdelivr 后充值页仍可用;P2-2 记录页真正分页或明确截断提示,含 >50 条测试);P3 登记到 T-401/T-403 或 Backlog。无 P1,但按任务看板领取规则应先完成 T-505,再进入 T-401;check/test/init 全绿并在 ../progress.md 留证据 DONE

Phase 5 · 后台与发布

ID 任务 依赖 验收要点 状态
T-401 运营后台完善 T-302, T-304 admin 可管理用户/钱包/ApiKey(脱敏)、配规则/汇率、检索充值订单/流水/调用记录;手工调点带原因并经计费层写流水 DONE
T-402 完整验收 MVP T-401, T-505 02-requirements.md 的 P0 验收全部通过(含用户端注册/充值/API Key/记录) DONE
T-403 部署 / 运行文档 T-402 新环境可按文档运行;明确图片接口超时配置;上线前必须引用一次真实图片生成耗时来设置 Gunicorn --timeout、Nginx proxy_read_timeout、客户端 read timeout;若尚无真实耗时,部署文档必须显式标注图片同步风险未退,不得声称已验证;生产必须配置真实 DJANGO_EMAIL_BACKEND / DJANGO_DEFAULT_FROM_EMAIL,否则 allauth 邮箱验证无法发信、用户无法完成登录;生产静态文件 serving:T-505 已把 Bootstrap/qrcode 自托管到 apps/portal/static/,但 settings 目前只有 STATIC_URL,DEBUG=False 下 Django 不发静态文件——必须配置 STATIC_ROOT + collectstatic + WhiteNoise 或 Nginx 托管 /static/,否则用户端 CSS 错版、充值二维码 404(P2-1 的国内可用性目标在生产失效);DRF 限流依赖共享缓存:多 Gunicorn worker 下默认 LocMemCache 是每进程的,会让 generate/api_auth_failure 限流按 worker 各算一份(实际速率≈worker 数×配置值),部署必须配置 Redis/Memcached 等共享 CACHES 后端并在文档说明,否则限流形同虚设 DONE

Phase 6 · 增强(MVP 后)

ID 任务 依赖 验收要点 状态
T-601 可用别名发现(portal 别名页 + GET /api/v1/models) T-202, T-301, T-503 让调用方能自助发现「能填哪些 model 能力别名」。① 新增 GET /api/v1/models(API Key 鉴权,继承 ExternalApiView,只复用认证失败限流;不要占用生成接口的 GenerateRateThrottle 额度),仅返回 ModelAlias.is_active=True 且 ai_model.is_active=True 的可调用别名;② 响应每项只含对外安全字段:alias、operation_type、capabilities、requires_image(按 api_type/provider 规则推导,如 images_edits=true)、pricing_status、prices[{resolution, points_cost}],缺 PricingRule 时标「暂未定价」而非报错;③ 实现时不得调用 AiModel.to_resolved_model() 或任何会解密 provider key 的路径,列表接口不依赖 AI_KEY_ENCRYPTION_KEY,也不读取/输出 api_key_encrypted;④ 不暴露具体 SKU/模型名/url/key/provider 原始配置,测试必须断言响应不含 ai_model.model、ai_model.url、api_key、api_key_encrypted、extra_body 等内部字段;⑤ portal 加只读「可用模型」页(session 鉴权),同字段、人类可读、单价以点数展示,并在导航中加入入口;⑥ 含测试:API Key 成功返回结构、缺/无效 Key 拒绝、Web session 不能调用外部 API、portal 未登录跳转、页面不泄露 SKU/URL/Key、缺定价别名显示暂未定价。标注:未来接 account_alias_permission(见 Backlog)后,本清单改为「只列该用户被授权的别名」,接口与页面均按 user 过滤 DONE
T-602 django-admin 中文化(第 1-3 层) - 让运营后台表名/分组/框架文字显示中文,仅显示层、不改业务逻辑与 DB 结构。第 1 层:config/settings.py 设 LANGUAGE_CODE='zh-hans' + USE_I18N=True,使 Django 自带 admin 界面与内置 auth(用户/组/权限)中文化(无迁移)。第 2 层:各 app AppConfig.verbose_name 设中文分组名(users/billing/ai/api/portal),无迁移。第 3 层:各模型 Meta.verbose_name/verbose_name_plural 设中文表名(User/UserWallet/ApiKey/PointsLedger/CallRecord/PricingRule/ExchangeRate/RechargeOrder/AiModel/ModelAlias/AiConfigAuditLog)。不含第 4 层字段级 verbose_name(字段列名保持英文,见 T-603)。第 3 层会产出 AlterModelOptions 等无 DB 变更迁移;验收:makemigrations(仅 options 变更)→ migrate → check 0 issues → test 全绿 → /admin/ 目视中文化,并在 ../progress.md 留证据 DONE
T-603 django-admin 中文化(第 4 层·字段级) T-602 给各模型字段加中文 verbose_name,使 admin 列表/编辑页的字段标签显示中文,在 T-602(1-3 层)之上做。范围覆盖 users/billing/ai(及 portal 相关)模型的业务字段,例如 points_balance→点数余额、key_prefix→Key 前缀、points_delta→点数变动、balance_after→变动后余额、amount_money→金额、exchange_rate→汇率、points_granted→到账点数、api_type/capabilities/is_active/created_at 等;admin.py 里自定义显示方法(@admin.display(description=...))同步中文。仅显示层:verbose_name 只改展示,不改字段名、不改代码引用、英文字段名保持不变;产出的是 AlterField(metadata-only,无 DB schema 变更)迁移。验收:makemigrations(仅 AlterField,无 schema 变更)→ migrate → check 0 issues → test 全绿 → /admin/ 字段标签目视中文,并在 ../progress.md 留证据。当前阻塞:代码与迁移已完成;ACCOUNT_EMAIL_VERIFICATION="none"(免邮箱验证)已定为既定策略(见 2026-07-06 决策),原假设 mandatory 的 2 条 portal 测试需按新策略更新(不再恢复 mandatory),由 T-605 处理;T-605 完成后本任务解封、重跑测试标 DONE BLOCKED
T-605 落实「免邮箱验证」策略 T-501 把 ACCOUNT_EMAIL_VERIFICATION="none"(注册即可用、不发验证邮件、邮箱仍必填且唯一)作为既定策略清理落地:① config/settings.py 把 ACCOUNT_EMAIL_VERIFICATION = "none"#"mandatory" 改为干净的 "none"(去行内注释),评估 ACCOUNT_LOGIN_ON_EMAIL_CONFIRMATION 是否仍需要;② 更新 apps/portal/tests.py 里假设 mandatory 的 2 条测试(signup 不再依赖验证邮件、未验证也可直接登录),改为断言「注册后可直接登录」;③ 同步全项目文档口径(02-requirements/05-coding-rules/api/04-architecture/03-tech-stack/routes/env/deployment/00-ai-start-here 中「邮箱验证」→「免邮箱验证,邮箱仍唯一」);④(可选)注册防刷改用图形验证码/限流补位。验收:check/test 全绿(含更新后的注册测试)并在 ../progress.md 留证据;完成后解封 T-603 TODO
T-604 中文敏感词本地过滤(本地 keyword provider) T-302, T-401 按 moderation.md 实施。范围收紧:T-604 只做输入 prompt 的本地敏感词快筛,不做云内容安全、不做输出审核、不做图片审核。关键时序:serializer 后先审 prompt,命中即 400 content_blocked;不得先下载 image_url,不得预扣点,不写 CallRecord / PointsLedger,不调上游。核心实现:新增 apps.moderation、SensitiveWord 模型/admin/迁移、keyword provider、归一化管线、Aho-Corasick matcher;ahocorapy 作为候选依赖,编码前必须验证 PyPI 可用性和 API 形状。缓存:matcher 进程内缓存,词库变更用共享 cache 版本号失效,不能只靠 post_save signal;生产依赖共享 cache。配置:MODERATION_ENABLED=false 默认 no-op;启用时 MODERATION_PROVIDER=keyword;MVP SensitiveWord.action 只支持 block。验收:check/test 全绿,覆盖 no-op、命中拦截且不扣点/不建记录/不调上游/不下载图片、归一化防绕过、词库变更后 matcher 重建;真实词库数据不进仓库,__pycache__ 不进 Git TODO

里程碑

  • M1:Django + admin 骨架可运行、自定义 User 就位(T-003)。
  • M2:服务端跑通一次 AI 生成(T-104)。
  • M3:计费 + 对外接口 + 充值闭环 + 上线前安全加固(T-306)。
  • M4:用户端(注册/充值/API Key/记录)可用(T-504)。
  • M5:运营后台 + MVP 验收 + 可部署(T-403)。
  • M6:可用别名发现(T-601)。

待办池(Backlog)

  • 异步生成(任务队列 + 轮询/回调)。
  • 按账号授权可用别名(account_alias_permission),防止调用未授权/昂贵模型。
  • 别名按比例分流到多个模型(灰度 / A/B / 故障转移);供应商 A 故障自动切 B。
  • 注册赠点 / 试用额度(需邮箱/图形验证码 + 限流防薅羊毛)。
  • 用量统计报表。
  • 退款对账自动化、API Key 轮换、Provider key 轮换(MultiFernet / 双 key 迁移)、限流细化。
  • 用户端 API Key 数量治理:限制每用户 active Key 数量(如 10 个)、提供轮换与过期策略,避免无限建 Key。
  • 充值下单风控:限制每用户 pending 订单数量或创建频率,避免反复点击刷出大量未支付订单并放大支付网关调用。
  • 用户端登录与凭证安全复核:评估 ACCOUNT_LOGIN_ON_EMAIL_CONFIRMATION=True 是否适合生产;评估新 API Key 明文短暂进入 session 的替代方案。
  • allauth account.EmailAddress 的 MySQL models.W036 条件唯一约束警告为已知 cosmetic 警告;邮箱唯一性由 user.email 约束承担,后续升级 allauth / 数据库兼容性时复核。
  • SSRF DNS rebinding 加固(image_url 下载):当前 download_image_input 校验时 getaddrinfo 解析一次、requests.get 又独立解析一次,两次之间存在 TOCTOU(恶意 DNS 可先返公网 IP 过校验、再返内网 IP 供实际请求)。彻底堵需把连接钉死到已校验的 IP(自定义 HTTPAdapter/connection 连已验证 IP + 保留 Host 头做 TLS/路由),对每次重定向同样处理。属安全深水区,规模化/公网大流量前处理;上线前安全检查应至少标注此残留。