两个女儿要冲东莞公办高中,我用 uni-app + FastAPI 写了个暑期全科备考小程序
目录
一、背景
家有俩女儿,老大今年六升初一,老二五升六。目标是冲东莞公办高中,暑假两个月是弯道超车的关键期。
市面上刷题类 App 要么太花哨分散注意力,要么科目不全、要付费。作为一个会写代码的老父亲,决定自己动手给女儿们写一个------专为暑期备考设计的全学科刷题小程序。
东莞中考考什么
语文120 / 数学120 / 英语120 / 物理90 / 化学90 / 道法100 / 历史100(开卷),7科全考。
这意味着小初衔接阶段就要打好语数英基础,初二再加理化也不慌。
目标
- 📱 小程序端:女儿刷题 + 打卡 + 错题复习
- 👨👧 家长端:爸爸实时掌握进度 + 批改主观题
- 🏆 激励体系:积分/等级/成就,让孩子有动力坚持
二、技术选型
| 层 | 技术 | 选型理由 |
|---|---|---|
| 前端 | uni-app (Vue 3) | 一套代码编译微信小程序 + H5,降低维护成本 |
| 后端 | Python FastAPI | 异步高性能,自动生成 OpenAPI 文档,学习成本低 |
| 数据库 | MySQL 8.0 + SQLAlchemy 2.0 | ORM 写起来舒服,复杂查询灵活 |
| 认证 | JWT (HS256) + bcrypt | 无状态 token,7 天有效期,适合小程序场景 |
| 部署 | Docker Compose | 三容器(MySQL + Backend + Nginx)一键编排 |
| 服务器 | 腾讯云 Ubuntu 22.04 (4核4G) | 国内访问快,性价比高 |
为什么是 uni-app 而不是原生小程序?
- H5 兜底:审核期间可以先用 H5 网页版跑起来,不耽误使用
- 学习成本低:Vue 3 生态成熟,前端组件化开发效率高
- 多端扩展:以后如果女儿想用 iPad,加个 App 端编译就行
为什么是 FastAPI 而不是 Spring Boot / Express?
- Python 生态里做教育类数据处理(题目导入、统计分析)更方便
- 自动生成 Swagger 文档,前后端联调效率极高
- 异步支持好,用
async/await写数据库查询很自然
三、数据库设计

