在线测验、随堂考试、认证答题这类场景,对可靠性的要求比普通表单高得多:考生答到一半网络断了,答案不能丢;有人改电脑本地时间想拖延,系统不能被绕过;考试到点必须收卷,不能有人继续作答;网络抖动重试,也不能交上去两份。这篇给一套以服务端时间为唯一准绳的断点续答与强制交卷方案。
先把风险点摆清楚,方案逐条对应:
这些问题的共同根源是“把时间和状态的裁判权交给了不可信的客户端”。核心原则只有一句:时间以服务端为准,答案以服务端暂存为准,交卷以服务端状态为准,客户端只负责采集和展示。
考生设备的时钟可以随意修改,倒计时若纯靠本地时间计算,改一下系统时间就能延长作答。正确做法是:开考时由服务端下发本场考试的权威时间信息——服务器当前时间、开考时刻、截止时刻、剩余时长;进入答题后,客户端定期向服务端校时,用“服务端时间差”修正本地倒计时,而不是直接信任本机时钟:
// 记录服务端与本地的时间差,倒计时基于它修正
let serverOffset = 0;
async function syncServerTime() {
const t0 = Date.now();
const { serverNow, deadline } = await fetch("/exam/time?paperId=" + paperId).then(r => r.json());
const t1 = Date.now();
const rtt = t1 - t0;
serverOffset = serverNow + Math.round(rtt / 2) - t1; // 估算并抵消网络往返
return { deadline, now: () => Date.now() + serverOffset };
}
// 是否到点,一律用修正后的时间判断
function remainSeconds(deadline) {
return Math.max(0, Math.round((deadline - (Date.now() + serverOffset)) / 1000));
}即使考生完全断网,也用最近一次校时得到的差值在本地估算,恢复网络后立即再校;是否真正截止,最终仍由服务端在每次接收答案和交卷时判定。
可靠的答题过程采用“三层保存”:本地草稿、增量上报、服务端暂存。
服务端可用两张表承载,一场考试一条主记录、每题一条答案明细:
CREATE TABLE exam_attempt (
attempt_id BIGINT PRIMARY KEY,
paper_id BIGINT NOT NULL,
user_id BIGINT NOT NULL,
status VARCHAR(16) NOT NULL, -- IN_PROGRESS / SUBMITTED / AUTO_CLOSED
start_at DATETIME NOT NULL,
deadline_at DATETIME NOT NULL,
submit_at DATETIME,
UNIQUE KEY uk_user_paper (paper_id, user_id)
);
CREATE TABLE exam_answer (
attempt_id BIGINT NOT NULL,
question_id BIGINT NOT NULL,
answer_text TEXT,
version INT NOT NULL, -- 每次更新递增
updated_at DATETIME NOT NULL,
PRIMARY KEY (attempt_id, question_id)
);断网期间答案先积压在本地队列并标记“待同步”,网络恢复后按题号顺序补传;服务端按版本号决定是否覆盖,旧版本不覆盖新版本,避免乱序到达把新答案冲掉。换设备登录时,直接拉取服务端暂存的答案继续作答,做到断点续答。
到点收卷必须由服务端兜底,不能只靠考生页面上的倒计时。客户端倒计时只用于提前提醒(如剩余5分钟提示)和到点后立即锁定界面;真正的截止发生在服务端:超过截止时刻后,答案上报接口不再接收修改,交卷按已暂存的最后版本生成成绩;对于到点仍未主动交卷的尝试,由定时扫描把进行中的记录批量置为“自动收卷”:
IN_PROGRESS(作答中)→ SUBMITTED(主动交卷)或 AUTO_CLOSED(到点自动收卷),一旦进入终态,答案与成绩不再变更。
这样即使考生卡在断网状态、页面一直没关,恢复后也会发现本场已按服务端暂存内容收卷,无法拖延。
弱网下考生连点“交卷”,或客户端超时重试,都可能发出多个交卷请求。用“交卷令牌+状态机”解决:进入考试时服务端下发一个幂等令牌,交卷请求必须携带;服务端处理时先检查 attempt 状态,只有处于 IN_PROGRESS 才执行收卷并置为终态,已处于终态的重复请求不再重复处理,直接返回第一次的交卷结果。前端在发出交卷后立即把按钮置灰,配合服务端状态判断,双保险杜绝重复交卷和答案覆盖。
几个容易遗漏的边界要提前定规则:答题中途闪退,重进先读本地草稿、再和服务端暂存按版本合并;同一账号在另一台设备登录续答,以服务端暂存为准拉取,并把原设备会话置为失效,避免两端同时写;断网恢复补传时若本场已截止,只接受截止前最后一次有效暂存,超时改动不计;全程把关键动作(开考、每次暂存、交卷、自动收卷)记日志,便于争议时回溯。
建议把在线考试能力抽象成独立服务,统一管理考试时间、作答暂存、交卷收卷三条主线:时间服务负责下发权威时刻与校时;作答服务负责增量暂存、版本合并与续答;收卷服务负责主动交卷、定时自动收卷和幂等。客户端尽量做薄,只承担采集、本地兜底和展示,所有裁决放在服务端。这套模型同样适用于问卷限时提交、在线测评、随堂练习等对时间和可靠性敏感的教育场景。
Q:完全断网到考试结束,答案还能保留吗?
A:断网期间答案先存本地草稿,恢复网络后按版本补传到服务端;若恢复时已到截止时间,以服务端收到的截止前最后一次有效暂存为准收卷。
Q:到点自动收卷和主动交卷有区别吗?
A:结果都是形成终态成绩,区别只在来源:主动交卷由考生触发,自动收卷由服务端到点扫描触发,终态后都不再接受修改。
Q:怎么防止考生改本地时间?
A:不把本地时间作为依据,倒计时用服务端校时差值估算,能否作答和交卷一律由服务端按其时钟判定。
在线考试可靠与否,关键在于别把裁判权交给客户端:时间、答案、交卷都以服务端为准,客户端只做兜底采集。把校时、暂存续答、强制收卷和幂等这四件事做扎实,断网、闪退、改时间、重复交卷就都不会让一场考试失控。本文为后端工程实践分享,具体实现以业务规则为准。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。