docs: add t-403 deployment guide

This commit is contained in:
QiuSW
2026-07-03 17:53:49 +08:00
parent 3fdb25945b
commit 3a661afaa5
15 changed files with 436 additions and 25 deletions
+14
View File
@@ -2,9 +2,23 @@
DJANGO_SECRET_KEY=change-me-generate-a-random-secret DJANGO_SECRET_KEY=change-me-generate-a-random-secret
DJANGO_DEBUG=true DJANGO_DEBUG=true
DJANGO_ALLOWED_HOSTS=127.0.0.1,localhost,testserver DJANGO_ALLOWED_HOSTS=127.0.0.1,localhost,testserver
DJANGO_CSRF_TRUSTED_ORIGINS=
DJANGO_TIME_ZONE=Asia/Shanghai DJANGO_TIME_ZONE=Asia/Shanghai
DJANGO_EMAIL_BACKEND=django.core.mail.backends.console.EmailBackend DJANGO_EMAIL_BACKEND=django.core.mail.backends.console.EmailBackend
DJANGO_DEFAULT_FROM_EMAIL=noreply@cmhub.local DJANGO_DEFAULT_FROM_EMAIL=noreply@cmhub.local
DJANGO_SESSION_COOKIE_SECURE=false
DJANGO_CSRF_COOKIE_SECURE=false
DJANGO_SECURE_SSL_REDIRECT=false
DJANGO_SECURE_HSTS_SECONDS=0
DJANGO_SECURE_HSTS_INCLUDE_SUBDOMAINS=false
DJANGO_SECURE_HSTS_PRELOAD=false
DJANGO_SECURE_PROXY_SSL_HEADER=false
# Static and shared cache
STATIC_URL=/static/
STATIC_ROOT=D:\chengma\cmhub\staticfiles
DJANGO_CACHE_BACKEND=django.core.cache.backends.locmem.LocMemCache
DJANGO_CACHE_LOCATION=cmhub-local-cache
# MySQL 8.4 # MySQL 8.4
MYSQL_HOST=127.0.0.1 MYSQL_HOST=127.0.0.1
+1 -1
View File
@@ -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 完整验收:用户可通过 allauth 自助注册、邮箱验证、登录、登出,扫码充值并轮询到账,生成 / 删除(吊销)API Key,查看余额、充值总额、分页充值记录与分页消费记录;运营可在 django-admin 检索用户、钱包、API Key、计费规则、汇率、充值订单、点数流水和调用记录,并通过计费层带原因手工调点。下一步进入 T-403 部署 / 运行文档。详见 [`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 部署 / 运行文档:用户可通过 allauth 自助注册、邮箱验证、登录、登出,扫码充值并轮询到账,生成 / 删除(吊销)API Key,查看余额、充值总额、分页充值记录与分页消费记录;运营可在 django-admin 检索用户、钱包、API Key、计费规则、汇率、充值订单、点数流水和调用记录,并通过计费层带原因手工调点;生产部署按 `docs/deployment.md` 执行。计划内 MVP 任务已完成,下一步是按部署文档上 VPS 配置真实邮件、支付、AI 模型和图片真实耗时验证。详见 [`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) 第四节计费时序。 > ⚠️ 涉及资金/点数。改动充值、扣费、退款、对账相关代码前,先读 [`docs/05-coding-rules.md`](docs/05-coding-rules.md) 第 8 节与 [`docs/04-architecture.md`](docs/04-architecture.md) 第四节计费时序。
+27 -1
View File
@@ -94,6 +94,21 @@ ALIPAY_NOTIFY_URL = os.environ.get("ALIPAY_NOTIFY_URL", "")
ALIPAY_DEBUG = env_bool("ALIPAY_DEBUG", False) ALIPAY_DEBUG = env_bool("ALIPAY_DEBUG", False)
ALLOWED_HOSTS = env_list("DJANGO_ALLOWED_HOSTS", "127.0.0.1,localhost,testserver") ALLOWED_HOSTS = env_list("DJANGO_ALLOWED_HOSTS", "127.0.0.1,localhost,testserver")
CSRF_TRUSTED_ORIGINS = env_list("DJANGO_CSRF_TRUSTED_ORIGINS", "")
SESSION_COOKIE_SECURE = env_bool("DJANGO_SESSION_COOKIE_SECURE", not DEBUG)
CSRF_COOKIE_SECURE = env_bool("DJANGO_CSRF_COOKIE_SECURE", not DEBUG)
SECURE_SSL_REDIRECT = env_bool("DJANGO_SECURE_SSL_REDIRECT", False)
SECURE_HSTS_SECONDS = env_int("DJANGO_SECURE_HSTS_SECONDS", 0)
SECURE_HSTS_INCLUDE_SUBDOMAINS = env_bool(
"DJANGO_SECURE_HSTS_INCLUDE_SUBDOMAINS",
False,
)
SECURE_HSTS_PRELOAD = env_bool("DJANGO_SECURE_HSTS_PRELOAD", False)
SECURE_PROXY_SSL_HEADER = (
("HTTP_X_FORWARDED_PROTO", "https")
if env_bool("DJANGO_SECURE_PROXY_SSL_HEADER", False)
else None
)
API_GENERATE_THROTTLE_RATE = os.environ.get("API_GENERATE_THROTTLE_RATE", "60/min") API_GENERATE_THROTTLE_RATE = os.environ.get("API_GENERATE_THROTTLE_RATE", "60/min")
API_AUTH_FAILURE_THROTTLE_RATE = os.environ.get("API_AUTH_FAILURE_THROTTLE_RATE", "30/min") API_AUTH_FAILURE_THROTTLE_RATE = os.environ.get("API_AUTH_FAILURE_THROTTLE_RATE", "30/min")
@@ -207,6 +222,16 @@ DATABASES = {
} }
} }
CACHES = {
"default": {
"BACKEND": os.environ.get(
"DJANGO_CACHE_BACKEND",
"django.core.cache.backends.locmem.LocMemCache",
),
"LOCATION": os.environ.get("DJANGO_CACHE_LOCATION", "cmhub-local-cache"),
}
}
# Password validation # Password validation
# https://docs.djangoproject.com/en/5.2/ref/settings/#auth-password-validators # https://docs.djangoproject.com/en/5.2/ref/settings/#auth-password-validators
@@ -242,7 +267,8 @@ USE_TZ = True
# Static files (CSS, JavaScript, Images) # Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/5.2/howto/static-files/ # https://docs.djangoproject.com/en/5.2/howto/static-files/
STATIC_URL = 'static/' STATIC_URL = os.environ.get("STATIC_URL", "/static/")
STATIC_ROOT = os.environ.get("STATIC_ROOT", str(BASE_DIR / "staticfiles"))
MEDIA_ROOT = os.environ.get("MEDIA_ROOT", str(BASE_DIR / "media")) MEDIA_ROOT = os.environ.get("MEDIA_ROOT", str(BASE_DIR / "media"))
MEDIA_URL = os.environ.get("MEDIA_URL", "/media/") MEDIA_URL = os.environ.get("MEDIA_URL", "/media/")
+2 -2
View File
@@ -38,7 +38,7 @@
## 当前阶段 ## 当前阶段
当前项目处于:**Phase 5 后台与发布**。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 5 后台与发布已完成计划内任务**。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 部署 / 运行文档。下一步是按 `deployment.md` 在 VPS 上配置真实邮件、支付、AI 模型和图片真实耗时验证,或从 Backlog 重新规划后续任务。
优先路径: 优先路径:
@@ -47,7 +47,7 @@
3. Phase 2:计费核心 —— T-201/T-202/T-203 已完成 UserWallet/ApiKey/PointsLedger/CallRecord、计费规则、汇率、计费计算、并发安全扣点与失败退点。 3. Phase 2:计费核心 —— T-201/T-202/T-203 已完成 UserWallet/ApiKey/PointsLedger/CallRecord、计费规则、汇率、计费计算、并发安全扣点与失败退点。
4. Phase 3:对外 API 与充值 —— T-301 Key 鉴权、T-302 生成接口、T-303 余额查询、T-304 充值回调、T-305 扫码下单与轮询、T-306 安全加固已完成。 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 用户端审核优化已完成。 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 部署 / 运行文档。 6. Phase 5:后台与发布 —— T-401 运营后台完善、T-402 完整验收 MVP、T-403 部署 / 运行文档已完成;计划内 MVP 任务已收尾。
## 领取任务规则 ## 领取任务规则
+6 -1
View File
@@ -24,7 +24,7 @@
| 充值对接 | 自助扫码:微信 V3 native + 支付宝当面付;下单取二维码 + 服务端回调(验签 + 幂等) | 已定 | T-304/T-305 已落地回调、扫码下单、状态轮询、HMAC mock 联调与 SDK 模式入口;生产需安装并配置 `wechatpayv3` / `python-alipay-sdk` 与真实商户密钥/证书 | | 充值对接 | 自助扫码:微信 V3 native + 支付宝当面付;下单取二维码 + 服务端回调(验签 + 幂等) | 已定 | T-304/T-305 已落地回调、扫码下单、状态轮询、HMAC mock 联调与 SDK 模式入口;生产需安装并配置 `wechatpayv3` / `python-alipay-sdk` 与真实商户密钥/证书 |
| 生成返回方式 | 同步 HTTP(无任务队列) | 已定 | MVP 简化;图片接口需调大网关/服务超时 | | 生成返回方式 | 同步 HTTP(无任务队列) | 已定 | MVP 简化;图片接口需调大网关/服务超时 |
| 任务队列 | 暂不引入(Celery/RQ) | 待定 | V2 异步化时再评估 | | 任务队列 | 暂不引入(Celery/RQ) | 待定 | V2 异步化时再评估 |
| 部署方式 | Docker + Gunicorn(gthread) + Nginx,单体 | 待定 | MVP 先 `runserver`;生产 Nginx 按路径把 `/api/generate/*`(图片长请求)与用户端页面**分流到不同 gunicorn/worker 池**,避免图片阻塞拖慢页面(见 `04-architecture.md` 5.1) | | 部署方式 | VPS / 宝塔 + Nginx + Gunicorn(gthread) + systemd,Django 单体 | 已定(MVP) | 生产按 `deployment.md` 执行;Nginx 按路径把 `/api/v1/generate/*`(图片长请求)分流到独立 Gunicorn 池,用户端 / admin / 余额 / 充值走普通池;`/static/` 托管 `STATIC_ROOT`,`/media/` 托管本地媒体或后续换对象存储;DRF 限流生产必须使用共享 Django cache |
| 测试 | Django 自带 `manage.py test`(unittest)/ 可选 pytest-django | 已定 | 先用内置 test runner,重点覆盖计费与回调;涉及 `select_for_update` 的并发扣点测试必须在 MySQL 上跑,SQLite 会忽略行锁导致假绿 | | 测试 | Django 自带 `manage.py test`(unittest)/ 可选 pytest-django | 已定 | 先用内置 test runner,重点覆盖计费与回调;涉及 `select_for_update` 的并发扣点测试必须在 MySQL 上跑,SQLite 会忽略行锁导致假绿 |
| 依赖管理 | 系统 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` | | 依赖管理 | 系统 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` |
@@ -51,12 +51,17 @@
| --- | --- | | --- | --- |
| 安装依赖(Windows) | `py -3.12 -m pip install -r requirements.txt` | | 安装依赖(Windows) | `py -3.12 -m pip install -r requirements.txt` |
| 安装依赖(Unix/WSL) | `python3.12 -m pip install -r requirements.txt` | | 安装依赖(Unix/WSL) | `python3.12 -m pip install -r requirements.txt` |
| 安装生产依赖(Linux) | `python3.12 -m pip install -r requirements-production.txt` |
| 基础检查(Windows) | `py -3.12 manage.py check` | | 基础检查(Windows) | `py -3.12 manage.py check` |
| 基础检查(Unix/WSL) | `python3.12 manage.py check` | | 基础检查(Unix/WSL) | `python3.12 manage.py check` |
| 测试(Windows) | `py -3.12 manage.py test` | | 测试(Windows) | `py -3.12 manage.py test` |
| 本地开发(Windows) | `py -3.12 manage.py runserver` | | 本地开发(Windows) | `py -3.12 manage.py runserver` |
| 创建后台管理员 | T-003 后执行 `py -3.12 manage.py createsuperuser` | | 创建后台管理员 | T-003 后执行 `py -3.12 manage.py createsuperuser` |
| 数据库迁移 | T-002 接入 MySQL 后执行 `makemigrations` / `migrate` | | 数据库迁移 | T-002 接入 MySQL 后执行 `makemigrations` / `migrate` |
| 生产静态文件收集 | `python3.12 manage.py collectstatic --noinput` |
| 生产共享 cache 表 | `python3.12 manage.py createcachetable cmhub_cache` |
| 生产 Gunicorn 普通池 | `gunicorn config.wsgi:application --bind 127.0.0.1:8001 --workers 2 --worker-class gthread --threads 4 --timeout 120` |
| 生产 Gunicorn 生成池 | `gunicorn config.wsgi:application --bind 127.0.0.1:8002 --workers 1 --worker-class gthread --threads 4 --timeout 360` |
| 导入 cmbot AI 模型配置 | `py -3.12 manage.py import_ai_models path\to\ai_models.json --create-default-aliases` | | 导入 cmbot AI 模型配置 | `py -3.12 manage.py import_ai_models path\to\ai_models.json --create-default-aliases` |
| AI 生成 smoke(录制标题) | `py -3.12 manage.py smoke_ai_generation title --recorded` | | AI 生成 smoke(录制标题) | `py -3.12 manage.py smoke_ai_generation title --recorded` |
| AI 生成 smoke(录制图片) | `py -3.12 manage.py smoke_ai_generation image --recorded` | | AI 生成 smoke(录制图片) | `py -3.12 manage.py smoke_ai_generation image --recorded` |
+1 -1
View File
@@ -347,7 +347,7 @@ CREATE TABLE call_record (
| 配置变更审计 | 改密钥/模型/别名映射无痕 | `AiConfigAuditLog` 自动记录后台 create/update/delete;密钥只记录 empty/set 变化,日志只读 | | 配置变更审计 | 改密钥/模型/别名映射无痕 | `AiConfigAuditLog` 自动记录后台 create/update/delete;密钥只记录 empty/set 变化,日志只读 |
| API Key 泄露 | 明文存库一旦泄露全泄 | Key **哈希存储**(sha256),明文只在创建时显示一次,库内存 `key_hash`+`key_prefix`,鉴权做哈希比对 | | API Key 泄露 | 明文存库一旦泄露全泄 | Key **哈希存储**(sha256),明文只在创建时显示一次,库内存 `key_hash`+`key_prefix`,鉴权做哈希比对 |
| 注册滥用 | 自助注册被批量刷 | 邮箱验证 + 生成接口限流(DRF throttle);**注册不送点数**,无免费额度可薅 | | 注册滥用 | 自助注册被批量刷 | 邮箱验证 + 生成接口限流(DRF throttle);**注册不送点数**,无免费额度可薅 |
| Web/API 抢 worker | 图片同步长请求占满 worker、拖慢用户端页面 | 单体下按路径把 `/api/generate/*` 与用户端页面**分流到不同 gunicorn/worker 池**(见 5.1) | | Web/API 抢 worker | 图片同步长请求占满 worker、拖慢用户端页面 | 单体下按路径把 `/api/v1/generate/*` 与用户端页面**分流到不同 gunicorn/worker 池**(见 5.1) |
| 充错账户 | 扫码订单未绑定发起用户 | 订单创建即绑定 `user`;回调按 `order_no` 定位订单→其 user 入账 | | 充错账户 | 扫码订单未绑定发起用户 | 订单创建即绑定 `user`;回调按 `order_no` 定位订单→其 user 入账 |
| SSRF / 任意 URL 下载 | `image_url` 让服务端请求调用方指定地址 | 仅允许公网 http/https;请求前解析 IP 并拒绝内网/回环/链路本地/保留地址;重定向逐跳校验;流式读取并限制大小 | | SSRF / 任意 URL 下载 | `image_url` 让服务端请求调用方指定地址 | 仅允许公网 http/https;请求前解析 IP 并拒绝内网/回环/链路本地/保留地址;重定向逐跳校验;流式读取并限制大小 |
+1 -1
View File
@@ -73,7 +73,7 @@
| --- | --- | --- | --- | --- | | --- | --- | --- | --- | --- |
| T-401 | 运营后台完善 | T-302, T-304 | admin 可管理用户/钱包/ApiKey(脱敏)、配规则/汇率、检索充值订单/流水/调用记录;手工调点带原因并经计费层写流水 | DONE | | T-401 | 运营后台完善 | T-302, T-304 | admin 可管理用户/钱包/ApiKey(脱敏)、配规则/汇率、检索充值订单/流水/调用记录;手工调点带原因并经计费层写流水 | DONE |
| T-402 | 完整验收 MVP | T-401, T-505 | `02-requirements.md` 的 P0 验收全部通过(含用户端注册/充值/API Key/记录) | 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` 后端并在文档说明,否则限流形同虚设 | TODO | | 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 |
## 里程碑 ## 里程碑
+1
View File
@@ -26,6 +26,7 @@
- [Phase 3 对外 API 与充值审核](phase-3-review.md):T-301~305 代码审核结论(鉴权/回调/幂等/入账逐条核对)与优化建议,对应任务 T-306;**P1 提示 `image_url` SSRF 上线前必修**。 - [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/明文只显一次/到账以回调为权威逐条核对)与优化建议(**零 P1**,P2/P3),对应任务 T-505。
- [MVP 完整验收报告](mvp-acceptance.md):T-402 对 `02-requirements.md` P0 验收项的逐项结论、测试证据和已知限制。 - [MVP 完整验收报告](mvp-acceptance.md):T-402 对 `02-requirements.md` P0 验收项的逐项结论、测试证据和已知限制。
- [部署 / 运行文档](deployment.md):T-403 生产部署步骤、宝塔 / Nginx / Gunicorn 配置、静态文件、共享 cache、图片超时与上线检查。
- [API 合约](api.md):对外接口、支付回调、AI 调用模块合约、错误码。 - [API 合约](api.md):对外接口、支付回调、AI 调用模块合约、错误码。
- [路由与页面结构](routes.md):API 路由与 django-admin 后台职责。 - [路由与页面结构](routes.md):API 路由与 django-admin 后台职责。
- [环境变量与配置](env.md):Django、数据库、AI 密钥加密、支付、对象存储等配置项。 - [环境变量与配置](env.md):Django、数据库、AI 密钥加密、支付、对象存储等配置项。
+20 -12
View File
File diff suppressed because one or more lines are too long
+302
View File
@@ -0,0 +1,302 @@
# 部署 / 运行文档(T-403)
> 面向 VPS + 宝塔面板 + Nginx + MySQL 8.4 的 MVP 部署说明。本文只写可执行步骤和上线前检查,不保存真实密钥、数据库密码、支付凭证或上游 API Key。
## 一、部署目标
生产形态:
```text
公网 HTTPS
-> 宝塔 / Nginx
/static/ -> STATIC_ROOT
/media/ -> MEDIA_ROOT(MVP 本地媒体;后续可换对象存储)
/api/v1/generate/* -> Gunicorn 长请求池(图片同步,超时更长)
其他路径 -> Gunicorn 普通请求池(用户端 / admin / 余额 / 充值)
-> cmhub Django 单体
-> MySQL 8.4
-> Django shared cache(MVP 可用 MySQL DatabaseCache;高并发换 Redis/Memcached)
```
当前约束:
- 使用系统 Python 3.12,不使用虚拟环境。
- 图片生成仍是同步接口;真实图片生成耗时尚未在生产链路验证,**上线前必须跑一次真实图片 smoke 并记录耗时**。
- 真实支付需微信 / 支付宝商户密钥、证书、生产 SDK 与公网回调地址;未配置前只能用 `PAYMENT_CALLBACK_MODE=mock`。
## 二、系统准备
1. 安装系统依赖:
```bash
python3.12 --version
python3.12 -m pip --version
```
2. 准备 MySQL 8.4:
- 使用 cmhub 专用 MySQL 8.4 实例,不复用已有 MySQL 5.7。
- 默认端口为 `3306`;如果 VPS 上已有 MySQL 占用 3306,则为 cmhub 的 MySQL 8.4 配专用端口,并同步写入 `.env` 的 `MYSQL_PORT`。
- 数据库字符集使用 `utf8mb4`,表引擎使用 InnoDB。
示例 SQL(密码仅占位):
```sql
CREATE DATABASE cmhub CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'cmhub'@'%' IDENTIFIED BY 'change-me-strong-password';
GRANT ALL PRIVILEGES ON cmhub.* TO 'cmhub'@'%';
FLUSH PRIVILEGES;
```
3. 拉取代码到生产目录,例如:
```bash
cd /www/wwwroot
git clone <your-remote-url> cmhub
cd /www/wwwroot/cmhub
```
## 三、安装依赖
生产 Linux 安装运行依赖:
```bash
python3.12 -m pip install -r requirements-production.txt
```
如果启用真实支付 SDK,还需按支付 SDK 文档在生产机安装:
```bash
python3.12 -m pip install wechatpayv3 python-alipay-sdk
```
如果暂未提供真实商户配置,不安装支付 SDK 也能以 `PAYMENT_CALLBACK_MODE=mock` 运行本地/测试流程。
## 四、配置 `.env`
从样例复制后只在部署机填写真实值:
```bash
cp .env.example .env
```
生产关键配置:
```dotenv
DJANGO_SECRET_KEY=change-me-to-a-long-random-secret
DJANGO_DEBUG=false
DJANGO_ALLOWED_HOSTS=cmhub.example.com,127.0.0.1
DJANGO_CSRF_TRUSTED_ORIGINS=https://cmhub.example.com
DJANGO_TIME_ZONE=Asia/Shanghai
DJANGO_EMAIL_BACKEND=django.core.mail.backends.smtp.EmailBackend
DJANGO_DEFAULT_FROM_EMAIL=noreply@cmhub.example.com
DJANGO_SESSION_COOKIE_SECURE=true
DJANGO_CSRF_COOKIE_SECURE=true
DJANGO_SECURE_PROXY_SSL_HEADER=true
DJANGO_SECURE_SSL_REDIRECT=false
DJANGO_SECURE_HSTS_SECONDS=0
MYSQL_HOST=127.0.0.1
MYSQL_PORT=3306
MYSQL_DATABASE=cmhub
MYSQL_USER=cmhub
MYSQL_PASSWORD=change-me
MYSQL_CHARSET=utf8mb4
MYSQL_CONNECT_TIMEOUT=30
MYSQL_READ_TIMEOUT=120
MYSQL_WRITE_TIMEOUT=120
STATIC_URL=/static/
STATIC_ROOT=/www/wwwroot/cmhub/staticfiles
MEDIA_ROOT=/www/wwwroot/cmhub/media
MEDIA_URL=/media/
DJANGO_CACHE_BACKEND=django.core.cache.backends.db.DatabaseCache
DJANGO_CACHE_LOCATION=cmhub_cache
AI_KEY_ENCRYPTION_KEY=base64-fernet-key
PAYMENT_CALLBACK_MODE=sdk
WECHAT_PAY_NOTIFY_URL=https://cmhub.example.com/api/v1/recharge/callback/wechat
ALIPAY_NOTIFY_URL=https://cmhub.example.com/api/v1/recharge/callback/alipay
```
生成 Fernet 主密钥:
```bash
python3.12 -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"
```
注意:
- `AI_KEY_ENCRYPTION_KEY` 生产不可随意更换;更换会导致已加密的 `AiModel.api_key_encrypted` 无法解密。
- 生产必须配置真实邮件服务,否则 allauth 邮箱验证邮件发不出,用户无法完成注册登录。
- `PAYMENT_CALLBACK_MODE=sdk` 必须配齐微信 / 支付宝商户配置;未配齐时先保持 `mock`。
- 宝塔 / Nginx 已强制 HTTPS 时,`DJANGO_SECURE_SSL_REDIRECT=false` 即可;全站 HTTPS 稳定后再把 `DJANGO_SECURE_HSTS_SECONDS` 调大,避免 HSTS 误锁域名。
## 五、初始化数据库与静态文件
```bash
python3.12 manage.py check
python3.12 manage.py migrate
python3.12 manage.py createcachetable cmhub_cache
python3.12 manage.py collectstatic --noinput
python3.12 manage.py createsuperuser
```
如果后续改用 Redis / Memcached,则把 `DJANGO_CACHE_BACKEND` / `DJANGO_CACHE_LOCATION` 换成对应共享后端配置,并安装对应 Python backend 依赖。多 Gunicorn worker 下不要使用默认 `LocMemCache`,否则生成接口和认证失败限流会按 worker 各算一份。
## 六、导入 AI 模型配置
真实上游调用前:
```bash
python3.12 manage.py import_ai_models /secure/cmhub/ai_models.json --create-default-aliases
```
要求:
- `/secure/cmhub/ai_models.json` 不提交到 git。
- `AiModel.timeout_seconds` 不能继续依赖默认 `0` 口径上线;图片模型必须按真实耗时设置读取超时。
- 运营后台需配置 `PricingRule` 和 `ExchangeRate`,否则生成和充值会因缺规则被拒绝。
## 七、Gunicorn 运行
建议把同步图片生成分流到独立 Gunicorn 池,避免长请求占满普通页面和 admin 的 worker。
普通请求池示例:
```bash
gunicorn config.wsgi:application \
--bind 127.0.0.1:8001 \
--workers 2 \
--worker-class gthread \
--threads 4 \
--timeout 120 \
--access-logfile - \
--error-logfile -
```
生成长请求池示例:
```bash
gunicorn config.wsgi:application \
--bind 127.0.0.1:8002 \
--workers 1 \
--worker-class gthread \
--threads 4 \
--timeout 360 \
--access-logfile - \
--error-logfile -
```
`--timeout` 必须按真实图片生成耗时校准:`Gunicorn timeout` 应大于 `AiModel.timeout_seconds`,Nginx `proxy_read_timeout` 应大于 Gunicorn timeout,调用方 read timeout 应不小于 Nginx。
systemd 单元可分别命名为 `cmhub-web.service` 和 `cmhub-generate.service`。服务的 `WorkingDirectory` 指向 `/www/wwwroot/cmhub`,`ExecStart` 使用上面的两条 Gunicorn 命令,环境变量由项目根目录 `.env` 在 Django settings 中读取。
## 八、宝塔 / Nginx 配置
宝塔中新建站点并绑定域名和 SSL 后,在站点 Nginx 配置中加入或调整:
```nginx
server {
listen 443 ssl http2;
server_name cmhub.example.com;
client_max_body_size 25m;
location /static/ {
alias /www/wwwroot/cmhub/staticfiles/;
expires 30d;
add_header Cache-Control "public";
}
location /media/ {
alias /www/wwwroot/cmhub/media/;
expires 7d;
add_header Cache-Control "public";
}
location /api/v1/generate/ {
proxy_pass http://127.0.0.1:8002;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
proxy_connect_timeout 30s;
proxy_send_timeout 360s;
proxy_read_timeout 360s;
}
location / {
proxy_pass http://127.0.0.1:8001;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
proxy_connect_timeout 30s;
proxy_send_timeout 120s;
proxy_read_timeout 120s;
}
}
```
说明:
- `/static/` 必须指向 `STATIC_ROOT`,否则 Bootstrap/qrcode 本地资源在 `DEBUG=False` 下 404。
- `/media/` 是 MVP 本地图片结果访问路径;如果改对象存储,应同步更新 `MEDIA_URL` 和存储配置。
- 宝塔若已在外层配置 HTTP 到 HTTPS 跳转,`DJANGO_SECURE_SSL_REDIRECT=false` 即可;如果由 Django 负责跳转,再设为 `true`。
## 九、上线前验证
基础验证:
```bash
python3.12 manage.py check
python3.12 manage.py check --deploy
python3.12 manage.py makemigrations --check --dry-run
python3.12 manage.py findstatic portal/vendor/bootstrap/bootstrap.min.css portal/vendor/qrcode/qrcode.js --verbosity 1
python3.12 manage.py collectstatic --dry-run --noinput
```
账务与用户端测试:
```bash
python3.12 manage.py test apps.billing apps.users --noinput --keepdb --verbosity 2
python3.12 manage.py test apps.api --noinput --keepdb --verbosity 2
python3.12 manage.py test apps.portal --noinput --keepdb --verbosity 2
```
AI smoke:
```bash
python3.12 manage.py smoke_ai_generation title --recorded
python3.12 manage.py smoke_ai_generation image --recorded
python3.12 manage.py smoke_ai_generation title
python3.12 manage.py smoke_ai_generation image
```
上线前必须记录真实 `image` smoke 的 `elapsed_ms`,并据此回填:
- 图片 `AiModel.timeout_seconds`
- Gunicorn 长请求池 `--timeout`
- Nginx `/api/v1/generate/` 的 `proxy_read_timeout`
- 接入方客户端 read timeout
如果真实图片 smoke 仍未跑通,只能按保守值(例如 300s~360s)上线内测,并在发布记录中明确“图片同步真实耗时风险未退”,不能声称已验证生产图片链路。
## 十、发布检查清单
- `DJANGO_DEBUG=false`。
- `DJANGO_SECRET_KEY`、`AI_KEY_ENCRYPTION_KEY`、MySQL 密码、支付密钥均为生产值且未提交。
- `DJANGO_ALLOWED_HOSTS`、`DJANGO_CSRF_TRUSTED_ORIGINS`、微信 / 支付宝 `notify_url` 使用同一 HTTPS 域名。
- 邮件后端可发送验证邮件。
- HTTPS 已由宝塔 / Nginx 强制跳转;如由 Django 强制跳转则设置 `DJANGO_SECURE_SSL_REDIRECT=true`。HSTS 只在确认全站 HTTPS 后启用。
- `STATIC_ROOT` 已 `collectstatic`,Nginx 可访问 `/static/portal/vendor/bootstrap/bootstrap.min.css` 和 `/static/portal/vendor/qrcode/qrcode.js`。
- `MEDIA_ROOT` 或对象存储可访问生成图片 URL。
- `DJANGO_CACHE_BACKEND` 为共享 cache,不是 `LocMemCache`。
- MySQL 是 8.4 / InnoDB / `utf8mb4`。
- 真实支付 SDK 与商户配置齐全后,`PAYMENT_CALLBACK_MODE=sdk`。
- 真实图片生成耗时已记录并用于设置超时链路;若未记录,发布说明必须标注风险。
- `manage.py check`、迁移、静态文件、关键测试和 smoke 均有记录。
+25 -3
View File
@@ -21,6 +21,13 @@
| `DJANGO_TIME_ZONE` | 否 | `Asia/Shanghai` | 默认按中国业务时区 | | `DJANGO_TIME_ZONE` | 否 | `Asia/Shanghai` | 默认按中国业务时区 |
| `DJANGO_EMAIL_BACKEND` | 否 | `django.core.mail.backends.console.EmailBackend` | allauth 注册邮箱验证发信后端;生产应改为真实 SMTP / 邮件服务 | | `DJANGO_EMAIL_BACKEND` | 否 | `django.core.mail.backends.console.EmailBackend` | allauth 注册邮箱验证发信后端;生产应改为真实 SMTP / 邮件服务 |
| `DJANGO_DEFAULT_FROM_EMAIL` | 生产是 | `noreply@cmhub.example.com` | allauth 邮件默认发件人 | | `DJANGO_DEFAULT_FROM_EMAIL` | 生产是 | `noreply@cmhub.example.com` | allauth 邮件默认发件人 |
| `DJANGO_SESSION_COOKIE_SECURE` | 生产是 | `true` | 生产 HTTPS 下 session cookie 仅允许安全连接传输 |
| `DJANGO_CSRF_COOKIE_SECURE` | 生产是 | `true` | 生产 HTTPS 下 CSRF cookie 仅允许安全连接传输 |
| `DJANGO_SECURE_SSL_REDIRECT` | 否 | `false` | 若 Nginx/宝塔已强制 HTTPS,可保持 `false`;若由 Django 强制跳转则设 `true` |
| `DJANGO_SECURE_HSTS_SECONDS` | 否 | `31536000` | 全站确认只走 HTTPS 后再启用;未确认前保持 `0`,避免 HSTS 误锁域名 |
| `DJANGO_SECURE_HSTS_INCLUDE_SUBDOMAINS` | 否 | `false` | 是否把 HSTS 应用于子域名 |
| `DJANGO_SECURE_HSTS_PRELOAD` | 否 | `false` | 是否声明 HSTS preload;提交 preload 前必须确认全部子域长期 HTTPS |
| `DJANGO_SECURE_PROXY_SSL_HEADER` | 反代 HTTPS 是 | `true` | Nginx 反代并传 `X-Forwarded-Proto https` 时开启,避免 Django 误判当前请求为 HTTP |
## 三、数据库配置 ## 三、数据库配置
@@ -64,7 +71,18 @@ AI 上游连接超时与读取超时不走全局环境变量:连接超时由 `
`image_url` 只允许 `http` / `https`,服务端会在请求前解析域名,拒绝私有网段、回环、链路本地、保留地址、组播、未指定地址;重定向后的目标地址也会重复执行同样校验。内网图片不应通过 `image_url` 传入,调用方应改用 `image_base64`。 `image_url` 只允许 `http` / `https`,服务端会在请求前解析域名,拒绝私有网段、回环、链路本地、保留地址、组播、未指定地址;重定向后的目标地址也会重复执行同样校验。内网图片不应通过 `image_url` 传入,调用方应改用 `image_base64`。
## 六、支付配置 ## 六、静态文件与限流缓存
| 变量 | 必填 | 示例 | 说明 |
| --- | --- | --- | --- |
| `STATIC_URL` | 否 | `/static/` | 静态资源 URL 前缀 |
| `STATIC_ROOT` | 生产是 | `/www/wwwroot/cmhub/staticfiles` | `collectstatic` 输出目录,Nginx/宝塔需托管此目录 |
| `DJANGO_CACHE_BACKEND` | 生产是 | `django.core.cache.backends.db.DatabaseCache` | Django cache 后端;开发可用 locmem,生产多 Gunicorn worker 必须用共享后端 |
| `DJANGO_CACHE_LOCATION` | 生产是 | `cmhub_cache` | cache 位置;DatabaseCache 时为表名,Redis/Memcached 时为连接地址 |
生产限流依赖 Django cache。默认 `LocMemCache` 只适合单进程本地开发;多 worker 部署时每个进程各算一份限流,会放大实际请求速率。MVP 可先用 MySQL 的 `DatabaseCache`,部署时执行 `python3.12 manage.py createcachetable cmhub_cache`;高并发后再换 Redis / Memcached 等共享 cache,并同步安装对应 backend 依赖。
## 七、支付配置
| 变量 | 必填 | 示例 | 说明 | | 变量 | 必填 | 示例 | 说明 |
| --- | --- | --- | --- | | --- | --- | --- | --- |
@@ -95,7 +113,7 @@ AI 上游连接超时与读取超时不走全局环境变量:连接超时由 `
缺少真实商户配置时,充值任务只能使用 mock 支付客户端;mock 必须在代码和测试中标明,不得伪装成真实支付。 缺少真实商户配置时,充值任务只能使用 mock 支付客户端;mock 必须在代码和测试中标明,不得伪装成真实支付。
## 七、对象存储配置 ## 八、对象存储配置
图片结果默认返回 URL,避免大 base64 进入同步响应体。对象存储选型未最终落地前,可使用本地开发存储,但生产必须给出可公开访问或可签名访问的 URL。 图片结果默认返回 URL,避免大 base64 进入同步响应体。对象存储选型未最终落地前,可使用本地开发存储,但生产必须给出可公开访问或可签名访问的 URL。
@@ -109,11 +127,15 @@ AI 上游连接超时与读取超时不走全局环境变量:连接超时由 `
| `S3_ACCESS_KEY_ID` | s3 是 | `change-me` | access key | | `S3_ACCESS_KEY_ID` | s3 是 | `change-me` | access key |
| `S3_SECRET_ACCESS_KEY` | s3 是 | `change-me` | secret key | | `S3_SECRET_ACCESS_KEY` | s3 是 | `change-me` | secret key |
## 八、上线前检查 ## 九、上线前检查
- `DJANGO_DEBUG=false`。 - `DJANGO_DEBUG=false`。
- `DJANGO_SECRET_KEY`、`AI_KEY_ENCRYPTION_KEY`、数据库密码、支付密钥均已使用生产值。 - `DJANGO_SECRET_KEY`、`AI_KEY_ENCRYPTION_KEY`、数据库密码、支付密钥均已使用生产值。
- `ALLOWED_HOSTS`、`CSRF_TRUSTED_ORIGINS`、支付 `notify_url` 使用同一公网域名。 - `ALLOWED_HOSTS`、`CSRF_TRUSTED_ORIGINS`、支付 `notify_url` 使用同一公网域名。
- `STATIC_ROOT` 已执行 `collectstatic`,Nginx/宝塔已托管 `/static/`;若继续用本地媒体存储,也必须托管 `/media/` 或切换对象存储。
- `DJANGO_CACHE_BACKEND` 已切到共享后端(如 DatabaseCache / Redis / Memcached),不是默认 `LocMemCache`。
- 生产 HTTPS 下 `DJANGO_SESSION_COOKIE_SECURE=true`、`DJANGO_CSRF_COOKIE_SECURE=true`,反代场景按需开启 `DJANGO_SECURE_PROXY_SSL_HEADER=true`。
- 全站 HTTPS 稳定后再设置 `DJANGO_SECURE_HSTS_SECONDS`;未确认子域名 HTTPS 前不要启用 includeSubDomains / preload。
- MySQL 为 8.4 LTS / InnoDB / `utf8mb4`,不是 SQLite 或已有 MySQL 5.7。 - MySQL 为 8.4 LTS / InnoDB / `utf8mb4`,不是 SQLite 或已有 MySQL 5.7。
- `image_url` 下载上限、生成限流、认证失败限流、单笔充值金额上限已按生产容量调整。 - `image_url` 下载上限、生成限流、认证失败限流、单笔充值金额上限已按生产容量调整。
- 图片同步链路的客户端、Nginx、Gunicorn、上游 read timeout 均按最慢图片模型放大到同一量级。 - 图片同步链路的客户端、Nginx、Gunicorn、上游 read timeout 均按最慢图片模型放大到同一量级。
+3 -2
View File
@@ -1,7 +1,7 @@
# cmhub 项目介绍(给管理层) # cmhub 项目介绍(给管理层)
> 面向决策与汇报的项目概览。技术细节见同目录架构与需求文档。 > 面向决策与汇报的项目概览。技术细节见同目录架构与需求文档。
> 日期:2026-07-03 | 阶段:Phase 5 后台与发布(T-402 已完成) > 日期:2026-07-03 | 阶段:Phase 5 后台与发布(计划内 MVP 任务已完成)
## 一句话概括 ## 一句话概括
@@ -107,7 +107,8 @@
- Phase 4 已完成 T-501~T-505:用户端注册 / 登录(allauth)、邮箱验证、登出、API Key 自助管理、个人中心汇总、充值记录、消费记录和充值页已落地;用户可创建充值订单、查看二维码票据并轮询到账;Key 明文只显示一次,库内只保留 hash 和 prefix,删除即吊销;记录页仅见本人数据并支持分页;Bootstrap 与 qrcode.js 已改为本地 static 自托管。 - Phase 4 已完成 T-501~T-505:用户端注册 / 登录(allauth)、邮箱验证、登出、API Key 自助管理、个人中心汇总、充值记录、消费记录和充值页已落地;用户可创建充值订单、查看二维码票据并轮询到账;Key 明文只显示一次,库内只保留 hash 和 prefix,删除即吊销;记录页仅见本人数据并支持分页;Bootstrap 与 qrcode.js 已改为本地 static 自托管。
- Phase 5 已完成 T-401:运营后台可管理/检索用户、钱包、API Key(脱敏)、计费规则、汇率、充值订单、点数流水和调用记录;手工调点必须填写原因,并经计费层锁钱包、写 `adjust` 流水。 - Phase 5 已完成 T-401:运营后台可管理/检索用户、钱包、API Key(脱敏)、计费规则、汇率、充值订单、点数流水和调用记录;手工调点必须填写原因,并经计费层锁钱包、写 `adjust` 流水。
- Phase 5 已完成 T-402:MVP P0 验收通过,注册/充值/API Key/调用/余额/记录/后台/别名映射均有测试证据,详见 `mvp-acceptance.md`。 - Phase 5 已完成 T-402:MVP P0 验收通过,注册/充值/API Key/调用/余额/记录/后台/别名映射均有测试证据,详见 `mvp-acceptance.md`。
- 下一步是 T-403:补部署 / 运行文档,明确生产静态文件 serving、邮件后端、共享缓存限流、图片同步超时和真实商户配置。 - Phase 5 已完成 T-403:已补部署 / 运行文档,明确宝塔/Nginx/Gunicorn、生产静态与媒体文件、真实邮件后端、共享缓存限流、图片同步超时、真实商户配置和上线检查,详见 `deployment.md`。
- 下一步是按 `deployment.md` 在 VPS 上配置真实环境,并补跑真实支付和真实 AI 图片耗时验证。
--- ---
*更多细节:愿景 `01-vision.md` | 需求与验收 `02-requirements.md` | 架构 `04-architecture.md` | 任务计划 `06-tasks.md`。* *更多细节:愿景 `01-vision.md` | 需求与验收 `02-requirements.md` | 架构 `04-architecture.md` | 任务计划 `06-tasks.md`。*
+1 -1
View File
@@ -40,7 +40,7 @@
## 进度 ## 进度
M1 骨架已完成;M2 已通过录制生成 smoke 验证 AI 调用链路;M3 计费、对外 API、充值闭环与上线前安全加固已完成到 T-306;M4 用户端已完成注册 / 登录、API Key 管理、个人中心 / 记录页、充值页和审核优化;M5 已完成 T-401 运营后台完善与 T-402 MVP 完整验收,下一步做 T-403 部署 / 运行文档。里程碑:M1 骨架可跑 · M2 跑通生成 · M3 计费充值闭环 · M4 用户端可用 · M5 验收上线。 M1 骨架已完成;M2 已通过录制生成 smoke 验证 AI 调用链路;M3 计费、对外 API、充值闭环与上线前安全加固已完成到 T-306;M4 用户端已完成注册 / 登录、API Key 管理、个人中心 / 记录页、充值页和审核优化;M5 已完成 T-401 运营后台完善、T-402 MVP 完整验收与 T-403 部署 / 运行文档。下一步是在 VPS 上按部署文档接真实邮件、支付、AI 模型并补真实图片耗时验证。里程碑:M1 骨架可跑 · M2 跑通生成 · M3 计费充值闭环 · M4 用户端可用 · M5 验收上线。
--- ---
*详见 `project-brief.md`(完整介绍)。* *详见 `project-brief.md`(完整介绍)。*
+28
View File
@@ -940,3 +940,31 @@
- 已知限制:真实微信/支付宝商户凭证和生产 SDK 未接入;真实 AI 上游 smoke 尚未执行;图片同步真实耗时仍未验证,需在 T-403 部署 / 运行文档中明确超时配置和风险口径。 - 已知限制:真实微信/支付宝商户凭证和生产 SDK 未接入;真实 AI 上游 smoke 尚未执行;图片同步真实耗时仍未验证,需在 T-403 部署 / 运行文档中明确超时配置和风险口径。
- 决策:T-402 不改业务代码,仅完成验收、口径修正和文档归档;MVP P0 结论为通过。 - 决策:T-402 不改业务代码,仅完成验收、口径修正和文档归档;MVP P0 结论为通过。
- 下一步:领取 T-403 部署 / 运行文档。 - 下一步:领取 T-403 部署 / 运行文档。
## 2026-07-03 T-403 部署 / 运行文档
- 状态:DONE
- 变更:
- 新增 `docs/deployment.md`:按 VPS / 宝塔 / Nginx / Gunicorn(gthread) / MySQL 8.4 写生产部署步骤,覆盖 `.env`、迁移、`collectstatic`、共享 cache、双 Gunicorn 池、Nginx 分流、上线检查和真实图片耗时验证口径。
- 新增 `requirements-production.txt`:Linux 生产运行在基础依赖外追加 Gunicorn,避免 Windows 本地开发强制安装生产 runner。
- `config/settings.py`:新增 `STATIC_ROOT`、`STATIC_URL` 环境变量支持;新增 `CSRF_TRUSTED_ORIGINS`;新增 `CACHES` 环境变量配置,生产可用 DatabaseCache / Redis / Memcached 等共享 cache;新增 HTTPS cookie、proxy SSL 与 HSTS 环境变量支持。
- `.env.example` / `docs/env.md`:同步新增生产静态、共享 cache、CSRF trusted origins、HTTPS cookie/proxy/HSTS 配置项。
- `docs/03-tech-stack.md`:部署方式定为 VPS / 宝塔 + Nginx + Gunicorn(gthread) + systemd,并记录生产安装、cache table、collectstatic 与 Gunicorn 命令。
- `docs/04-architecture.md`:修正部署分流路径为实际的 `/api/v1/generate/*`。
- 同步更新 `README.md`、`docs/00-ai-start-here.md`、`docs/README.md`、`docs/06-tasks.md`、`docs/current-state.md`、`docs/project-brief.md`、`docs/project-onepager.md`;T-403 标记 DONE,计划内 MVP 任务暂无下一个 TODO。
- 验证:
- `./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 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 `OperationalError 2013` / 连接重置 / 事务中断被记 ERROR。
- `Test-NetConnection 43.128.3.240 -Port 3306`:`TcpTestSucceeded=True`。
- `py -3.12 manage.py test apps.api.tests.GenerateApiTests --noinput --keepdb --verbosity 2`:通过,14 tests OK。
- 阻塞:T-403 文档与配置支撑无功能阻塞;完整 API 套件单次运行仍受远程 MySQL 间歇断连影响。
- 已知限制:真实 AI 上游和真实图片耗时仍未验证,因当前环境未配置 `AI_KEY_ENCRYPTION_KEY` 且数据库无真实 AiModel/ModelAlias;真实微信/支付宝商户配置和 SDK 仍待生产环境提供。`deployment.md` 已明确未跑通真实图片 smoke 前不能声称生产图片链路已验证。
- 决策:生产限流共享 cache 的 MVP 推荐路径先用 Django `DatabaseCache` + MySQL cache 表,不引入 Redis 作为当前代码依赖;高并发后可切换 Redis/Memcached 并安装对应 backend。生产静态资源采用 `STATIC_ROOT + collectstatic + Nginx/宝塔托管`,不在应用内引入 WhiteNoise。
- 下一步:计划内 MVP 任务已完成;进入 VPS 实际部署、真实邮件/支付/AI 配置和真实图片耗时验证,或从 Backlog 重新拆后续任务。
+4
View File
@@ -0,0 +1,4 @@
-r requirements.txt
# Linux production runtime. Keep Windows development on requirements.txt.
gunicorn>=23,<24