Files
ClassHelper/docs/dev/api.md
T
LoveChengke 817af02357 feat(permissions,clients,ai,system,server,web,client,shared)!: 权限组 + 客户端设备远控与自动更新 + 大模型助手 + 系统诊断(1.3.0)
功能级准入改为「权限键」,`requireRole` 退成默认模板。

权限组
- 权限目录(39 键:键 / 中文名 / 分类 / 默认角色)收口到 shared,路由挂 requirePermission / requireAnyPermission
- 有效权限每请求实时算,改完分组立刻生效:ADMIN 全集且 permission:manage、system:manage 不可剥夺;
  CLASS_DEVICE 空集(不受分组影响);有分组 = 并集(因此也能用来**收回**权限);无分组 = 角色默认模板
- 新增「权限组」页:组增删改、成员分配、生效权限预览
- 班级维度的科目规则(assertCanManageSubjectContent)故意不可被权限组覆盖

客户端设备(ClassHelper 桌面端)
- 设备身份是 deviceKey(首次启动生成的 UUID,saveConfig 拒绝改写、退出登录也保留)
- 心跳走「DB 记状态 + 客户端定时拉」,不引入新的长连接:30 秒一次,在线窗口 90 秒
- 远控命令 shutdown / restart / logoff / lock:服务端有效期 10 分钟,客户端弹默认选中「取消」的对话框、
  60 秒未确认按取消处理,执行后必须回执(重复回执幂等),同一台机器同一条命令只弹一次
- 新增「客户端设备」页:版本一览、一键推送更新、下发远控

客户端安装包由服务端下载并缓存(教室机常常没有外网,浏览器也受 CSP 限制)
- POST /api/update/client/fetch 由服务端下载到 data/client-releases/<版本>/ 并登记 sha256 + size
- 自动安装三条护栏:没有 sha256 就拒收、必须等到非上课且主窗口隐藏满 5 分钟、冒烟模式永不安装

大模型助手:只解析、不落库
- POST /api/ai/interpret 返回**待确认**的结构化动作(建作业 / 发通知 / 叫人),
  真正落库仍调既有端点 —— 权限断言、上课时段紧急通知保护、ClassIsland 推送全部自动复用
- 模型给的 params 用与手动入口**同一份** zod 再校验(模型输出不可信)
- API Key 走 AES-256-GCM(密钥由 JWT_SECRET 经 scrypt 派生)加密存 AppSetting,只进不出
- provider=mock 是离线确定性解析,自动化验收一律用它;每次解析写一条 AiAudit(不含密钥)

系统与运维
- 开发者模式(服务端内存缓存开关,热路径不查库;开启后 500 才附 error.stack)、运行诊断(绝不回传密钥)
- 开机自启动运行期可改:Windows 改 HKCU\...\Run、Linux systemctl enable/disable,
  **非安装形态一律返回 supported:false**,不往开发机的注册表里写自启项
- 服务端侧栏折叠只用 CSS 收拢(不重建 DOM),关怀模式只放大字号与控件、不动任何几何令牌

客户端
- 崩溃自愈:渲染进程挂掉 / 超 10 秒无响应先静默重建主窗口(10 分钟最多 3 次),
  未捕获异常只在「没在上课、没人看着」时才重启进程;冒烟模式绝不重启(会杀掉验证实例)
- 上线补弹离线期间收到的消息只做一次:起点只在「可达 → 不可达」的边界写,恢复时取最近 5 条按时间升序推

发布内容批量
- POST /notifications/bulk、POST /homeworks/bulk:**单班失败不影响整批**,逐班返回结果
- 作业的科目名跨班**按名匹配**已有课程,找不到就整班记失败,绝不自动建课
  (自动建课会把创建者写成任课老师,等于越权改任教关系)
- 通知与作业同口径按「所属日期」归档,新增 /notifications/days 与 /notifications/creators

BREAKING CHANGE: 能不能做某件事不再看角色名,而是看权限键(GET /api/permissions/me)。
未分组的账号按角色默认模板生效,行为与 1.2.0 一致;分组之后生效集合只由组的并集决定。
服务端首次启动会自动应用 1 个数据库迁移(权限组 3 表 / AppSetting / 客户端设备·命令·安装包 /
AiAudit / Notification.assignDate / User.careMode,历史通知按本地日期回填),升级前请先备份。
2026-10-11 11:49:32 +08:00

