系统架构
数据库设计
EduBuddy 使用 SQLite 作为嵌入式数据库,共包含 13 张数据表,采用 UUID(TEXT 类型)作为主键,启用 WAL 模式和外键约束。
数据库配置
-- 启用 WAL 模式(Write-Ahead Logging)
PRAGMA journal_mode = WAL;
-- 启用外键约束
PRAGMA foreign_keys = ON;
表关系图
users (用户)
├── learning_paths (学习路线) [created_by → users.id]
│ └── learning_path_nodes (路线节点) [path_id → learning_paths.id]
│ └── user_learning_paths (用户报名) [user_id → users.id, path_id → learning_paths.id]
│
├── questions (题目) [created_by → users.id]
│ ├── practice_records (练习记录) [user_id → users.id, question_id → questions.id]
│ └── quizzes (测验) [created_by → users.id]
│ └── quiz_records (测验记录) [user_id → users.id, quiz_id → quizzes.id]
│
├── knowledge_mastery (知识点掌握度) [user_id → users.id]
├── learning_goals (学习目标) [user_id → users.id]
├── study_suggestions (学习建议) [user_id → users.id]
├── file_uploads (文件上传) [user_id → users.id]
├── user_shares (资源分享) [from_user_id → users.id, to_user_id → users.id]
├── notes (笔记) [user_id → users.id]
└── daily_stats (每日统计) [user_id → users.id]
核心数据表详解
users — 用户表
| 字段 | 类型 | 说明 |
id | TEXT PK | UUID v4 |
username | TEXT UNIQUE | 登录用户名 |
email | TEXT UNIQUE | 邮箱 |
password_hash | TEXT | bcrypt 哈希(cost=12) |
display_name | TEXT | 显示名称 |
role | TEXT | student / teacher / admin |
grade | TEXT | 年级(可选) |
subjects | TEXT | JSON 数组,学习科目列表 |
is_active | INTEGER | 1=启用,0=禁用(软删除) |
last_login | DATETIME | 最后登录时间 |
questions — 题目表
| 字段 | 类型 | 说明 |
id | TEXT PK | UUID v4 |
title | TEXT | 题目简标题 |
content | TEXT | 题目完整内容 |
subject | TEXT | 科目(数学/物理/英语等) |
grade | TEXT | 适用年级(可选) |
topic | TEXT | 知识点标签 |
difficulty | TEXT | easy / medium / hard |
question_type | TEXT | multiple_choice / fill_blank / short_answer / essay / calculation |
options | TEXT | JSON 数组(选择题选项) |
correct_answer | TEXT | 正确答案 |
explanation | TEXT | 解题说明(Markdown) |
is_public | INTEGER | 1=公开,0=私有 |
created_by | TEXT FK | → users.id |
practice_records — 练习记录表
| 字段 | 类型 | 说明 |
id | TEXT PK | UUID v4 |
user_id | TEXT FK | → users.id |
question_id | TEXT FK | → questions.id |
user_answer | TEXT | 学生提交的答案 |
is_correct | INTEGER | 1=正确,0=错误 |
score | REAL | AI 评分(0-100) |
feedback | TEXT | AI 批改反馈 |
time_spent | INTEGER | 答题用时(秒) |
practiced_at | DATETIME | 答题时间 |
knowledge_mastery — 知识点掌握度表
| 字段 | 类型 | 说明 |
id | TEXT PK | UUID v4 |
user_id | TEXT FK | → users.id |
subject | TEXT | 科目 |
topic | TEXT | 知识点名称 |
mastery_level | REAL | 掌握度 0-100 |
practice_count | INTEGER | 累计练习次数 |
correct_count | INTEGER | 答对次数 |
| 唯一约束:(user_id, subject, topic) |
learning_paths — 学习路线表
| 字段 | 类型 | 说明 |
id | TEXT PK | UUID v4 |
title | TEXT | 路线标题 |
subject | TEXT | 科目 |
grade | TEXT | 适用年级 |
difficulty | TEXT | beginner / intermediate / advanced |
estimated_hours | INTEGER | 预计学时(小时) |
is_public | INTEGER | 1=公开 |
created_by | TEXT FK | → users.id |
daily_stats — 每日学习统计表
| 字段 | 类型 | 说明 |
user_id | TEXT FK | → users.id |
date | TEXT | 日期(YYYY-MM-DD) |
study_minutes | INTEGER | 当日学习分钟数 |
questions_practiced | INTEGER | 当日练习题数 |
correct_answers | INTEGER | 当日答对数 |
avg_score | REAL | 当日平均分 |
| 唯一约束:(user_id, date) |
数据库索引
-- 练习记录按用户查询(高频)
CREATE INDEX idx_practice_records_user ON practice_records(user_id);
-- 练习记录按题目查询
CREATE INDEX idx_practice_records_question ON practice_records(question_id);
-- 知识点掌握度按用户查询(高频)
CREATE INDEX idx_knowledge_mastery_user ON knowledge_mastery(user_id);
-- 题目按科目筛选(高频)
CREATE INDEX idx_questions_subject ON questions(subject);
-- 每日统计按用户+日期查询(高频)
CREATE INDEX idx_daily_stats_user_date ON daily_stats(user_id, date);
-- 测验记录按用户查询
CREATE INDEX idx_quiz_records_user ON quiz_records(user_id);
JSON 字段说明
SQLite 不原生支持数组/JSON 类型,以下字段使用 TEXT 存储 JSON 字符串:
| 表.字段 | JSON 结构 | 示例 |
users.subjects | 字符串数组 | ["数学","物理"] |
questions.options | 字符串数组(选择题) | ["A. 选项1","B. 选项2","C. 选项3","D. 选项4"] |
questions.tags | 字符串数组 | ["一元二次方程","因式分解"] |
learning_path_nodes.prerequisites | 节点 ID 数组 | ["node-id-1","node-id-2"] |
learning_path_nodes.resources | 资源字符串数组 | ["《高中数学》第3章","https://..."] |
quiz_records.answers | 答案对象数组 | [{"questionId":"...","answer":"A"}] |
daily_stats.topics_studied | 知识点字符串数组 | ["二次方程","函数"] |
在应用层读取这些字段时,需要调用 JSON.parse();写入时调用 JSON.stringify()。路由代码中已统一处理。
数据库备份
# 直接复制数据库文件(需确保没有写操作)
cp ./data/edubuddy.db ./backups/edubuddy-$(date +%Y%m%d).db
# 使用 SQLite 在线备份(安全)
sqlite3 ./data/edubuddy.db ".backup ./backups/edubuddy-$(date +%Y%m%d).db"