Files
ny-exam-platform/README.md
T

128 lines
8.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# NY 问卷考试系统数据平台
基于 **PHP 8 + 卷王问卷系统(SurveyKing)社区版开放 API** 的实时数据统计平台。
选择某个问卷 / 考试后,自动拉取答卷 → 用「姓名」关联学号名单 → 按
**学号、姓名、成绩、提交时间、考试时长** 依次排列展示,并支持定时自动刷新。
## 功能
| 功能 | 说明 |
| --- | --- |
| 问卷/考试切换 | 下拉选择,实时显示该问卷的答卷数据 |
| 学号自动匹配 | 用答卷中的「姓名」题在学号名单中查找对应学号(自动识别姓名题,也可手动指定) |
| 实时刷新 | 5 秒 / 10 秒 / 15 秒 / 30 秒 / 60 秒 / 2 分钟可选,数据变化的行会高亮闪烁;**每次刷新后新增的提交以绿色底纹标出** |
| 统计卡片 | 名单总人数、已提交、缺考、提交率、平均分、**合格率**(≥60% 满分)、最高分、最低分、平均用时;**选择班级后,全部卡片只统计该班级** |
| 缺考名单 | 名单中有、但未提交的人 |
| 未匹配答卷 | 提交了但姓名不在名单中的答卷,方便排查 |
| 名单导入 | 上传 `.xlsx` / `.csv`,自动识别「学号」「姓名」「班级」列(班级可选);支持覆盖 / 追加、手动增删改 |
| 导出 | 成绩明细 CSV(学号/姓名/班级/成绩/提交时间/考试时长)、缺考名单 CSV(学号/姓名/班级) |
| 排序 / 班级 | 点击表头排序,**成绩明细默认按提交时间降序**;支持学号/姓名/班级/成绩/提交时间/时长排序;按班级下拉筛选 |
| 全屏 / 自适应 | 右上角「全屏」按钮进入全屏(布局自动铺满、表格更高);页面适配手机 / 平板 / 桌面三端 |
## 环境要求
- PHP ≥ 8.0(需启用 `curl`、`json`、`mbstring`、`openssl`(可选,用于 RSA 加密密码);`zip` 可选,无 zip 扩展时会用内置解析器读 xlsx)
- 无需数据库、无需 Composer 依赖,数据以 JSON 存放在 `data/`
## 部署
### 方式一:PHP 内置服务器(最快体验)
在本目录执行:
```bash
php -S 0.0.0.0:8080
```
浏览器打开 <http://localhost:8080>。
Windows 也可双击 `启动平台.bat`。
### 方式二:放到 PHP 站点目录
把整个 `ny-exam-platform` 目录复制到 Apache / Nginx + PHP-FPM 的站点目录下,
确保进程对 `data/` 目录有写权限即可。
## 首次配置
打开页面 → 右上角 **设置**:
| 配置项 | 示例 | 说明 |
| --- | --- | --- |
| 卷王系统地址 | `https://data.exam.nxxy335.top` | 卷王后台地址(默认值),结尾不要带 `/` |
| 接口前缀 | `/api` | 社区版默认 `/api`;企业版可填 `/admin-api` |
| 登录账号 | `admin` | 卷王后台管理员账号 |
| 登录密码 | `123456` | 卷王后台密码 |
| 姓名题关键词 | `姓名,名字,学生姓名` | 用于自动识别哪道题填的是姓名 |
| 满分 | `100` | 用于合格率;**每套试卷可单独设置**——保存时作用于当前选中的试卷,未选试卷时作为所有试卷的默认值 |
| 校验 HTTPS 证书 | 默认勾选 | 访问 https 卷王时校验证书;自签名证书可取消勾选 |
| 默认刷新间隔 | `15` | 秒 |
填完点 **测试连接**,显示「连接成功」后点 **保存**。
> 登录流程(社区版 v1.13):`GET /api/system` 取 RSA 公钥 → 公钥加密密码 →
> `POST /api/public/login` 换取 JWT。
> **注意**:v1.13 服务端的 `JwtTokenFilter` 只从 Cookie `sk-token` 读取令牌(**不读 `Authorization` 头**),
> 所以登录后程序会把 `Set-Cookie` 里的 `sk-token` 原样带在后续每个请求的 `Cookie` 头里(同时也带 `Authorization` 头以兼容旧版)。
> 程序已内置该流程与令牌缓存,并向下兼容 `/system/auth/login`、`/admin-api/oauth2/token` 两种登录方式。
**关于 401 "Full authentication is required to access this resource"**
若你用自己写的客户端对接卷王社区版 v1.13,即使登录成功、拿到了 JWT,调用 `/project/list` 等接口仍可能返回 401。
原因是 v1.13 服务端只认 Cookie 里的 `sk-token`,不认 `Authorization` 头。务必在登录响应里取 `Set-Cookie: sk-token=...`,
并在后续每个请求带上 `Cookie: sk-token=<token>`。本平台已处理此细节。
## 学号名单
- **平台启动时不预设任何名单**,请通过右上角 **名单管理** 上传你自己的名单(`.xlsx` / `.csv`)。
- 名单文件可包含「学号」「姓名」两列,**「班级」为可选列**;含班级时,成绩明细会显示班级列并支持按班级筛选(一份考试涉及多个班级时很实用)。
- 上传方式:名单管理 → 点击或拖拽文件 → 选择覆盖 / 追加 → **保存名单**;也可手动增 / 删 / 改。
- 名单数据保存在 `data/roster.json`。
## 目录结构
```
ny-exam-platform/
├─ index.php 主页面
├─ api.php 后端接口(代理卷王 API)
├─ bootstrap.php 配置 / 公共函数 / 自动加载
├─ lib/
│ ├─ SurveyKingClient.php 卷王 API 客户端(登录、令牌缓存、项目、答卷)
│ ├─ DataService.php 数据组装(姓名→学号匹配、统计、排序)
│ ├─ Roster.php 学号名单管理(xlsx/csv 导入与持久化)
│ └─ XlsxReader.php 零依赖 XLSX 读取器
├─ assets/ 前端样式与脚本
├─ data/ 配置、名单、令牌缓存(需可写)
└─ tools/ 开发辅助(PHP 语法校验脚本)
```
## 统计口径与显示说明
- **班级联动统计**:顶部「班级」下拉选择某一班级后,统计卡片(总人数 / 已提交 / 缺考 / 提交率 / 平均分 / 合格率 / 最高分 / 最低分 / 平均用时)与成绩明细、缺考名单都只统计该班级;选「全部班级」则统计整体。切换班级在本地即时重算,不额外请求服务器。
- **面板计数随班级**:选择班级后,成绩明细标题右侧的「共 X 人已提交 · 名单 X 人」、缺考名单标题右侧的「共 X 人」也都只统计该班级(不再用全部班级的数据)。
- **每套试卷独立满分**:不同试卷满分不同,可在「设置 → 满分」中按当前选中的试卷单独设置,保存后仅作用于该试卷;未选中试卷时设置的满分作为所有试卷的默认值。合格率按「成绩 ≥ 满分 × 60%」计算。
- **合格率**:成绩 ≥ 满分 × 60% 视为合格(满分见上)。
- **平均用时不换行**:平均用时(如「9 分 55 秒」)强制单行显示,避免「秒」字换行。
- **成绩明细不再显示每人的「状态」列**:缺考人员统一在「缺考名单」面板查看,成绩明细只保留学号 / 姓名 / 班级 / 成绩 / 提交时间 / 考试时长。
- **新增数据标记**:每次刷新后,相较上一次新出现的提交(按学号判断)整行显示绿色底纹,方便一眼看出谁刚交卷;首次加载不标记。
## 常见问题
**1. 页面提示"无法连接卷王系统"**
检查:地址端口是否正确、服务器防火墙是否放行、接口前缀是否为 `/api`、账号密码是否正确。
可直接在服务器本机执行 `curl http://127.0.0.1:1991/api/system` 验证。
**2. 读到了答卷但学号为空**
说明答卷里的姓名与名单不一致。处理:
- 在「姓名字段」下拉里手动指定正确的题目;
- 或到「设置」里补充姓名题关键词;
- 未匹配的答卷会显示在「未匹配答卷」面板,可据此核对姓名是否写错(如多了空格)。
**3. 成绩列全是 `-`**
说明该问卷不是考试模式或没有开启自动评分,卷王未返回 `examScore`。
**4. 考试时长显示 `-`**
答卷的 `metaInfo.answerInfo` 中没有开始/结束时间(例如是后台手动录入的答卷)。
**5. 想让同一学号只保留最新提交**
设置里勾选「同一学号只保留最新一份答卷」(默认开启);取消勾选则保留全部提交记录。