feat(exam/auth): 登录闸门 + 交卷稳定性加固(R4-R8)

R4 登录/交卷背压闸门
- app/middleware/concurrency_guard.js: 新增。超限即 429 + Retry-After(立即拒绝式,
  不排队;实测 429 P50=34ms)。RETRY_AFTER 收敛为单一有依据常量 = '2'
  (≈1.8 个满载窗口;实测 bcryptjs 1129ms / 原生 265ms 每窗口)。
  配额:LOGIN_MAX=16 / SUBMIT_MAX=24 / HEAVY_REQ_MAX=20,各 *REQ_MAX 覆盖。
- config/config.default.js: 闸门配额与连接池容量配置;DB dialect 锁 mysql。

R7 顺序红线 + R8 Retry-After 同源 + 原生 bcrypt
- 3 处硬编码 '2' 收敛为单一常量;补"勿因换实现而删闸门"红线注释。
- bcryptjs -> bcrypt@6(N-API,Windows/Linux 均免编译;drop-in,存量哈希 $2b$10$ 兼容)。
- app/model/account.js / app/service/auth.js: 同步哈希实现注释(D4 全族 5 处陈旧
  SHA256 注释修正)。

答题链路与综合大题
- app/controller/exam.js: 中职/高职综合大题接口(GET big-question / POST big-answer,
  逐问 2000 字上限校验);uploadWord 对中职/高职封堵(400)。
- app/service/exam.js: 抽题快照 question_snapshot 落库、答案归集键修正。
- app/controller/auth.js / app/service/auth.js: toClientUser() 唯一出口,
  未发布态按组剔除 score,exam_score/word_score/ppt_score 任何情况下不下发。
- app/router.js: 新增大题与管理端路由。
- app/extend/helper.js: 下载文件名 RFC5987 编码(禁双重编码)。
- app.js: 池容量断言按 env 区分单/多 worker(防 dev 假日志)。

验证:ci-gates.sh 9/9 全绿;verify_login_guard.py 28/28(含 D 组变异 9/9);
真实登录/抽题/交卷端到端 code=0;前端构建 BUILD_RC=0。
This commit is contained in:
2026-09-22 09:16:55 +08:00
parent 8ef18da395
commit 23604f2718
12 changed files with 535 additions and 91 deletions

View File

@@ -190,7 +190,7 @@ class AuthController extends Controller {
* 1. 校验身份证号、手机号、短信验证码
* 2. 通过身份证号查找选手
* 3. 检查账号是否已激活
* 4. SHA256 哈希密码,更新 activated=1
* 4. bcrypt 加盐哈希密码(BCRYPT_ROUNDS=10,见 extend/helper.js),更新 activated=1
* 5. 清除 session 中的短信验证码
*/
async activate() {
@@ -263,7 +263,8 @@ class AuthController extends Controller {
* 流程:
* 1. 通过手机号查找选手
* 2. 检查账号是否激活
* 3. SHA256(输入密码) 与数据库密码比对
* 3. bcrypt 比对密码(helper.comparePassword;兼容存量 SHA256 旧哈希,
* 并在验证成功后透明升级为 bcrypt)
* 4. 生成 JWT Token,返回用户信息和 Token
*/
async login() {
@@ -324,7 +325,7 @@ class AuthController extends Controller {
*
* 流程:
* 1. 通过用户名从 admins 表查找管理员
* 2. 密码比对(SHA256)
* 2. bcrypt 比对密码(兼容存量 SHA256 旧哈希)
* 3. 生成管理员 JWT Token
*/
async adminLogin() {