25 KiB
Raw Blame History

title, description
title description
REST API ClassHelper后端全部接口一览:方法、路径、权限与说明。

REST API

通用约定

  • 所有接口前缀 /api(设备接口也一样);
  • 统一响应体:{ "success": true, "data": {}, "message": "" },错误为 success: false + code;
  • 除登录、健康检查与设备接口外,都需要 Authorization: Bearer <token>;
  • ClassIsland 设备接口用 X-ClassIsland-Token,不走 JWT(见ClassIsland 插件);
  • 请求体校验走 zod(middleware/validate.ts),错误由 middleware/error.ts 统一映射。

接口一览

方法 路径 权限 说明
POST /auth/login 公开 登录(教师 / 管理员的工号 + 密码);学生不是账号,拿学号登录只会得到 401
POST /auth/class-login 公开 班级账号登录:班级码 + 班级密码 → 班级会话
GET /auth/me 登录 当前用户(学生附带班级 / 年级)
PATCH /auth/password 登录 修改自己的密码(班级会话 → 班级密码)
POST /auth/logout 登录 退出(无状态,客户端丢弃 token)
GET /permissions/me 登录 当前账号的生效权限(无权限键;班级端会话返回空集)
GET /permissions/catalog permission:manage 权限键目录(权限页整棵勾选树的唯一来源)
GET / POST /permissions/groups permission:manage 权限组列表 / 新建
PATCH / DELETE /permissions/groups/:id permission:manage 改名 / 改描述 / 整体替换权限键;内置组不可删
GET /permissions/users permission:manage 成员列表(所属权限组 + 生效权限数量)
PUT /permissions/users/:id/groups permission:manage 整体替换某账号的权限组(空数组 = 回落角色默认模板)
GET /classes 登录 班级列表(按权限收敛)
GET /classes/:id 班级可见 班级详情(学生 / 课程 / 班主任 / 各科任课老师)
POST /classes 管理员 创建班级(自动生成班级码,可指定班主任)
PATCH / DELETE /classes/:id 管理员 / 教师 编辑 / 删除班级
PATCH /classes/:id/class-account 管理员 设置 / 重置班级账号(班级码 + 班级密码)
PATCH /classes/:id/head-teacher 管理员 设置 / 更换班主任
GET /classes/:id/classisland-status 班级可见 本班 ClassIsland 联动状态(教室客户端设置页用)
GET /classes/:id/notification-channel 班级可见 读取「通知显示到哪个端」
PATCH /classes/:id/notification-channel 班级账号 / 教师 / 管理员 设置「通知显示到哪个端」
GET / POST /classes/:id/students 班级可见 / ADMIN·TEACHER 学生名单 / 添加学生
DELETE /classes/:id/students/:userId ADMIN·TEACHER 移出学生
PUT /classes/:id/subject-teachers ADMIN 设置各科任课老师(班级 + 科目 + 教师)
GET / POST / PATCH / DELETE /courses 登录(写:班主任 / 管理员) 课程管理
GET /schedules?classId=&week=&dayOfWeek= 登录 课表列表(week 过滤周次范围)
GET /schedules/grid?classId=&week= 登录 周视图(7 列结构,供客户端直接渲染)
GET /schedules/current?classId=&at= 登录 当前上课状态(inClass / current / next;at 为诊断用时间覆盖)
POST / PATCH / DELETE /schedules[/:id] 班主任 / 管理员 课表增删改(广播 schedule:updated)
GET /homeworks?classId=&courseId=&date=&pendingOnly=&keyword= 登录 作业列表(支持按所属日期;学生带完成状态,教师带完成人数)
GET /homeworks/days?classId=&from=&to= 登录 哪些天有作业(日期高亮)
GET /homeworks/:id 班级可见 作业详情
POST /homeworks 教师 / 管理员 / 班级账号 发布作业(附 assignDate;班级账号用于教室机器录入,归属班主任)
PATCH / DELETE /homeworks/:id 教师 / 管理员 修改 / 删除(广播 homework:new / homework:updated)
PATCH /homeworks/:id/status 登录 标记完成 / 取消(广播 homework:status)
GET / PATCH /homeworks/:id/submissions 登录 未交名单:读名单 / 回写全班完成状态
POST /homeworks/bulk homework:write 一次给多个班级布置同一份作业(subjectName 跨班按名匹配科目;逐班结果)
GET /notifications?classId=&priority=&unreadOnly=&keyword= 登录 通知列表(带已读状态;另支持 date / from / to / createdBy)
GET /notifications/unread-count 登录 未读数(红点)
POST /notifications 教师 / 管理员 发布通知(广播 notification:new);上课时段发紧急通知需 confirmDuringClass
POST /notifications/:id/read、/notifications/read-all 登录 标记已读(班级设备会写全班)
DELETE /notifications/:id 教师 / 管理员 删除通知
GET /notifications/days?classId=&from=&to=&createdBy= 登录 通知按天归档(日期 + 条数,日期取 assignDate)
GET /notifications/creators?classId=&from=&to=&date= 登录 通知按发布人归档
POST /notifications/bulk notification:publish 一次给多个班级发同一条通知(逐班结果,单班失败不影响整批)
POST /calls 教师 / 管理员 叫人(urgent 决定 URGENT / HIGH;广播 notification:new 与 call:new)
GET /grades/my 登录 个人成绩(班级会话 → 全班总览)
GET /grades?classId=&courseId=&userId=&examName= 教师 / 管理员 班级成绩
GET /grades/stats?classId=&courseId=&examName= 教师 / 管理员 等级分布 + 各课程平均得分率
POST /grades、/grades/bulk 班主任 / 管理员 单条 / 批量录入(广播 grade:updated)
PATCH / DELETE /grades/:id 班主任 / 管理员 修改 / 删除成绩
GET /students?classId=&keyword= 管理员 学生名单
POST / PATCH / DELETE /students[/:id] 管理员 学生名单增删改(无密码概念;重置密码接口已下线 → 404)
GET /teachers?keyword= 管理员 教师列表
POST / PATCH / DELETE /teachers[/:id] 管理员 新建 / 编辑 / 删除教师账号
POST /teachers/:id/reset-password 管理员 修改 / 重置教师密码(newPassword 可选,留空 = 默认初始密码)
GET /dashboard/summary、/dashboard/term 登录 仪表盘汇总 / 学期周次
GET /imports/template?kind=&format= 管理员 / 班主任 导入模板下载(csv 走 JSON,xlsx 走二进制)
POST /imports/table/preview 管理员 / 班主任 上传表格(base64)解析预览:列名 + 前 20 行 + 校验问题 + 建议映射
POST /imports/table/commit 管理员 / 班主任 按字段映射与写入模式导入(成绩 / 学生名单 / 教师名单)
POST /imports/time-layout/preview 管理员 / 本班班主任 解析 ClassIsland 时间配置 JSON(只解析不落库)
GET / POST / DELETE /imports/time-layout[/:id] 管理员 / 本班班主任 时间配置列表 / 导入(replace 覆盖、merge 合并)/ 删除
POST /imports/class-plan/preview、/imports/class-plan 管理员 / 本班班主任 ClassIsland 课程表解析预览 / 导入(支持单双周)
GET / POST / PATCH / DELETE /integrations/devices[/:id] 管理员 / 教师 ClassIsland 联动设备管理(令牌只存 sha256,令牌前缀用于人眼识别)
POST /integrations/devices/:id/token 管理员 / 教师 重置设备令牌(旧令牌立即失效,明文只返回一次)
POST /integrations/classisland/notify 管理员 / 教师 下发提醒到该班 ClassIsland 设备(广播 classisland:notification)
GET /database/status 管理员 数据库状态(连接 / 版本 / 体积 / 23 张表行数 / 备份列表 / 定时配置)
POST /database/test-connection 管理员 测试任意目标库连通性(provider 仅 sqlite / mysql)
GET / POST /database/backups 管理员 备份列表 / 立即备份(gzip JSON 快照,存 <数据目录>/backups/)
POST / DELETE /database/backups/:name/restore · /database/backups/:name 管理员 从备份恢复(整库覆盖)/ 删除备份
GET /database/export、/database/sqlite-file 管理员 下载 JSON 快照 / 下载数据库文件(后者仅 SQLite)
POST /database/import 管理员 导入快照(base64,整库覆盖;可在 SQLite / MySQL 之间互迁)
GET / PUT /database/backup-schedule 管理员 定时备份配置(enabled / intervalHours / keepCount)
POST / GET /database/switch、/database/switch/jobs/:id 管理员 一键切换数据库(异步任务)/ 轮询进度
POST /integrations/classisland/report 设备令牌 插件上报状态 + 课表 + 节次时间
GET /integrations/classisland/pending 设备令牌 插件拉取尚未确认的提醒(离线期间老师发的通知,重连后补齐)
POST /integrations/classisland/ack 设备令牌 插件确认提醒已弹出(确认后不再补发)
GET /integrations/classisland/class-plan 设备令牌 插件拉取本班课表(开启镜像时)用于写回 ClassIsland
GET /update/check 登录 更新检查(服务端代查 GitHub 最新 Release;?force=1 绕过缓存仅管理员生效)
POST /clients/heartbeat 登录 客户端每 30 秒上报(无权限键);返回更新目标 + 安装包信息 + 一条待执行命令
POST /clients/commands/:id/ack 登录 命令执行回执(无权限键,靠 body 的 deviceKey 校验归属,幂等)
POST /clients/commands remote:control 下发远控命令(关机 / 重启 / 注销 / 锁屏;自动撤销该机更早的未回执命令)
GET /clients/devices?classId= client:manage 客户端设备列表(版本 / 在线 / 待执行命令)
POST /clients/updates client:update-push 统一下发客户端更新(按设备 / 班级 / 全部)
DELETE /clients/devices/:id client:manage 移除设备记录(换机 / 旧机器清理)
GET /update/client/releases client:manage 服务端已缓存了哪些客户端安装包
POST /update/client/fetch client:update-push 服务端代教室机器从 GitHub 下载安装包并登记 sha256 + size
POST /update/client/releases client:update-push 手工登记已放进磁盘的安装包(内网 / U 盘分发兜底)
DELETE /update/client/releases/:version client:update-push 删除缓存的安装包(元数据 + 磁盘文件)
GET /update/client/download/:version 登录 下载安装包(故意不挂权限键,班级端要能下;res.download)
GET / PUT /ai/settings ai:manage 大模型设置(API Key 加密存储、只进不出,只回 hasApiKey / keyHint)
POST /ai/interpret ai:use 一句话解析(只解析不落库);回 action / params / preview / confidence
GET /ai/audit?limit= ai:manage 助手使用记录(每次解析写一条 AiAudit)
GET / PUT /system/settings system:manage 系统设置(developerMode:开启后 500 响应带 error.stack)
GET / PUT /system/autostart system:manage 开机自启状态 / 开关(Windows 注册表 Run 项,Linux systemd 单元)
GET /system/diagnostics system:manage 运行诊断(版本 / Node / 运行时长 / 数据目录 / 在线客户端数 / 已加载模块)
GET /health 公开 健康检查(含版本号与已挂载模块)

