Files
ClassFeedback/.pi/skills/class-feedback-report/docs/API.md

15 KiB
Raw Blame History

HydroOJ 课程插件 API 使用文档

本文档覆盖 课程插件(hydrooj-course) 当前源码注册的 JSON API,包括兼容查询、课程页面内部展示、教学反馈、学生周报和首刷写入接口。

API 只输出课程与学习事实,不自动生成评价或发送消息。面向家长或学生的文字必须由上层工具生成并由教师审核。

1. 接口总览

方法 路径 用途 主要权限 稳定性
GET /course/api/list 查询当前账号可见课程 已登录用户 兼容接口
GET /course/api/detail/:cid 查询课程与章节结构 可访问该课程 兼容接口
GET /course/api/analysis/:cid/:csid 查询章节成绩、首刷和最终结果 教师或授权校区查看者 敏感兼容接口
GET /course/api/v1/courses/:cid/summary 查询当前查看学生的课程进度摘要 可访问该课程 页面内部接口
GET /course/api/v1/courses/:cid/sections/:csid/view 按需查询一个章节的授权展示数据 可访问该课程 页面内部接口
GET /course/api/v1/courses/:cid/students 分页搜索授权范围学生 教师或授权校区查看者 页面内部接口
GET /course/api/v1/courses/:cid/sections/:csid/feedback/overview 章节概览与逐题统计 教师或授权校区查看者 v1
GET /course/api/v1/courses/:cid/sections/:csid/feedback/students 分页筛选学生反馈事实 教师或授权校区查看者 v1
GET /course/api/v1/courses/:cid/sections/:csid/feedback/students/:uid 单生逐题反馈证据 教师或授权校区查看者 v1
GET /course/api/v1/weekly-report/students 搜索授权范围内的周报学生 教师或授权校区查看者 v1
GET /course/api/v1/weekly-report/students/:uid 查询单生一周课程与提交事实 教师或授权校区查看者 v1
POST /course/:cid/s/:csid/practice/:tid/:pid/start 开启或重启首刷计时 已登录且满足课程规则 页面内部接口

页面内部展示接口只返回授权后的摘要、章节 HTML 和公开用户渲染字段,均设置 Cache-Control: private, no-store。

2. 认证、域路径与安全边界

  • 所有接口使用当前 HydroOJ 登录会话,不接受通用管理员 Token。
  • 主域直接使用表格中的路径;非主域在前面加 /d/:domainId。
  • 调用时使用 Accept: application/json,跨页面请求保留同源 Cookie。
  • 不要把真实 Cookie、账号密码、学生姓名、代码或响应原文写入仓库、聊天记录和公开日志。
  • analysis 可能返回代码、编译错误、测试点和提交历史,只能在受控教师工具中按需开启。
  • 教学反馈 v1 不返回邮箱、手机号、IP、源代码、完整提交历史、编译器原文或测试点详情,并设置 Cache-Control: private, no-store 与 X-Content-Type-Options: nosniff。

浏览器调用示例:

const response = await fetch('/course/api/list', {
  credentials: 'same-origin',
  headers: { Accept: 'application/json' },
});
if (!response.ok) throw new Error(`HTTP ${response.status}`);
const data = await response.json();

命令行调试示例:

curl -H "Accept: application/json" \
  -H "Cookie: sid=<authorized-session-cookie>" \
  "https://oj.example.com/d/example/course/api/list"

2.1 老师快速使用

老师通常按下面的顺序取数:

  1. 浏览器登录 OJ 后访问 /course/api/list,取得课程 id。
  2. 访问 /course/api/detail/:cid,取得章节 id 和关联作业结构。
  3. 查看班级整体情况时调用反馈 overview;筛选学生时调用反馈 students;需要逐题证据时再调用单生详情。
  4. 生成一周学习报告时先调用 /course/api/v1/weekly-report/students?q=... 找到学生,再按返回的 uid 调用周报详情。

课程页面上的进度摘要、章节 HTML 和人员选择属于内部展示接口。外部报表优先使用反馈与周报 API,避免依赖 HTML 字段。

仓库提供只读验收脚本,密码通过环境变量传入,结果只输出 HTTP、响应结构、耗时与并发错误数:

COURSE_API_USERNAME="teacher" COURSE_API_PASSWORD="..." \
node scripts/verify-course-api-live.mjs \
  --base-url="https://oj.example.com" \
  --concurrency=30

3. 课程列表

GET /course/api/list?page=1&q=CSP
参数 默认值 说明
page 1 正整数页码;每页数量读取系统设置 pagination.course
q 空 按课程标题搜索;少于 2 个字符时按标题前缀匹配

普通用户只会看到自己拥有、班级被分配或未限制班级的课程;原生教师按教师权限查看。

{
  "courses": [
    {
      "id": "<cid>",
      "title": "CSP-J 基础",
      "tag": ["循环", "数组"],
      "group": "CSP-J",
      "cover": "/file/course-cover.webp",
      "attend": 32,
      "sectionCount": 12,
      "beginAt": "2026-08-01T00:00:00.000Z",
      "penaltySince": null
    }
  ]
}

