16 KiB
Hulumath-Web 代码贡献指南
本指南面向人类开发者和 AI 编程助手。开始修改前,请完整阅读本文。
葫芦数学不是通用内容站,而是围绕 MathBTI、数学人生、比赛、工具箱、视频探索和长期成长构建的“数学人生宇宙”。本仓库已经进入可部署的 Django 生产形态,贡献时应优先保持业务一致性、数据安全和可回滚性,不要把它当作一次性 Demo。
1. 必须遵守的协作规则
以下规则没有例外:
- 禁止直接向
main推送。 - 所有改动必须通过 Pull Request(PR)提交。
- 所有 PR 必须由仓库负责人 Jacky 审查并明确批准后才能合并。
- CI 未通过时不得合并,不得通过删除测试、降低规则或跳过检查来“修复” CI。
- 不得提交密码、Token、SSH 私钥、生产
.env.production、真实用户数据或数据库备份。 - 不得直接在生产服务器上改源码后绕过 Git。紧急修复也应补回 PR。
- 不得回退、覆盖或整理与当前任务无关的他人改动。
如果你没有仓库写权限,请从 fork 创建分支并向本仓库提交 PR。
2. AI 协作者五分钟快速入口
AI 在采取任何修改动作前,至少完成以下步骤:
- 阅读本文件。
- 阅读任务直接涉及的代码、测试和迁移。
- 执行
git status --short --branch,确认工作区是否已有他人改动。 - 用
rg搜索现有实现、调用方和测试,不凭文件名猜测行为。 - 对照下方“文档优先级”和“不可破坏的系统约束”。
- 明确最小修改范围,避免顺手重构。
- 修改后运行与风险相匹配的检查。
- 总结行为变化、测试结果、迁移影响和未验证风险。
AI 不应:
- 在没有阅读上下文时批量重写模块。
- 因测试失败而删除断言、吞掉异常或放宽安全校验。
- 自行修改产品规则、奖励数值、剧情事实或历史人物设定。
- 使用字符串替换模拟结构化数据迁移。
- 为了“代码更现代”而更换框架、数据库或部署方式。
- 自动合并 PR。最终审查与合并权属于 Jacky。
3. 文档与事实来源优先级
遇到冲突时按以下顺序判断:
- 当前任务中仓库负责人的明确要求。
- 本文件中的贡献与工程规则。
- 数据库迁移、当前代码、测试和 API 契约所表达的真实行为。
Bible.md中的产品定位、核心循环和架构决策。docs/DEPLOYMENT.md与docs/BAOTA_UBUNTU_FROM_ZERO.md中的生产约束。README.md中的使用入口。docs/TUTORIAL.md及旧脚本只作为历史和内容迁移参考。
旧 Flask、旧 SQLite 和旧前端文档不能覆盖当前 Django/MySQL 生产设计。
docs/ 中可能包含 Word 导出、UTF-16 或历史格式文件。编辑器显示乱码时,不要直接重写或删除原文件;优先使用同名 Markdown 版本,必要时先确认编码和来源。
4. 当前技术栈
- Django 4.2
- Django REST Framework
- Django Channels
- MySQL 8.0.35
- Redis 7.x Channel Layer
- Gunicorn + UvicornWorker
- Nginx
- WhiteNoise
- 原生 HTML、CSS、JavaScript Web 客户端
- pytest、pytest-django、pytest-cov
- Ruff
- Gitea Actions
本地最低兼容 Python 版本为 3.9,建议使用 Python 3.11 或 3.12。生产环境使用 Python 3.12。
当前 Web 客户端没有 Node 构建步骤。不要仅为一个小功能引入 Node、前端框架或新的打包链。
5. 仓库结构与模块所有权
backend/
├── accounts/ 用户、邀请码、会话、游客迁移、审计
├── math_life/ MathBTI、数学人格、剧情版本、存档、人物 Skill
├── contest/ 题库、比赛、实时匹配、判分、Rating、反作弊
├── progression/ 五维能力、数学精灵、卡牌、奖励流水
├── content/ 视频、知识卡片、人物内容、收藏、观看进度
├── latex_lab/ 公式文档、课程、练习与 LaTeX 判定
├── engagement/ 签到、通知等回访能力
├── common/ 通用 API、日志、健康检查、管理后台
├── config/ Django 设置、URL、ASGI/WSGI
├── templates/ 网站与 Django Admin 模板
└── static/ CSS、JavaScript 等静态资源源文件
scripts/ 部署、生产冒烟检查等运维脚本
docs/ 部署、迁移、产品和内容文档
.gitea/workflows/ PR CI 与合并后自动部署
静态资源特别说明
backend/static/是源文件,应在这里修改。backend/staticfiles/是collectstatic产物,不应手工编辑。- 普通功能 PR 不应提交
backend/staticfiles/的意外变化。 - 生产部署会自动执行
collectstatic并生成带指纹资源。
6. 核心请求路径
典型请求路径如下:
浏览器
→ Nginx
→ Gunicorn/Uvicorn ASGI
→ Django URL / Channels 路由
→ View / Consumer
→ Service
→ Model / MySQL / Redis
职责建议:
- View/Consumer:鉴权、解析请求、返回响应。
- Service:事务、业务规则、判分、奖励、状态迁移。
- Model:数据结构、约束、索引和轻量领域属性。
- Serializer:输入校验与 API 表达。
- Template/JS:交互与展示,不承担正式判分和奖励真相。
复杂业务不要全部写进 View,也不要把正式规则只放在浏览器。
7. 不可破坏的系统约束
7.1 服务端是正式数据唯一事实来源
以下结果必须由服务端决定并持久化:
- MathBTI 正式结果
- 剧情存档、选择、关系和结局
- Contest 计时、答案、分数、Rating 和反作弊标记
- 视频完成状态
- 五维能力、精灵经验、卡牌和奖励
浏览器状态只能用于临时 UI,不得代替正式数据库记录。
7.2 幂等性
剧情选择、比赛提交、奖励发放、游客数据迁移等可重试写操作必须幂等。
- 优先使用数据库唯一约束和事务保证幂等。
- 客户端正式提交应提供
Idempotency-Key。 - 不要只依赖“按钮禁用”防止重复请求。
- HTTP 测试环境不是安全上下文,前端不能假设
crypto.randomUUID()一定存在。
7.3 数据库
- 生产数据库必须为 MySQL 8.0.35 或更高兼容版本。
- 禁止把生产改回 SQLite、MySQL 5.7 或直接暴露数据库公网端口。
- 字符集必须支持
utf8mb4。 - 表引擎使用 InnoDB。
- 事务隔离级别为
READ COMMITTED。 - 实时匹配依赖
SELECT ... FOR UPDATE SKIP LOCKED和匹配索引。
SQLite 只用于快速本地测试。涉及锁、排序规则、事务或 MySQL 特性的改动必须在 MySQL 上验证。
7.4 Redis 与 WebSocket
- Redis 是 Channels 的消息层,不是可随意移除的缓存依赖。
- ASGI 初始化顺序经过特殊处理,模型相关 Consumer 必须在 Django App Registry 初始化后导入。
- 修改
config/asgi.py、Consumer 或路由时,必须验证 ASGI 导入和 WebSocket。
7.5 内容版本
- 已发布题目和剧情内容使用版本模型,避免直接覆盖历史运行所依赖的内容。
StoryRun应指向具体StoryVersion。- 比赛题目应指向具体
QuestionVersion。 - 修改种子内容时保持命令可重复执行。
- 种子命令不能加入每次生产部署,以免覆盖运营修改。
7.6 用户和权限
- 使用项目自定义 UUID 用户模型,不要绕过
AUTH_USER_MODEL。 - 普通用户不能看到或访问运营后台。
is_staff、is_superuser等权限字段不得通过普通用户 API 写入。- 任何后台入口可见性都不能替代服务端权限校验。
7.7 生产部署
- 生产进程由 systemd 管理,不使用宝塔 Python 项目管理器。
- 应用监听
127.0.0.1:8000,由 Nginx 对外代理。 - 当前测试生产入口使用
4321,不要开放应用内部8000。 - 部署前自动备份 MySQL,执行迁移和静态资源收集。
- 部署后验证 HTTP、数据库、Redis、WebSocket、页面和视频目录。
- 不要在自动回退中执行破坏性数据库反向迁移。
8. API 与前端约定
API
- HTTP API 使用
/api/v1/前缀。 - WebSocket 使用
/ws/,业务 WebSocket 通常使用/ws/v1/。 - API 异常使用统一结构:
{
"error": {
"code": "request_error",
"message": "请求未能完成",
"details": {},
"request_id": "..."
}
}
- 新接口应遵循现有鉴权、CSRF、限流和 Request ID 约定。
- 不要在响应中泄露内部异常、密钥或敏感用户字段。
前端
- 复用
backend/static/js/app.js中的api()、状态和渲染模式。 - 使用 DOM API 和
textContent表达不可信内容,避免直接拼接 HTML。 - 修改用户可见流程时同时检查桌面端和移动端。
- 当前生产可能运行在纯 HTTP IP 环境,不要无条件依赖安全上下文 API。
- 后台主题源文件为
backend/static/admin/css/hulumath_admin.css。 - CSS 修改要检查颜色对比度、禁用态、长内容滚动和系统深色偏好。
9. 本地开发
初始化
python3 -m venv .venv
.venv/bin/pip install -r requirements-dev.txt
make migrate
make seed
make run
本地地址:
网站:http://127.0.0.1:8000/
后台:http://127.0.0.1:8000/admin/
邀请码:HULU2026
创建本地管理员:
cd backend
../.venv/bin/python manage.py createsuperuser
环境变量
从 .env.example 或 .env.production.example 复制本地文件,不要修改并提交真实值。
cp .env.example .env.local
set -a
source .env.local
set +a
生产要求 DJANGO_DEBUG=false、MySQL DATABASE_URL、Redis REDIS_URL、强随机 DJANGO_SECRET_KEY 和正确的 Host/CORS/CSRF 来源。
10. 推荐修改流程
10.1 创建分支
先同步 main,再创建语义清晰的分支:
git switch main
git pull --ff-only origin main
git switch -c feat/short-description
常用前缀:
feat/:用户可见功能fix/:缺陷修复ci/:CI/CDdocs/:文档refactor/:无行为变化的重构test/:测试改进
10.2 先读后改
查找实现和调用方:
rg "目标类名|函数名|API 路径" backend
rg --files backend/<app>
至少阅读:
- 目标模块的 model/service/view/serializer
- 对应 URL 或 routing
- 现有测试
- 相关迁移
- 调用该行为的前端代码
10.3 小步提交
- 一个 PR 解决一个明确问题。
- 优先提交可运行的垂直切片。
- 不夹带格式化整个仓库、目录重命名或无关依赖升级。
- 提交信息使用祈使式或清楚的类型前缀,例如:
feat: add story resume endpoint
fix: make contest submission idempotent
ci: add production smoke check
docs: document content authoring flow
11. 数据模型与迁移
修改 Django Model 时:
cd backend
../.venv/bin/python manage.py makemigrations
../.venv/bin/python manage.py makemigrations --check --dry-run
../.venv/bin/python manage.py migrate
要求:
- 迁移文件必须随 Model 变更提交。
- 为唯一性、幂等性和高频查询使用数据库约束或索引。
- 高风险迁移采用“先扩展、后切换、再清理”。
- 不在同一发布中删除旧字段并立即依赖不可回退的新结构。
- 数据迁移必须可审查、可重复或明确记录一次性边界。
- 禁止直接复制 SQLite 文件、MySQL 数据目录或跨数据库 dump 作为迁移方案。
PR 描述中必须说明:
- 是否新增迁移
- 是否锁表或扫描大表
- 是否需要数据回填
- 应用代码如何兼容发布前后的 schema
- 回滚时数据库如何处理
12. 测试与质量门禁
最小本地检查
文档以外的代码改动至少运行:
.venv/bin/ruff check backend scripts
make check
make test
与 PR CI 对齐
.venv/bin/ruff check backend scripts
cd backend
../.venv/bin/python manage.py makemigrations --check --dry-run
../.venv/bin/python manage.py check
../.venv/bin/python -c \
"from config.asgi import application; print(type(application).__name__)"
cd ..
.venv/bin/python -m pytest -q \
--cov=backend \
--cov-config=.coveragerc \
--cov-report=term \
--cov-fail-under=75
Gitea CI 还会在隔离的 MySQL 8.0.35 容器中执行:
python backend/manage.py check --database default
python backend/manage.py check_mysql
python backend/manage.py migrate --noinput
pytest -q
测试原则
- Bug 修复必须先理解复现条件,并增加能防止回归的测试。
- Service 层规则优先写单元测试。
- API 权限和响应写请求测试。
- MySQL 锁、并发和事务行为不能只用 SQLite 测试。
- 前端缺陷至少增加静态资产断言;关键交互应进行浏览器验证。
- 用户流程、后台样式和响应式布局应附截图或录屏。
- 覆盖率是下限,不是目标;不要为了数字测试无意义代码。
13. PR 要求
PR 标题应描述结果,而不是过程:
fix: prevent duplicate contest settlement
feat: add actuarial story import
PR 描述至少包含:
## 背景
为什么需要修改。
## 变更
具体改变了什么行为和模块。
## 验证
执行了哪些测试,结果是什么。
## 数据与部署
是否有迁移、种子、环境变量、静态资源或回滚影响。
## 截图
涉及 UI 时提供修改前后截图。
提交 PR 后:
- 等待
CI / test全部通过。 - 处理审查意见,不要无解释地关闭讨论。
- 请求 Jacky 审查。
- 只有 Jacky 明确批准后才可合并。
- 合并后观察
PR合并自动部署的release-check和deploy。 - 部署失败时保留日志,先判断是代码、迁移、网络还是冒烟检查问题。
14. 安全与隐私
- 不记录真实密码、Cookie、Session、Token 或私钥。
- 日志中使用 Request ID,避免打印完整请求体和敏感字段。
- 最小化收集未成年人信息,不要求真实学校、姓名或精确年龄。
- 新的用户输入必须校验长度、类型和权限。
- 文件上传、富文本、外部 URL 和管理员批量操作需要单独安全评审。
- 不要通过前端隐藏代替后端权限控制。
- 不要关闭 CSRF、CORS、密码校验或生产安全检查来解决局部问题。
15. 内容贡献规范
剧情、数学人物和题目既是内容,也是生产数据。
剧情
- 使用版本化 Story 内容。
- 节点 ID 稳定且唯一。
- 每个 choice 的
next必须存在。 - 结局节点不再提供 choice。
- 真实人物内容应列出事实来源和虚构边界。
- 不擅自改写已发布存档所依赖的版本。
题目
- 正确答案和解释属于服务端版本。
- 不把正式答案提前发送给未提交的客户端。
- 题号必须为正整数且不重复。
- 题目更新创建新版本,不覆盖历史正式尝试。
- 注意 Unicode 数学符号与普通 ASCII 输入的归一化边界。
视频与成长
- 五维能力固定为:眼光、人文、侦探、建模、联结。
- 视频完成奖励必须幂等。
- 内容筛选字段和能力映射保持后台、API、前端一致。
16. 不应出现在普通 PR 中的改动
除非任务明确要求,否则不要:
- 替换 Django、MySQL、Redis、Channels 或部署方案。
- 将模块化单体拆成微服务。
- 引入 Kubernetes。
- 新建长期并行的第二套前端或后端。
- 批量重写全部剧情和种子数据。
- 修改生产服务器路径、端口、systemd 服务名或 Gitea Secrets。
- 提交
.env.production、备份、数据库文件和运行日志。 - 手改
backend/staticfiles/。 - 重新运行生产种子命令覆盖运营数据。
- 降低覆盖率门槛、删除 Ruff 规则或跳过 MySQL CI。
需要做上述变更时,先提交设计说明并获得 Jacky 明确批准。
17. 完成定义
一个贡献只有同时满足以下条件才算完成:
- 需求行为已实现,且没有明显超出范围。
- 代码遵循现有模块边界。
- 数据约束、事务和幂等性得到处理。
- 测试覆盖新增行为和关键失败路径。
- Ruff、Django check、迁移检查和 pytest 通过。
- 涉及 MySQL、Redis、WebSocket 或部署时完成对应验证。
- 涉及 UI 时检查桌面端、移动端和颜色对比度。
- 文档、环境变量示例和迁移说明已同步。
- 没有提交秘密或生成垃圾。
- PR 已由 Jacky 审查并明确批准。
不确定时,不要猜测产品规则。把问题、已知事实、可选方案和影响写进 PR,请仓库负责人决策。