另有三个探针(不带 /api 前缀):/healthz(存活)、/readyz(就绪)、以及 /api/health(详情)。

几条容易踩的接口约定

约定 说明
POST /homeworks 故意不加 requireRole 权限判在 homeworks.service.createHomework 里(staff 或本班班级账号),因为教室机器也要能录作业
班级账号的 sub 是班级 id 写 createdBy 前要换成该班班主任,否则撞 User 外键
登录限流挂在两个入口上 /auth/login 与 /auth/class-login 都挂 loginRateLimiter()
角色判定在密码校验之前 否则"密码错 / 密码对但账号停用"的差异会把学生账号密码变成可离线验证的预言机
会渲染成链接的字段要校验 scheme Homework.attachmentUrl 只允许 http(s):// 或站内相对路径
表格导入 8MB ↔ 请求体 12MB 文件以 base64 放进 JSON(约 4/3 倍),超限映射成 413 IMPORT_TOO_LARGE
三个接口故意不挂权限键 /permissions/me、/clients/heartbeat、/update/client/download/:version —— 前两者由班级端(CLASS_DEVICE,权限集为空)调用,下载接口要能装包;保护只有"已登录 + 限流"
查询参数校验失败是 422 middleware/validate.ts 的 zod 解析错误由错误中间件映射成 422 VALIDATION_ERROR(不是 400)
批量发布逐班判定、不整批回滚 /notifications/bulk 与 /homeworks/bulk 的单班失败(403 越权 / URGENT_DURING_CLASS)不影响其它班,成功班照常落库并实时推送
/ai/interpret 只解析不落库 助手只回结构化参数与中文预览;落库由前端确认后调既有端点 —— 权限与上课时段保护都在那条路径上

相关