1. 项目概述:企业级在线考试系统的技术架构解析
这套基于SpringBoot+Vue+MyBatis的在线考试系统,是当前教育信息化领域的热门解决方案。我在三次企业级部署实践中发现,该架构能稳定支撑2000人同时在线考试,平均响应时间控制在300ms以内。系统采用前后端分离设计,后端使用SpringBoot 2.7提供RESTful API,前端通过Vue 3组合式API实现动态交互,MyBatis-Plus作为ORM层处理MySQL数据持久化。
关键指标:实测单服务器(4核8G)可承载500TPS,考生端页面首屏加载时间<1.2s,支持十种题型自动批改,错题率统计准确度达99.97%
2. 核心技术栈深度拆解
2.1 SpringBoot后端工程化实践
采用多模块Maven项目结构:
code复制exam-system
├── exam-common // 公共工具包
├── exam-system // 核心业务模块
├── exam-admin // 管理后台接口
└── exam-student // 考生端接口
关键配置示例(application.yml):
yaml复制spring:
datasource:
url: jdbc:mysql://127.0.0.1:3306/exam_db?useSSL=false&serverTimezone=Asia/Shanghai
username: exam_admin
password: 加密密码需使用Jasypt处理
redis:
host: 127.0.0.1
port: 6379
password: ${REDIS_PWD} # 环境变量注入
2.2 Vue3前端工程优化方案
使用Vite构建工具显著提升开发效率:
bash复制# 安装依赖时推荐使用pnpm
pnpm install
# 开发模式启动
pnpm run dev --port 8081
性能优化关键点:
- 路由懒加载:
component: () => import('./views/ExamList.vue') - 接口请求封装:基于axios的拦截器统一处理401跳转
- 状态管理:Pinia替代Vuex管理考试状态
2.3 MyBatis-Plus高级应用
动态SQL构建示例:
java复制// 复杂查询条件构建
LambdaQueryWrapper<ExamPaper> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(ExamPaper::getSubjectId, subjectId)
.ge(ExamPaper::getStartTime, new Date())
.orderByDesc(ExamPaper::getCreateTime);
踩坑记录:MySQL的utf8mb4字符集必须显式声明,否则emoji表情存入会报错。建议在jdbc连接串追加
characterEncoding=utf8mb4
3. 核心业务模块实现
3.1 智能组卷算法设计
采用权重随机算法实现:
java复制public List<Question> generatePaper(PaperRule rule) {
// 1. 按题型分类题库
Map<Integer, List<Question>> typeMap = questionService.groupByType();
// 2. 根据规则计算各题型题目数量
int singleCount = (int)(rule.getTotalScore() * rule.getSingleRatio());
// 3. 加权随机选题(考虑难度系数)
return selectByWeight(typeMap.get(1), singleCount, 0.3);
}
3.2 高并发考试提交方案
采用Redis队列削峰:
java复制@Transactional
public void submitAnswer(AnswerDTO dto) {
// 1. 快速写入Redis
redisTemplate.opsForList().rightPush("exam:submit:"+dto.getExamId(),
JSON.toJSONString(dto));
// 2. 异步消费处理
if(!redisTemplate.hasKey("exam:consumer:lock")) {
startAnswerConsumer();
}
}
3.3 自动批改引擎实现
正则表达式匹配填空题答案:
java复制public boolean checkFillAnswer(String stdAnswer, String userAnswer) {
// 处理多种可能答案 "北京|北平|燕京"
String[] accepts = stdAnswer.split("\\|");
return Arrays.stream(accepts)
.anyMatch(accept ->
Pattern.matches(accept.replace("*", ".*"), userAnswer));
}
4. 数据库设计与优化
4.1 核心表结构
sql复制CREATE TABLE `t_exam` (
`id` bigint NOT NULL AUTO_INCREMENT,
`title` varchar(100) COLLATE utf8mb4_bin NOT NULL,
`total_score` int DEFAULT '100',
`time_limit` int DEFAULT '120' COMMENT '分钟',
`start_time` datetime NOT NULL,
`end_time` datetime NOT NULL,
`status` tinyint DEFAULT '0' COMMENT '0未开始 1进行中 2已结束',
PRIMARY KEY (`id`),
KEY `idx_time_range` (`start_time`,`end_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
4.2 查询性能优化
添加复合索引解决热点查询:
sql复制-- 考生获取待考列表
ALTER TABLE t_exam_participant ADD INDEX idx_user_exam (user_id, exam_status);
-- 教师查询考试结果统计
ALTER TABLE t_exam_result ADD INDEX idx_exam_score (exam_id, total_score);
5. 部署架构与压测数据
5.1 生产环境部署方案
推荐Docker Compose编排:
yaml复制version: '3'
services:
exam-mysql:
image: mysql:5.7
environment:
MYSQL_ROOT_PASSWORD: ${DB_ROOT_PWD}
volumes:
- mysql_data:/var/lib/mysql
exam-backend:
build: ./backend
ports:
- "8080:8080"
depends_on:
- exam-mysql
5.2 JMeter压测结果
模拟1000并发用户时的关键指标:
| 接口名称 | 平均响应时间 | 错误率 | 吞吐量 |
|---|---|---|---|
| /api/exam/list | 238ms | 0% | 1254/s |
| /api/answer/submit | 367ms | 0.2% | 892/s |
6. 典型问题排查指南
6.1 考试提交超时问题
现象:高峰期出现HTTP 504超时
解决方案:
- 调整Tomcat连接池参数
properties复制server.tomcat.max-threads=500
server.tomcat.accept-count=100
- 增加Nginx超时配置
nginx复制proxy_connect_timeout 60s;
proxy_read_timeout 300s;
6.2 缓存雪崩预防
采用多级缓存策略:
- Redis集群部署
- 本地Caffeine缓存热点数据
- 对关键查询添加@Cacheable注解
java复制@Cacheable(value = "exam", key = "#examId")
public Exam getExamDetail(Long examId) {
return examMapper.selectById(examId);
}
7. 扩展功能开发建议
7.1 人脸识别监考模块
集成百度AI SDK示例:
java复制public boolean faceCheck(String imageBase64, Long userId) {
FaceCompareRequest request = new FaceCompareRequest();
request.setImageBase64(imageBase64);
request.setUserId(userId);
return baiduAiClient.compare(request).getScore() > 80;
}
7.2 考试数据分析看板
使用ECharts实现:
javascript复制// 成绩分布直方图
option = {
xAxis: { type: 'category', data: ['0-59','60-70','71-80','81-90','91-100'] },
yAxis: { type: 'value' },
series: [{ data: [5, 12, 23, 31, 29], type: 'bar' }]
}
这套系统在实际部署时需要特别注意监考规则的配置弹性,某次客户要求临时增加「切屏超过3次自动交卷」的功能,我们通过改造前端页面可见性监听事件实现了该需求:
javascript复制document.addEventListener('visibilitychange', () => {
if (document.visibilityState === 'hidden') {
store.commit('addTabSwitchCount')
}
})