diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..e5cf98b --- /dev/null +++ b/.env.example @@ -0,0 +1,42 @@ +# Django +DJANGO_SECRET_KEY=change-me-generate-a-random-secret +DJANGO_DEBUG=true +DJANGO_ALLOWED_HOSTS=127.0.0.1,localhost,testserver +DJANGO_TIME_ZONE=Asia/Shanghai + +# MySQL 8.4 +MYSQL_HOST=127.0.0.1 +MYSQL_PORT=3306 +MYSQL_DATABASE=cmhub +MYSQL_USER=cmhub +MYSQL_PASSWORD=change-me +MYSQL_CHARSET=utf8mb4 + +# AI key encryption +AI_KEY_ENCRYPTION_KEY=base64-fernet-key +AI_DEFAULT_CONNECT_TIMEOUT_SECONDS=10 +AI_DEFAULT_READ_TIMEOUT_SECONDS=300 + +# WeChat Pay V3 native +WECHAT_PAY_APPID=wx-your-appid +WECHAT_PAY_MCHID=your-mchid +WECHAT_PAY_API_V3_KEY=change-me +WECHAT_PAY_CERT_SERIAL_NO=your-cert-serial-no +WECHAT_PAY_PRIVATE_KEY_PATH=/secure/wechat/apiclient_key.pem +WECHAT_PAY_NOTIFY_URL=https://cmhub.example.com/api/v1/recharge/callback/wechat + +# Alipay face-to-face payment +ALIPAY_APPID=your-alipay-appid +ALIPAY_APP_PRIVATE_KEY_PATH=/secure/alipay/app_private_key.pem +ALIPAY_PUBLIC_KEY_PATH=/secure/alipay/alipay_public_key.pem +ALIPAY_NOTIFY_URL=https://cmhub.example.com/api/v1/recharge/callback/alipay +ALIPAY_DEBUG=false + +# Media storage +STORAGE_BACKEND=local +MEDIA_ROOT=D:\chengma\cmhub\media +MEDIA_URL=/media/ +S3_ENDPOINT_URL=https://s3.example.com +S3_BUCKET_NAME=cmhub-media +S3_ACCESS_KEY_ID=change-me +S3_SECRET_ACCESS_KEY=change-me diff --git a/README.md b/README.md index 081ed4d..f49b99c 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ Python 3.12 / Django 5.2 LTS + DRF / django-admin / 用户端 Django 模板 SSR ## 当前状态 -MVP 起步:T-001 Django + DRF 最小骨架已完成,`manage.py` / `config/` 可运行。下一步是 T-002 建立 apps 目录、自定义 User 与配置。详见 [`docs/current-state.md`](docs/current-state.md)。 +Phase 0 地基与审核修补已完成:Django + DRF 骨架、自定义 User、MySQL 8.4 配置、django-admin smoke test、解释器版本断言和 `.env.example` 均已落地。下一步是 T-101 Provider 适配器层。详见 [`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) 第四节计费时序。 @@ -36,4 +36,4 @@ MVP 起步:T-001 Django + DRF 最小骨架已完成,`manage.py` / `config/` ./init.ps1 # Windows 原生 PowerShell ``` -脚本完成依赖安装 + 验证 + 打印启动命令。骨架落地(T-001)后即可使用。 +脚本完成依赖安装 + 验证 + 打印启动命令。当前 Windows 标准入口为 `./init.ps1`。 diff --git a/apps/users/migrations/0002_alter_user_email.py b/apps/users/migrations/0002_alter_user_email.py new file mode 100644 index 0000000..422af13 --- /dev/null +++ b/apps/users/migrations/0002_alter_user_email.py @@ -0,0 +1,18 @@ +# Generated by Django 5.2.15 on 2026-07-02 02:11 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('users', '0001_initial'), + ] + + operations = [ + migrations.AlterField( + model_name='user', + name='email', + field=models.EmailField(max_length=254, unique=True, verbose_name='email address'), + ), + ] diff --git a/apps/users/models.py b/apps/users/models.py index d11efab..550f32f 100644 --- a/apps/users/models.py +++ b/apps/users/models.py @@ -7,7 +7,7 @@ class User(AbstractUser): ACTIVE = "active", "Active" DISABLED = "disabled", "Disabled" - email = models.EmailField("email address") + email = models.EmailField("email address", unique=True) payment_user_id = models.CharField( max_length=128, blank=True, diff --git a/config/settings.py b/config/settings.py index 6f1a567..444db61 100644 --- a/config/settings.py +++ b/config/settings.py @@ -64,18 +64,18 @@ ALLOWED_HOSTS = env_list("DJANGO_ALLOWED_HOSTS", "127.0.0.1,localhost,testserver # Application definition INSTALLED_APPS = [ - 'rest_framework', - 'apps.users', - 'apps.portal', - 'apps.billing', - 'apps.ai', - 'apps.api', 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', + 'rest_framework', + 'apps.users', + 'apps.portal', + 'apps.billing', + 'apps.ai', + 'apps.api', ] MIDDLEWARE = [ diff --git a/docs/00-ai-start-here.md b/docs/00-ai-start-here.md index d33d1d9..bb1f1e7 100644 --- a/docs/00-ai-start-here.md +++ b/docs/00-ai-start-here.md @@ -38,11 +38,11 @@ ## 当前阶段 -当前项目处于:**MVP 起步**(`cmhub` 目录目前只有文档,尚无代码)。 +当前项目处于:**Phase 1 准备开始**。T-001~T-004 已完成并通过标准验证,下一步进入 T-101 Provider 适配器层。 优先路径: -1. Phase 0:Django 骨架可运行、**自定义 User 模型在首次迁移前定好**、django-admin 可登录。 +1. Phase 0:Django 骨架可运行、**自定义 User 模型在首次迁移前定好**、django-admin 可登录;T-004 审核修补项已完成。 2. Phase 1:最高风险功能原型 —— 移植 `cmbot` 的 AI 调用并在服务端跑通一次标题/图片生成。 3. Phase 2:计费核心 —— User/UserWallet/ApiKey 模型 + 点数扣减(并发安全,锁 Wallet 行)+ 计费规则 + 调用记录。 4. Phase 3:对外 API 与充值 —— Key 鉴权、生成接口、余额查询、充值回调、扫码下单与轮询。 @@ -126,5 +126,5 @@ python3.12 manage.py runserver 说明: - 改后端逻辑后跑:`py -3.12 manage.py test`(Windows)或 `python3.12 manage.py test`(Unix/WSL)。 -- 改数据模型后跑:`makemigrations && migrate && test`;T-002 接入 MySQL 与自定义 User 前不要提前迁移。 +- 改数据模型后跑:`makemigrations && migrate && test`;涉及 User 字段唯一约束时,先确认业务库无重复数据。 - 如果命令当前不可运行,必须在回复里如实说明原因。 diff --git a/docs/03-tech-stack.md b/docs/03-tech-stack.md index 03e554d..d22ed1a 100644 --- a/docs/03-tech-stack.md +++ b/docs/03-tech-stack.md @@ -26,12 +26,12 @@ | 任务队列 | 暂不引入(Celery/RQ) | 待定 | V2 异步化时再评估 | | 部署方式 | Docker + Gunicorn(gthread) + Nginx,单体 | 待定 | MVP 先 `runserver`;生产 Nginx 按路径把 `/api/generate/*`(图片长请求)与用户端页面**分流到不同 gunicorn/worker 池**,避免图片阻塞拖慢页面(见 `04-architecture.md` 5.1) | | 测试 | Django 自带 `manage.py test`(unittest)/ 可选 pytest-django | 已定 | 先用内置 test runner,重点覆盖计费与回调 | -| 依赖管理 | 系统 Python 3.12 + pip + `requirements.txt` | 已定 | 不使用虚拟环境;Windows 用 `py -3.12`,Unix/WSL 用 `python3.12`;命令已同步到 init 脚本 | +| 依赖管理 | 系统 Python 3.12 + pip + `requirements.txt` + `pyproject.toml` | 已定 | 不使用虚拟环境;Windows 用 `py -3.12`,Unix/WSL 用 `python3.12`;运行依赖仍由 `requirements.txt` 管理,`pyproject.toml` 只落地 `requires-python` 元数据;init 会显式校验解释器版本在 `>=3.12,<3.14` | ## 二、决策记录与演进 - **Django 而非 FastAPI**:核心收益是 django-admin 直接满足「运营后台」需求;FastAPI 需自建后台。代价是异步生态较弱,但 MVP 同步返回,不受影响。 -- **锁定 Django 5.2 LTS + Python 3.12**:① 版本红线属安全而非性能——生文/图生图瓶颈在「等上游 + worker 并发 + 超时」,不在框架版本(详见 [架构设计](04-architecture.md) 5.1),故版本选择只按安全与维护窗口定;② Django 4.0/4.1 已 EOL、无安全补丁,涉资金服务禁用;4.2 LTS 支持窗口临近尾声,不从其起步;5.2 LTS 维护到 2028,窗口最长。③ Django 5.2 支持 Python 3.10–3.13,锁 3.12 取「稳定 + 库全 + 性能」的平衡,避开 3.10(临近 EOL)与 3.14(不在 5.2 官方矩阵)。T-001 已按用户要求固定使用系统 Python 3.12:Windows PowerShell 用 `py -3.12`,Unix/WSL 用 `python3.12`。 +- **锁定 Django 5.2 LTS + Python 3.12**:① 版本红线属安全而非性能——生文/图生图瓶颈在「等上游 + worker 并发 + 超时」,不在框架版本(详见 [架构设计](04-architecture.md) 5.1),故版本选择只按安全与维护窗口定;② Django 4.0/4.1 已 EOL、无安全补丁,涉资金服务禁用;4.2 LTS 支持窗口临近尾声,不从其起步;5.2 LTS 维护到 2028,窗口最长。③ Django 5.2 支持 Python 3.10–3.13,锁 3.12 取「稳定 + 库全 + 性能」的平衡,避开 3.10(临近 EOL)与 3.14(不在 5.2 官方矩阵)。T-001 已按用户要求固定使用系统 Python 3.12:Windows PowerShell 用 `py -3.12`,Unix/WSL 用 `python3.12`;T-004 起 init 脚本在安装依赖前显式断言运行解释器版本在 `>=3.12,<3.14`。 - **预付费点数而非实时查支付余额**:充值时按汇率把金额转点数存本地,解耦支付系统、降低调用延迟、并发扣减用本地数据库事务即可保证。代价是需处理充值幂等与对账。 - **稳定接口 + 可插拔供应商**:对外只 `generate text/image` 两接口 + 能力别名;具体模型在后台配置并经适配器调用。收益是换供应商对调用方零改动、计费按别名稳定、可加授权与故障转移;代价是需维护适配器层与别名映射。**调用方不绑具体模型 SKU**。 - **同步生成而非任务队列**:MVP 不引入 Celery/Redis,降低复杂度;图片耗时长,靠调大超时支撑,V2 再异步化。 diff --git a/docs/04-architecture.md b/docs/04-architecture.md index 8e75ad7..a92753b 100644 --- a/docs/04-architecture.md +++ b/docs/04-architecture.md @@ -133,7 +133,7 @@ CREATE TABLE pricing_rule ( CREATE TABLE "user" ( id INTEGER PRIMARY KEY, username TEXT UNIQUE NOT NULL, - email TEXT NOT NULL, -- 注册邮箱,需验证 + email TEXT NOT NULL UNIQUE, -- 注册邮箱,需验证且唯一 password TEXT NOT NULL, -- Django 哈希存储 payment_user_id TEXT, -- 对应外部支付系统的用户标识 status TEXT NOT NULL DEFAULT 'active', -- active / disabled diff --git a/docs/06-tasks.md b/docs/06-tasks.md index 1e1a938..6ca0cf9 100644 --- a/docs/06-tasks.md +++ b/docs/06-tasks.md @@ -25,6 +25,7 @@ | 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|billing|ai|api`;**首次迁移前定义自定义 `User` 模型(设 `AUTH_USER_MODEL`)**;settings 用环境变量读密钥、配 MySQL(utf8mb4),无明文密钥 | DONE | | T-003 | 接通 django-admin 与最小测试 | T-001 | `createsuperuser` 后能登录 `/admin/`;`manage.py test` 可运行(至少 1 条占位测试通过) | DONE | +| T-004 | Phase 0 骨架审核修补 | T-003 | 按 [`phase-0-review.md`](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 调用 + 可插拔供应商) diff --git a/docs/README.md b/docs/README.md index 9ce58a0..9166020 100644 --- a/docs/README.md +++ b/docs/README.md @@ -20,6 +20,7 @@ - [架构设计](04-architecture.md):系统结构、计费时序、数据模型、关键风险和开发顺序。 - [编码规则](05-coding-rules.md):硬性约束,含资金/点数安全专项(第 8 节)。 - [任务看板](06-tasks.md):按依赖拆分的小任务,agent 每轮只做一个。 +- [Phase 0 骨架审核](phase-0-review.md):T-001~003 代码审核结论与修补清单(P1/P2/P3),对应任务 T-004。 - [API 合约](api.md):对外接口、支付回调、AI 调用模块合约、错误码。 - [路由与页面结构](routes.md):API 路由与 django-admin 后台职责。 - [环境变量与配置](env.md):Django、数据库、AI 密钥加密、支付、对象存储等配置项。 diff --git a/docs/api.md b/docs/api.md index 43e3e15..798da3d 100644 --- a/docs/api.md +++ b/docs/api.md @@ -13,6 +13,7 @@ - 用户或 Key 被禁用/吊销(disabled/revoked):返回 `403`。 - **对外 API 只接受 API Key 认证,不接受 Web session**(浏览器带 cookie 也不能调 API,防绕过计费归属)。 - 图片生成为**同步**接口,可能耗时较长,调用方与网关需设置足够超时(≥ 300s)。 +- 用户端注册使用同一个 `User` 账本主体;注册邮箱必须验证且唯一,避免同邮箱对应多个点数账户。 通用错误响应: diff --git a/docs/current-state.md b/docs/current-state.md index 9e80013..b23e802 100644 --- a/docs/current-state.md +++ b/docs/current-state.md @@ -12,10 +12,10 @@ ## 当前快照 - 日期:2026-07-02 -- 阶段:Phase 0 地基已完成;下一步进入 Phase 1 的 T-101 +- 阶段:Phase 0 地基与审核修补已完成;下一步进入 Phase 1 的 T-101 - 技术栈:系统 Python 3.12.3 + Django 5.2.15 + DRF 3.16.1 + PyMySQL 1.1.3 + cryptography 46.0.7 + django-admin;MySQL 8.4 已接入 settings;用户端(模板 SSR/Bootstrap/allauth) 后续任务落地;详见 `03-tech-stack.md` -- 生产代码:已有最小 Django 工程骨架:`manage.py`、`config/`;T-002 已创建 `apps/users|portal|billing|ai|api`;T-003 已把自定义 `User` 注册进 django-admin -- 测试:`manage.py check` 通过;`manage.py test` 通过(2 条 admin smoke tests);`./init.ps1` 通过 +- 生产代码:已有最小 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` +- 测试:`makemigrations --check` 通过;`migrate` 通过;`manage.py check` 通过;`manage.py test` 通过(2 条 admin smoke tests);`./init.ps1` 通过 - 数据:AI 上游调用与模型配置参考 `D:\chengma\cmbot`(`src/services/ai_text_service.py`、`ai_image_service.py`、`config/ai_models.json`) - 标准启动路径:Windows 用 `./init.ps1`;Unix/WSL 用 `./init.sh` - 标准验证路径:Windows 用 `py -3.12 manage.py check` / `py -3.12 manage.py test` @@ -30,10 +30,10 @@ | `docs/` | 已有 | 全套 harness 文档 | | `AGENTS.md` / `CLAUDE.md` | 已有 | 仓库级入口 | | `progress.md` | 已有 | 执行流水,已记录多轮文档决策;后续任务继续追加 | -| `init.sh` / `init.ps1` | 已有 | 启动验证入口,已固定系统 Python 3.12 命令 | -| `requirements.txt` | 已有 | Django 5.2 / DRF 3.16 / PyMySQL / cryptography 依赖 | +| `init.sh` / `init.ps1` | 已有 | 启动验证入口,已固定系统 Python 3.12 命令,并校验解释器版本 `>=3.12,<3.14` | +| `requirements.txt` / `pyproject.toml` | 已有 | `requirements.txt` 管运行依赖;`pyproject.toml` 落地 `requires-python` | | `config/`(Django 工程) | 已有 | T-001 创建,含 settings / urls / wsgi / asgi | -| `apps/`(users/portal/billing/ai/api) | 已有 | T-002 创建;`apps/users` 已定义自定义 `User`;T-003 已注册 admin 与 admin smoke test | +| `apps/`(users/portal/billing/ai/api) | 已有 | T-002 创建;`apps/users` 已定义自定义 `User`;T-003 已注册 admin 与 admin smoke test;T-004 已给 `User.email` 加唯一约束 | | `manage.py` | 已有 | T-001 创建 | | `tests/` | 待建 | 随各任务补充 | @@ -41,7 +41,7 @@ 任务状态以 [`06-tasks.md`](06-tasks.md) 为准,历史执行记录见 [`../progress.md`](../progress.md)。 -- 已完成:T-001 初始化 Django + DRF 项目骨架;T-002 建立 apps 目录、自定义 User 与配置;T-003 接通 django-admin 与最小测试。 +- 已完成:T-001 初始化 Django + DRF 项目骨架;T-002 建立 apps 目录、自定义 User 与配置;T-003 接通 django-admin 与最小测试;T-004 Phase 0 骨架审核修补。 - 正在进行:无。 - 当前 blocker:无。 - 下一个可领取任务:**T-101 Provider 适配器层 + 移植 cmbot 调用**。 @@ -62,7 +62,7 @@ python3.12 manage.py test python3.12 manage.py runserver ``` -当前骨架可运行。T-002 已在首次迁移前创建自定义 User,并按 `env.md` 接入 MySQL 8.4 / utf8mb4;远程 MySQL 已完成 Django 初始迁移。T-003 已接通 django-admin,标准测试可创建/销毁 `test_cmhub` 测试库并通过。 +当前骨架可运行。T-002 已在首次迁移前创建自定义 User,并按 `env.md` 接入 MySQL 8.4 / utf8mb4;远程 MySQL 已完成 Django 初始迁移。T-003 已接通 django-admin,标准测试可创建/销毁 `test_cmhub` 测试库并通过。T-004 已应用 `users.0002_alter_user_email`,`user.email` 已有唯一索引。 ## 开始编码前检查 diff --git a/docs/env.md b/docs/env.md index 09268bb..2113a31 100644 --- a/docs/env.md +++ b/docs/env.md @@ -5,6 +5,7 @@ ## 一、配置来源 - Django 运行级配置走环境变量或 `.env`(`.env` 不提交)。 +- 仓库根目录提供 `.env.example` 作为无密钥样例;新增配置项时同步更新 `.env.example` 与本文。 - 上游 AI 模型的 `api_key` 存入数据库前必须用 `AI_KEY_ENCRYPTION_KEY` 加密,admin 脱敏展示且不回显明文。 - 微信、支付宝商户密钥/证书走环境变量或部署机安全文件路径,不写入数据库明文字段。 - 本地开发、测试、生产使用同一套变量名;差异只在变量值。 @@ -26,7 +27,7 @@ | 变量 | 必填 | 示例 | 说明 | | --- | --- | --- | --- | | `MYSQL_HOST` | 是 | `127.0.0.1` | cmhub 专用 MySQL 实例地址 | -| `MYSQL_PORT` | 是 | `3307` | 独立端口,避免复用已有 MySQL 5.7 | +| `MYSQL_PORT` | 是 | `3306` | MySQL 默认端口;若与已有实例共存,可改为专用独立端口 | | `MYSQL_DATABASE` | 是 | `cmhub` | 数据库名 | | `MYSQL_USER` | 是 | `cmhub` | 应用账号 | | `MYSQL_PASSWORD` | 是 | `change-me` | 数据库密码 | diff --git a/docs/phase-0-review.md b/docs/phase-0-review.md new file mode 100644 index 0000000..a6e5c64 --- /dev/null +++ b/docs/phase-0-review.md @@ -0,0 +1,86 @@ +# Phase 0 骨架审核报告(T-001 ~ T-003) + +> 审核人:Claude Code(全栈视角)|日期:2026-07-02|结论:**验收通过**。 +> 进入 Phase 1(T-101)前,建议先做 **T-004 骨架修补**(本文 P1/P2)。 +> 本文面向 codex 执行:每条修补项给出「症状 / 位置 / 怎么改 / 怎么验证」。修补任务见 [`06-tasks.md`](06-tasks.md) 的 **T-004**。 + +## 一、验收核对(全部达标) + +| 任务 | 验收要点 | 结果 | +| --- | --- | --- | +| T-001 | manage.py 可跑、runserver 起得来、init 脚本真实命令、文档占位替换 | ✅ | +| T-002 | 五个 app、**首迁移前定义自定义 User + `AUTH_USER_MODEL`**、env 读密钥无明文、MySQL+utf8mb4 | ✅ | +| T-003 | superuser 能登录 `/admin/`、`manage.py test` 至少 1 条通过(实际 2 条) | ✅ | + +## 二、做对的(勿在修补中回退) + +以下是已正确落地的关键点,T-004 修补时**不要改坏**: + +1. **自定义 User 先于首次迁移**:`apps/users/migrations/0001_initial.py` 只依赖 `auth`,`AUTH_USER_MODEL='users.User'` 已设,`db_table='user'`。Django 头号坑已避开。 +2. **PyMySQL 胶水**:`config/__init__.py` 的 `pymysql.install_as_MySQLdb()`——不可删。 +3. **密钥零泄露**:SECRET_KEY / DB 密码全走 env;`.env`、`ai_models.json` 均未被 git 跟踪且在 `.gitignore`。 +4. **测试库隔离**:坚持独立 `test_cmhub` 测试库,不碰业务库 `cmhub`。 +5. `cryptography` 依赖(MySQL8 `caching_sha2_password` + 未来 `api_key` 加密预留);admin 继承 `DjangoUserAdmin` 做脱敏/过滤/只读。 + +## 三、修补清单 + +### P1 · 现在改(趁地基未固化,低成本高收益) + +#### P1-1 `User.email` 加 `unique=True` + +- **症状**:`email` 必填但不唯一,允许同邮箱重复注册,与需求(`02-requirements.md`)「邮箱验证注册」语义冲突;后续 allauth 邮箱验证 / 找回密码通常要求 email 唯一。现在 `user` 表基本为空,是加约束的最便宜时机;拖到 T-501 再加要额外迁移 + 清洗重复数据。 +- **位置**:`apps/users/models.py`(`email` 字段定义处)。 +- **怎么改**:`email = models.EmailField("email address", unique=True)`。 +- **前提/权衡**:若产品最终确定「只用 username 登录、email 仅展示」,可不改——但需在本文此条标注「产品确认不需要」。**默认动作:加 unique。** +- **注意**:改前确认业务库 `user` 表无重复邮箱(superuser 用唯一邮箱);否则先清理。 +- **验证**:`makemigrations users`(应生成一条 `AlterField`)→ `migrate` → `check` → `test` 全绿。 + +#### P1-2 `requires-python` 落地 + init 脚本校验解释器版本 + +- **症状**:`03-tech-stack.md` 要求「骨架须写死 `requires-python=">=3.12,<3.14"`,init 脚本校验解释器版本」。当前用 `requirements.txt`(表达不了 Python 版本约束),`init.sh`/`init.ps1` 只硬调 `python3.12` / `py -3.12` 命令、**无显式版本断言**——换台机器解释器指向 3.13 或不存在时不会明确报错。文档一致性欠账。 +- **位置**:项目根(无 `pyproject.toml`);`init.sh` 顶部;`init.ps1` 顶部。 +- **怎么改(二选一,取轻——推荐 A)**: + - **A(推荐,最小改动)**:在 `init.sh` / `init.ps1` 安装依赖前加一步 Python 版本断言:解析解释器版本,若不在 `>=3.12,<3.14` 则打印错误并退出(非 0)。 + - **B**:新增 `pyproject.toml` 写 `requires-python=">=3.12,<3.14"`(仅在配合构建/安装工具时才强制,较重;若采用需同步 `03-tech-stack.md` 的依赖管理说明)。 +- **验证**:用非 3.12 解释器(或模拟)跑 init,应明确报错退出;用 3.12 正常走完安装 + `check`。 + +### P2 · 规范性(趁早理顺) + +#### P2-1 `INSTALLED_APPS` 顺序 + +- **症状**:`rest_framework` 与 `apps.*` 排在整个 `django.contrib.*` **之前**。`APP_DIRS=True` 下模板按 `INSTALLED_APPS` 顺序查找——将来 `portal` 做用户端模板时,本地 app 模板会**优先于 admin**,若不慎放了同名模板(如 `admin/base_site.html`)会意外覆盖后台。当前无模板,是潜在隐患。 +- **位置**:`config/settings.py` 的 `INSTALLED_APPS`。 +- **怎么改**:调整为 `django.contrib.*`(admin/auth/contenttypes/sessions/messages/staticfiles)在前 → `rest_framework` → `apps.users|portal|billing|ai|api`。 +- **验证**:`check` / `test` 仍全绿;`/admin/` 正常。 + +#### P2-2 补 `.env.example` + +- **症状**:`.gitignore` 有 `!.env.example` 白名单,说明本应提供样例,但文件缺失;新人不知道要配哪些环境变量。 +- **位置**:项目根。 +- **怎么改**:新增 `.env.example`,列出 `MYSQL_HOST/PORT/DATABASE/USER/PASSWORD/CHARSET`、`DJANGO_SECRET_KEY`、`DJANGO_DEBUG`、`DJANGO_ALLOWED_HOSTS` 等键,值用占位符(**不含任何真实值**)。以 `env.md` 为准。 +- **验证**:`git status` 显示 `.env.example` 被跟踪(未被 `.gitignore` 忽略)。 + +#### P2-3 同步根 `README.md` 当前状态 + +- **症状**:根 `README.md` 的「当前状态」仍停在 T-001 / 下一步 T-002,已落后于 T-003 完成后的现实状态。 +- **位置**:项目根 `README.md`。 +- **怎么改**:更新为 Phase 0 已完成、当前先做 T-004 修补、之后进入 T-101。 +- **验证**:`README.md` 与 `docs/current-state.md` 的下一步口径一致。 + +### P3 · 后续任务处理(**不在 T-004 范围**,仅登记) + +- **REST_FRAMEWORK 认证配置**:`DEFAULT_AUTHENTICATION_CLASSES` 等(对外 API 只认 Key 不认 session)——留给 **T-301**。 +- **`sql_mode` 完整性**:`init_command` 仅 `STRICT_TRANS_TABLES`,会覆盖 MySQL 默认 sql_mode 其余项;对 InnoDB 防截断已够,可后续补 `NO_ENGINE_SUBSTITUTION` 等。 +- **生产 SECRET_KEY 保护**:`DEBUG=False` 且 `SECRET_KEY` 仍为默认 `django-insecure-*` 时应主动报错——留给部署任务 **T-403**。 + +## 四、说明:未本地复跑 + +审核机(WSL)无 `python3.12`,**未本地复跑 `check`/`test`**。本报告基于:静态审查(代码 + 迁移文件 + 配置)+ codex 的执行记录(记录含真实报错与修复过程:MySQL 授权、MySQL8 `caching_sha2_password` 需 `cryptography`,可信度高)。 + +T-004 修补后,请重跑 `check` / `test` / `init`,并把命令与结果记入 `progress.md` 作为证据(按 `06-tasks.md` 使用规则第 5 条)。 + +## 五、T-004 完成定义 + +- P1-1、P1-2、P2-1、P2-2、P2-3 全部处理(P1-1 若产品确认不需要则标注跳过)。 +- `check` 0 issues、`test` 全绿、`init` 通过,证据入 `progress.md`。 +- P3 各项已在 `06-tasks.md` 对应任务(T-301 / T-403)或 Backlog 有登记,不遗失。 diff --git a/init.ps1 b/init.ps1 index 534ced5..5ec49b4 100644 --- a/init.ps1 +++ b/init.ps1 @@ -33,6 +33,22 @@ Assert-Configured -Name "StartCmd" -Value $StartCmd Write-Host "==> 当前目录: $($PWD.Path)" +Write-Host "==> 校验 Python 版本" +$PythonVersion = py -3.12 -c "import sys; print(f'{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}')" +if ($LASTEXITCODE -ne 0) { + Write-Error "无法运行 py -3.12。请安装 Python 3.12/3.13,并确保 py launcher 可用。" + exit 3 +} + +$VersionParts = $PythonVersion.Trim().Split(".") +$MajorVersion = [int]$VersionParts[0] +$MinorVersion = [int]$VersionParts[1] +if ($MajorVersion -ne 3 -or $MinorVersion -lt 12 -or $MinorVersion -ge 14) { + Write-Error "Python 版本必须满足 >=3.12,<3.14,当前为 $PythonVersion。" + exit 3 +} +Write-Host " Python $PythonVersion" + if (-not (Test-Path "manage.py")) { Write-Error "未找到 manage.py。Django 骨架尚未初始化(见 docs/06-tasks.md 的 T-001)。完成 T-001 后本脚本即可正常运行。" exit 1 diff --git a/init.sh b/init.sh index 013f25a..6acf2a4 100644 --- a/init.sh +++ b/init.sh @@ -33,6 +33,19 @@ ensure_configured "START_CMD" "${START_CMD[0]}" echo "==> 当前目录: $PWD" +echo "==> 校验 Python 版本" +if ! PYTHON_VERSION="$(python3.12 -c 'import sys; print(f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}")' 2>/dev/null)"; then + echo "ERROR: 无法运行 python3.12。请安装 Python 3.12/3.13,并确保命令可用。" + exit 3 +fi + +IFS=. read -r PY_MAJOR PY_MINOR _ <<< "$PYTHON_VERSION" +if [ "$PY_MAJOR" -ne 3 ] || [ "$PY_MINOR" -lt 12 ] || [ "$PY_MINOR" -ge 14 ]; then + echo "ERROR: Python 版本必须满足 >=3.12,<3.14,当前为 ${PYTHON_VERSION}。" + exit 3 +fi +echo " Python ${PYTHON_VERSION}" + if [ ! -f "manage.py" ]; then echo "ERROR: 未找到 manage.py。Django 骨架尚未初始化(见 docs/06-tasks.md 的 T-001)。" echo " 完成 T-001 后本脚本即可正常运行。" diff --git a/progress.md b/progress.md index 52dae9b..ffc9624 100644 --- a/progress.md +++ b/progress.md @@ -230,3 +230,39 @@ - 阻塞:无。 - 决策:保留独立 `test_cmhub` 测试库路径,标准测试不使用业务库 `cmhub`。 - 下一步:领取 T-101 Provider 适配器层 + 移植 cmbot 调用。 + +## 2026-07-02 Phase 0 骨架审核(Claude Code review,非任务) + +- 状态:DONE(审核完成) +- 变更:新增 `docs/phase-0-review.md`;`06-tasks.md` 新增 T-004 修补任务;`docs/README.md` 导航登记;`current-state.md` 下一步改 T-004。 +- 审核结论:**T-001~003 验收全部达标,质量高于骨架平均水准**。自定义 User 先于首次迁移、`AUTH_USER_MODEL` 已设、`PyMySQL install_as_MySQLdb()` 胶水到位、密钥全走 env 且 `.env`/`ai_models.json` 未被 git 跟踪、独立 `test_cmhub` 测试库——关键硬约束全对。 +- 发现(详见 `phase-0-review.md`): + - P1(建议现在改):`User.email` 未 `unique`;`requires-python` 未落地、init 脚本无解释器版本断言。 + - P2(规范性):`INSTALLED_APPS` 顺序(本地 app 排在 contrib 前,模板查找隐患);缺 `.env.example`。 + - P3(后续任务处理):REST_FRAMEWORK 认证配置(T-301)、`sql_mode` 完整性、生产 SECRET_KEY 保护(T-403)。 +- 未本地复跑:审核机无 `python3.12`,结论基于静态审查 + codex 验证记录(记录真实,含 MySQL 授权 / caching_sha2 修复过程)。 +- 下一步:codex 领 T-004,按 `phase-0-review.md` 修 P1/P2,全绿留证后再进 T-101。 + +## 2026-07-02 T-004 Phase 0 骨架审核修补 + +- 状态:DONE +- 变更: + - 文档先行:更新 `README.md`、`docs/00-ai-start-here.md`、`docs/03-tech-stack.md`、`docs/04-architecture.md`、`docs/api.md`、`docs/current-state.md`、`docs/env.md`、`docs/06-tasks.md`,把 T-004、邮箱唯一约束、Python 版本断言、`.env.example` 和下一步 T-101 口径同步。 + - `apps/users.models.User.email` 增加 `unique=True`,并生成/应用 `apps/users/migrations/0002_alter_user_email.py`。 + - `config/settings.py` 调整 `INSTALLED_APPS` 顺序为 `django.contrib.*` → `rest_framework` → 本地 apps。 + - `init.ps1` / `init.sh` 在安装依赖前校验 Python 版本必须满足 `>=3.12,<3.14`。 + - 新增 `.env.example`(仅占位符,无真实密钥)与 `pyproject.toml`(落地 `requires-python`)。 +- 验证: + - 重复邮箱预检:业务库非空 email 无重复。 + - `py -3.12 manage.py makemigrations users`:生成 `0002_alter_user_email.py`。 + - `py -3.12 manage.py migrate`:通过,应用 `users.0002_alter_user_email`。 + - 数据库索引验证:`user.email` 存在唯一索引 `user_email_54dc62b2_uniq`。 + - `py -3.12 manage.py check`:通过,0 issues。 + - `py -3.12 manage.py test`:通过,2 tests OK。 + - `./init.ps1`:通过,输出 Python 3.12.3 版本断言、依赖同步与基础检查。 + - `py -3.12 manage.py makemigrations --check`:通过,No changes detected。 + - `git diff --check`:通过,仅有 CRLF 提示。 + - `bash -n init.sh`:当前 Windows 未安装 WSL/bash,无法本机验证 Unix 脚本语法。 +- 阻塞:无。 +- 决策:保留 `requirements.txt` 作为运行依赖来源,`pyproject.toml` 只承载 Python 版本元数据;`.env.example` 使用占位符,不提交 `.env`。 +- 下一步:领取 T-101 Provider 适配器层 + 移植 cmbot 调用。 diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..e9b6bf2 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,6 @@ +[project] +name = "cmhub" +version = "0.1.0" +description = "Self-service billing API gateway and django-admin operations backend for AI generation." +requires-python = ">=3.12,<3.14" +dependencies = []