班级助手 API 文档
面向网页前端、小程序、Android 与第三方集成;以当前仓库源码为准。
路径未版本化
最后更新:2026-10-25
生产环境必须通过 HTTPS 访问;Android 客户端已关闭明文流量。本文不包含任何真实学生信息、班级码、密码、签名密钥或服务器 IP。
接口行为、字段和错误码以
src/adapters/http/api/、src/core/errors.js、src/core/security.js 及实际路由为准。
本页是总览:基础约定、认证摘要、错误码、限流、分页和公开接口都在这里;具体接口按主题拆到左侧子页面。
第一次接入建议先读 认证与 CSRF,再看
班级上下文与接口总览。
基础约定
- 基址:
/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
Cookie / Bearer / X-API-Token、令牌换取、CSRF 与同源
班级上下文
?class 规则、无班级/越权错误与接口总览
账户
auth/token、me、密码设置与关闭
待办
列表、发布、勾选/取消、定位与审核
课表与日历
schedule、calendar/events 与字段
分组
本班分组、mine 与 memberCount
通知
列表、未读数、增量、单条已读与全部已读
身份绑定
identities、绑定码与 bind
扫码登入
auth/desk、二维码、轮询与 auth/mini
App 版本
server 层 /api/app/version 与 APK 下载
Web 层
HTML 表单、Cookie/令牌边界、404 与兼容性
错误码
| HTTP | error.code | 含义 | 典型场景 |
|---|---|---|---|
| 400 | BAD_REQUEST | 请求有误 | 参数缺失或格式不对、没有班级、开始/结束时间非法、绑定码尝试过多、关闭密码前没有其他登入方式、JSON 解析失败 |
| 401 | UNAUTHORIZED | 未认证或凭证无效 | 未登入、令牌无效或过期、绑定码错误、账号不存在 |
| 403 | FORBIDDEN | 已认证但无权 | 不是班级成员、Cookie 写操作来源不同源、账号未通过审核(令牌方式)、无权发布班级待办 |
| 404 | NOT_FOUND | 资源不存在 | 班级不存在、待办不存在或不属于本班、通知不存在或已读、扫码会话过期、接口不存在 |
| 409 | CONFLICT | 冲突 | 唯一键冲突(如重复绑定/重复加入)、数据库唯一索引冲突 |
| 429 | TOO_MANY_REQUESTS | 请求过多 | 统一错误码中的限流项;当前 /api 路由未挂通用限流,Web 层登入/注册/班级码会返回 429(绑定码接口超限返回 400,见 限流) |
| 500 | INTERNAL | 服务器错误 | 未捕获异常、数据库错误等;生产环境不返回内部细节 |
- 数据库唯一键冲突会被翻译成 409,不按 500 处理。
- 429 响应可能带
Retry-After(秒),客户端应等待后重试。 /api下不存在的路径返回 JSON 404,不会落到网页的 404 页面。
限流
API 路由本身没有全局限流;下列限制按真实代码区分“API 层”和“Web 层”:
| 位置 | 接口/操作 | 限制 | 超限响应 |
|---|---|---|---|
| API | POST /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/ 下的运维与安全文档。