4. 课程详情

GET /course/api/detail/:cid

用于读取课程基本信息、章节顺序和每个章节关联的三类作业。章节中的 homeworks 按 tid1、tid2、tid3 组织;考试章节通常只使用 tid1。

{
  "course": {
    "id": "<cid>",
    "title": "CSP-J 基础",
    "tag": ["循环", "数组"],
    "attend": 32
  },
  "sections": [
    {
      "id": "<csid>",
      "title": "循环结构",
      "pin": 1,
      "type": "normal",
      "homeworks": {
        "tid1": {
          "id": "<tid>",
          "title": "📝 课堂练习",
          "problemCount": 6
        }
      }
    }
  ]
}

已删除、缺失或当前账号无权读取的题目不会计入 problemCount。

5. 页面轻量展示 API

这三条接口供课程详情页局部更新使用,均返回 Cache-Control: private, no-store。浏览器端只在当前页面会话中使用 15~60 秒的内存缓存,权限判断不会跨请求缓存。

5.1 课程进度摘要

GET /course/api/v1/courses/:cid/summary
GET /course/api/v1/courses/:cid/summary?uid=123

uid 省略时查询自己。教师只能查询授权范围内学生。响应中的 sections[] 包含 csid、total、done、visible 和 percent;成绩未公开时 done、percent 为 null。

5.2 单章节展示

GET /course/api/v1/courses/:cid/sections/:csid/view
GET /course/api/v1/courses/:cid/sections/:csid/view?uid=123

响应包含授权后的章节 html、首刷控件数据 practice、当前章节 summary 和是否存在待评测记录的 pending。本接口只读取当前章节及解锁所需前置条件。

5.3 课程人员分页

GET /course/api/v1/courses/:cid/students?group=班级组名&page=1&q=学生名
参数 默认值 说明
group 空 当前账号可查看的小组;课程页面要求老师先选组再请求
page 1 正整数页码,每页固定 50 人
q 空 在所选组内按用户名、显示名或 UID 搜索,最长 100 字符

返回 students[]、page、pageSize 和 hasMore。课程页面滚动到底时才请求下一页,切换小组会清空原姓名搜索。

6. 章节成绩分析(敏感兼容接口)

GET /course/api/analysis/:cid/:csid
GET /course/api/analysis/:cid/:csid?withCode=true
GET /course/api/analysis/:cid/:csid?withHistory=true&withCode=true
参数 默认值 说明
withCode false 返回最终采用记录的代码、语言、编译错误和测试点摘要
withHistory false 返回全部尝试历史;单次最多读取 5000 条记录

权限与限制:

  • 原生教师和授权校区查看者可以读取授权范围内的成绩。
  • 只有原生教师可以真正启用 withCode、withHistory;校区查看者传入后仍会被强制关闭。
  • 单次最多读取 500 名报名学生。
  • historyTruncated=true 表示提交历史达到 5000 条上限,调用方不得把响应描述为完整历史。
  • 学生、班级、校区、记录 ID、代码和测试点均属于敏感教学数据。
{
  "course": { "title": "CSP-J 基础" },
  "section": { "id": "<csid>", "title": "循环结构" },
  "homeworks": [],
  "students": [],
  "campuses": [],
  "campusGroups": [],
  "historyTruncated": false,
  "generatedAt": "2026-08-25T00:00:00.000Z"
}

每名学生的 homeworks.*.problems 可能包含:

  • firstAttempt:首刷用时、得分、满分、状态、记录 ID 和提交时间;
  • 当前最终 status、score、maxScore、scorePercent、submitCount、bestRid;
  • withCode=true 时的 code、lang、error、testCases;
  • withHistory=true 时的 history。

新工具优先使用下面的教学反馈 v1。只有需要代码或完整提交诊断时才调用本接口。

7. 教学反馈 API v1

基础路径:

/course/api/v1/courses/:cid/sections/:csid/feedback

7.1 通用约定

  • schemaVersion 当前为 1.0;v1 内只增加可选字段,不改变已有字段含义。
  • 比率以百分数返回,例如 72.5 表示 72.5%。
  • 没有有效分母时比率为 null,不会把“没有数据”伪装成 0。
  • 时间使用 ISO 8601,用时使用毫秒。
  • 缺失或无权查看的题目不进入题数、完成率和反馈统计。
  • 单次最多分析 1000 名授权范围内的报名学生。meta.enrollmentTruncated=true 时不得表述为完整全班统计。
{
  "generatedAt": "2026-08-25T00:00:00.000Z",
  "enrollmentLimit": 1000,
  "enrollmentTruncated": false,
  "analyzedStudentCount": 32,
  "rateUnit": "percent",
  "thresholds": {
    "lowFirstAttemptRate": 60,
    "recoveryGain": 30,
    "repeatedUnsolvedAttempts": 3
  }
}

7.2 章节反馈概览

