摘要
企业在线考试中经常会遇到这样一个问题:
同一个账号能不能同时在电脑和手机上进入考试?
看起来这只是一个“是否允许重复登录”的权限问题,真正进入正式考试场景以后,却会迅速变成一个典型的并发一致性问题。
假设员工已经在电脑上做到第35题,又使用手机登录同一账号。如果两台设备都允许继续答题:
电脑把第35题修改成A,手机又改成C,最终应该保存哪个?
PC已经断网,手机重新进入,这属于合法的断点续考,还是第二设备抢占?
新设备登录后直接让旧Token失效,会不会导致旧设备最后几道答案来不及保存?
两台设备在最后一秒同时点击交卷,系统会不会生成两次成绩?
Exam Session已经提交,但旧设备还有延迟请求到达,是否还能覆盖最终答卷?
因此,企业在线考试系统不能简单使用“限制一个账号只能登录一次”解决问题。
更完整的技术链路应该是:
User → Device ID → Token → Exam Session → Heartbeat → Session Lock → Answer Version → Submit Idempotency → Audit Log
以宏远培训考试系统的企业考试设计思路为例,真正需要保证的不是“绝对禁止第二台设备打开页面”,而是:
同一名考生、同一场考试,在任何时刻只能有一个合法的考试执行状态;断网可以恢复,换设备可以受控,答案不能乱序覆盖,最终交卷只能成功一次。
一、先看一个真实考试中很容易出现的场景
假设员工张某参加:
2026年度安全知识考试考试时长:
60分钟上午10:00,张某通过公司电脑进入考试。
系统创建:
UserID: U10086 ExamID: E20260809001 ExamSessionID: ES202608090001考试进行到10:25时,张某已经完成35道题。
此时他又在手机浏览器中登录相同账号。
如果系统什么都不限制,就可能形成:
PC ↓ Exam Session A ↓ 继续答题同时:
Mobile ↓ Exam Session B ↓ 也继续答题问题马上就出现了。
这已经不再是:
“一个账号登录了两次。”
而是:
“同一场考试同时存在两个写入源。”
对于正式考试来说,这才是最危险的问题。
二、为什么普通网站的“多设备登录”逻辑不能直接用于考试?
很多普通业务系统允许:
电脑登录 手机登录 平板登录甚至同时保持多个Token。
因为用户在不同设备上的行为通常不会产生严重冲突。
例如:
在电脑查看个人资料;
在手机查看通知。
两边同时在线没有什么问题。
但是考试系统不同。
考试过程中会持续产生:
当前试卷 考试剩余时间 题目答案 答题版本 切屏状态 网络状态 交卷状态 最终成绩这些数据都是:
有状态数据。
如果两个设备同时修改同一个Exam Session,就必须解决并发冲突。
因此,在线考试中的登录控制不能只围绕:
User Login设计。
必须围绕:
Exam Session设计。
三、第一层:User登录态和Exam Session必须分开
一个比较常见的误区是:
Token = 考试Session实际上二者应该是不同概念。
用户Token解决什么?
解决:
你是谁? 登录是否有效? 有没有访问系统的权限?例如:
AccessToken RefreshToken UserIDExam Session解决什么?
解决:
你正在参加哪场考试? 什么时候开始? 使用哪张试卷? 当前在哪台设备考试? 答案保存到哪个版本? 考试是否已经交卷?例如:
ExamSessionID ExamID UserID DeviceID StartTime Deadline Status LatestAnswerVersion SubmitVersion因此:
账号可以处于登录状态,不代表一定允许创建第二个考试Session。
这两个概念必须分开。
四、宏远培训考试系统为什么更应该以Exam Session为核心?
以宏远培训考试系统面向企业正式考试的应用场景为例,一场考试不仅有“员工账号”,还会关联:
考试任务 人员范围 试卷 随机组卷结果 开始时间 结束时间 答题记录 自动保存 交卷记录 成绩 操作日志因此更合理的设计不是:
发现第二个Token → 立即踢掉第一个Token而应该首先判断:
这个用户当前有没有RUNNING状态的Exam Session?例如:
UserID:10086 ExamID:20001 ExamSession:ES001 Status:RUNNING如果第二台设备再次请求:
POST /exam/start系统首先应该查询:
是否存在活动Exam Session?存在的话,就不能轻易再创建:
ES002否则同一名考生可能拥有两张正在运行的考试状态。
五、核心原则:同一用户+同一考试,只允许一个活动Exam Session
数据库层可以建立逻辑唯一约束:
UserID + ExamID + Active Status从业务上保证:
一个用户 + 一场考试 = 一个RUNNING Session例如:
ES202608090001对应:
User:10086 Exam:20001 Status:RUNNING第二台设备进入时,不重新创建考试。
而应该进入:
Resume / Takeover Decision也就是判断:
恢复原Session,还是抢占原Session。
六、Device ID的作用是什么?
为了判断第二次访问来自:
原设备重连还是:
另一台设备可以引入:
Device ID例如:
PC: DeviceID = D-PC-89173 手机: DeviceID = D-MOBILE-31028Device ID可以综合:
客户端生成的设备标识 浏览器实例标识 设备类型 系统类型 浏览器类型但需要注意:
Device ID只能作为设备识别依据之一,不能把它当成绝对可靠的硬件身份证。
Web浏览器中的设备标识可能会因为:
清除缓存 隐私模式 更换浏览器 重新安装发生变化。
所以正式考试系统应该使用:
Device ID + Exam Session + Token + Heartbeat
综合判断。
七、第二台设备进入以后,系统应该有哪些策略?
在线考试系统至少可以支持三种策略。
策略一:考试开始后锁定设备
例如:
第一次进入设备: PC那么整个考试期间:
只允许PC继续考试手机登录后提示:
当前考试已在其他设备进行, 请返回原设备继续考试。优点:
规则最简单,考试控制最严格。
适合:
正式统考 竞赛考试 强防作弊考试缺点也很明显。
如果PC真正发生故障:
电脑死机 浏览器损坏 设备断电考生无法换设备继续考试。
八、策略二:允许换设备,但新设备必须接管原Session
这是一种更灵活的方案。
例如:
PC:
ExamSession: ES001 Device: D-PC手机进入以后,管理员规则允许设备切换。
则系统执行:
确认身份 ↓ 检测现有Exam Session ↓ 申请Takeover ↓ 旧设备Session Lock失效 ↓ 新Device绑定到原Exam Session ↓ 继续原来的Deadline ↓ 恢复原来的试卷和答案注意:
这里非常关键的一点是:
换设备不是重新考试,而是接管原Exam Session。
因此:
StartTime不变 Deadline不变 Paper Snapshot不变 已保存答案不变这就避免了通过换设备:
重新获得考试时间 重新抽一张试卷 绕过原答题记录九、宏远培训考试系统在这里的优势:换设备不等于重新开考
企业正式考试最怕出现:
PC考了30分钟 ↓ 手机重新登录 ↓ 又获得60分钟或者随机组卷场景:
PC抽到试卷A ↓ 手机重新登录 ↓ 又随机生成试卷B因此,宏远培训考试系统这类企业考试平台更合理的设计应该是:
User ↓ Exam ↓ Exam Session ↓ Candidate Paper Snapshot第二设备恢复时仍然关联:
同一个ExamSessionID 同一个PaperSnapshot 同一个StartTime 同一个Deadline这样可以保证考试规则不因换设备发生变化。
十、策略三:允许多设备登录系统,但只允许一个设备执行考试
这是企业场景中很实用的一种方式。
比如员工:
PC登录宏远培训考试系统同时:
手机也登录宏远培训考试系统这本身可以允许。
员工在手机上可以查看:
培训任务 课程 通知 个人档案但是当PC已经存在:
RUNNING Exam Session手机再次进入同一场考试时,系统限制:
不能同时成为Active Exam Device也就是说:
限制的是考试执行权,而不是整个账号的登录权。
这种设计比简单“一登录就互踢”更加合理。
十一、Token互踢到底应该怎么做?
很多系统的互踢逻辑是:
新Token生成 ↓ 旧Token全部失效这种方式用于普通后台管理问题不大。
但正式考试中存在风险。
例如:
10:20:01 PC刚提交第38题答案 10:20:01.100 手机登录 10:20:01.200 PC Token立即失效如果PC的答案请求还在网络中:
10:20:01.300 答案请求到达服务器服务器发现Token已经失效,就可能直接拒绝。
这样就有机会产生:
最后一次答案未保存。所以考试中的互踢不能粗暴地只做:
Token Invalid还需要考虑:
正在进行中的Exam Session。十二、宏远考试更适合“登录Token”和“考试Session Token”分层
可以设计两类凭证。
第一类:
Login Access Token负责普通系统身份认证。
第二类:
Exam Session Token只负责当前考试。
例如:
ExamSessionToken { userId, examId, sessionId, deviceId, sessionVersion }这样即使账号发生:
重新登录系统也不会无条件立刻破坏正在进行的考试数据。
真正决定:
能否继续答题的是:
Exam Session当前绑定状态。十三、Session Version可以用来实现设备接管
假设PC进入考试时:
SessionVersion = 1Exam Session中保存:
ActiveDevice = PC SessionVersion = 1PC每次保存答案都携带:
SessionVersion = 1后来手机合法接管考试。
服务器执行:
ActiveDevice = MOBILE SessionVersion = 2这时PC仍然可能还有网络请求到达:
Device = PC SessionVersion = 1服务器发现:
1 < 2说明这是:
已经失效的旧设备请求。
就可以拒绝继续写入。
这种方式比单纯判断Token字符串更加清晰。
十四、Heartbeat为什么很重要?
现在出现另一个问题:
如果旧设备已经断网了,还需要“互踢”吗?
例如PC:
10:20:00 最后Heartbeat 10:20:05 网络断开员工发现网络有问题,立即拿手机继续。
手机:
10:20:20 请求进入考试这时候系统需要判断:
旧设备是:
仍然活跃?还是:
已经掉线?这就需要:
Heartbeat例如每隔:
15秒 / 30秒上报一次:
ExamSessionID DeviceID CurrentAnswerVersion ServerTime服务器维护:
LastHeartbeat十五、如何判断旧设备是否已经离线?
例如:
Current ServerTime: 10:21:00 LastHeartbeat: 10:20:20已经超过40秒。
如果系统阈值设置:
Heartbeat Timeout: 30秒可以将设备标记:
DISCONNECTED手机进入以后就更倾向于判断:
这是断线恢复 / 设备接管而不是:
两个设备同时考试。十六、Heartbeat不能直接决定考试结束
需要注意:
Heartbeat只是:
在线状态判断。不是:
考试状态的唯一来源。考生可能只是:
网络短时阻塞所以一次Heartbeat丢失不应该立即:
自动交卷 释放Session 删除答案更加合理的状态可能是:
RUNNING ↓ SUSPECTED_OFFLINE ↓ DISCONNECTED如果重新连接:
DISCONNECTED ↓ RUNNING考试Deadline仍然保持不变。
十七、怎么区分“断网重连”和“第二台设备登录”?
可以综合判断几个字段。
例如:
ExamSessionID DeviceID SessionVersion Token LastHeartbeat IP UserAgent场景一:
DeviceID相同 SessionID相同 Heartbeat短暂中断基本可以判断:
原设备重连。
场景二:
DeviceID不同 旧设备Heartbeat仍正常说明:
很可能是第二台设备同时进入。
场景三:
DeviceID不同 旧设备已经长时间Heartbeat超时可能属于:
换设备恢复。
系统可以根据管理员设置决定:
允许接管 要求重新认证 禁止换设备十八、宏远培训考试系统可以怎样处理设备切换?
对于正式考试场景,可以提供不同安全等级。
普通内部考试
允许:
断线重连 换浏览器 换设备接管但继续:
原Session 原试卷 原剩余时间 原答题记录重要岗位考试
可以要求:
换设备需要重新身份验证例如重新进行:
账号认证 人脸核验再允许接管。
高安全考试
可以设置:
考试开始后锁定终端禁止主动换设备。
这样宏远培训考试系统就可以根据:
内部培训 岗位考试 集团统考 技能竞赛设置不同安全策略,而不是所有考试都使用同一种互踢规则。
十九、两台设备都能答题时,真正危险的是答案冲突
假设由于网络延迟,PC和手机短时间内都认为自己仍然合法。
PC:
Q20 = A AnswerVersion = 108手机:
Q20 = C AnswerVersion = 109如果系统只按照:
最后到达服务器决定答案,就存在风险。
因为:
Version 109可能先到。
随后:
Version 108因为网络较慢后到。
如果数据库执行普通:
UPDATE Answer最终就会被旧答案:
A覆盖。
二十、Answer Version必须参与并发控制
所以宏远培训考试系统这类正式考试平台,在多设备和断点续考场景中,答题保存还需要:
Answer Version例如:
Q20 Version 108: Answer A Version 109: Answer C服务端只允许:
IncomingVersion > StoredVersion时更新。
如果:
IncomingVersion <= StoredVersion直接拒绝旧数据覆盖。
这样就实现:
乐观并发控制。
二十一、但是只有Answer Version仍然不够
如果已经完成设备接管:
SessionVersion = 2旧PC仍然发送:
AnswerVersion = 200而手机:
AnswerVersion = 150如果只比较AnswerVersion,旧PC反而可能继续覆盖手机。
因此请求最好同时携带:
SessionVersion + AnswerVersion服务器先判断:
SessionVersion是否合法?再判断:
AnswerVersion是否更新?这就形成两层防护。
二十二、完整的答题请求可以长什么样?
例如:
{ "examSessionId": "ES202608090001", "deviceId": "DEVICE-MOBILE-002", "sessionVersion": 2, "questionId": 20, "answer": ["C"], "answerVersion": 109 }服务端处理顺序:
验证用户身份 ↓ 验证Exam Session ↓ 验证Session状态 ↓ 验证Active Device ↓ 验证SessionVersion ↓ 验证Deadline ↓ 验证AnswerVersion ↓ 保存答案 ↓ 返回Server Ack这样一次简单的“保存答案”,实际上已经包含完整的并发控制。
二十三、Session Lock解决什么问题?
换设备只是一个问题。
还需要防止:
两台应用服务器同时处理:
接管Exam Session例如手机和PC几乎在同一毫秒请求:
Takeover如果系统部署:
Load Balancer ↓ App Server A App Server B服务器A认为:
PC获得锁服务器B认为:
手机获得锁就可能形成双Active。
因此需要:
Session Lock二十四、Redis可以用于实现考试Session锁
例如:
exam:session:lock:ES202608090001使用类似:
SET NX的方式控制:
同一时刻只有一个接管流程执行。流程可以是:
获取Session Lock ↓ 再次读取Exam Session ↓ 判断当前Active Device ↓ 执行接管 ↓ SessionVersion + 1 ↓ 写入新Device ↓ 释放Lock这样能够避免:
并发接管造成状态分裂。
二十五、Session Lock不能一直锁60分钟
这是一个容易出现的误区。
不是说:
考试开始就对Redis加:
60分钟分布式锁。否则:
服务故障 锁失效异常会很难处理。
Session Lock更适合用于:
创建Session 设备接管 状态切换 最终交卷这些:
短事务关键区。
平时答题不需要一直持有分布式锁。
二十六、两台设备同时点击交卷,会发生什么?
这是文章另一个核心问题。
假设:
PC:
10:59:58.100 点击交卷手机:
10:59:58.150 也点击交卷两个请求同时进入服务器。
如果业务逻辑写成:
保存答案 ↓ 计算成绩 ↓ 创建成绩记录 ↓ 生成证书执行两次,就可能导致:
两条成绩 重复排名 重复生成证书 培训档案重复更新因此:
交卷必须天然支持幂等。
二十七、Submit Idempotency应该怎样设计?
最简单的业务唯一键可以是:
ExamSessionID因为一个考试Session正常情况下只能:
成功交卷一次。例如:
SubmitKey = submit:ES202608090001第一次请求:
状态: RUNNING通过CAS或者锁操作更新:
RUNNING ↓ SUBMITTING第二个请求到达时发现:
SUBMITTING或:
SUBMITTED就不再执行第二次完整交卷逻辑。
二十八、为什么先变成SUBMITTING,而不是直接SUBMITTED?
因为最终交卷往往不是一个单字段更新。
还要执行:
Flush最后答案 ↓ 生成Final Answer Snapshot ↓ 客观题判分 ↓ 生成Score ↓ 更新Exam Session ↓ 写交卷日志如果直接:
RUNNING → SUBMITTED但中间判分失败,就会出现:
状态显示已交卷实际上:
成绩没有生成。所以可以设置:
RUNNING ↓ SUBMITTING ↓ SUBMITTED异常:
SUBMIT_FAILED支持后端恢复。
二十九、宏远培训考试系统的交卷设计为什么要强调幂等?
企业考试中的交卷请求很容易重复。
不仅是考生:
连续点两次。还可能来自:
浏览器网络超时重试 网关重试 前端自动交卷 服务端超时自动交卷例如:
前端: Deadline到点 → 自动Submit几乎同时:
服务端: Timeout Finalizer → 自动交卷如果没有幂等控制:
正常设计反而会制造重复提交。
所以宏远培训考试系统这类正式考试平台,交卷必须把:
人工交卷 前端到时交卷 服务器兜底交卷统一收口到:
同一个幂等Submit Pipeline。
三十、最终交卷应该冻结Answer Version
当:
ExamSession进入:
SUBMITTING以后,系统应该确定:
FinalAnswerVersion例如:
FinalAnswerVersion = 196那么迟到的旧设备请求:
AnswerVersion = 197是否还能保存?
正常情况下:
不能。
因为正式答卷已经进入冻结阶段。
服务器应该返回:
EXAM_ALREADY_SUBMITTING或者:
EXAM_SUBMITTED不能再修改最终答案。
三十一、最难处理的是“交卷请求和最后一次答案同时到达”
例如:
请求A:
保存Q50答案请求B:
提交试卷几乎同时到服务器。
如果B先执行:
生成Final Snapshot而A稍晚才到,最后一道题就可能不进入答卷。
因此最终Submit不能简单:
直接读取数据库当前答案。应该存在:
Final Flush / Barrier三十二、推荐的交卷流程
完整流程可以设计:
Submit Request ↓ 获取短期Session Lock ↓ CAS: RUNNING → SUBMITTING ↓ 停止接受新的普通答题版本 ↓ 确认客户端Final Version ↓ Flush Pending Answers ↓ 确认Server Confirmed Version ↓ 生成Final Answer Snapshot ↓ 计算成绩 ↓ 状态: SUBMITTED ↓ 记录Submit Log这样才能实现:
点击交卷时,最后一次合法答案能够确定地进入正式答卷。
三十三、旧设备被踢以后,正在编辑的答案怎么办?
例如:
PC:
正在编辑Q40手机申请接管。
系统不应该直接:
啪一下让PC消失更加合理的做法是:
新设备申请接管 ↓ 旧设备进入READ_ONLY / REVOKING ↓ 尝试Flush已产生的合法Answer Version ↓ 更新SessionVersion ↓ 旧设备失去写权限 ↓ 手机成为Active Device这里可以设置一个非常短的:
Grace Window用于完成服务器已经接收到的请求。
但不能无限等待。
否则旧设备一直不退出,新设备就无法接管。
三十四、旧设备应该看到什么提示?
体验层也很重要。
例如PC发现:
SessionVersion失效系统不应该只显示:
401 Unauthorized而应该明确提示:
当前考试已在另一台设备继续, 本设备已停止答题。并立即:
锁定选项 停止自动保存 停止提交这样既减少用户困惑,也避免继续产生无效请求。
三十五、宏远培训考试系统的考试监控还能记录设备变化
对于正式考试,管理员最好能够看到:
考生姓名 账号 Exam Session 首次设备 当前设备 设备切换次数 登录IP 最后Heartbeat 断网时间 恢复时间 Token失效原因 SessionVersion变化 最终交卷设备例如:
10:00:02 PC进入考试 Device: PC-001 10:25:16 PC网络断开 10:26:01 手机申请恢复 10:26:03 身份验证通过 10:26:04 SessionVersion: 1 → 2 10:26:04 手机接管成功 10:58:36 手机完成交卷这样管理员遇到:
“我只是断网,为什么系统说我换设备?”
或者:
“我的账号是不是别人登录过?”
就可以通过日志核查。
三十六、这也是考试防作弊的一部分
多设备控制不仅涉及数据一致性,也涉及考试安全。
例如:
考生在电脑做题;
同时用手机登录同一个账号查看其他题目。
如果系统完全允许:
两个设备同时获得正式试卷就会增加:
第二设备协作 试题拍照 答案同步等风险。
因此宏远培训考试系统在正式考试中可以结合:
单设备考试 人脸核验 切屏限制 IP限制 考试Session 操作日志形成更完整的考试控制。
需要注意:
多设备限制不能替代其他防作弊措施。
它解决的是:
账号和考试Session并发。不是万能防作弊方案。
三十七、为什么不能仅靠IP判断是不是同一设备?
一些系统可能会想:
IP相同 = 同一设备。这是不可靠的。
例如一个企业办公室:
500台电脑可能经过同一个公网出口。
它们对服务器显示的公网IP:
完全一样。反过来,同一台手机:
Wi-Fi → 5GIP马上改变。
因此IP可以作为:
日志和风险判断因素但不能单独作为:
Device Identity。三十八、同样不能只依赖MAC地址
Web考试系统通常无法可靠获取真实MAC地址。
即使通过专用客户端获取,现代操作系统也可能:
随机化MAC所以更实际的考试终端识别应该是:
设备实例标识 Token Exam Session Heartbeat UserAgent IP 必要时专用客户端信息联合判断。
三十九、Redis在整个架构中扮演什么角色?
宏远培训考试系统在高并发考试场景中,可以通过Redis维护:
Active Exam Session Device Binding SessionVersion Heartbeat Latest Answer Version Submit Lock Idempotency Key例如:
exam:session:ES001保存:
{ "userId": 10086, "examId": 20001, "activeDevice": "DEVICE-PC-001", "sessionVersion": 3, "status": "RUNNING", "lastHeartbeat": 1786242012000, "latestAnswerVersion": 196 }这样高频状态判断不需要每次都访问关系数据库。
但最终:
考试Session 最终答卷 交卷结果 成绩 日志仍然需要持久化。
四十、数据库应该保存哪些关键数据?
至少建议保存:
ExamSession UserID ExamID PaperSnapshotID StartTime Deadline InitialDeviceID FinalDeviceID SessionVersion Status FinalAnswerVersion SubmitTime另外维护:
ExamDeviceLog TokenLog HeartbeatLog AnswerVersionLog SubmitLog这样即使考试结束以后,仍然可以追溯:
谁在哪台设备参加了考试? 是否中途换过设备? 为什么换设备? 哪个设备最终交卷? 是否出现过重复交卷请求?四十一、异常情况下数据库和Redis状态不一致怎么办?
例如:
Redis:
SessionVersion = 4数据库:
SessionVersion = 3这可能发生在:
异步落库还没完成或者:
服务突然故障。因此关键状态变更,例如:
设备接管 交卷 考试结束最好不能只存在Redis。
可以采用:
Redis快速运行态 + 数据库持久化状态的方式。
特别是:
SUBMITTED必须最终有数据库确认。
四十二、重新启动应用服务器以后,考试还能继续吗?
如果考试Session只放在:
Java内存Map应用服务器重启以后:
全部Session消失。这显然不能满足企业正式考试。
更合理的架构是:
App Server 无状态化 ↓ Redis 运行态 ↓ Database 正式状态这样应用服务器A挂掉以后:
Load Balancer可以把请求转到:
App Server BB仍然能够从:
Redis / Database恢复Exam Session。
这也是宏远培训考试系统面向大规模正式考试时,系统架构稳定性的重要体现。
四十三、多设备控制最重要的不是“踢人”,而是状态机
Exam Session可以设计为:
CREATED ↓ RUNNING ↓ DISCONNECTED ↓ RUNNING或者发生设备接管:
RUNNING ↓ TRANSFERRING ↓ RUNNING最终:
RUNNING ↓ SUBMITTING ↓ SUBMITTED异常还可以有:
FORCE_SUBMITTED TIMEOUT CANCELLED所以从系统设计上看:
设备互踢实际上只是Exam Session状态机中的一个事件。
这比简单维护:
online = true / false更加完整。
四十四、宏远培训考试系统在企业场景中的核心优势
把上面的技术链路放到实际产品中,可以看到宏远培训考试系统的优势并不只是:
支持账号登录。而是形成了一套完整的考试身份与数据控制体系。
1. Exam Session唯一管理
同一人员参加同一场考试时,不因为:
刷新 重新登录 断网 换设备轻易生成新的考试状态。
保证:
考试时间不重置 试卷不重新生成 答题记录不丢失2. 断点续考与设备恢复
在允许的规则下,可以从:
原Exam Session继续考试,而不是重新开考。
3. Token与考试Session分离
账号重新登录不会简单粗暴地破坏考试数据。
考试权限由:
Exam Session状态单独控制。
4. SessionVersion防止旧设备继续写入
设备接管以后,旧设备即使还有延迟请求,也不能继续修改当前答卷。
5. Answer Version防止旧答案覆盖新答案
即使网络乱序,也能够按照版本控制保存正确的最新答案。
6. Submit Idempotency防止重复交卷
用户重复点击、自动交卷和服务器兜底交卷最终进入同一个幂等链路。
7. 日志全流程留痕
管理员可以追踪:
登录 设备 断网 恢复 接管 答题保存 交卷形成完整考试证据链。
四十五、管理员真正需要的不是一句“禁止重复登录”
传统设计可能只提供一个开关:
允许重复登录: 是 / 否对于正式考试来说其实太粗。
宏远培训考试系统更有价值的方式,是将规则细化成:
是否允许多设备登录系统 是否允许多设备进入考试 考试开始后是否锁定设备 断网后是否允许原设备恢复 是否允许换设备继续考试 换设备是否需要重新认证 第二设备进入时是否互踢 允许设备切换次数 异常设备切换是否记录这样才能覆盖企业真实考试的复杂场景。
四十六、一个推荐的完整技术链路
整个架构可以总结为:
User Login ↓ Access Token ↓ 进入考试 ↓ 检查Active Exam Session ↓ 无Session → 创建Exam Session 已有Session → 判断Resume / Takeover进入考试以后:
Device ID ↓ Exam Session Token ↓ Heartbeat ↓ SessionVersion ↓ Answer Version ↓ Redis运行态 ↓ 数据库Checkpoint如果设备切换:
Takeover Request ↓ Session Lock ↓ 判断旧Heartbeat ↓ 身份验证 ↓ SessionVersion + 1 ↓ 绑定新Device ↓ 旧设备失去写权限 ↓ 恢复原试卷和剩余时间交卷时:
Submit Request ↓ Idempotency Key ↓ Session Lock ↓ RUNNING → SUBMITTING ↓ Final Flush ↓ Final Answer Snapshot ↓ 成绩计算 ↓ SUBMITTED ↓ Submit Log这样:
设备控制、答案控制和交卷控制
才真正形成完整闭环。
四十七、最终设计原则
同一个账号到底能不能同时登录两台设备考试?
真正答案不应该只是:
能或者:
不能。而应该根据考试场景确定。
但无论采用哪一种策略,底层都应该遵守几个原则。
第一,同一用户同一场考试只能存在一个有效Exam Session。
第二,登录Token和Exam Session应该分开管理。
第三,设备切换不能重新计算考试时间。
第四,设备切换不能重新随机生成试卷。
第五,Heartbeat用于识别在线状态和断线恢复。
第六,SessionVersion用于控制当前合法设备。
第七,Answer Version用于防止旧答案覆盖新答案。
第八,Session Lock用于关键状态切换的并发控制。
第九,交卷必须设计Submit Idempotency。
第十,所有设备切换、断线、恢复和交卷操作必须可追溯。
四十八、结语
在线考试系统中的“账号重复登录”,表面看是一个用户权限问题。
但如果真正进入企业正式考试、高并发统考和随机组卷场景,就会发现它背后同时涉及:
身份认证 设备识别 考试Session 断线重连 并发控制 答案版本 分布式锁 幂等交卷 日志审计因此成熟的企业考试系统不能只实现:
“新设备登录,把旧设备踢下线。”
更加可靠的设计应该是:
User → Device ID → Token → Exam Session → Heartbeat → Session Lock → Session Version → Answer Version → Submit Idempotency → Audit Log
以宏远培训考试系统为例,真正值得企业关注的并不是简单的“禁止两个设备同时考试”,而是:
当电脑断网、手机重新进入、Token发生变化、两个设备短时间并发提交、最后一秒重复交卷时,系统仍然能够保证考试时间不重置、试卷不变化、答案不乱序、成绩不重复,并且所有异常都有日志可以追溯。
这才是企业级在线考试系统在多设备场景中真正应该解决的问题。
从产品能力上可以归纳成四句话:
正常登录管得住;
断网重连恢复得回;
多设备并发写不乱;
重复交卷只成功一次。
对于集团统考、岗位考核、安全培训和大规模集中考试来说,这类底层设计的重要性,往往远高于一个简单的“禁止重复登录”开关。