ER 图概览
┌─────────┐ ┌──────────┐ ┌──────────────┐
│ users │───<│ answers │>───│ questions │
│ 用户表 │ │ 答题记录 │ │ 题目表 │
└──┬───┬──┘ └────┬──────┘ └──────┬───────┘
│ │ │ │
│ │ ┌──────┴──────┐ ┌──────┴───────┐
│ └────<│ parent_child │ │ subjects │
│ │ 家长绑定 │ │ 学科表 │
│ └─────────────┘ └──────────────┘
│
├──────────< check_ins 打卡表
├──────────< wrong_book 错题本
├──────────< user_points 积分表
└──────────< user_achievements 成就表
核心表设计
users 表 --- 用户(三角色:student / parent / admin)
python
class User(Base):
id # 主键
username # 登录名
password_hash # bcrypt 哈希
display_name # 显示名
role # student | parent | admin
grade_selections # JSON: [{"grade":"grade5","semester":"下"}]
bind_code # 6位随机绑定码(学生专属)
questions 表 --- 题目(11种题型)
python
class Question(Base):
id, subject_id # 科目外键
grade_level # "grade5" / "grade6" / "junior1"
question_type # 11种题型枚举
content, options(JSON) # 题干 + 选项(兼容4种格式)
answer # 正确答案
explanation # 解析
difficulty # easy / medium / hard
unit_number, unit_name # 教材单元定位
lesson_number, lesson_name
题型枚举 --- 客观题 4 种 + 主观题 7 种:
| 类型 | 判分方式 |
|---|---|
choice 选择 / fill_blank 填空 / true_false 判断 / short_answer 简答 |
自动判分 |
vertical_calc 竖式计算 / word_problem 应用题 / drawing 作图 / composition 作文 / translation 翻译 / reading_subjective 阅读理解 / dictation 默写 |
家长批改 |
answer_records 表 --- 答题记录
python
class AnswerRecord(Base):
user_id, question_id # 双外键
user_answer # 用户答案
is_correct # 判分结果
time_spent # 耗时(秒)
submission_mode # text | photo | notebook
image_urls(JSON) # 拍照上传图片
review_status # auto_graded | pending | reviewed
reviewed_by, reviewed_at # 批改人 + 时间
parent_child_bindings 表 --- 家长绑定孩子
python
class ParentChildBinding(Base):
parent_id, child_id # 自关联 users 表
# UniqueConstraint(parent_id, child_id)
# 每个家长最多绑定 5 个孩子
为什么是 JSON 字段?
- grade_selections:支持"五下 + 六上"同时选,年级切换器切换时只显示对应题目
- options:兼容选择题的 4 种前端传值格式(纯字符串数组 / 对象数组 / JSON 字符串 / 字典)
- tags / image_urls:灵活扩展,不用频繁改表结构
四、核心功能
1. 多学段注册 + 年级切换
注册时可以选择多个年级(比如"五下语文" + "五下英语" + "六上数学"),首页顶部有年级切换器,选中哪个年级就看哪个年级的题目。
教材目录覆盖人教版 1-9 年级上下册,468 个单元。目前种子题目主要覆盖五下 / 六上语数英。
2. 刷题系统
- 单元刷题:每单元 30 题,支持顺序 / 乱序切换(Fisher-Yates 洗牌算法)
- 答题进度条:实时显示已答题数 / 总题数
- 完成弹窗:正确率 RGB 渐变(红→黄→绿),直观反馈
- 题型全覆盖:选择题(4 种 options 格式兼容)、判断题、填空题、主观题(拍照上传 + 家长批改)
3. 打卡系统
- 每日首次答题自动打卡 (幂等,基于
UniqueConstraint(user_id, check_date)) - 连续打卡加成:基础 +5 分,连续 N 天额外加成(最高 +14)
- 月度打卡日历:彩色学科圆点标注每天刷了哪些科目
- 日详情:科目进度条 + 章节分布
4. 错题本
- 三级分组:学科 → 单元 → 课次
- 掌握 / 未掌握筛选
- 从错题本直接重新作答(选择 / 判断 / 填空三种 UI)
- 标记"已掌握"可获得 +3 积分
5. 家长端(多孩子支持)
- 绑定码机制:学生端生成 6 位绑定码,家长输入即绑定(限绑 5 个孩子)
- 打卡概览:一眼看到每个孩子今日是否打卡、连续天数、积分
- 答题详情:按科目筛选(点"数学"只看数学题)
- 主观题批改:按章节折叠、标记对错、自动入错题本 / 加分
- 答题用时监控:过快(<20 秒)标记 ⚡ 提醒
6. 激励系统
积分规则
| 行为 | 积分 |
|---|---|
| 答对客观题 | +2 |
| 答错客观题 | +1 |
| 主观题批改为正确 | +2 |
| 标记错题掌握 | +3 |
| 每日打卡 | +5 + 连续加成 |
等级(10 级)
| 等级 | 积分门槛 | 称号 |
|---|---|---|
| Lv1-2 | 0 / 50 | 学习新芽 |
| Lv3-4 | 120 / 220 | 知识探索者 |
| Lv5-6 | 360 / 540 | 勤奋学霸 |
| Lv7-8 | 760 / 1020 | 优秀少年 |
| Lv9-10 | 1320 / 1660 | 中考冲刺者 / 中考之星 |
12 枚成就徽章
- 🏅 坚持类:初次打卡 / 坚持一周 / 坚持一月 / 暑假全勤
- 📝 答题类:百题达人 / 千题王者 / 全科探索 / 高手出击
- 📈 成长类:错题终结者 / 超越自我 / 积分达人 / 全面发展
成就检测在答题、打卡、批改、错题掌握时自动触发,前端答题页弹窗金色动画。
五、前后端架构

目录结构
studyapp/
├── backend/
│ ├── main.py # FastAPI 入口,43 个端点
│ ├── models.py # SQLAlchemy 模型(11 张表)
│ ├── schemas.py # Pydantic 验证 + 等级表 + 成就定义
│ ├── curriculum_pep.json # 人教版 468 单元教材目录
│ ├── seed/ # 种子题目 JSON
│ ├── docker-compose.yml # 三容器编排
│ ├── Dockerfile
│ └── requirements.txt
├── studyapp-mini/ # uni-app 前端
│ ├── pages/
│ │ ├── login/ # 登录
│ │ ├── register/ # 三步注册
│ │ ├── index/ # 首页(学科网格 + 年级切换)
│ │ ├── quiz/ # 答题(swiper + 成就弹窗)
│ │ ├── stats/ # 统计
│ │ ├── wrong-book/ # 错题本(三级分组)
│ │ ├── profile/ # 我的(积分 + 成就)
│ │ └── review/ # 家长批改
│ ├── api/index.js # 统一请求封装 + token 管理
│ ├── manifest.json # 多端配置
│ └── App.vue
└── nginx/
└── nginx.conf # 反向代理 /api/* → FastAPI
API 设计
43 个端点分 8 个模块:
| 模块 | 端点数 | 核心功能 |
|---|---|---|
| auth | 3 | 注册 / 登录 / 获取用户信息 |
| questions | 4 | 科目列表 / 题目筛选 / 题目详情 / 教材目录 |
| answers | 4 | 提交答案 / 上传图片 / 答题历史 / 每日明细 |
| wrong-book | 4 | CRUD + 掌握标记 |
| check-in | 5 | 打卡状态 / 日历 / 日详情 / 家长概览 |
| binding | 4 | 绑定码 / 绑定 / 列表 / 解绑 |
| review | 2 | 待批改列表 / 批改 |
| points | 3 | 积分查询 / 成就列表 / 家长查孩子成就 |
| stats | 1 | 用户统计总览 |
| admin | 4 | 用户管理 / 年级编辑 / 密码重置 / 删除 |
关键代码片段
客观题自动判分:
python
OBJECTIVE_TYPES = {"choice", "fill_blank", "true_false", "short_answer"}
SUBJECTIVE_TYPES = {"vertical_calc", "word_problem", "drawing",
"composition", "translation", "reading_subjective", "dictation"}
if question.question_type in SUBJECTIVE_TYPES:
review_status = "pending" # 等待家长批改
else:
is_correct = (user_answer.strip().upper() == question.answer.strip().upper())
review_status = "auto_graded"
打卡幂等性:
python
# MySQL 层面保证:UniqueConstraint(user_id, check_date)
# 业务层面:先查再插入
existing = db.query(CheckIn).filter(
CheckIn.user_id == user_id,
CheckIn.check_date == today
).first()
if existing:
return # 今天已打卡,幂等跳过
成就检测:
python
def check_and_unlock_achievements(user_id, db):
stats = calculate_user_stats(user_id, db) # 全量统计
already_unlocked = get_unlocked_codes(user_id, db)
to_unlock = []
if stats["total_answers"] >= 100 and "answer_100" not in already_unlocked:
to_unlock.append("answer_100")
# ... 更多条件判断
db.add_all(to_unlock)
db.commit()
return [a.code for a in to_unlock] # 返回新解锁的成就码
六、部署与运维
Docker Compose 编排
yaml
services:
mysql:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
MYSQL_DATABASE: studyapp
volumes:
- mysql_data:/var/lib/mysql
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
backend:
build: .
depends_on:
mysql:
condition: service_healthy
ports:
- "8000:8000"
nginx:
image: nginx:alpine
ports:
- "80:80"
volumes:
- ./nginx.conf:/etc/nginx/conf.d/default.conf
depends_on:
- backend
Nginx 配置要点
nginx
# SPA 路由支持(H5 用 hash 模式,这个给静态资源兜底)
location / {
try_files $uri $uri/ /index.html;
}
# API 反向代理
location /api/ {
proxy_pass http://backend:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
# 静态资源(上传图片)
location /uploads/ {
alias /app/uploads/;
}
数据迁移
从本地开发机迁移到云服务器用的是 mysqldump:
bash
# 导出
mysqldump -u studyapp -p studyapp > backup.sql
# 导入(远程)
zcat backup.sql.gz | docker exec -i studyapp-mysql mysql -ustudyapp -p studyapp
七、踩坑记录
坑 1:H5 端答题页空白
现象:微信开发者工具正常显示题目,H5 网页端题目容器高度为 0。
原因 :uni-app 中 .question-swiper { flex: 1 } 在 H5 渲染时无法给子元素 height: 100% 提供"确定高度",导致高度链断裂。
解决:
css
.page {
height: 100vh;
overflow: hidden;
}
.q-scroll {
position: absolute;
top: 0; left: 0; right: 0; bottom: 0;
overflow-y: auto;
}
坑 2:Nginx 304 缓存导致前端更新不生效
开发阶段频繁更新 JS/CSS,但浏览器缓存旧版本。
解决:开发阶段在 nginx.conf 加:
nginx
add_header Cache-Control "no-cache, must-revalidate";
生产环境换成版本号 hash 文件名。
坑 3:选择题 options 格式不统一
前端有时传 ["A. xxx", "B. xxx"],有时传 [{"label":"A","text":"xxx"}],有时是 JSON 字符串,有时是字典。
解决 :后端写了一个 normalize_options() 统一处理 4 种格式,不管前端传什么都能入库为统一格式。
坑 4:Docker Hub 在国内拉镜像失败
云服务器在国内,直接 docker pull 经常超时。
解决 :配置腾讯云镜像加速器 /etc/docker/daemon.json。
坑 5:uni-app 条件编译在 .js 文件里写法不同
.vue 文件用 <!-- #ifdef H5 -->,但 .js 文件里要用 // #ifdef H5。一开始用了 #ifndef H5 导致 H5 编译时走了小程序逻辑。
解决:
javascript
// #ifdef H5
const BASE_URL = '/api'
// #endif
// #ifdef MP-WEIXIN
const BASE_URL = 'https://your-server.com/api'
// #endif
八、展望
已完成(M0-M3,约 85%)
- ✅ 数据库设计(11 张表) + 576 题种子数据
- ✅ 43 个 API 接口全部上线
- ✅ 8 个前端页面(登录/注册/首页/答题/统计/错题本/我的/批改)
- ✅ 积分/等级/成就系统
- ✅ 腾讯云部署 + Nginx 反向代理
- 🚧 小程序审核中 + 域名备案中
下一步(M4)
- 🤖 智能推题引擎:基于错题记录和薄弱知识点,自动推荐针对性题目
- 📝 中考真题专项:整理东莞/广东近 5 年中考真题,7 科全
- 📊 学期长期追踪:暑假结束后继续用,按学期规划学习
- 🌐 开源:整理代码规范和文档,逐步开源到 GitHub
总结
这个项目的核心思路是:用工程师的办法解决家庭教育问题。
不需要花哨的动画和社交功能,就做好三件事:刷题、改错、坚持。
技术栈很"朴实"------uni-app + FastAPI + MySQL + Docker,没有微服务、没有消息队列、没有 Redis 缓存。但就是因为简单,一个人下班后断断续续写也能跑起来。
如果这篇文章对你做类似项目有帮助,或者你也是为孩子写代码的家长,欢迎在评论区交流 👇
📌 技术栈 :uni-app (Vue3) + FastAPI + MySQL 8.0 + Docker Compose
📌 代码量 :后端 ~2000 行 / 前端 ~8000 行
📌 开发周期 :6 周(业余时间)
📌 当前状态:H5 可用,小程序审核中