Files
cmhub/docs/03-tech-stack.md

14 KiB
Raw Permalink Blame History

技术栈(Tech Stack)

“用什么”的统一速查表。选型与理由在此集中维护;“怎么把它们搭起来”见 架构设计。 未定项必须标为待定,不要让 agent 在代码里自行决定。

一、技术栈一览

维度 选型 状态 理由 / 说明
语言 Python 3.12(锁定 >=3.12,<3.14) 已定 复用 cmbot 的 Python AI 调用代码(cmbot 为 3.11+,向上兼容);3.12 稳定、库全、性能好,落在 Django 5.2 支持窗口内
Web 框架 Django 5.2 LTS 已定 自带 ORM、迁移、admin,适合「API + 运营后台」;选 LTS 维护到 2028,安全更新窗口最长。禁用已 EOL 的 4.0/4.1(无安全补丁,资金服务不可用)
API 框架 Django REST Framework (DRF) 已定 鉴权、序列化、参数校验、限流现成
运营后台 django-admin 已定 近零代码即得用户/点数/记录的增删改查与检索,省 80% 后台工作量
用户端 Django 模板 SSR + Bootstrap 5 + django-allauth 已定 T-501 已落地自助注册/登录/登出与最小 dashboard;T-605 起注册策略固定为免邮箱验证、注册即可用,邮箱仍必填且唯一;T-608 已落地新用户注册成功经计费层赠送 10 点并写注册赠点流水;T-502 已落地 API Key 自助生成 / 删除页;T-503 已落地个人中心、充值记录和点数记录页;T-504 已落地充值页(创建订单、展示二维码票据、轮询到账后刷新);T-505 已把 Bootstrap 与 qrcode.js 改为本地 static 自托管,并给记录页加分页;MVP 先用 Django form + Bootstrap 模板,不为简单表单引入 crispy-forms
后台美化 django-unfold 或 simpleui 待定 仅外观,MVP 可先用原生 admin,后期按需引入
AI 上游对接 Provider 适配器层(按 api_type 注册)+ 能力别名 映射 + requests HTTP 客户端 已定 对外只暴露 generate text/image 两接口与别名;换供应商改后台映射,不动对外契约。移植 cmbot 的调用逻辑到各适配器。当前 3 模型机制不同:文本 chat、nano-banana2 chat 多模态返图、gpt-image-2 images/edits 改图(详见 04 3.1)
供应商密钥存储 应用层 Fernet 加密(cryptography) 已定 AiModel.api_key_encrypted 加密入库、admin 写入型字段不回显;加密主密钥 AI_KEY_ENCRYPTION_KEY 走环境变量,配置清单见 env.md
图片结果存储 本地存储返回 URL(S3 兼容后续可替换) 已定 T-302 已用 Django default_storage + MEDIA_ROOT / MEDIA_URL 落地本地存储,同步响应返回 image_url;T-614 异步 worker 通过 MEDIA_PUBLIC_BASE_URL / PUBLIC_BASE_URL 生成绝对结果 URL;生产对象存储后续可替换
配置变更审计 自建 AiConfigAuditLog 审计表 + django-admin 只读查看 已定 记录 AiModel / ModelAlias / api_key 变更的 actor、时间、目标、动作和字段差异;密钥只记录 empty/set 状态,不记录明文或密文
数据库 MySQL 8.4 LTS(cmhub 专用独立实例) 已定 满足 Django 5.2 的 MySQL ≥8.0.11;引擎 InnoDB + 字符集 utf8mb4;行锁 select_for_update / 条件更新保并发扣点。不复用 VPS 已有的 MySQL 5.7(跑不了 Django 5.2、无 CHECK 约束)。开发亦用 MySQL,勿用 SQLite(不支持 select_for_update)
MySQL 驱动 PyMySQL + cryptography 已定 PyMySQL 负责 Django 连接 MySQL;MySQL 8 默认 caching_sha2_password 认证需要 cryptography 支持;客户端连接/读/写超时通过 MYSQL_CONNECT_TIMEOUT / MYSQL_READ_TIMEOUT / MYSQL_WRITE_TIMEOUT 配置
对外鉴权 API Key(DRF 自定义 Authentication,哈希存储比对) 已定 用户自助生成 Key;API 只认 Key、不挂 SessionAuthentication,防浏览器 cookie 绕过计费
用户端鉴权 Django Session(+ allauth 注册登录,免邮箱验证) 已定 T-501 已落地 /signup /login /logout 与 /dashboard;T-605 固定 ACCOUNT_EMAIL_VERIFICATION="none",新用户注册后可直接使用;T-608 显式配置注册限流 ACCOUNT_SIGNUP_RATE_LIMIT(默认 20/m/ip,由 allauth signup rate limit 执行);T-502 已落地 /apikeys;T-503 已落地 /records/recharge 与 /records/usage;T-504 已落地 /recharge;用户端页面与 recharge/create 走 session + CSRF;后台账号也用 Session 登录
充值对接 自助扫码:微信 V3 native + 支付宝当面付;下单取二维码 + 服务端回调(验签 + 幂等) 已定 T-304/T-305 已落地回调、扫码下单、状态轮询、HMAC mock 联调与 SDK 模式入口;生产需安装并配置 wechatpayv3 / python-alipay-sdk 与真实商户密钥/证书
生成返回方式 旧同步 HTTP + 生图异步提交/轮询并存 已定 T-614 起 POST /api/v1/generate/image/tasks 提交任务、GET /api/v1/generate/image/tasks/{task_id} 轮询;旧 /api/v1/generate/image 同步接口保留给老客户端
任务队列 DB 任务表 + management command worker;暂不引入 Celery/RQ 已定(Phase 6) T-614 使用 ImageGenerationTask + MySQL select_for_update(skip_locked) + run_image_tasks worker;Celery/Redis 仅在 DB worker 不够时再评估
部署方式 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 会忽略行锁导致假绿
依赖管理 系统 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 并发 + 超时」,不在框架版本(详见 架构设计 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。
  • 供应商密钥存储采用 Fernet 应用层加密:T-102 已落地 AiModel.api_key_encrypted,密文带 fernet: 前缀;明文只在 admin 表单提交或调用 resolve_alias() 后进入内存,不入库、不回显。主密钥来自 AI_KEY_ENCRYPTION_KEY,生产不可随意更换,除非后续做密钥轮换。
  • 配置变更审计采用专表而非只依赖 admin LogEntry:T-103 起后台保存/删除 AiModel、ModelAlias 时写 AiConfigAuditLog,记录谁、何时、对哪个配置做了 create/update/delete、哪些字段发生变化;api_key 变更只记 empty/set,不保存明文或 Fernet 密文。
  • 生成返回方式演进:MVP 先用同步 HTTP 降低复杂度;T-612 给旧同步生图加上游硬截止止血;T-613 抽共享生成 core;T-614 已新增生图异步提交 / 轮询路径,但不删除旧同步接口。
    • 旧同步接口继续兼容:老桌面端仍可调用 /api/v1/generate/image,依赖「超时链路 + worker 容量」配对。每模型 timeout_seconds、AI_IMAGE_UPSTREAM_DEADLINE_SECONDS、Gunicorn --timeout、Nginx proxy_read_timeout 和客户端 read timeout 必须按真实图片耗时配置。
    • 新异步接口优先给新版客户端:新版桌面端提交 /api/v1/generate/image/tasks 获得 task_id,再轮询 /api/v1/generate/image/tasks/{task_id},避免 HTTP 长连接占住生成池,也支持客户端重启后继续查结果。
    • 任务队列选择:T-614 采用数据库任务表 + management command worker,不引入 Celery/RQ/Redis;后续只有在 DB 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",免邮箱验证、注册即可用,邮箱仍必填且唯一;T-608 已把注册成功后的初始化改为经 apps.billing 发放 10 点试用点数并写 signup_bonus 流水,且显式配置 ACCOUNT_SIGNUP_RATE_LIMIT 做基础注册限流。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,哈希存储)。新注册用户一次性赠送 10 点,必须走计费层和点数流水。

三、构建与运行命令

以下为当前真实命令;Windows 原生 PowerShell 使用 py -3.12,Unix/WSL 使用 python3.12。

用途 命令
安装依赖(Windows) py -3.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
基础检查(Unix/WSL) python3.12 manage.py check
测试(Windows) py -3.12 manage.py test
本地开发(Windows) py -3.12 manage.py runserver
创建后台管理员 T-003 后执行 py -3.12 manage.py createsuperuser
数据库迁移 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
AI 生成 smoke(录制标题) py -3.12 manage.py smoke_ai_generation title --recorded
AI 生成 smoke(录制图片) py -3.12 manage.py smoke_ai_generation image --recorded
格式化 / 静态检查 ruff check .(待定,确定后写死)

统一入口仍是根目录 ./init.ps1(Windows)或 ./init.sh(Unix/WSL)。T-001 阶段只做框架检查,不跑迁移;T-002 接入 MySQL 与自定义 User 后再启用迁移路径。

四、依赖纪律

  • 新增第三方依赖前,先说明用途、替代方案和维护成本。
  • 不确定的技术选型先更新本文,再进入代码(如 Celery、unfold、pytest)。
  • 不允许同一职责并存两套方案(如两套鉴权、两套 HTTP 客户端)。
  • 敏感配置(上游 api_key、支付密钥、SECRET_KEY、数据库密码)只走环境变量或后台配置,不写进代码与文档样例;变量名集中维护在 env.md。