From 02aa12e8b9bd0b022855bd09955a7c7c7227a812 Mon Sep 17 00:00:00 2001 From: QiuSW <105186638@qq.com> Date: Wed, 8 Jul 2026 14:19:00 +0800 Subject: [PATCH] docs: add signup bonus requirement --- README.md | 4 ++-- docs/00-ai-start-here.md | 10 +++++----- docs/01-vision.md | 3 ++- docs/02-requirements.md | 15 ++++++++------- docs/03-tech-stack.md | 2 +- docs/04-architecture.md | 41 ++++++++++++++++++++++++++++++++++------ docs/05-coding-rules.md | 6 ++++-- docs/06-tasks.md | 4 +++- docs/README.md | 2 +- docs/api.md | 5 ++++- docs/brand.md | 2 +- docs/current-state.md | 18 +++++++++--------- docs/mvp-acceptance.md | 2 +- docs/phase-4-review.md | 2 +- docs/project-brief.md | 4 ++-- docs/project-onepager.md | 4 ++-- docs/routes.md | 4 ++-- progress.md | 21 ++++++++++++++++++++ 18 files changed, 104 insertions(+), 45 deletions(-) diff --git a/README.md b/README.md index 47a33f9..7962793 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ ## 它做什么 -- 用户端(自助):公开首页、注册/登录、扫码充值、查看充值记录/剩余点数/消费记录、查看可用模型、生成与删除 API Key、下载桌面端(注册不送免费点数)。 +- 用户端(自助):公开首页、注册/登录、扫码充值、查看充值记录/剩余点数/消费记录、查看可用模型、生成与删除 API Key、下载桌面端(新用户注册成功赠送 100 点试用点数)。 - 对外提供 HTTP API:生成标题、生成图片(同步返回)、查询点数余额、查询可用能力别名。 - 按「操作类型 + 能力别名(+ 可选分辨率)」计费,调用消耗不同点数;余额不足返回「点数不足,请先充值」。 - **预付费点数模型**:用户在外部支付系统充值,付款成功由支付系统回调本服务,按汇率把金额转成点数存在本地;之后调用直接扣本地点数,扣费与上游解耦、低延迟。 @@ -25,7 +25,7 @@ Python 3.12 / Django 5.2 LTS + DRF / django-admin / 用户端 Django 模板 SSR ## 当前状态 -Phase 2 计费核心已完成,Phase 3 对外 API 与充值已完成到 T-306,Phase 4 用户端已完成 T-501~T-505,Phase 5 已完成 T-401 运营后台完善、T-402 MVP 完整验收与 T-403 部署 / 运行文档,Phase 6 已完成 T-601 可用别名发现、T-602/T-603 django-admin 中文化、T-604 中文敏感词本地过滤、T-605 免邮箱验证策略落地、T-606 公开首页 + 客户端下载入口与 T-607 桌面端最新版本检查接口。用户可通过公开首页进入注册、登录和下载入口;登录后可扫码充值并轮询到账,生成 / 删除(吊销)API Key,查看余额、充值总额、分页充值记录、分页消费记录与可用模型;桌面端可匿名请求最新客户端版本 JSON;运营可在 django-admin 检索用户、钱包、API Key、计费规则、汇率、充值订单、点数流水、调用记录和客户端发布版本,并通过计费层带原因手工调点;生产部署按 `docs/deployment.md` 执行。生产侧仍需补真实支付回调到账闭环、配置并发布客户端下载包和图片真实耗时验证;邮件服务仅用于后续密码找回/通知等邮件能力,不阻塞注册登录。详见 [`docs/current-state.md`](docs/current-state.md)。 +Phase 2 计费核心已完成,Phase 3 对外 API 与充值已完成到 T-306,Phase 4 用户端已完成 T-501~T-505,Phase 5 已完成 T-401 运营后台完善、T-402 MVP 完整验收与 T-403 部署 / 运行文档,Phase 6 已完成 T-601 可用别名发现、T-602/T-603 django-admin 中文化、T-604 中文敏感词本地过滤、T-605 免邮箱验证策略落地、T-606 公开首页 + 客户端下载入口与 T-607 桌面端最新版本检查接口。用户可通过公开首页进入注册、登录和下载入口;登录后可扫码充值并轮询到账,生成 / 删除(吊销)API Key,查看余额、充值总额、分页充值记录、分页消费记录与可用模型;桌面端可匿名请求最新客户端版本 JSON;运营可在 django-admin 检索用户、钱包、API Key、计费规则、汇率、充值订单、点数流水、调用记录和客户端发布版本,并通过计费层带原因手工调点;生产部署按 `docs/deployment.md` 执行。2026-07-08 新增需求已登记为 T-608:新用户注册成功赠送 100 点试用点数,当前代码待按任务实现。生产侧仍需补真实支付回调到账闭环、配置并发布客户端下载包和图片真实耗时验证;邮件服务仅用于后续密码找回/通知等邮件能力,不阻塞注册登录。详见 [`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) 第四节计费时序。 diff --git a/docs/00-ai-start-here.md b/docs/00-ai-start-here.md index 3fef120..d082e74 100644 --- a/docs/00-ai-start-here.md +++ b/docs/00-ai-start-here.md @@ -6,7 +6,7 @@ `cmhub` 是「**自助用户端 + 计费 API + 运营后台**」三合一服务:终端用户在用户端自助注册、扫码充值、管理 API Key,用 API Key 调用把 `cmbot` 的「生成标题」「生成图片」能力封装成的 HTTP 接口,按**点数计费**;运营用 django-admin 管理用户、点数与记录。 -第一版 MVP 做:**用户自助注册登录 → 扫码充值转点数 → 自助生成/删除 API Key → 带 Key 调用按规则算点数 → 点数足够则调上游生成并扣点 / 不足则报错 → 写调用与消费记录 → django-admin 运营后台**。**注册不送免费点数,必须充值才有点数。** +第一版 MVP 做:**用户自助注册登录 → 注册成功赠送 100 点试用点数 → 扫码充值转点数 → 自助生成/删除 API Key → 带 Key 调用按规则算点数 → 点数足够则调上游生成并扣点 / 不足则报错 → 写调用与消费记录 → django-admin 运营后台**。注册赠点也是账本资产,必须经计费层入账并写点数流水。 ## 必读顺序 @@ -38,7 +38,7 @@ ## 当前阶段 -当前项目处于:**Phase 6 增强任务推进期**。Phase 2 计费核心已完成到 T-204;Phase 3 已完成 T-301 API Key 鉴权、T-302 生成标题 / 图片接口、T-303 余额查询接口、T-304 充值回调、T-305 扫码充值下单 + 轮询与 T-306 对外 API 安全加固;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「桌面端最新版本检查接口」。生产侧仍需补真实支付回调到账闭环、配置并发布客户端下载包和图片生成真实耗时验证;邮件服务仅用于后续密码找回/通知等邮件能力,不阻塞注册登录。 +当前项目处于:**Phase 6 增强任务推进期**。Phase 2 计费核心已完成到 T-204;Phase 3 已完成 T-301 API Key 鉴权、T-302 生成标题 / 图片接口、T-303 余额查询接口、T-304 充值回调、T-305 扫码充值下单 + 轮询与 T-306 对外 API 安全加固;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「新用户注册赠送 100 点试用点数」已登记为下一项 TODO。生产侧仍需补真实支付回调到账闭环、配置并发布客户端下载包和图片生成真实耗时验证;邮件服务仅用于后续密码找回/通知等邮件能力,不阻塞注册登录。 优先路径: @@ -48,7 +48,7 @@ 4. Phase 3:对外 API 与充值 —— T-301 Key 鉴权、T-302 生成接口、T-303 余额查询、T-304 充值回调、T-305 扫码下单与轮询、T-306 安全加固已完成。 5. Phase 4:用户端(Django 模板 SSR)—— T-501 注册登录、T-502 API Key 管理、T-503 个人中心 / 记录页、T-504 充值页与 T-505 用户端审核优化已完成。 6. Phase 5:后台与发布 —— T-401 运营后台完善、T-402 完整验收 MVP、T-403 部署 / 运行文档已完成;计划内 MVP 任务已收尾。 -7. Phase 6:增强(MVP 后)—— T-601 可用别名发现已完成,实现 `/api/v1/models` 与 portal 只读「可用模型」页;T-602 已完成 django-admin 分组/表名中文化;T-603 已完成字段级中文标签代码与 no-op 迁移并人工确认 admin 字段中文化;T-604 已完成中文敏感词本地过滤;T-605 已完成免邮箱验证策略落地;T-606 已完成公开首页 + 客户端下载入口;T-607 已完成桌面端最新版本检查接口。下一步需先确认业务优先级或拆新任务。 +7. Phase 6:增强(MVP 后)—— T-601 可用别名发现已完成,实现 `/api/v1/models` 与 portal 只读「可用模型」页;T-602 已完成 django-admin 分组/表名中文化;T-603 已完成字段级中文标签代码与 no-op 迁移并人工确认 admin 字段中文化;T-604 已完成中文敏感词本地过滤;T-605 已完成免邮箱验证策略落地;T-606 已完成公开首页 + 客户端下载入口;T-607 已完成桌面端最新版本检查接口;T-608 已拆为「新用户注册赠送 100 点试用点数」,下一步可领取实现。 ## 领取任务规则 @@ -68,7 +68,7 @@ MVP 只做: -- **用户端(Django 模板 SSR + Bootstrap)**:自助注册/登录、扫码充值、查看充值记录/充值总额/剩余点数/消费记录、自助生成与删除 API Key。 +- **用户端(Django 模板 SSR + Bootstrap)**:自助注册/登录、注册成功一次性赠送 100 点试用点数、扫码充值、查看充值记录/充值总额/剩余点数/消费记录、自助生成与删除 API Key。 - 对外两个生成接口:`POST /api/v1/generate/title`、`POST /api/v1/generate/image`(同步返回结果)。 - API Key 鉴权,识别所属注册用户(API 只认 Key,不认 Web session)。 - 点数计费:按「操作类型 + 能力别名(+ 可选分辨率)」查计费规则得点数;本地余额原子扣减;不足报错。 @@ -81,7 +81,7 @@ MVP 不做: - 异步任务队列(图片生成第一版走**同步等待**返回,不引入 Celery)。 - 自建支付/收银台(充值走外部支付系统,本服务发起扫码下单并接收回调,不碰资金清算)。 - 面向终端消费者的「内容生成产品页」(用户端只做账户自助,生成结果仍只经 API 输出)。 -- 注册赠送免费点数(必须充值才有点数)。 +- 复杂活动赠点、邀请奖励或历史用户自动补发;除 T-608 明确定义的新用户一次性 100 点注册试用额度外,不做其他免费点数活动。 - 多币种、分账、自动退款对账。 ## 事实来源 diff --git a/docs/01-vision.md b/docs/01-vision.md index ca2f83f..db18618 100644 --- a/docs/01-vision.md +++ b/docs/01-vision.md @@ -21,6 +21,7 @@ - **核心流程优先**:先把「鉴权 → 计费 → 生成 → 扣点 → 记录」这条主链路做顺。 - **真实数据优先**:点数、金额、扣费、充值不造假;不用演示逻辑冒充计费逻辑。 - **账目必须对得上**:每一笔点数变动都有流水、可追溯到充值订单或调用记录。 +- **试用额度可控**:新用户注册送 100 点试用点数降低首次接入门槛,但必须按账本发放、可追溯、可防重复。 - **小步交付**:每一步都能运行、能验证、能回退。 - **少即是稳**:MVP 不追求完整,只追求最小闭环可靠;先同步、先单机、先后台手工开通。 - **可维护**:复用 `cmbot` 现有 AI 调用代码,不另起一套。 @@ -39,7 +40,7 @@ - 不做自建支付/收银台,付款在外部支付系统完成,本服务发起扫码下单并接收回调,不碰资金清算。 - 不做异步任务队列(第一版图片生成走同步等待)。 - 不做面向终端消费者的「内容生成产品页」;用户端只做账户自助(注册/充值/API Key 管理/记录查询)。 -- **注册不赠送免费点数**:必须充值才有点数,降低薅羊毛/滥用风险。 +- 不做复杂活动赠点、邀请奖励或历史用户自动补发;当前只做新用户注册一次性 100 点试用额度,且必须配套防刷、幂等和流水留痕。 - 不做多币种、分账、自动退款对账等容易膨胀、不属于 MVP 的能力。 > MVP 的具体功能范围与验收标准,见 [需求](02-requirements.md)。 diff --git a/docs/02-requirements.md b/docs/02-requirements.md index 4f2e4f4..9b1788c 100644 --- a/docs/02-requirements.md +++ b/docs/02-requirements.md @@ -25,7 +25,7 @@ | 功能 | 用户能做什么 | 优先级 | | --- | --- | --- | -| 用户注册/登录 | 终端用户在用户端自助注册、登录(**免邮箱验证、注册即可用;邮箱仍必填且唯一**);**注册不送免费点数** | P0 | +| 用户注册/登录 | 终端用户在用户端自助注册、登录(**免邮箱验证、注册即可用;邮箱仍必填且唯一**);注册成功一次性赠送 **100 点**试用点数 | P0 | | API Key 自助管理 | 登录后自助生成/删除 API Key;Key 只在生成时显示一次(库内哈希存储) | P0 | | 扫码充值 | 用户端发起充值→展示支付二维码→扫码付款→回调到账;金额按汇率转点数 | P0 | | 个人中心 | 查看剩余点数、充值总额、充值记录、点数使用(消费)记录 | P0 | @@ -45,13 +45,13 @@ | --- | --- | --- | | 异步生成 | 图片生成改任务队列 + 轮询/回调,提升并发 | V2 | | 用量统计报表 | 按用户/时间/模型聚合消耗与成本 | V2 | -| 注册赠点 / 试用额度 | 若开放,需配套邮箱/图形验证码 + 限流防薅羊毛 | V2 | +| 复杂活动赠点 / 邀请奖励 | 注册 100 点之外的活动额度、邀请返利、历史用户补发等,需单独风控与审批 | V2 | | API Key 轮换 / 授权 | Key 到期轮换、按 Key 限授权别名 | V2 | | 退款对账自动化 | 外部退款联动本地点数冲正 | V3 | ## 四、核心用户故事(MVP) -1. 作为新用户,我在用户端自助注册登录,扫码充值一笔钱,看到对应点数按汇率到账。 +1. 作为新用户,我在用户端自助注册登录后先获得 100 点试用点数;后续扫码充值一笔钱,看到对应点数按汇率到账。 2. 作为已登录用户,我自助生成一把 API Key(明文只显示一次),也能删除不用的 Key。 3. 作为接入方,我带着 API Key 调用生成标题接口,传入 prompt,能拿到生成的标题。 4. 作为接入方,我调用生成图片接口,等待若干秒后在同一个响应里拿到图片结果。 @@ -68,8 +68,8 @@ - **点数计费与扣减**:调用前按规则算出点数 N;余额 ≥ N 才放行并精确扣 N;余额 < N 返回点数不足错误码且不调上游。并发同时多次调用,最终扣点总额正确,**不出现超扣或扣成负数**。 - **余额查询 API**:返回的余额等于该账号点数流水累加结果。 - **充值转点数**:模拟一次支付系统回调(含订单号、金额、签名),验签通过后按汇率加点;**同一订单号重复回调只入账一次**(幂等);验签失败不入账。 -- **调用记录 / 点数流水**:每次成功/失败调用、每次充值/退款/调整都各生成一条记录,字段完整、可在后台检索。 -- **用户注册/登录**:新用户能自助注册(**免邮箱验证、注册即可用;邮箱仍必填且唯一**)、登录;注册后点数为 0(不送免费点数)。 +- **调用记录 / 点数流水**:每次成功/失败调用、每次充值/退款/调整/注册赠点都各生成一条记录,字段完整、可在后台检索。 +- **用户注册/登录**:新用户能自助注册(**免邮箱验证、注册即可用;邮箱仍必填且唯一**)、登录;注册成功后点数为 100,且有一条对应的注册赠点流水。重复提交或服务重试不得重复赠送。 - **API Key 自助管理**:登录用户能生成 API Key(明文只显示一次,库内存哈希)、能删除;删除落库为吊销,吊销后该 Key 调用返回 403,缺失/无效/不存在 Key 返回 401。 - **扫码充值**:用户端发起充值→展示二维码→(mock 或真实)支付成功回调后点数按汇率到账,充值记录与流水各一条;同一订单号重复回调只入账一次。 - **个人中心**:剩余点数 = 点数流水累加;充值总额、充值记录、消费记录与实际一致。 @@ -87,14 +87,15 @@ | 生成返回方式 | **同步**等待返回结果(图片接口需把网关/服务超时调到 ≥ 300s) | | 模型选择方式 | 对外用**能力别名**(如 `image-hd`),不暴露具体模型名;后台配置别名→模型映射 | | 充值入口 | 用户端自助发起扫码充值(本服务向支付系统下单取二维码),付款成功由支付系统**服务端回调**入账 | -| 注册是否送点数 | 否。注册不送免费点数,必须充值才有点数 | -| 暂不支持 | 异步队列、注册赠点、多币种、分账、自动退款对账 | +| 注册是否送点数 | 是。新用户注册成功一次性赠送 100 点试用点数;只对功能上线后的新注册用户自动发放,历史用户是否补发需另行审批和单独任务 | +| 暂不支持 | 异步队列、复杂活动赠点 / 邀请奖励、多币种、分账、自动退款对账 | ## 七、待确认 / 风险点 - **支付商户配置**:协议字段、签名/验签方式、成功应答已明确;上线前仍需提供微信/支付宝真实商户密钥、证书、公网 `notify_url`。配置到位前,先以 mock 支付客户端开发和测试。 - **汇率与点数单价**:1 元 = 多少点?标题/图片/各模型各分辨率分别多少点?由运营/产品确认,配置在后台。 - **资金风险**:充值入账必须幂等防重复加点;扣点必须并发安全防超扣;上游失败必须不扣点或冲正。规则由产品确认,实现按 `05-coding-rules.md` 第 8 节。 +- **注册赠点风险**:注册送点虽然不来自支付回调,但仍是可消费点数,必须防重复发放、防并发重试重复入账,并配套注册限流 / 图形验证码等防刷手段。 - **凭证风险**:上游 AI 的 api_key、支付系统密钥属敏感信息,只能放环境变量/后台配置,不入代码与文档样例。 - **第三方平台风险**:上游 vectorengine 可能限流、超时、变更;需处理超时、错误透传与失败不扣点。 - **耗时风险**:图片同步生成耗时长,需确认网关/反向代理/客户端超时上限,避免连接中断导致已扣点但结果丢失。 diff --git a/docs/03-tech-stack.md b/docs/03-tech-stack.md index 071d579..d45b2b7 100644 --- a/docs/03-tech-stack.md +++ b/docs/03-tech-stack.md @@ -41,7 +41,7 @@ - **同步可用的前提是「超时链路 + 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。T-501 已接入 django-allauth 65.18.0,使用 session 与 CSRF;T-605 起注册策略固定为 `ACCOUNT_EMAIL_VERIFICATION="none"`,免邮箱验证、注册即可用,邮箱仍必填且唯一;注册成功只创建 0 点 `UserWallet`,不写赠点流水。T-502 已用 Django Form + Bootstrap 模板落地 `/apikeys`,生成 Key 后明文只显示一次,删除写为 `revoked`。T-503 已用只读 Django TemplateView 落地 dashboard 汇总、充值记录和消费记录。T-504 已用 Django Form + Bootstrap 模板落地 `/recharge`:页面 POST 创建 pending 充值订单,展示支付二维码票据,并用浏览器轮询 `/api/v1/recharge/status`,订单 paid 后刷新余额。T-505 已把 Bootstrap 5 CSS 与 qrcode.js vendoring 到 `apps/portal/static/portal/vendor/`,页面不再依赖 jsdelivr,充值/消费记录页改为 Django `Paginator` 分页。放弃 Vue/React 前后端分离(两套项目/部署,与单体 MVP 调性冲突)。用户模型:`User`(auth) 持登录态、`UserWallet` 持点数(扣点锁 wallet、与 auth 解耦)、`ApiKey`(User 1:N,哈希存储)。注册不送免费点数。 +- **用户端用 Django 模板 SSR 单体,不引前端框架**:需求含终端用户自助(注册/充值/API Key/记录),选 Django 模板 + Bootstrap + allauth 与后端同工程单体部署,复用 Django auth/session,开发部署最快、最契合单机 MVP;代价是交互不如 SPA,可后续加 HTMX。T-501 已接入 django-allauth 65.18.0,使用 session 与 CSRF;T-605 起注册策略固定为 `ACCOUNT_EMAIL_VERIFICATION="none"`,免邮箱验证、注册即可用,邮箱仍必填且唯一;T-608 已登记把注册成功后的初始化改为经 `apps.billing` 发放 100 点试用点数并写 `signup_bonus` 流水。T-502 已用 Django Form + Bootstrap 模板落地 `/apikeys`,生成 Key 后明文只显示一次,删除写为 `revoked`。T-503 已用只读 Django TemplateView 落地 dashboard 汇总、充值记录和消费记录。T-504 已用 Django Form + Bootstrap 模板落地 `/recharge`:页面 POST 创建 pending 充值订单,展示支付二维码票据,并用浏览器轮询 `/api/v1/recharge/status`,订单 paid 后刷新余额。T-505 已把 Bootstrap 5 CSS 与 qrcode.js vendoring 到 `apps/portal/static/portal/vendor/`,页面不再依赖 jsdelivr,充值/消费记录页改为 Django `Paginator` 分页。放弃 Vue/React 前后端分离(两套项目/部署,与单体 MVP 调性冲突)。用户模型:`User`(auth) 持登录态、`UserWallet` 持点数(扣点锁 wallet、与 auth 解耦)、`ApiKey`(User 1:N,哈希存储)。新注册用户一次性赠送 100 点,必须走计费层和点数流水。 ## 三、构建与运行命令 diff --git a/docs/04-architecture.md b/docs/04-architecture.md index aafb981..aca5b75 100644 --- a/docs/04-architecture.md +++ b/docs/04-architecture.md @@ -18,7 +18,7 @@ 组件落位: -- **用户端层(Django 模板 SSR)**:公开首页、注册/登录(Django auth / allauth)、个人中心(余额/充值总额/充值记录/消费记录)、API Key 自助管理、发起扫码充值、模型目录与客户端下载入口。入口 `apps/portal/`,公开首页匿名可访问,其他自助页面用 session 鉴权。T-501 已落地 `/signup`、`/login`、`/logout` 与最小 `/dashboard`;注册成功通过 allauth adapter 创建 0 点 `UserWallet`,不写赠点流水。T-502 已落地 `/apikeys`,用户可自助生成和删除(吊销)自己的 API Key,明文只显示一次,列表只显示 prefix。T-503 已扩展 `/dashboard` 并新增 `/records/recharge`、`/records/usage`,只读展示当前用户余额、充值订单与消费流水。T-504 已落地 `/recharge`,用户可创建 pending 充值订单、查看二维码票据,并轮询订单状态;到账仍以服务端回调或主动查单入账后的本地订单状态为准。T-505 已把 Bootstrap/qrcode.js 改成本地 static 自托管,并把充值/消费记录页从固定切片改为分页。T-606 已把 `/` 改为公开首页,并新增 `DownloadRelease` 下载版本配置用于展示 Windows 客户端版本、下载地址、SHA256 与发布说明;T-607 已新增公开 JSON 版本检查接口给桌面端自动更新使用。 +- **用户端层(Django 模板 SSR)**:公开首页、注册/登录(Django auth / allauth)、个人中心(余额/充值总额/充值记录/消费记录)、API Key 自助管理、发起扫码充值、模型目录与客户端下载入口。入口 `apps/portal/`,公开首页匿名可访问,其他自助页面用 session 鉴权。T-501 已落地 `/signup`、`/login`、`/logout` 与最小 `/dashboard`;T-608 需把注册成功后的钱包初始化改为经计费层一次性发放 100 点试用点数,并写 `signup_bonus` 点数流水。T-502 已落地 `/apikeys`,用户可自助生成和删除(吊销)自己的 API Key,明文只显示一次,列表只显示 prefix。T-503 已扩展 `/dashboard` 并新增 `/records/recharge`、`/records/usage`,只读展示当前用户余额、充值订单与消费流水。T-504 已落地 `/recharge`,用户可创建 pending 充值订单、查看二维码票据,并轮询订单状态;到账仍以服务端回调或主动查单入账后的本地订单状态为准。T-505 已把 Bootstrap/qrcode.js 改成本地 static 自托管,并把充值/消费记录页从固定切片改为分页。T-606 已把 `/` 改为公开首页,并新增 `DownloadRelease` 下载版本配置用于展示 Windows 客户端版本、下载地址、SHA256 与发布说明;T-607 已新增公开 JSON 版本检查接口给桌面端自动更新使用。 - **用户与账号层**:注册用户 `User`、点数钱包 `UserWallet`、`ApiKey`(一用户多把、哈希存储)。入口 `apps/users/`。 - **API 层(DRF)**:对外生成接口、余额查询、支付回调接收、扫码下单、公开版本检查接口。入口 `apps/api/`。生成/余额/模型目录这类对外业务 API **只认 API Key,不接受 Web session**;充值下单/状态查询属于用户端流程,走 Web session + CSRF;支付回调走平台验签;T-607 的客户端下载版本检查接口为公开只读例外,不需要 API Key,不读取用户、不扣点。 - **计费层**:点数计算、原子扣减(锁 `UserWallet` 行)、退点、充值入账、流水记账。入口 `apps/billing/`。 @@ -56,6 +56,7 @@ T-306 已实现对外 API 安全加固:`download_image_input()` 在请求前 - 计费规则查询:按「操作类型 + 能力别名(+ 可选分辨率)」算出本次点数 N。**按别名定价,不按具体供应商 SKU 定价**,这样后台换底层模型时计费不变。 - 点数原子扣减与退回:数据库事务 + 行锁,保证并发不超扣、不为负。 - 充值入账:接收已验签的支付回调数据,按汇率换算点数,幂等入账,写流水。 +- 注册赠点:新用户注册成功后一次性发放 100 点试用点数,必须幂等、防并发重复,并写注册赠点流水。 - 手工调点:运营后台只收集点数变动和原因,必须调用计费层 `adjust_wallet_points()`;服务在事务内锁 `UserWallet`,拒绝扣成负数,并写 `PointsLedger(change_type=adjust)`。 - 点数流水记账:所有点数变动(充值/消费/调整/冲正)都生成一条流水。 - 是点数余额的唯一写入方。 @@ -212,7 +213,7 @@ T-102 已实现 `AiModel` / `ModelAlias` 的 Django models、admin、迁移与 CREATE TABLE "user" ( id INTEGER PRIMARY KEY, username TEXT UNIQUE NOT NULL, - email TEXT NOT NULL UNIQUE, -- 注册邮箱,需验证且唯一 + email TEXT NOT NULL UNIQUE, -- 注册邮箱,必填且唯一;当前免邮箱验证 password TEXT NOT NULL, -- Django 哈希存储 payment_user_id TEXT, -- 对应外部支付系统的用户标识 status TEXT NOT NULL DEFAULT 'active', -- active / disabled @@ -228,6 +229,14 @@ CREATE TABLE user_wallet ( updated_at TEXT NOT NULL ); +-- 注册赠点幂等标记(每个用户最多一条;MySQL 兼容,不使用条件唯一约束) +CREATE TABLE signup_bonus_grant ( + id INTEGER PRIMARY KEY, + user_id INTEGER UNIQUE NOT NULL REFERENCES "user"(id), + points_granted BIGINT NOT NULL DEFAULT 100, + created_at TEXT NOT NULL +); + -- API Key(一个用户可有多把;哈希存储,明文只在创建时显示一次) CREATE TABLE api_key ( id INTEGER PRIMARY KEY, @@ -262,7 +271,7 @@ CREATE TABLE recharge_order ( 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 + change_type TEXT NOT NULL, -- recharge / consume / adjust / refund / signup_bonus points_delta BIGINT NOT NULL, -- 正为加、负为减 balance_after BIGINT NOT NULL, -- 变动后余额,便于对账 ref_order_id INTEGER, -- 数值引用 RechargeOrder.id;T-304 已加 ref_order_id+change_type 唯一兜底 @@ -295,13 +304,13 @@ CREATE TABLE call_record ( 需要说明: - 主键自增;`api_key.key_hash`、`pricing_rule(operation_type, alias, resolution)`、`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)`。 +- 重要索引:`call_record(user_id, created_at)`、`points_ledger(user_id, created_at)`、`recharge_order(order_no)`、`api_key(key_hash)`、`signup_bonus_grant(user_id)`。 - 不软删除业务流水;用户/账号可标记 `disabled` 而非物理删;API Key 用 `revoked` 状态而非物理删。 - 服务端生成字段:`api_key.key_hash`/`key_prefix`、`points_balance`、`balance_after`、各 `created_at` / `updated_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` 状态,避免调用结果与账务动作混在一个字段里。 -- T-201 已落地 `UserWallet` / `ApiKey` 于 `apps.users`,`PointsLedger` / `CallRecord` 于 `apps.billing`;T-203 已落地扣点/退点服务;T-304 已落地 `RechargeOrder`、回调幂等入账服务和 `points_ledger(ref_order_id, change_type)` 复合唯一约束,`ref_order_id` 当前仍为数值引用 `RechargeOrder.id`;T-305 已落地 `create_recharge_order()`,负责创建 pending 订单、锁定汇率/点数并回填二维码票据;T-401 已落地 `adjust_wallet_points()`,手工调整点数必须带原因并写 `adjust` 流水,后台钱包余额字段只读;T-501 已保证 allauth 自助注册路径创建 `UserWallet(points_balance=0)`,且不写 `PointsLedger`,避免把注册初始化误记为赠点或充值;T-502 已把 API Key 自助管理接到 `ApiKey.create_for_user()`,删除动作写为 `revoked` 状态而非物理删除;T-503 已把个人中心和记录页接到只读查询,余额用 `get_balance_snapshot()`,充值总额按 paid `RechargeOrder` 汇总,入账 / 消费 / 退款按 `PointsLedger` 汇总;T-504 已把 `/recharge` 页面接到 `create_recharge_order()` 与 `/api/v1/recharge/status`。 +- T-201 已落地 `UserWallet` / `ApiKey` 于 `apps.users`,`PointsLedger` / `CallRecord` 于 `apps.billing`;T-203 已落地扣点/退点服务;T-304 已落地 `RechargeOrder`、回调幂等入账服务和 `points_ledger(ref_order_id, change_type)` 复合唯一约束,`ref_order_id` 当前仍为数值引用 `RechargeOrder.id`;T-305 已落地 `create_recharge_order()`,负责创建 pending 订单、锁定汇率/点数并回填二维码票据;T-401 已落地 `adjust_wallet_points()`,手工调整点数必须带原因并写 `adjust` 流水,后台钱包余额字段只读;T-608 需新增 `signup_bonus` 流水类型和 MySQL 兼容的注册赠点幂等标记(如 `SignupBonusGrant(user UNIQUE)`),并把 allauth 自助注册路径改为调用 `grant_signup_bonus()` 发放 100 点;T-502 已把 API Key 自助管理接到 `ApiKey.create_for_user()`,删除动作写为 `revoked` 状态而非物理删除;T-503 已把个人中心和记录页接到只读查询,余额用 `get_balance_snapshot()`,充值总额按 paid `RechargeOrder` 汇总,入账 / 消费 / 退款 / 注册赠点按 `PointsLedger` 汇总;T-504 已把 `/recharge` 页面接到 `create_recharge_order()` 与 `/api/v1/recharge/status`。 ## 四、计费时序(核心,务必照此实现) @@ -357,6 +366,26 @@ CREATE TABLE call_record ( - **兜底**:回调可能丢失,须提供按 `order_no` 主动查单补入账;前端每 ~1s 轮询订单状态仅作 UX。 - 仅商户密钥/证书为真实值待提供(微信 appid/mchid/apiv3_key/证书、支付宝 appid/公私钥/RSA2、notify_url 域名),协议已明确,可先 mock。 +### 4.3 注册赠点(新用户 100 点试用额度) + +```text +1. allauth 注册成功并保存 User(邮箱必填且唯一,当前免邮箱验证) +2. 调用 billing.grant_signup_bonus(user, points=100) +3. 事务:创建或锁定该用户的 UserWallet;创建 SignupBonusGrant(user UNIQUE) 幂等标记 + 若标记已存在 → 直接返回已发放,不改余额、不写新流水 + 否则 points_balance += 100 + 写 points_ledger(signup_bonus, +100, balance_after, reason="new_user_registration") +4. 注册后 dashboard / balance API 读取到包含 100 点赠点后的余额 +``` + +要点: + +- 注册赠点不是充值收入,但属于可消费点数,必须纳入点数账本和后台审计。 +- 不允许在 adapter / view / signal 中直接 `points_balance=100`;所有余额变动必须走计费层。 +- 幂等要有数据库级兜底。MySQL 不支持条件唯一约束,不要用 partial unique;也不要用 `PointsLedger(user, change_type)` 这类会误伤其他流水类型的全局唯一约束。推荐独立 `SignupBonusGrant(user UNIQUE)` 标记表。 +- 只对 T-608 上线后的新注册用户自动发放。历史用户是否补发不在本流程内,需单独审批、单独任务、单独批处理流水。 +- 由于当前注册免邮箱验证,注册送点会提高刷号动机。T-608 实施时至少要有基础注册限流;图形验证码 / 人机验证可作为上线前风控项继续增强。 + ## 五、关键技术难点 | 难点 | 说明 | 应对 | @@ -372,7 +401,7 @@ CREATE TABLE call_record ( | 配置热生效 | 后台改模型/密钥后运行时仍用旧值 | 不在进程内长缓存;每次查库或保存时失效缓存 | | 配置变更审计 | 改密钥/模型/别名映射无痕 | `AiConfigAuditLog` 自动记录后台 create/update/delete;密钥只记录 empty/set 变化,日志只读 | | API Key 泄露 | 明文存库一旦泄露全泄 | Key **哈希存储**(sha256),明文只在创建时显示一次,库内存 `key_hash`+`key_prefix`,鉴权做哈希比对 | -| 注册滥用 | 自助注册被批量刷 | **免邮箱验证**(`ACCOUNT_EMAIL_VERIFICATION="none"`),防刷改由注册限流 / 图形验证码补位 + 生成接口限流(DRF throttle);**注册不送点数**,无免费额度可薅 | +| 注册滥用 | 自助注册被批量刷以获取 100 点试用额度 | **免邮箱验证**(`ACCOUNT_EMAIL_VERIFICATION="none"`)下必须补注册限流 / 图形验证码 / 人机验证等防刷;注册赠点走 `SignupBonusGrant(user UNIQUE)` 幂等标记 + `PointsLedger(signup_bonus)` 留痕,防重复发放 | | Web/API 抢 worker | 图片同步长请求占满 worker、拖慢用户端页面 | 单体下按路径把 `/api/v1/generate/*` 与用户端页面**分流到不同 gunicorn/worker 池**(见 5.1) | | 充错账户 | 扫码订单未绑定发起用户 | 订单创建即绑定 `user`;回调按 `order_no` 定位订单→其 user 入账 | | SSRF / 任意 URL 下载 | `image_url` 让服务端请求调用方指定地址 | 仅允许公网 http/https;请求前解析 IP 并拒绝内网/回环/链路本地/保留地址;重定向逐跳校验;流式读取并限制大小 | diff --git a/docs/05-coding-rules.md b/docs/05-coding-rules.md index a44655a..e0f76d4 100644 --- a/docs/05-coding-rules.md +++ b/docs/05-coding-rules.md @@ -6,7 +6,7 @@ ## 0. 黄金法则 1. **不臆造**:数据字段、文件、接口、依赖、支付回调字段,不确定就查证或询问。 -2. **守范围**:只做当前任务要求的事,不顺手加后续功能(如异步队列、报表、注册赠点)。 +2. **守范围**:只做当前任务要求的事,不顺手加后续功能(如异步队列、报表、复杂活动赠点)。 3. **照架构**:使用既定技术栈(Django+DRF+admin)和模块边界,不擅自引入新框架。 4. **小步改**:一次只解决一个问题,不夹带无关重构。 5. **可验证**:改完必须能迁移、能测试、对得上验收标准。 @@ -28,7 +28,7 @@ ## 3. 范围纪律 - MVP 只做 `02-requirements.md` 中列为 P0 的功能。 -- V2 / V3 功能(异步队列、报表、注册赠点、自动退款对账)只记录,不实现。(自助注册/扫码充值/API Key 管理已纳入 MVP) +- V2 / V3 功能(异步队列、报表、复杂活动赠点 / 邀请奖励、自动退款对账)只记录,不实现。(自助注册/扫码充值/API Key 管理已纳入 MVP;新用户一次性注册送 100 点已拆为 T-608) - 需求明确排除的非目标不得实现(自建收银台、多币种、分账等)。 - 不为“将来可能用到”提前抽象。 @@ -37,6 +37,7 @@ - 技术栈以 `03-tech-stack.md` 为准;新增依赖前先说明理由,未经确认不引入重量级依赖(Celery、Redis 等)。 - 模块职责以 `04-architecture.md` 为准,不跨层偷写逻辑。 - **点数余额只能经 `apps/billing` 修改**:余额存 `UserWallet`(与 auth `User` 分离),扣点锁 wallet 行;API 层/Admin/用户端都不得直接写 `points_balance`。 +- **注册赠点也只能经 `apps/billing` 修改**:T-608 的 100 点试用额度必须走 `grant_signup_bonus()` 这类计费层服务,写 `PointsLedger(signup_bonus)`,不得在 allauth adapter、view 或 signal 里直接把钱包余额设为 100。 - **API Key 哈希存储**(sha256),明文只在生成时返回一次;不存明文、任何页面不回显。 - **对外 API 只挂 API Key 认证,不挂 SessionAuthentication**;用户端页面用 session + CSRF;自助注册**免邮箱验证**(`ACCOUNT_EMAIL_VERIFICATION="none"`,邮箱仍必填且唯一)、生成接口须限流。 - **用户端与后台账号分离**:portal 与 `/admin/` 共用同一个 Django 会话(同一 `sessionid`),后台访问只多一道 `is_staff` 门槛。因此 **终端用户一律 `is_staff=False`**(普通用户登 portal 进不了后台,无泄露);**运营用专用 admin superuser 登录 `/admin/`,不要拿这个账号登 portal**——账号分离(方案 A)就是「解耦入口」的正解,不要为同账号双会话去拆 cookie/子域名。详见 `04-architecture.md`。 @@ -90,6 +91,7 @@ python manage.py runserver - **回调防护与兜底**:支付回调端点必须 `@csrf_exempt`(网关调用无 CSRF token);回调可能丢失,须提供按 `order_no` 主动查单兜底,避免已付款却一直 pending。 - **失败必退**:调用上游失败时,已预扣的点数必须冲正退回。 - **全程留痕**:每笔点数变动写 `points_ledger`,每次调用写 `call_record`,运营手工调整必须填原因。 +- **注册赠点幂等**:新用户 100 点试用额度必须有数据库级幂等保护,同一用户无论注册回调、重试或并发触发多少次,都最多发放一次;不得用 MySQL 不支持的条件唯一约束。 - **敏感信息**:密钥/凭证只用环境变量或后台配置,不写真实值进代码与文档。 - 资金相关改动必须有对应测试(并发扣点、重复回调、上游失败退点至少各一条)。 - 不绕过支付系统的鉴权、验签或风控。 diff --git a/docs/06-tasks.md b/docs/06-tasks.md index eee3acc..ec7af40 100644 --- a/docs/06-tasks.md +++ b/docs/06-tasks.md @@ -86,6 +86,7 @@ | T-604 | 中文敏感词本地过滤(本地 keyword provider) | T-302, T-401 | 按 [`moderation.md`](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 | DONE | | T-606 | 公开首页 + 客户端下载入口 | T-501 | 给网站补「前门」并提供桌面端下载。**路由改造**:`/` 从「重定向到 `/dashboard`」改为**公开首页**(匿名可访问、不跳登录);已登录用户显示「进入控制台」,匿名显示「注册/登录 + 下载客户端」。**首页内容**(SSR 模板):项目一句话介绍(生成标题/图片、按点数计费)+ 三步上手(注册→充值→建 API Key→桌面端填 Key)+ 下载入口 + 文档链接。**`DownloadRelease` 模型**:`platform`(如 windows)、`version`、`file`(FileField,**前期存 `MEDIA_ROOT`、服务器托管**) + `external_url`(URLField 可选,**后续切对象存储/CDN 用,有则优先**)、`sha256`、`is_current`(每平台仅一个当前版本)、`release_notes`、时间戳;admin 可上传安装包并标记当前版本;迁移。**下载区块**:展示当前 release 的版本、下载按钮、**SHA256 校验值**、可选 release notes;无 current release 时优雅提示「暂未发布」。**托管策略**:**前期安装包放本服务器**(生产由 **Nginx 直接服务 media/下载文件、不走 Django**,与 T-403 static/media serving 一致,大文件不占 gunicorn worker);`external_url` 预留,后续切对象存储只改后台链接不改代码。**安全**:下载走 HTTPS,页面展示 SHA256 供校验;**代码签名**作为决策登记——未签名 Windows 安装包会被 SmartScreen 拦「未知发布者」、macOS 被 Gatekeeper 拦,首页先给「如何忽略警告」说明,正式签名后续补(挂 Backlog / deployment)。**含测试**:`/` 匿名 200 不跳登录、下载区展示当前 release、无 current release 优雅处理、已登录用户显示「进入控制台」。**视觉原型(已定 v1)**:按 `prototypes/cmhub-homepage-v1.svg` 落地——「生成台」方向:靛蓝=生成 / 琥珀=点数;Hero 为「商品图 + 一句话 → 吸睛标题 + 生成主图 + 点数计量」转化图;四步上手 01–04、两张能力卡、深色计费 band、下载区(版本/SHA256/未签名提示);配色、间距、结构照此原型转成 Django 模板(Bootstrap + 本地 static)。**视觉一致性(brand token)**:按 [`brand.md`](brand.md) 抽出共享 CSS 变量(如 `apps/portal/static/portal/brand.css`),**首页与现有 portal 页面(dashboard/记录/充值/API Key)一起套用同一套 token**(把配色/字体变量灌进现有 Bootstrap,各页只引用变量不散写 hex),确保落地页与登录后控制台风格一致——**不是只做漂亮首页**。验收 `check`/`test`/`init` 全绿并在 `../progress.md` 留证据 | DONE | | T-607 | 桌面端最新版本检查接口 | T-606 | 给桌面端自动检查更新提供只读 JSON 合约。**新增路由**:`GET /api/v1/client/releases/latest?platform=windows`,公开匿名可访问,**不需要 API Key、不读取用户、不扣点、不占用生成接口限流**;只返回发布元数据,不返回本地文件系统路径、后台 ID、内部状态或任何用户数据。**请求参数**:`platform` 支持 `windows` / `macos` / `linux`,缺省按 `windows`;非法平台返回 `400 bad_request`。**响应结构**:有当前版本时返回 `{platform, release:{version, download_url, sha256, release_notes, published_at}}`;无当前版本或当前版本没有下载地址时返回 `{platform, release:null, message:"暂未发布"}` 且 HTTP 200,方便客户端安静处理。**数据来源**:复用 `DownloadRelease`,只查 `platform + is_current=True`;`download_url` 继续按 `external_url` 优先,否则由 `file.url` 生成绝对 HTTPS URL;`published_at` 可先使用 `updated_at`。**缓存与安全**:可加短 TTL 公共缓存(如 60 秒);生产下载仍走 HTTPS + SHA256 校验;不得把 `MEDIA_ROOT` 或服务器路径暴露给客户端。**含测试**:匿名无 Key 可访问;当前 release 返回完整结构和绝对下载 URL;无 current release 返回 `release:null`;非法 platform 返回 400;`external_url` 优先于 `file`;响应不含本地路径、模型/密钥/用户字段;`check`/目标测试通过并在 `../progress.md` 留证据 | DONE | +| T-608 | 新用户注册赠送 100 点试用点数 | T-501, T-203, T-401 | 把 2026-07-08 的产品口径落地为账本安全实现。**业务口径**:只对 T-608 上线后的新注册用户自动发放 100 点;历史用户是否补发不在本任务内,需单独审批和批处理任务。**数据与服务**:新增 `PointsLedger.ChangeType.SIGNUP_BONUS`(展示为注册赠点)和 MySQL 兼容的数据库级幂等兜底(推荐 `SignupBonusGrant(user UNIQUE)`,不要用 partial unique / 条件唯一约束);新增 `apps.billing.services.grant_signup_bonus(user, points=100)`,在事务内锁/创建钱包、发放点数、写 `PointsLedger(signup_bonus,+100,balance_after,reason)`,重复调用或并发触发不得重复发放。**接入点**:allauth adapter 注册成功后调用 billing 服务,不得在 portal 直接写 `points_balance`;失败要清晰暴露并保持注册 / 账务一致性。**页面与文案**:公开首页、注册页、dashboard/记录页等用户端文案同步「注册送 100 点」;消费记录页能展示注册赠点正向流水;admin 可检索该流水。**防刷**:在免邮箱验证策略下,至少补基础注册限流或明确依赖 allauth/Django 限流配置;图形验证码 / 人机验证若暂不做,必须在文档登记为上线前风控风险。**测试**:注册后余额为 100 且有一条 `signup_bonus` 流水;重复调用服务不重复加点;并发触发不重复发放;现有用户不会自动补发;余额 API / dashboard / 使用记录显示一致;`check`/迁移/目标测试通过并在 `../progress.md` 留证据 | TODO | ## 里程碑 @@ -95,13 +96,14 @@ - M4:用户端(注册/充值/API Key/记录)可用(T-504)。 - M5:运营后台 + MVP 验收 + 可部署(T-403)。 - M6:可用别名发现(T-601)。 +- M7:注册试用额度安全落地(T-608)。 ## 待办池(Backlog) - 异步生成(任务队列 + 轮询/回调)。 - 按账号授权可用别名(`account_alias_permission`),防止调用未授权/昂贵模型。 - 别名按比例分流到多个模型(灰度 / A/B / 故障转移);供应商 A 故障自动切 B。 -- 注册赠点 / 试用额度(需邮箱/图形验证码 + 限流防薅羊毛)。 +- 复杂活动赠点、邀请奖励、历史用户批量补发(需单独审批、风控、批处理与流水留痕)。 - 用量统计报表。 - 退款对账自动化、API Key 轮换、Provider key 轮换(MultiFernet / 双 key 迁移)、限流细化。 - 用户端 API Key 数量治理:限制每用户 active Key 数量(如 10 个)、提供轮换与过期策略,避免无限建 Key。 diff --git a/docs/README.md b/docs/README.md index b35cc8b..3d1476d 100644 --- a/docs/README.md +++ b/docs/README.md @@ -24,7 +24,7 @@ - [Phase 1 AI 层审核](phase-1-review.md):T-101~104 代码审核结论与修补清单(P1/P2/P3),对应任务 T-105;重点提示图片同步风险未退与 `parameters` 越权计费隐患。 - [Phase 2 计费核心审核](phase-2-review.md):T-201~203 代码审核结论(资金安全逐条核对)与加固清单(**零 P1**,P2/P3),对应任务 T-204。 - [Phase 3 对外 API 与充值审核](phase-3-review.md):T-301~305 代码审核结论(鉴权/回调/幂等/入账逐条核对)与优化建议,对应任务 T-306;**P1 提示 `image_url` SSRF 上线前必修**。 -- [Phase 4 用户端审核](phase-4-review.md):T-501~504 代码审核结论(注册不送点/越权/CSRF/明文只显一次/到账以回调为权威逐条核对)与优化建议(**零 P1**,P2/P3),对应任务 T-505。 +- [Phase 4 用户端审核](phase-4-review.md):T-501~504 代码审核结论(按当时口径核对注册不送点/越权/CSRF/明文只显一次/到账以回调为权威;2026-07-08 新需求已由 T-608 改为注册送 100 点)与优化建议(**零 P1**,P2/P3),对应任务 T-505。 - [MVP 完整验收报告](mvp-acceptance.md):T-402 对 `02-requirements.md` P0 验收项的逐项结论、测试证据和已知限制。 - [部署 / 运行文档](deployment.md):T-403 生产部署步骤、宝塔 / Nginx / Gunicorn 配置、静态文件、共享 cache、图片超时与上线检查。 - [API 合约](api.md):对外接口、支付回调、AI 调用模块合约、错误码。 diff --git a/docs/api.md b/docs/api.md index 3b850d3..a6aa96c 100644 --- a/docs/api.md +++ b/docs/api.md @@ -15,6 +15,7 @@ - 例外:客户端下载版本检查接口只返回公开发布元数据,设计为匿名只读接口,不需要 API Key,不读取用户、不扣点。 - 图片生成为**同步**接口,可能耗时较长,调用方与网关需设置足够超时(≥ 300s)。 - 用户端注册使用同一个 `User` 账本主体;注册邮箱**必填且唯一**(`ACCOUNT_EMAIL_VERIFICATION="none"`,**不做邮箱验证**、注册即可用),唯一约束避免同邮箱对应多个点数账户。 +- T-608 起新用户注册成功一次性赠送 **100 点**试用点数;赠点必须经计费层写入钱包和 `PointsLedger(change_type=signup_bonus)`,不得直接改余额字段。历史用户是否补发不属于默认注册流程。 - API Key 库内只存 `key_hash`(SHA-256)与 `key_prefix`,明文只在创建时返回一次,不在 admin、日志或调用记录中回显。 - 每次生成调用写 `CallRecord`;只允许保存 `result_ref` / `result_summary` 这类引用或摘要,不保存 provider `raw`、base64 图片或敏感上游字段。 @@ -34,7 +35,7 @@ T-306 已实现对外 API 安全加固:`image_url` 下载只允许 `http` / `h T-604 目标口径:生成接口在 serializer 基础校验后先执行 prompt 本地敏感词检查;命中返回 `400 content_blocked`,且不得下载 `image_url`、不得预扣点、不得写 `CallRecord` / `PointsLedger`、不得调用上游。T-604 不启用输出审核和图片审核;详细规则见 [`moderation.md`](moderation.md)。 -T-501 已实现用户端注册 / 登录基线:`/signup` `/login` `/logout` 走 django-allauth + Django session + CSRF;**免邮箱验证、注册即可用(`ACCOUNT_EMAIL_VERIFICATION="none"`,邮箱仍必填且唯一)**,注册成功创建 0 点 `UserWallet`,不创建赠点流水。对外 API 仍只认 API Key,不接受 Web session。(策略见 T-605 与 2026-07-06 决策) +T-501 已实现用户端注册 / 登录基线:`/signup` `/login` `/logout` 走 django-allauth + Django session + CSRF;**免邮箱验证、注册即可用(`ACCOUNT_EMAIL_VERIFICATION="none"`,邮箱仍必填且唯一)**。T-608 需把注册成功后的 0 点钱包初始化改为一次性发放 100 点注册试用额度,并创建 `signup_bonus` 流水。对外 API 仍只认 API Key,不接受 Web session。(免验证策略见 T-605 与 2026-07-06 决策) T-502 已实现用户端 API Key 自助管理基线:`/apikeys` 走 Django session + CSRF;登录用户可生成和删除自己的 Key,生成后的明文只在重定向后的首个页面显示一次,库内只保存 `key_hash` 与 `key_prefix`。用户端“删除”落库为 `revoked`,保留历史记录关联;吊销后的 Key 调用生成 / 余额接口返回 `403 account_disabled`,缺失、无效或不存在的 Key 仍返回 `401 unauthorized`。 @@ -243,6 +244,7 @@ calculate_points_cost(operation_type: str, alias: str, resolution: str | None = quote_recharge_points(amount, currency: str = "CNY", at=None) -> RechargeQuote calculate_points_granted(amount, currency: str = "CNY", at=None) -> int create_recharge_order(..., user, amount, pay_method: str, currency: str = "CNY") -> RechargeOrder +grant_signup_bonus(user, points: int = 100) -> SignupBonusGrantResult precharge_call(..., user, points_cost: int, operation_type: str, alias: str, ...) -> CallCharge mark_call_success(call_record: CallRecord, ...) -> CallRecord refund_call_points(call_record: CallRecord, ...) -> RefundResult @@ -257,6 +259,7 @@ query_and_apply_recharge_payment(order_no: str, query_func) -> RechargeResult - 缺计费规则抛 `NoPricingRuleError(code="no_pricing_rule")`,API 层应翻译为上方同名错误码。 - `ExchangeRate` 按 `currency + effective_from` 取当前 active 汇率;充值下单时应锁定当时的 `exchange_rate` / `points_granted` 到订单,回调入账不得按新汇率重算。 - 金额换点数采用 `floor(amount * points_per_unit)`,点数为整数。 +- `grant_signup_bonus()` 是注册赠点唯一入口:只对当前用户首次成功发放 100 点,锁定 / 创建 `UserWallet`,写 `PointsLedger(change_type=signup_bonus, points_delta=+100)`,并用 MySQL 兼容的唯一幂等标记防重复发放。 - `create_recharge_order()` 创建 `RechargeOrder(status=pending)` 并绑定发起用户;写入订单创建时的金额、币种、汇率、预计点数,再调用支付网关下单取二维码票据并回填 `code_url` / `expires_at`;支付平台下单失败时订单标记 `failed`。 - `precharge_call()` 使用事务 + `select_for_update()` 锁 `UserWallet` 行;余额不足抛 `InsufficientPointsError(code="insufficient_points")`,不创建 `CallRecord`、不写 `PointsLedger`、不调上游。 - 预扣成功后写 `CallRecord(status=pending)` 与 `PointsLedger(change_type=consume, points_delta=-N)`;上游成功只更新调用记录,余额不再变化。 diff --git a/docs/brand.md b/docs/brand.md index 6f4e30c..c638a92 100644 --- a/docs/brand.md +++ b/docs/brand.md @@ -79,7 +79,7 @@ cmhub 有双重身份——**AI 生成** + **按点数计费**。视觉主线 ## 5. 语气(Voice) - **说人话**:按用户看得懂的说,不用系统术语(说「管理 API Key」,不说「凭证配置」)。 -- **诚实**:如实说「注册不送点数」「失败自动退点」「首次安装点『仍要运行』」——不粉饰。 +- **诚实**:如实说「注册送 100 点试用点数」「失败自动退点」「首次安装点『仍要运行』」——不粉饰。 - **动词优先、句子式大小写**:按钮说清「做完会发生什么」(「下载 .exe」而非「获取」)。 - **同一动作全程同名**:按钮「充值」→ 结果提示也用「充值」。 diff --git a/docs/current-state.md b/docs/current-state.md index 1280da3..7e3e0ae 100644 --- a/docs/current-state.md +++ b/docs/current-state.md @@ -11,10 +11,10 @@ ## 当前快照 -- 日期:2026-07-06 -- 阶段: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 桌面端最新版本检查接口已完成;后续仍需处理真实支付回调到账闭环、客户端下载包发布和真实图片耗时验证 +- 日期:2026-07-08 +- 阶段: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 新用户注册赠送 100 点试用点数已登记为 TODO;后续仍需处理真实支付回调到账闭环、客户端下载包发布和真实图片耗时验证 - 技术栈:系统 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 已接入 allauth,新增 portal 路由、注册适配器、登录/注册/登出模板和最小 dashboard,注册成功创建 0 点钱包且不写赠点流水;T-502 已新增 `/apikeys`、API Key 创建表单、列表页和删除(吊销)动作,生成后明文只显示一次,列表只显示 prefix;T-503 已扩展 `/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 环境变量支持;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 已新增 `ClientLatestReleaseView` 与 `/api/v1/client/releases/latest`,公开匿名返回当前客户端版本 JSON。 +- 生产代码:已有最小 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 已接入 allauth,新增 portal 路由、注册适配器、登录/注册/登出模板和最小 dashboard,当前代码注册成功仍创建 0 点钱包且不写赠点流水;T-608 待把该路径改为经 billing 一次性发放 100 点并写 `signup_bonus` 流水;T-502 已新增 `/apikeys`、API Key 创建表单、列表页和删除(吊销)动作,生成后明文只显示一次,列表只显示 prefix;T-503 已扩展 `/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 环境变量支持;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 已新增 `ClientLatestReleaseView` 与 `/api/v1/client/releases/latest`,公开匿名返回当前客户端版本 JSON。 - 用户端导航:顶部导航 active 状态已修复,`portal/base.html` 基于 `request.resolver_match.url_name` 高亮当前页面入口,并用 `aria-current="page"` 标记;「充值」不再在非充值页固定深色高亮。 - 测试: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-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` 警告。 @@ -30,7 +30,7 @@ - 数据: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 适配器**(可插拔供应商);自助扫码充值;注册不送点数。详见 `04-architecture.md` 与 2026-06-29 / 2026-07-01 的 `progress.md` 决策 +- 设计基线:**自助用户端 + 对外 API + 运营后台**三合一单体;用户模型 `User`(auth)/`UserWallet`(点数,锁 wallet 扣点)/`ApiKey`(1:N,哈希存储);对外两接口 + **能力别名 + Provider 适配器**(可插拔供应商);自助扫码充值;新用户注册成功一次性赠送 100 点试用点数(T-608 待实现,必须经 billing 写 `signup_bonus` 流水)。详见 `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 承载敏感词版本号。 - 当前 blocker:微信正式下单已能返回二维码,但线上微信回调曾出现 `PaymentVerificationError`,仍需单独修复并完成“付款后自动入账”闭环验收;支付宝恢复依赖开放平台把 `43.128.3.240` 加入可信 IP。线上真实标题生成已跑通并验证扣点;图片同步真实耗时风险仍未退,T-403 已在 `deployment.md` 明确上线前必须记录真实图片 smoke 耗时;未跑通前只能按保守超时内测,不能声称已验证。 @@ -54,9 +54,9 @@ - 已完成: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 桌面端最新版本检查接口。 - 正在进行:无。 -- 待开始:看板当前无新的编号任务;之后可继续处理真实支付回调到账闭环、配置并发布客户端下载包、真实图片耗时验证,或从 Backlog 拆新任务。 +- 待开始:T-608 新用户注册赠送 100 点试用点数;之后可继续处理真实支付回调到账闭环、配置并发布客户端下载包、真实图片耗时验证,或从 Backlog 拆新任务。 - 当前 blocker:支付商户真实密钥/证书与生产 SDK 依赖仍待提供;微信回调到账闭环仍需真实支付验收;真实 AI 标题生成已在线上跑通,图片生成真实耗时仍需补测。 -- 下一个可领取任务:看板当前无新的编号任务,需先确认下一项业务优先级或新增任务。 +- 下一个可领取任务:T-608 新用户注册赠送 100 点试用点数。 ## 当前可运行内容 @@ -114,15 +114,15 @@ gunicorn config.wsgi:application --bind 127.0.0.1:8002 --workers 1 --worker-clas - `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 ` 鉴权:成功后 `request.user` 为所属用户、`request.auth` 为 `ApiKey`,缺失/无效 Key 返回 401,用户或 Key 禁用返回 403,外部 API 不接受 Web session。T-302 已落地生成接口:请求别名解析后按规则计费,预扣成功才调用 Provider,成功确认调用记录,`AiProviderError` / `AiCapabilityError` 等失败路径会退点;图片结果保存到本地 media 并返回 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 已落地 allauth 注册 / 登录:`ACCOUNT_EMAIL_VERIFICATION="none"`,免邮箱验证、注册即可用,邮箱仍必填且唯一;注册成功创建 0 点钱包、不写点数流水。T-502 已落地 API Key 自助管理:`/apikeys` 登录访问,生成后完整明文只显示一次,列表只显示 prefix,不显示 hash 或历史明文;删除为吊销 `revoked`,吊销后外部 API 返回 403。T-503 已落地个人中心与记录页:`/dashboard` 展示剩余点数、充值总额、入账点数、净消耗点数和最近记录;`/records/recharge` 展示当前用户充值订单;`/records/usage` 展示当前用户 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 已落地公开版本检查接口:`GET /api/v1/client/releases/latest` 匿名 200,无需 API Key;支持 `windows`/`macos`/`linux`,返回当前版本 JSON 或 `release:null`。真实标题上游生成已在线上跑通并验证扣点;图片生成真实耗时仍需补测。 +当前骨架可运行。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 ` 鉴权:成功后 `request.user` 为所属用户、`request.auth` 为 `ApiKey`,缺失/无效 Key 返回 401,用户或 Key 禁用返回 403,外部 API 不接受 Web session。T-302 已落地生成接口:请求别名解析后按规则计费,预扣成功才调用 Provider,成功确认调用记录,`AiProviderError` / `AiCapabilityError` 等失败路径会退点;图片结果保存到本地 media 并返回 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 已落地 allauth 注册 / 登录:`ACCOUNT_EMAIL_VERIFICATION="none"`,免邮箱验证、注册即可用,邮箱仍必填且唯一;当前代码注册成功仍创建 0 点钱包、不写点数流水,T-608 待改为注册成功经 billing 发放 100 点并写 `signup_bonus` 流水。T-502 已落地 API Key 自助管理:`/apikeys` 登录访问,生成后完整明文只显示一次,列表只显示 prefix,不显示 hash 或历史明文;删除为吊销 `revoked`,吊销后外部 API 返回 403。T-503 已落地个人中心与记录页:`/dashboard` 展示剩余点数、充值总额、入账点数、净消耗点数和最近记录;`/records/recharge` 展示当前用户充值订单;`/records/usage` 展示当前用户 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 已落地公开版本检查接口:`GET /api/v1/client/releases/latest` 匿名 200,无需 API Key;支持 `windows`/`macos`/`linux`,返回当前版本 JSON 或 `release:null`。真实标题上游生成已在线上跑通并验证扣点;图片生成真实耗时仍需补测。 ## 开始编码前检查 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-607 已完成,看板当前无新的编号任务。 -5. 下一步需先确认业务优先级,或把真实支付回调到账闭环、客户端下载包发布、真实图片耗时验证拆成新任务。 +4. 在 `docs/06-tasks.md` 领取第一个 `TODO` 且依赖均 `DONE` 的任务;当前为 T-608。 +5. 下一步优先实现 T-608;之后再按业务优先级拆真实支付回调到账闭环、客户端下载包发布、真实图片耗时验证。 ## 维护规则 diff --git a/docs/mvp-acceptance.md b/docs/mvp-acceptance.md index f87d57d..d2e112f 100644 --- a/docs/mvp-acceptance.md +++ b/docs/mvp-acceptance.md @@ -8,7 +8,7 @@ | P0 验收项 | 结论 | 主要证据 | | --- | --- | --- | -| 用户注册 / 登录 | PASS | `apps.portal`:注册创建 0 点钱包且不写赠点流水;T-605 后注册为免邮箱验证、注册后可直接登录,邮箱仍必填且唯一 | +| 用户注册 / 登录 | PASS | T-402 历史验收口径为 `apps.portal` 注册创建 0 点钱包且不写赠点流水;T-605 后注册为免邮箱验证、注册后可直接登录,邮箱仍必填且唯一。2026-07-08 新需求已登记为 T-608,后续需改为注册赠送 100 点并写 `signup_bonus` 流水 | | API Key 自助管理 | PASS | `apps.portal`:Key 明文只显示一次、列表仅显示 prefix、删除为吊销、不可删除他人 Key;`apps.api`:缺失/无效 Key 为 401,吊销 Key 为 403 | | 扫码充值 | PASS | `apps.portal` / `apps.api`:创建 pending 订单不直接加点、不写流水;二维码票据展示;状态轮询只允许本人订单;主动查单可补入账 | | 个人中心 / 记录 | PASS | `apps.portal`:余额、充值总额、入账点数、消费/退款记录与实际订单/流水一致;充值记录和消费记录分页且仅见本人 | diff --git a/docs/phase-4-review.md b/docs/phase-4-review.md index e698f27..bcccb30 100644 --- a/docs/phase-4-review.md +++ b/docs/phase-4-review.md @@ -1,7 +1,7 @@ # Phase 4 用户端审核报告(T-501 ~ T-504) > 审核人:Claude Code(全栈视角)|日期:2026-07-03|结论:**功能验收全部达标,测试覆盖极全,零 P1(无安全/资金/越权缺陷)**。 -> Phase 4 是自助用户端(Django 模板 SSR + allauth)。逐条比对验收:注册不送点数、session+CSRF、越权(IDOR)防护、API Key 明文只显一次、吊销后调用 403、记录仅见本人、充值到账以回调为权威——**全部正确落地且有专测**。剩余为前端资源与 UX 优化项(P2/P3)。 +> Phase 4 是自助用户端(Django 模板 SSR + allauth)。逐条比对当时验收:注册不送点数、session+CSRF、越权(IDOR)防护、API Key 明文只显一次、吊销后调用 403、记录仅见本人、充值到账以回调为权威——**全部正确落地且有专测**。2026-07-08 新需求已由 T-608 改为「新用户注册送 100 点」,本报告保留历史审核口径。剩余为前端资源与 UX 优化项(P2/P3)。 > 本文面向 codex 执行:每条给出「症状 / 位置 / 怎么改 / 怎么验证」。修补任务见 [`06-tasks.md`](06-tasks.md) 的 **T-505**;按任务看板领取规则,当前应先完成 T-505,再进入 T-401。 ## 一、验收核对(功能全部达标) diff --git a/docs/project-brief.md b/docs/project-brief.md index 7127d5b..b9f5ec8 100644 --- a/docs/project-brief.md +++ b/docs/project-brief.md @@ -60,7 +60,7 @@ **第一版做**: -- 用户端:自助注册登录、扫码充值、查看点数/充值/消费记录、自助生成与删除 API Key(注册不送免费点数)。 +- 用户端:自助注册登录、注册成功赠送 100 点试用点数、扫码充值、查看点数/充值/消费记录、自助生成与删除 API Key。 - 生成标题、生成图片两个接口(同步返回结果)。 - 点数计费、扣点、余额查询。 - 充值到账(对接已有支付系统)。 @@ -69,7 +69,7 @@ **第一版先不做**(留待后续迭代): -- 高并发异步处理、用量统计报表、自动退款对账、注册赠点等(自助注册/扫码充值/API Key 管理已纳入第一版)。 +- 高并发异步处理、用量统计报表、自动退款对账、复杂活动赠点 / 邀请奖励等(自助注册/扫码充值/API Key 管理与注册送 100 点已纳入第一版口径)。 > 取舍原则:先把「充值 → 调用 → 计费 → 记录」最小闭环做稳,再扩展。 diff --git a/docs/project-onepager.md b/docs/project-onepager.md index 18308bb..30e8c21 100644 --- a/docs/project-onepager.md +++ b/docs/project-onepager.md @@ -29,8 +29,8 @@ ## 第一版范围 -做:自助用户端(注册/扫码充值/API Key 管理/记录)、两个生成接口、点数计费、充值到账、调用记录、运营后台。 -先不做:异步高并发、报表、注册赠点(后续迭代;自助注册/充值/API Key 已在第一版)。 +做:自助用户端(注册注册送 100 点/扫码充值/API Key 管理/记录)、两个生成接口、点数计费、充值到账、调用记录、运营后台。 +先不做:异步高并发、报表、复杂活动赠点 / 邀请奖励(后续迭代;自助注册/充值/API Key 已在第一版)。 ## 需要拍板 diff --git a/docs/routes.md b/docs/routes.md index 4deb1e3..6a0adc3 100644 --- a/docs/routes.md +++ b/docs/routes.md @@ -8,7 +8,7 @@ | 路由 | 方法 | 职责 | 鉴权 | | --- | --- | --- | --- | | `/` | GET | 公开首页:项目介绍、四步上手、客户端下载入口、API/控制台入口 | 公开 | -| `/signup` `/login` `/logout` | GET/POST | 自助注册(**免邮箱验证、注册即可用**)/登录/登出(Django auth/allauth) | 公开 | +| `/signup` `/login` `/logout` | GET/POST | 自助注册(**免邮箱验证、注册即可用**;注册成功一次性赠送 100 点试用点数)/登录/登出(Django auth/allauth) | 公开 | | `/dashboard` | GET | 个人中心:剩余点数、充值总额、快捷入口 | session | | `/recharge` | GET/POST | 发起充值:选金额→展示支付二维码→轮询到账 | session | | `/records/recharge` | GET | 充值记录 | session | @@ -16,7 +16,7 @@ | `/apikeys` | GET/POST | API Key 管理:列表 / 生成 / 删除(删除即吊销,明文只显示一次) | session | | `/models` | GET | 可用模型:只读展示可调用能力别名、能力、是否需要原图和点数单价 | session | -T-501 已落地 `/signup`、`/login`、`/logout` 与最小 `/dashboard`。T-502 已落地 `/apikeys`:登录用户只能管理自己的 Key,生成后明文只显示一次,列表只显示 prefix,删除为吊销 `revoked`。T-503/T-505 已扩展 `/dashboard` 为个人中心汇总,并落地 `/records/recharge` 与 `/records/usage`:充值总额按已支付订单统计,入账 / 消费 / 退款点数按 `PointsLedger` 统计,记录页只查询当前登录用户数据并分页展示。T-504/T-505 已落地 `/recharge`:登录用户可选择金额和支付方式创建 pending 充值订单,页面用本地 static 自托管 qrcode.js 展示二维码票据并轮询 `/api/v1/recharge/status`,到账后刷新余额。 +T-501 已落地 `/signup`、`/login`、`/logout` 与最小 `/dashboard`;T-608 需把注册后 0 点初始化改为经计费层一次性发放 100 点试用点数并写流水。T-502 已落地 `/apikeys`:登录用户只能管理自己的 Key,生成后明文只显示一次,列表只显示 prefix,删除为吊销 `revoked`。T-503/T-505 已扩展 `/dashboard` 为个人中心汇总,并落地 `/records/recharge` 与 `/records/usage`:充值总额按已支付订单统计,入账 / 消费 / 退款点数按 `PointsLedger` 统计,记录页只查询当前登录用户数据并分页展示。T-504/T-505 已落地 `/recharge`:登录用户可选择金额和支付方式创建 pending 充值订单,页面用本地 static 自托管 qrcode.js 展示二维码票据并轮询 `/api/v1/recharge/status`,到账后刷新余额。 T-601 已落地 `/models`:登录用户可查看当前公开可调用别名、能力、是否需要原图和点数单价;页面不展示底层 SKU、模型 URL、provider key、`api_key_encrypted` 或 `extra_body`。 T-606 已落地 `/` 公开首页:匿名访问返回 200,不再重定向到 `/dashboard`;匿名用户看到注册 / 登录 / 下载入口,登录用户看到「进入控制台」。首页下载区读取 `DownloadRelease(platform=windows, is_current=True)`,优先使用 `external_url`,否则使用后台上传文件的 `file.url`;无当前版本时显示「暂未发布」。 diff --git a/progress.md b/progress.md index 5460def..499deed 100644 --- a/progress.md +++ b/progress.md @@ -1455,3 +1455,24 @@ - `docs/api.md`:在版本检查接口段落补充对接文档链接。 - 验证:纯文档修改;`git diff --check` 通过。 - 下一步:按业务优先级继续处理真实支付回调到账闭环、客户端下载包发布或真实图片耗时验证。 + +## 2026-07-08 决策:T-608 新用户注册赠送 100 点试用点数 + +- 状态:DONE(文档决策与任务拆分,未改代码)。 +- 背景:产品新增需求「用户注册送 100 点」。该需求涉及可消费点数,不能简单把注册后的钱包初始值从 0 改成 100;必须纳入 billing 账本、流水、幂等和防刷设计。 +- 需求口径: + - 新用户注册成功后一次性赠送 100 点试用点数。 + - 只对 T-608 上线后的新注册用户自动发放;历史用户是否补发需另行审批和单独批处理任务。 + - 当前注册策略仍为免邮箱验证、注册即可用、邮箱必填且唯一;注册送点会提高刷号动机,实施时需补基础注册限流,并登记图形验证码 / 人机验证等上线前风控项。 +- 技术口径: + - 注册赠点必须经 `apps.billing` 服务发放,不得在 allauth adapter / view / signal 中直接写 `UserWallet.points_balance`。 + - 新增 `PointsLedger.ChangeType.SIGNUP_BONUS`,写 `PointsLedger(signup_bonus, +100, balance_after, reason="new_user_registration")`。 + - 采用 MySQL 可落地的数据库级幂等兜底,推荐 `SignupBonusGrant(user UNIQUE)`;不使用 MySQL 不支持的 partial unique / 条件唯一约束,也不使用会误伤其他流水类型的 `PointsLedger(user, change_type)` 全局唯一。 + - `grant_signup_bonus(user, points=100)` 负责锁/创建钱包、创建幂等标记、加点和写流水;重复调用或并发触发不得重复发放。 +- 文档变更: + - `README.md`、`docs/00-ai-start-here.md`、`docs/01-vision.md`、`docs/02-requirements.md`、`docs/03-tech-stack.md`、`docs/04-architecture.md`、`docs/05-coding-rules.md`、`docs/api.md`、`docs/routes.md`、`docs/brand.md`:同步新需求、账本规则、幂等与防刷要求。 + - `docs/06-tasks.md`:新增 T-608「新用户注册赠送 100 点试用点数」,状态 TODO,并把复杂活动赠点 / 历史用户补发移回 Backlog。 + - `docs/current-state.md`:记录 T-608 为下一个可领取任务,并明确当前代码仍是旧行为,尚未实现注册送点。 + - `docs/mvp-acceptance.md`、`docs/phase-4-review.md`、`docs/README.md`:标注 T-501/T-402/Phase 4 审核中的“注册不送点”属于历史口径,已被 T-608 新需求变更。 +- 验证:纯文档修改;后续执行 T-608 时需补迁移、目标测试、`check` 和 `git diff --check` 证据。 +- 下一步:领取 T-608,先实现 billing 层注册赠点服务和幂等数据结构,再接 allauth adapter、页面文案和测试。