GET /course/api/v1/courses/:cid/sections/:csid/feedback/overview
GET /course/api/v1/courses/:cid/sections/:csid/feedback/overview?group=班级组名

主要返回:

  • summary:学生数、开做数、提交数、完成人数、待关注人数、完成率、首刷得分率、最终得分率、平均尝试次数和平均首刷用时;
  • homeworks[].problems[]:逐题开做率、提交率、通过率、平均尝试次数、首刷覆盖率和得分率;
  • groups、campuses:当前账号可筛选的范围,不返回成员 UID 列表。

group 只能使用当前账号可查看的课程班级组名。

7.3 学生反馈列表

GET /course/api/v1/courses/:cid/sections/:csid/feedback/students
参数 默认值 限制 说明
page 1 正整数 页码
limit 50 最大 100 每页人数
group 空 当前账号授权组名 只统计该班级/组
signal 空 见标签表 只返回具有该标签的学生
q 空 最长 100 字符 按用户名或 UID 搜索

响应包含 summary、homeworks、students、pagination、filters 和 meta。列表中的 homeworks 只返回每类作业的题数与完成计数,不展开逐题证据。

7.4 单个学生反馈详情

GET /course/api/v1/courses/:cid/sections/:csid/feedback/students/:uid

只有该学生已报名当前课程且位于调用者授权范围内时才返回。student.homeworks.*.problems 展开逐题首刷与当前最终结果,但仍不返回代码、完整历史或用户隐私字段。

若 Hydro 作业状态尚未异步写回但首刷快照已存在,final.source 为 firstAttemptFallback;正常读取作业状态时为 contestStatus。

7.5 确定性反馈标签

code 含义 判定口径
NOT_STARTED 尚未开始 已布置题目但没有首刷或提交
INCOMPLETE 尚未完成 已开始但没有通过全部题目
LOW_FIRST_ATTEMPT 首刷掌握不足 有记录的首刷总得分率低于 60%
REPEATED_UNSOLVED 反复尝试仍未通过 至少一题提交 3 次仍未通过
RECOVERED_AFTER_PRACTICE 订正后提升明显 同一批有首刷记录的题,最终得分率比首刷提高至少 30 个百分点
COMPLETED 已完成全部题目 所有有效题目均已通过

标签携带中文 evidence,只能作为教师复核线索,不能直接当作对学生的自动评价。

推荐调用顺序:

  1. 请求 overview,确认统计范围、人数与 enrollmentTruncated。
  2. 请求 students?signal=... 筛出需要关注或明显进步的学生。
  3. 只对需要写反馈的学生请求单人详情。
  4. AI 根据事实起草文字;最终反馈必须由教师审核。

8. 学生周报 API v1

GET /course/api/v1/weekly-report/students?q=学生名&limit=20
GET /course/api/v1/weekly-report/students/:uid?start=2026-09-01&end=2026-09-08

搜索接口返回授权范围学生的 uid、名称、小组和可见课程数。详情接口返回指定自然周的课程、章节、作业、提交事实、首次提交通过率、课程首刷通过率和训练候选。

start、end 使用 YYYY-MM-DD,end 不包含在统计范围内,最长 31 天。includeCode=true 只允许原生教师,并会返回敏感源代码;普通数据访问不要开启。完整字段和统计口径见 API_WEEKLY_REPORT.md。

9. 首刷开始(页面内部写接口)

POST /course/:cid/s/:csid/practice/:tid/:pid/start

请求体为空。成功响应:

{
  "ok": true,
  "problemUrl": "/p/1001?tid=<tid>",
  "state": {}
}

业务拒绝通常仍返回 HTTP 200:

{
  "ok": false,
  "message": "请先完成前一类练习。"
}

该接口会参加关联作业并写入首刷进度,不是通用第三方 API。调用方必须同时检查 HTTP 状态与 ok,且不得用 GET、预加载或批量探测触发它。

服务端依次校验:登录、课程可见性、学生报名、章节归属、普通 homework、题目归属、三槽解锁和作业时间。教师仍需满足本人前一槽完成条件,但不受学生报名和课程开放时间限制。

10. 错误与完整性判断

HTTP 状态 常见原因
400 参数格式错误、未知反馈标签、查询文本过长
403 未登录、没有成绩权限、越权访问课程、班级或学生
404 路由未部署,或课程、章节不存在,或章节不属于课程

调用方必须检查:

  • HTTP 状态码和 Content-Type;
  • 首刷接口的 ok;
  • 反馈 v1 的 meta.enrollmentTruncated;
  • 旧 analysis 的 historyTruncated。

仅返回 JSON 不代表业务成功,也不能用登录跳转或静态路由检查代替真实授权账号验收。

11. 版本与现网核验

  • 本文档以当前工作区源码路由为准。
  • 某个部署环境是否包含全部接口,应通过登录后的真实 HTTP 请求逐项核验。
  • 2026-08-25 的历史 qonnwolf 核验结果见 API_CHECK_REPORT_2026-08-25.md;每次发布以当次登录态验收结果为准。