系统架构

后端架构

EduBuddy 后端是一个基于 Node.js + Express + TypeScript 的 RESTful API 服务,采用分层架构(路由层 → 服务层 → 数据访问层),通过中间件链实现安全、限流、日志等横切关注点。

技术栈

技术版本用途
Node.js18+运行时
Express4.xHTTP 框架,路由、中间件
TypeScript5.x类型安全,编译为 JS
better-sqlite312.xSQLite 同步驱动
openai4.xAI 服务 SDK
jsonwebtoken9.xJWT 签发与验证
bcryptjs2.x密码哈希(cost factor 12)
multer1.x文件上传处理
helmet7.xHTTP 安全头
cors2.x跨域资源共享
express-rate-limit7.xAPI 速率限制
compression1.xGzip 响应压缩
winston3.x结构化日志
uuid9.x生成 UUID v4 主键
pdf-parse1.xPDF 文字提取

中间件链

每个请求经过以下中间件处理链(按注册顺序):

HTTP Request ↓ [1] helmet() → 设置安全响应头(X-Frame-Options, CSP 等) ↓ [2] cors() → 验证 Origin,开发环境允许 localhost:3000/5173 ↓ [3] compression() → Gzip 压缩响应体 ↓ [4] rateLimit() → 全局速率限制(200次/15分钟) ↓ (AI 接口额外经过 aiLimiter:20次/分钟) [5] express.json() → 解析 JSON 请求体(最大 10MB) ↓ [6] Route Handler → 具体路由处理 ↓ [7] authMiddleware → JWT 验证(受保护路由) ↓ [8] Business Logic → 数据库操作 / AI 调用 ↓ [9] Error Handler → 全局错误捕获,生产环境隐藏详情 ↓ HTTP Response

路由模块

后端路由按业务领域拆分为 6 个独立模块:

模块文件挂载路径主要功能
routes/auth.ts/api/auth注册、登录、获取用户信息、更新资料、修改密码、用户列表
routes/questions.ts/api/questions题目 CRUD、AI 生成、AI 解题、提交答案、从文本提取题目
routes/progress.ts/api/progress知识点掌握度、每日统计、练习记录、AI 分析、评估报告、目标管理
routes/learningPaths.ts/api/learning-paths学习路线 CRUD、AI 生成、报名、进度更新、节点管理
routes/upload.ts/api/uploadPDF/图片/语音/文本上传解析,上传历史记录
routes/sharing.ts/api/sharing发送分享、收件箱、发件箱、标记已读、未读计数

认证中间件

middleware/auth.ts 实现 JWT 验证,扩展了 Express 的 Request 类型:

// 扩展 Request 类型
export interface AuthRequest extends Request {
  user?: {
    userId: string;
    username: string;
    role: string;
  };
}

// 中间件逻辑
export function authMiddleware(req, res, next) {
  const token = req.headers.authorization?.split(' ')[1]; // Bearer 
  if (!token) return res.status(401).json({ message: '未提供认证令牌' });
  
  const payload = verifyToken(token);     // 调用 utils/jwt.ts
  if (!payload) return res.status(401).json({ message: '令牌无效或已过期' });
  
  req.user = payload;  // 注入用户信息
  next();
}

数据库访问

database/db.ts 实现单例模式的数据库连接管理:

let db: Database | null = null;

export function getDB(): Database {
  if (!db) {
    db = new BetterSQLite(DB_PATH);
    db.pragma('journal_mode = WAL');   // 启用 WAL 模式
    db.pragma('foreign_keys = ON');    // 启用外键约束
    initializeSchema(db);              // 首次运行建表
  }
  return db;
}
better-sqlite3 提供同步 API,所有数据库操作在主线程同步执行,无需 async/await。这使得代码更简洁,且避免了异步操作中的事务管理复杂性。对于 I/O 密集型操作(主要是 AI 调用),仍使用 async/await。

文件上传处理

文件上传使用 multer 中间件,配置如下:

日志系统

utils/logger.ts 使用 Winston 构建结构化日志:

错误处理规范

所有路由使用统一的错误响应格式:

// 成功响应
{ "success": true, "data": { ... }, "message": "操作成功" }

// 失败响应
{ "success": false, "message": "错误描述" }

// 常见 HTTP 状态码
200 - 查询/更新成功
201 - 创建成功
400 - 参数错误(必填项缺失、格式不正确)
401 - 未认证或 Token 无效
403 - 无权限(如删除他人题目)
404 - 资源不存在
409 - 冲突(如用户名已存在)
413 - 文件过大
429 - 速率限制超出
500 - 服务器内部错误

知识点掌握度算法

答题后,系统自动更新 knowledge_mastery 表,掌握度计算公式:

// 掌握度 = min(100, 正确率 × log10(练习次数+1) × 200)
mastery = Math.min(100,
  (correctCount / totalCount) * 100 * Math.log10(totalCount + 1) * 2
)

该公式综合考虑了正确率练习次数两个维度: