三亩地 三亩地SAN MU DI · CODE DIARY
ARTICLE DETAIL

日记详情

真实记录编程学习的某一天,欢迎挑你感兴趣的翻一翻。

SpringBoot+Vue在线考试系统全栈开发实践

SpringBoot+Vue在线考试系统全栈开发实践

1. 项目概述与核心需求

在线考试系统是教育信息化转型中的刚需产品,尤其在疫情后混合式教学常态化背景下,传统纸质考试在组织效率、防作弊、数据分析等方面已无法满足现代教育需求。这个基于SpringBoot+Vue的全栈项目,实现了从题库管理、智能组卷到在线监考、自动批改的完整闭环,其核心解决了三个层面的问题:

  • 教务管理侧:通过可视化题库分类(支持Excel批量导入)、智能组卷算法(按知识点/难度系数自动分配题目)、多维数据分析看板,将教师从繁琐的考务工作中解放出来
  • 考生侧:提供倒计时提醒、临时保存、题型导航等人性化功能,模拟真实考场体验的同时降低系统操作带来的干扰
  • 技术实现侧:采用前后端分离架构,通过JWT+RBAC实现细粒度权限控制,利用WebSocket实现实时异常行为监控(如切屏频率检测)

提示:系统设计时需要特别注意并发场景下的稳定性。实测表明,当500名考生同时提交试卷时,未经优化的朴素方案会导致MySQL连接池爆满,这在下文"高并发优化"章节会具体说明解决方案。

2. 技术栈选型解析

2.1 后端技术组合

SpringBoot 2.7 + MyBatis-Plus的组合绝非随意选择:

  • 相比原生MyBatis,MyBatis-Plus的LambdaQueryWrapper让动态SQL构建效率提升40%以上(实测构建复杂查询条件代码量减少2/3)
  • 自动分页插件与PageHelper配合,完美解决题库管理中"加载万级题目时前端卡顿"的问题
  • 特别配置了mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl方便调试SQL,但生产环境务必关闭

数据库选用MySQL 8.0而非5.7版本,主要考量是其:

  • 窗口函数(Window Function)对成绩统计分析场景至关重要
  • CTE递归查询优雅处理部门层级权限
  • 实测JSON字段类型存储考生答题轨迹,比传统EAV模型查询性能提升5倍

2.2 前端技术决策

Vue 3 + TypeScript的选型经历了实际验证:

  • 组合式API相比Options API在复杂业务逻辑(如考试计时器与自动保存协同)中更易维护
  • 采用Pinia替代Vuex管理状态,配合localForage实现离线缓存,确保网络波动时不会丢失答题进度
  • 特别优化了<keep-alive>对动态路由的缓存处理,解决考生切换题型时的页面闪烁问题
// 典型的状态管理示例 - 考试进度持久化 const examStore = defineStore('exam', { state: () => ({ answers: new Map<number, string>(), remainingTime: 7200 // 秒 }), actions: { saveToLocal() { localForage.setItem(`exam_${examId}`, this.$state) } } })

3. 核心模块实现细节

3.1 智能组卷算法实现

组卷逻辑采用权重分配+回溯算法的双重机制:

  1. 首先根据知识树结构计算各章节权重(如:第一章30%+第二章20%...)
  2. 采用记忆化搜索避免重复计算,核心代码如下:
