Files
ny-exam-platform/docs/guide/scores.md
T
nxxy335 a3be81b261 fix(security): 修复上传名单文件可被 URL 直连访问(nginx 下可 getshell)
严重漏洞:roster_import 使用 $_FILES 的原始扩展名拼 roster_YYYYMMDD_HHMMSS.<ext>
落在 Web 根目录内的 data/uploads/,既无扩展名白名单(可上传 .php),文件名又
可按秒枚举;而 bootstrap 只写了 data/.htaccess,nginx 不读 .htaccess,
生产环境(nginx + PHP-FPM)下可直接远程代码执行。

修复(api.php roster_import 四层防护):
- 扩展名白名单:仅 xlsx / xlsm / csv / txt
- 随机文件名:bin2hex(random_bytes(16)),杜绝枚举
- 解析完成后 @unlink 立即删除,Web 目录下不留存任何上传文件
- 20MB 大小限制 + UPLOAD_ERR 检查

加固(bootstrap.php):data 及 cache/uploads/snapshots 每个目录都写
.htaccess(Deny from all)+ index.html 占位;README 与文档补充 nginx
location ^~ /data/ { deny all; return 404; } 规则。

测试:新增 e2e tools/test-upload-guard.js(11 断言,覆盖 .php/.phtml 被拒、
无残留、合法 csv 导入成功且即删、无可枚举文件名),并入 tests/run-all.sh。
全套 90 项通过。

docs: 新增 VitePress 文档站(18 页)
- 使用指南 12 页:简介/部署/快速上手/系统设置/名单管理/统计口径/试卷设置/
  快照趋势异常/导出打印/数据看板/随机抽取/常见问题
- 安全 2 页:安全总览(含本次漏洞复盘)与部署加固清单(P0-P2)
- 开发 4 页:架构与目录/接口一览/SQLite 存储/测试
- 配图迁移至 docs/public/images/;.gitignore 忽略文档构建产物
2026-09-10 22:42:13 +08:00

3.6 KiB
Raw Blame History

成绩明细与统计口径

这一页说明每个数字到底是怎么算出来的,以及表格里的每一列是什么含义。

统计卡片

卡片 计算方式
名单总人数 名单条目数;被当前试卷设为免考的人不计入
已提交 匹配到名单、且提交了答卷的人数(去重后的代表成绩行数)
缺考 max(0, 名单总人数 − 已提交)
提交率 已提交 ÷ 名单总人数 × 100%
平均分 所有有成绩的答卷成绩平均值;无成绩者不计入分母
最高分 / 最低分 同上,取自所有有成绩的答卷
合格率 成绩 ≥ 满分 × 合格率阈值 的人数 ÷ 已提交人数。阈值全局默认 60%,可在 试卷设置 针对单卷覆盖
平均用时 所有有时长的答卷时长平均值(秒),格式化为「X 分 Y 秒」

::: tip 卡片标题会跟着配置变 合格率卡片的标题会实时显示当前阈值,例如「合格率 (≥70%满分)」,避免你误以为还是 60%。 :::

班级 / 组别联动

选择顶部 班级 下拉后:

  • 全部统计卡片只统计该班级;
  • 成绩明细只显示该班级;
  • 缺考名单只显示该班级,标题右侧计数同步变化;
  • 切换班级是本地即时重算,不会额外请求服务器。

再选 组别 可做二次切片。

成绩明细列

列 说明
学号 / 姓名 / 班级 / 组别 来自名单;未匹配到的答卷不会进入本表
成绩 卷王返回的 examScore;该问卷不是考试模式时为 -
考试次数 该学生全部提交次数,与「去重」开关无关
提交时间 最近一次提交时间(重考低分也会让这行按最新时间排序到最上面)
考试时长 最近一次答卷的时长;后台手动录入等没有起止时间的答卷为 -

点击任意一行可查看该学生的每一次提交(时间、成绩、时长),最高分有标记。

排序与搜索

  • 点击表头即可排序,支持学号 / 姓名 / 班级 / 组别 / 成绩 / 考试次数 / 提交时间 / 时长;
  • 默认排序键是最近提交时间,所以刚交卷的人一定在最上面;
  • 搜索框支持学号 / 姓名模糊匹配,200 ms 防抖;
  • 成绩明细无分页,采用虚拟滚动,几千行也不卡;页面切到后台时自动暂停刷新。

新数据高亮

每次刷新时,与上一次相比新出现的提交(按学号判断)整行显示绿色底纹, 30 秒后自动清除;也可以点 清除高亮 立刻去掉。首屏加载不标记。

去重策略

设置里的「同一学号去重」只决定代表成绩取哪一份(最新 / 最高):

开关 成绩明细 考试次数 点击行内的历史
开启 一人一行 仍是全部次数 仍是全部提交
关闭 一人多行 仍是全部次数 仍是全部提交

best_score 字段始终是跨全部尝试的真实最高分。

未匹配答卷

提交了、但姓名不在名单中的答卷会单独成面板。常见原因:

  • 姓名前后有空格、写了别名、用了繁体字;
  • 姓名题选错了(到 姓名字段 下拉里手动指定);
  • 名单里根本没有这个人。

可直接在面板里点 加入名单 补录。

缺考名单

名单中有、但未提交的人。支持 复制学号(逗号分隔)、复制姓名、打印。

相关