班级助手 API 文档

面向网页前端、小程序、Android 与第三方集成;以当前仓库源码为准。

路径未版本化 最后更新:2026-10-25

基础约定

  • 基址:/api。所有接口路径都相对于站点根,例如 /api/todos;没有额外的版本前缀。
  • 协议:生产环境必须使用 HTTPS;Cookie 默认按 COOKIE_SECURE=1 配置只走安全连接。
  • 请求体:API 客户端统一使用 Content-Type: application/json。JSON 请求体上限 64 KB;解析失败返回 400 BAD_REQUEST。
  • 响应:UTF-8 JSON。除扫码二维码图片和 /api/app/version 外,均使用统一信封(见下)。API router 内的响应统一带 Cache-Control: no-store;身份解析早期失败的响应不保证带该头。
  • 编码:字符串按 UTF-8;时间、ID、数组等字段类型见各接口说明。

成功响应

{
  "ok": true,
  "data": { },
  "meta": { }
}

成功时 ok 恒为 true;data 一定存在(无数据时为 null);meta 只有接口声明了业务元数据时才出现,不保证每个接口都有。

失败响应

{
  "ok": false,
  "error": {
    "code": "FORBIDDEN",
    "message": "你还不是这个班的成员"
  }
}

HTTP 状态码表达错误类别,客户端应优先按状态码和 error.code 处理;message 是给人看的中文提示,不要依赖其文本做分支判断。

认证摘要

  • 网页 Cookie:登入网页后自动携带,默认名称 class32601.sid,HttpOnly、SameSite=Lax。
  • Bearer:Authorization: Bearer <token>,推荐给 App、小程序、脚本和第三方端。
  • 备用头:X-API-Token: <token>,与 Bearer 等价。
  • CSRF:网页表单需要 _csrf 或 X-CSRF-Token;/api/** 豁免网页 CSRF,但 Cookie 写操作会校验来源(优先 Origin,缺失时看 Referer,必须同源)。
认证、CSRF 与同源校验完整说明

错误码

HTTPerror.code含义典型场景
400BAD_REQUEST请求有误参数缺失或格式不对、没有班级、开始/结束时间非法、绑定码尝试过多、关闭密码前没有其他登入方式、JSON 解析失败
401UNAUTHORIZED未认证或凭证无效未登入、令牌无效或过期、绑定码错误、账号不存在
403FORBIDDEN已认证但无权不是班级成员、Cookie 写操作来源不同源、账号未通过审核(令牌方式)、无权发布班级待办
404NOT_FOUND资源不存在班级不存在、待办不存在或不属于本班、通知不存在或已读、扫码会话过期、接口不存在
409CONFLICT冲突唯一键冲突(如重复绑定/重复加入)、数据库唯一索引冲突
429TOO_MANY_REQUESTS请求过多统一错误码中的限流项;当前 /api 路由未挂通用限流,Web 层登入/注册/班级码会返回 429(绑定码接口超限返回 400,见 限流)
500INTERNAL服务器错误未捕获异常、数据库错误等;生产环境不返回内部细节
  • 数据库唯一键冲突会被翻译成 409,不按 500 处理。
  • 429 响应可能带 Retry-After(秒),客户端应等待后重试。
  • /api 下不存在的路径返回 JSON 404,不会落到网页的 404 页面。

限流

API 路由本身没有全局限流;下列限制按真实代码区分“API 层”和“Web 层”:

位置接口/操作限制超限响应
APIPOST /api/identities/bind同一来源 IP 10 分钟内最多 8 次失败;成功绑定会清零400 BAD_REQUEST(“尝试次数太多了,请 10 分钟后再试”)
Web学号密码登入 POST /user/password同一 IP + 学号 10 分钟最多 15 次;成功登入清零429 登入页
Web学号注册 POST /user/register同一 IP 10 分钟最多 10 次429 注册页
Web班级码加入 POST /classes/join同一 IP 10 分钟最多 20 次失败429 错误页

Web 层限流需要正确设置 TRUST_PROXY=1 才能拿到真实客户端 IP,否则反向代理后所有请求会共享同一个限流桶。绑定码接口的来源标识优先取 req.ip,取不到时回退到 X-Forwarded-For 请求头。

分页与时间格式

  • 分页:当前 API 没有统一的 meta.total / page / size 分页信封。GET /api/notices 支持 limit(1–200,默认 50);GET /api/notices/pending 固定扫描最近 20 条,最多返回 5 条正文;待办列表内部最多取 300 条;其余列表接口返回当前用户可见的数据。需要分页的客户端应自行切片。
  • 数据库时间:待办、通知等字段(如 start_at、due_at、created_at、readAt)通常是 YYYY-MM-DD HH:mm:ss 的北京时间墙钟字符串,可能为 null。
  • 日历时间:GET /api/calendar/events 的事件开始/结束是带 +08:00 的 ISO 8601 字符串。
  • 健康检查/生成时间:GET /api/health 的 time 和日历的 generatedAt 是 ISO UTC(以 Z 结尾)。
  • 课表:termStart 为 YYYY-MM-DD;weekDates 是数组,索引 1–7 对应周一到周日(索引 0 不使用);todayWeekday 为 1–7,查看非本周时为 0。

公开接口

GET /api/health

认证:否。班级:否。用途:部署后检查服务是否可用。

{
  "ok": true,
  "data": {
    "service": "class-assistant",
    "time": "2026-10-25T07:41:14.373Z",
    "platforms": ["wechat_mp", "wechat_mini", "qq", "web"]
  }
}

platforms 来自平台注册表,只表示“登记了哪些平台”,不代表都可用;可用性看 /api/platforms 的 enabled。

GET /api/platforms

认证:否。班级:否。用途:渲染“去绑定 XX”入口。

{
  "ok": true,
  "data": [
    { "id": "wechat_mp", "name": "微信公众号", "kind": "wechat", "icon": "wechat", "enabled": false },
    { "id": "wechat_mini", "name": "微信小程序", "kind": "wechat", "icon": "wechat", "enabled": true },
    { "id": "qq", "name": "QQ 机器人", "kind": "qq", "icon": "qq", "enabled": true },
    { "id": "web", "name": "网页", "kind": "web", "icon": "globe", "enabled": true }
  ]
}

enabled 规则:小程序需要配置 AppID 和 Secret;公众号需要配置 AppID;QQ/网页当前恒为 true。示例中的 enabled 值随部署配置变化。

相关文档:学生使用指南。需要运行环境信息(端口、反向代理、数据库迁移)请查看仓库内 docs/ 下的运维与安全文档。