CSDN-项目分享-暑期备考小程序

两个女儿要冲东莞公办高中,我用 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 而不是原生小程序?

  1. H5 兜底:审核期间可以先用 H5 网页版跑起来,不耽误使用
  2. 学习成本低:Vue 3 生态成熟,前端组件化开发效率高
  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 可用,小程序审核中

相关推荐
IsJunJianXin4 小时前
pdd小程序 cdp 保存响应体
linux·服务器·小程序·pdd小程序·拼多多响应体解密·小程序cdp·拼多多rpc取响应体
Haibakeji1 天前
长沙餐饮门店点餐配送小程序定制开发
大数据·小程序
2501_915918411 天前
iOS App性能测试工具的实现方法与优化循环指南
android·ios·小程序·https·uni-app·iphone·webview
程序鉴定师1 天前
2026济南十大App制作公司测评(精简版):覆盖小程序、定制开发与跨平台方案
大数据·小程序
斯内普吖1 天前
(开源)高校素拓分管理系统小程序实战指南 基于 Java + SpringBoot + uni-app + Vue + MySQL
java·spring boot·mysql·小程序·uni-app·开源
上海观智网络2 天前
上海小程序定制开发合同怎么签?需要注意什么?
经验分享·笔记·小程序
it-10242 天前
抖音快手短视频去水印微信小程序/一键去水印/小程序去水印接口代码
微信小程序·小程序·php
万岳科技3 天前
教育培训小程序如何构建线上线下一体化教学体系
小程序·apache
全职计算机毕业设计3 天前
智慧仓储出入库小程序+后台管理系统 —— 全流程数字化资产管控平台
小程序