架构概览
EduBuddy 采用经典的 前后端分离 架构,前端为 SPA(单页应用),后端提供 RESTful API,通过 JWT 进行无状态鉴权,AI 功能通过 OpenAI 兼容 SDK 集成。
整体架构图
┌─────────────────────────────────────────────────────────────────┐
│ 浏览器(用户) │
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ React SPA(端口 5173/80) │ │
│ │ │ │
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │
│ │ │Dashboard │ │Questions │ │Progress │ │Learning │ │ │
│ │ │ Page │ │ Page │ │ Page │ │Paths Page│ │ │
│ │ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │ │
│ │ ↕ Axios HTTP / JWT Bearer Token │ │
│ └─────────────────────────────────────────────────────────┘ │
└──────────────────────────┬──────────────────────────────────────┘
│ HTTP REST API
↓
┌─────────────────────────────────────────────────────────────────┐
│ Node.js + Express 后端(端口 3001) │
│ │
│ ┌─────────┐ ┌──────────┐ ┌──────────┐ ┌────────────────┐ │
│ │Helmet │ │CORS │ │RateLimit │ │Compression │ │
│ │(安全头) │ │(跨域控制)│ │(速率限制)│ │(Gzip 压缩) │ │
│ └─────────┘ └──────────┘ └──────────┘ └────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ 路由层(Routes) │ │
│ │ /auth /questions /progress /learning-paths │ │
│ │ /upload /sharing │ │
│ └──────────────────────────────────────────────────────────┘ │
│ ↕ JWT 鉴权中间件 │
│ ┌──────────────────┐ ┌──────────────────────────────┐ │
│ │ AI Service 层 │ │ 数据库访问层 │ │
│ │ (aiService.ts) │ │ (better-sqlite3 同步 API) │ │
│ └──────────────────┘ └──────────────────────────────┘ │
│ ↕ ↕ │
└───────────┬──────────────────────────────┬──────────────────────┘
│ │
↓ ↓
┌─────────────────────┐ ┌──────────────────────────────────┐
│ OpenAI 兼容 API │ │ SQLite 数据库 │
│ (GPT-4o/DeepSeek │ │ (WAL 模式,12 张数据表) │
│ /通义千问 等) │ │ 本地文件:edubuddy.db │
└─────────────────────┘ └──────────────────────────────────┘
核心设计决策
1. 为什么选择 SQLite?
EduBuddy 选择 SQLite 作为嵌入式数据库,理由如下:
- 零配置部署:无需安装独立数据库服务,数据库即一个文件
- WAL 模式:启用 Write-Ahead Logging 支持并发读写,适合中等并发场景
- 同步 API:
better-sqlite3提供同步接口,避免回调地狱,代码更清晰 - 轻量迁移:数据库文件直接备份/迁移,运维简单
2. 无状态 JWT 鉴权
所有受保护 API 使用 JWT Bearer Token 鉴权:
- Token 签发于登录/注册时,有效期 7 天
- Payload 包含
userId、username、role - 中间件
authMiddleware解析 Token 并将用户信息注入req.user - Token 存储于客户端
localStorage,401 响应时自动跳转登录页
3. AI 调用统一封装
所有 AI 功能通过 backend/src/services/aiService.ts 统一管理:
- 底层使用 OpenAI Node.js SDK,兼容任何 OpenAI 格式的 API
- 每个功能对应一个语义化函数(如
generateQuestion、solveQuestion) - 所有函数统一处理 JSON 解析异常,提供降级返回
- 通过环境变量动态切换服务商和模型
4. 速率限制分层
两层速率限制保护服务:
- 全局 API 限制:200 次 / 15 分钟,防止基本的接口滥用
- AI 接口专属限制:20 次 / 分钟,控制 AI API 调用费用
数据流示例:AI 生成题目
用户点击「AI 生成题目」
↓
前端 questionAPI.generate({ subject, topic, difficulty, ... })
↓
POST /api/questions/generate [带 JWT Token]
↓
aiLimiter 速率检查 → authMiddleware 鉴权
↓
questions.ts 路由处理器
↓
调用 aiService.generateQuestion(subject, topic, difficulty, type, count)
↓
构造 Prompt → OpenAI SDK chat.completions.create()
↓
AI 返回 JSON 字符串 → 解析为对象数组
↓
循环插入 questions 表(UUID 主键)
↓
返回 { success: true, data: { ids, questions } }
↓
前端刷新题目列表并提示「成功生成 N 道题目」
模块依赖关系
| 模块 | 依赖 | 被依赖 |
|---|---|---|
server.ts | 所有路由、中间件、数据库 | —(入口) |
routes/* | database/db、services/aiService、middleware/auth | server.ts |
services/aiService.ts | openai、utils/logger | routes/questions、routes/progress、routes/learningPaths、routes/upload |
database/db.ts | better-sqlite3、database/schema.sql | 几乎所有路由 |
middleware/auth.ts | utils/jwt | 所有需鉴权的路由 |
utils/jwt.ts | jsonwebtoken | middleware/auth、routes/auth |
utils/logger.ts | winston | 全局 |