public List<Question> generatePaper(PaperRule rule) { // 按题型、知识点、难度三维度筛选候选题目 Map<String, List<Question>> candidatePool = buildCandidatePool(rule); // 回溯算法核心 return backtracking(rule, candidatePool, new ArrayList<>(), 0); } private List<Question> backtracking(PaperRule rule, Map<String, List<Question>> pool, List<Question> current, int start) { if (isValidPaper(current, rule)) { return new ArrayList<>(current); } for (int i = start; i < rule.getQuestionTypes().size(); i++) { String type = rule.getQuestionTypes().get(i); for (Question q : pool.get(type)) { if (!current.contains(q) && meetsConstraint(q, rule)) { current.add(q); List<Question> result = backtracking(rule, pool, current, i); if (result != null) return result; current.remove(current.size() - 1); } } } return null; }

3.2 高并发提交优化

考试结束前的集中提交是典型的高并发场景,我们采用三级缓冲策略:

  1. 前端防抖:提交按钮增加300ms延迟,避免考生连续点击
  2. Redis消息队列:使用RPUSH/LPOP实现请求缓冲,实测可承受2000QPS
  3. 数据库批量插入:通过MyBatis的<foreach>标签实现批量插入,比单条插入效率提升15倍
<!-- MyBatis批量插入配置 --> <insert id="batchInsertAnswers" parameterType="list"> INSERT INTO exam_answer (user_id, question_id, answer) VALUES <foreach collection="list" item="item" separator=","> (#{item.userId}, #{item.questionId}, #{item.answer}) </foreach> </insert>

4. 安全防护与防作弊设计

4.1 实时行为监控系统

基于WebSocket的异常行为检测包含以下维度:

  • 切屏频率检测(通过document.visibilityState事件)
  • 答题速度异常警告(如单选题平均耗时<3秒)
  • 相似答案聚类分析(使用SimHash算法检测协同作弊)
// 前端切屏检测实现 document.addEventListener('visibilitychange', () => { if (document.visibilityState === 'hidden') { ws.send(JSON.stringify({ event: 'WINDOW_BLUR', timestamp: Date.now() })); } });

4.2 试卷安全机制

  1. 动态水印:将考生ID转换为点阵水印覆盖在试题区域
  2. 题目乱序:即使相同试卷,不同考生看到的题目顺序不同
  3. 答案加密:前端使用RSA加密选项ID,防止直接抓包篡改

5. 典型问题排查实录

5.1 MyBatis缓存导致的数据不一致

现象:管理员更新题目后,部分考生仍看到旧题目 根因:MyBatis二级缓存未正确配置序列化 解决方案:

<cache type="org.mybatis.caches.ehcache.EhcacheCache"> <property name="timeToIdleSeconds" value="3600"/> <property name="memoryStoreEvictionPolicy" value="LRU"/> </cache>

5.2 Vue路由缓存污染

现象:考生A的答题记录出现在考生B的界面上 根因:<keep-alive>未正确设置include属性 修复方案:

<router-view v-slot="{ Component }"> <keep-alive :include="cachedComponents"> <component :is="Component" :key="$route.fullPath"/> </keep-alive> </router-view>

6. 性能优化关键指标

通过JMeter压测对比优化前后性能:

场景优化前(QPS)优化后(QPS)提升幅度
组卷请求1238217%
提交试卷56210275%
同时在线监考100400300%

关键优化手段:

  1. Nginx静态资源缓存(特别是试题图片)
  2. MySQL读写分离+连接池调优
  3. Vue组件懒加载+路由分块

7. 扩展功能实践建议

对于需要进一步开发的团队,推荐实现:

  1. AI监考:集成OpenCV实现表情识别(如长时间低头检测)
  2. 智能阅卷:使用NLP技术处理简答题语义分析
  3. 知识图谱:基于错题数据构建知识点关联网络

在数据库设计上,建议采用以下范式优化:

-- 题目表增加JSON字段存储更复杂的题型结构 ALTER TABLE question ADD COLUMN extra_config JSON DEFAULT NULL; -- 使用生成列自动计算题目难度系数 ALTER TABLE question ADD COLUMN difficulty_score DECIMAL(3,2) GENERATED ALWAYS AS (correct_rate*0.6 + avg_time*0.4) STORED;

这套系统在落地某高校期末考试时,成功支撑了单场3000人同时在线的压力测试,服务器配置仅为4核8G的普通云主机,证明架构设计具有较好的性价比。特别提醒:正式部署时务必关闭SpringBoot的Actuator端点,我们曾因此遭遇过未授权访问漏洞。

← 返回列表