# 接口一览 所有接口统一走 `api.php?action=`,响应为 JSON: ```jsonc // 成功 { "ok": true, "data": { ... } } // 失败 { "ok": false, "error": "错误信息" } ``` - 未登录时返回 `{ "ok": false, "error": "...", "auth": false }`,前端会清会话并跳登录页; - **写操作**必须在 JSON 体里带 `_csrf` 字段(或 `X-CSRF-Token` 头), 令牌从 `config_get` 接口获取。 ## 写操作白名单 `config_save`、`roster_save`、`roster_import`、`roster_clear`、 `snapshot_save`、`snapshot_delete`、`unmatched_map`、`project_config_save` ## 接口清单 ### 认证与配置 | Action | 方法 | 参数 | 说明 | | --- | --- | --- | --- | | `auth_check` | GET | — | 是否已登录 | | `config_get` | GET | — | 返回配置(密码不回显)+ `csrf_token` + `storage` / `db_driver` / `db_connected` | | `config_save` | POST | JSON 配置对象 | 保存;返回 `conn_changed` 与 `ping` 结果 | | `config_test` | POST | 连接信息 | 测试连接,不落库 | | `ping` | GET | — | 探测卷王是否可达 | | `diag` | GET | — | 诊断信息(存储后端、DB 状态等) | > **只有连接信息(地址 / 前缀 / 账号 / 密码 / 证书校验)变化时**,`config_save` 才会清除令牌并重新登录。 ### 数据 | Action | 方法 | 参数 | 说明 | | --- | --- | --- | --- | | `projects` | GET | — | 卷王项目(问卷 / 考试)列表 | | `data` | GET | `projectId`、`nameQid`、`force` | 主系统数据:统计、成绩行、名单、缺考、未匹配、异常 | | `dashboard` | GET | `projectId`、`nameQid`、`force` | 按班级 / 组别聚合的看板数据 | `data` 响应关键字段: ``` stats { total, submitted, absent, rate, avg, pass_rate, max, min, avg_duration } rows[] { sid, name, cls, grp, score, best_score, attempts, attempts_list[], submit_ts, submit_at, last_submit_ts, duration, duration_txt } roster[] { sid, name, cls, grp, login } absent[] 缺考名单 unmatched[] 未匹配答卷 anomalies { fast_high[], frequent[], identical[] } project_cfg 本卷配置 _cache { cached, at } ``` ### 名单 | Action | 方法 | 参数 | 说明 | | --- | --- | --- | --- | | `roster` | GET | — | 名单与元信息 | | `roster_import` | POST | `multipart`:`file`、`mode=replace\|append` | 导入(扩展名白名单 + 随机名 + 解析后即删) | | `roster_save` | POST | JSON `{ students: [...] }` | 全量替换(DELETE + INSERT) | | `roster_clear` | POST | — | 清空 | | `unmatched_map` | POST | JSON `{ name, sid, cls, grp, login }` | 未匹配答卷补入名单(学号已存在则更新姓名) | ### 每卷配置 | Action | 方法 | 参数 | 说明 | | --- | --- | --- | --- | | `project_config` | GET | `projectId` | 返回 `cfg`、`effective`(生效值)、`global`(全局默认) | | `project_config_save` | POST | JSON `{ full_score, pass_rate, exempt[] }` | 留空/0 = 清除该项,回落全局 | ### 快照 | Action | 方法 | 参数 | 说明 | | --- | --- | --- | --- | | `snapshot_save` | POST | `projectId`、JSON `{ label }` | 保存当前成绩快照 | | `snapshot_list` | GET | `projectId` | 列表 | | `snapshot_delete` | POST | `projectId`、JSON `{ file }` | 删除 | | `snapshot_trend` | GET | `projectId`(可逗号分隔多个,跨卷对比) | 趋势 | ### 导出 | Action | 方法 | 参数 | 说明 | | --- | --- | --- | --- | | `export` | GET | `projectId`、`cls`、`grp`、`mode=latest`、`split=cls`、`sort`、`cols` | 成绩明细 `.xlsx` | | `export_absent` | GET | 同上 | 缺考名单 `.xlsx` | | `draw_export` | GET | `type=import\|list` | 白名单导入模板 / 白名单列表 | `sort` 取值:`time` / `sid` / `score_desc` / `score_asc`。 ## 前端调用约定 ```js // 读 const d = await api('data', { params: { projectId, force: 1 } }) // 写:api() 会自动注入 _csrf await api('roster_save', { json: { students } }) ``` ## 相关 - [架构与目录](./architecture.md) - [测试](./testing.md)