From d44d466a90f374f884733c5daebe62ff59cfccff Mon Sep 17 00:00:00 2001 From: qubiaobiao <3294694717@qq.com> Date: Wed, 30 Sep 2026 15:46:43 +0800 Subject: [PATCH] =?UTF-8?q?fix(exam):=20=E8=80=83=E8=AF=95=E7=BB=84?= =?UTF-8?q?=E5=B7=B2=E4=BA=A4=E5=8D=B7=E7=A6=81=E6=AD=A2=E5=86=8D=E8=BF=9B?= =?UTF-8?q?=E5=85=A5=E4=B8=8E=E9=87=8D=E5=A4=8D=E6=8F=90=E4=BA=A4=EF=BC=88?= =?UTF-8?q?4006=20=E9=97=A8=E6=8E=A7=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/controller/exam.js | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/app/controller/exam.js b/app/controller/exam.js index aadc974..7b695a5 100644 --- a/app/controller/exam.js +++ b/app/controller/exam.js @@ -216,6 +216,20 @@ class ExamController extends Controller { return; } + // 🔴 已正式交卷禁止再次进入(考试组一次性答题,测试反馈 #3)。 + // 这里是「进入考试」的真实门控:题目经此下发,拒绝即等于无法进入答题界面, + // 前端按钮置灰只是 UX 层,绕过前端直调本接口同样被拦。 + // hasSubmitted 仅在 exam_submissions.submit_time 落库后为 true ⇒ + // 未交卷的意外退出(断网/断电)不受影响,仍可重进恢复作答。 + // 仅约束考试组;本科/教师走作品提交,不在此限制。 + if (group === 'zhongzhi' || group === 'gaozhi') { + const alreadySubmitted = await service.exam.hasSubmitted(aggregation.aggregation_uid, group); + if (alreadySubmitted) { + ctx.body = ctx.helper.error(4006, '您已提交试卷,无法再次进入考试'); + return; + } + } + // 团队锁:同一小组只能一人进入正式考试(原子检查+占位,G1 修复) const teamGate = await this._enterExamWithTeamLock(aggregation); if (!teamGate.allowed) { @@ -852,6 +866,19 @@ class ExamController extends Controller { } } + // 🔴 窗口内重复交卷拦截(考试组,测试反馈 #3)。 + // 背景:下方窗口内分支会重新校验请求体答案并覆盖写库 ⇒ 已交卷者若可重进, + // 即可「改答案 → 再交卷」无损刷分。入口 questions() 已拦重进,此处再堵住 + // 绕过前端直接重放 submit-all 的路径。 + // ⚠️ 只作用于窗口打开时;窗口关闭后走上方结算通道(那是补写标记,不是重新作答)。 + if (windowOpen && (group === 'zhongzhi' || group === 'gaozhi')) { + const alreadyDone = await service.exam.hasSubmitted(aggregation.aggregation_uid, group); + if (alreadyDone) { + ctx.body = ctx.helper.error(4006, '您已提交试卷,不能重复提交'); + return; + } + } + // 团队锁:同一小组只能一人进入正式考试(G1 修复,防绕过 questions 直接提交) // ⚠️ 结算通道跳过:一是窗口已关闭不应再产生 setTeamLock 写副作用, // 二是结算绝不能被队友残留锁拦下(否则又回到「答案在库却交不上」)。