课评: 转出学生归档(王子墨/张立坤/黄俊博)+ 周四班级总结 + 新生章梓皓档案 + C++示例代码

This commit is contained in:
chengzi
2026-09-29 22:43:54 +08:00
parent 6f57dfe67e
commit 73cce4c4f5
116 changed files with 6817 additions and 248 deletions

View File

@@ -0,0 +1,374 @@
# 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`。
浏览器调用示例:
```js
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();
```
命令行调试示例:
```bash
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、响应结构、耗时与并发错误数:
```bash
COURSE_API_USERNAME="teacher" COURSE_API_PASSWORD="..." \
node scripts/verify-course-api-live.mjs \
--base-url="https://oj.example.com" \
--concurrency=30
```
## 3. 课程列表
```http
GET /course/api/list?page=1&q=CSP
```
| 参数 | 默认值 | 说明 |
| --- | --- | --- |
| `page` | `1` | 正整数页码;每页数量读取系统设置 `pagination.course` |
| `q` | 空 | 按课程标题搜索;少于 2 个字符时按标题前缀匹配 |
普通用户只会看到自己拥有、班级被分配或未限制班级的课程;原生教师按教师权限查看。
```json
{
"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. 课程详情
```http
GET /course/api/detail/:cid
```
用于读取课程基本信息、章节顺序和每个章节关联的三类作业。章节中的 `homeworks` 按 `tid1`、`tid2`、`tid3` 组织;考试章节通常只使用 `tid1`。
```json
{
"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 课程进度摘要
```http
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 单章节展示
```http
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 课程人员分页
```http
GET /course/api/v1/courses/:cid/students?group=班级组名&page=1&q=学生名
```
| 参数 | 默认值 | 说明 |
| --- | --- | --- |
| `group` | 空 | 当前账号可查看的小组;课程页面要求老师先选组再请求 |
| `page` | `1` | 正整数页码,每页固定 50 人 |
| `q` | 空 | 在所选组内按用户名、显示名或 UID 搜索,最长 100 字符 |
返回 `students[]`、`page`、`pageSize` 和 `hasMore`。课程页面滚动到底时才请求下一页,切换小组会清空原姓名搜索。
## 6. 章节成绩分析(敏感兼容接口)
```http
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、代码和测试点均属于敏感教学数据。
```json
{
"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
基础路径:
```text
/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` 时不得表述为完整全班统计。
```json
{
"generatedAt": "2026-08-25T00:00:00.000Z",
"enrollmentLimit": 1000,
"enrollmentTruncated": false,
"analyzedStudentCount": 32,
"rateUnit": "percent",
"thresholds": {
"lowFirstAttemptRate": 60,
"recoveryGain": 30,
"repeatedUnsolvedAttempts": 3
}
}
```
### 7.2 章节反馈概览
```http
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 学生反馈列表
```http
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 单个学生反馈详情
```http
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
```http
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`](./API_WEEKLY_REPORT.md)。
## 9. 首刷开始(页面内部写接口)
```http
POST /course/:cid/s/:csid/practice/:tid/:pid/start
```
请求体为空。成功响应:
```json
{
"ok": true,
"problemUrl": "/p/1001?tid=<tid>",
"state": {}
}
```
业务拒绝通常仍返回 HTTP `200`:
```json
{
"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`](./API_CHECK_REPORT_2026-08-25.md);每次发布以当次登录态验收结果为准。