1. 项目概述与技术栈选型
这个基于Spring Boot 2和Vue 3的在线远程考试系统,是一个典型的现代前后端分离架构的教育类应用。系统采用当前主流的技术组合:后端使用Spring Boot 2框架,前端采用Vue 3组合式API开发,数据持久层使用MyBatis-Plus增强,数据库选用MySQL 8.0的最新特性。
为什么选择这样的技术栈组合?从实际开发经验来看,Spring Boot 2在微服务领域已经非常成熟,相比Spring Boot 3对Java版本要求更低(最低Java 8即可),兼容性更好。Vue 3的组合式API让前端代码组织更灵活,特别适合复杂交互场景的考试系统。MyBatis-Plus在传统MyBatis基础上提供了更多开箱即用的CRUD操作,而MySQL 8.0的窗口函数、CTE等特性可以很好地支持考试数据分析需求。
提示:虽然Spring Boot 3已经发布,但在企业实际项目中,Spring Boot 2仍然是更稳妥的选择,特别是需要考虑与历史系统兼容时。
2. 系统核心功能模块设计
2.1 考生端功能实现
考生端采用Vue 3 + Element Plus构建,主要包含以下核心功能:
- 考试接入与身份验证:通过JWT实现无状态认证,考生登录后获取token,后续请求携带token进行鉴权。这里特别要注意考试场景下的防作弊设计,我们采用定期刷新token的机制(比如每5分钟刷新一次),同时记录设备指纹信息。
java复制// JWT配置示例
@Configuration
public class JwtConfig {
@Value("${jwt.secret}")
private String secret;
@Bean
public JwtDecoder jwtDecoder() {
return NimbusJwtDecoder.withSecretKey(new SecretKeySpec(secret.getBytes(), "HmacSHA256")).build();
}
}
- 实时考试引擎:这是系统的核心难点。我们采用WebSocket保持前后端实时通信,实现倒计时同步、题目保存防丢失等功能。实测中发现,直接使用原生WebSocket在弱网环境下容易断开,最终采用了SockJS作为降级方案。
2.2 管理端功能实现
管理端同样基于Vue 3,但采用了不同的布局风格和组件库(使用VxeTable处理大量数据),主要功能包括:
- 题库管理:支持多种题型(单选、多选、判断、填空、简答)的CRUD操作,采用MyBatis-Plus的动态SQL能力实现复杂条件查询:
java复制// MyBatis-Plus条件查询示例
public Page<Question> queryQuestions(QuestionQuery query) {
return page(new Page<>(query.getPage(), query.getSize()),
Wrappers.<Question>lambdaQuery()
.eq(Question::getSubjectId, query.getSubjectId())
.like(StringUtils.isNotBlank(query.getKeyword()),
Question::getContent, query.getKeyword())
.eq(Question::getType, query.getType())
.orderByAsc(Question::getSort));
}
- 考试编排:可以设置考试时间、试题随机策略、防作弊规则等。这里特别要注意事务处理,我们采用Spring的
@Transactional注解保证数据一致性。
3. 关键技术实现细节
3.1 前后端分离架构实践
系统采用严格的前后端分离架构,通过RESTful API进行通信。在实际部署时,我们遇到了跨域问题,解决方案是:
- 后端通过
@CrossOrigin注解或全局CorsFilter配置 - 前端axios实例统一设置baseURL和withCredentials
- Nginx配置中添加CORS头信息
对于文件上传这类特殊需求,我们采用了阿里云OSS直传方案,前端获取临时凭证后直接上传到OSS,避免了文件流经应用服务器。
3.2 高并发考试场景优化
考试系统往往面临瞬时高并发的挑战,我们通过以下手段进行优化:
- Redis缓存:将热点数据如考试规则、题目基本信息等缓存到Redis
- 数据库连接池:使用HikariCP替代默认连接池,配置合理的连接数
- 异步日志:采用Logback的AsyncAppender避免日志IO阻塞业务线程
- 限流措施:使用Guava RateLimiter对关键接口进行限流
java复制// 限流示例
@RestController
@RequestMapping("/api/exam")
public class ExamController {
private final RateLimiter rateLimiter = RateLimiter.create(100); // 每秒100个请求
@PostMapping("/submit")
public Result submitAnswer(@RequestBody AnswerDTO dto) {
if (!rateLimiter.tryAcquire()) {
throw new BusinessException("系统繁忙,请稍后再试");
}
// 处理提交逻辑
}
}
4. 数据库设计与优化
4.1 MySQL 8.0特性应用
我们充分利用了MySQL 8.0的新特性:
- 窗口函数:用于考试成绩排名统计
sql复制SELECT
student_id,
score,
RANK() OVER (PARTITION BY exam_id ORDER BY score DESC) AS rank
FROM exam_record;
- JSON字段:存储动态扩展的考试配置
- CTE(公共表表达式):简化复杂查询
4.2 分库分表考虑
虽然初始版本采用单库设计,但我们提前考虑了分库分表方案:
- 按照考试ID哈希分片
- 历史考试数据归档策略
- 使用ShardingSphere实现透明分片
5. 安全防护措施
5.1 防作弊机制
- 题目乱序:服务端随机题目顺序,客户端只接收题目ID和内容
- 防切屏:通过window.blur事件监听,超过阈值强制交卷
- 答案水印:在考生答案中嵌入隐形水印,防止抄袭
5.2 接口安全
- SQL注入防护:MyBatis-Plus内置防护 + 定期SQL注入扫描
- XSS防护:前端DOMPurify过滤 + 后端Jackson转义
- CSRF防护:虽然JWT方案本身不受CSRF影响,但仍添加了SameSite Cookie属性
java复制// XSS防护配置示例
@Bean
public Jackson2ObjectMapperBuilderCustomizer jsonCustomizer() {
return builder -> builder.deserializers(new XssStringJsonDeserializer());
}
public class XssStringJsonDeserializer extends JsonDeserializer<String> {
@Override
public String deserialize(JsonParser p, DeserializationContext ctxt) {
return HtmlUtils.htmlEscape(p.getValueAsString());
}
}
6. 部署与监控
6.1 容器化部署
系统支持传统部署和Docker容器化部署。Docker Compose文件示例:
yaml复制version: '3'
services:
mysql:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: exam_system
ports:
- "3306:3306"
volumes:
- ./mysql/data:/var/lib/mysql
- ./mysql/conf:/etc/mysql/conf.d
redis:
image: redis:6
ports:
- "6379:6379"
volumes:
- ./redis/data:/data
backend:
build: ./backend
ports:
- "8080:8080"
depends_on:
- mysql
- redis
frontend:
build: ./frontend
ports:
- "80:80"
6.2 监控方案
- Spring Boot Actuator:暴露健康检查端点
- Prometheus + Grafana:监控系统指标
- ELK:日志收集与分析
7. 开发中的经验教训
在实际开发这个考试系统过程中,有几个特别值得分享的经验:
- MyBatis-Plus字段映射问题:当数据库字段使用了下划线命名,而实体类使用驼峰时,需要特别注意配置:
yaml复制mybatis-plus:
configuration:
map-underscore-to-camel-case: true
-
Vue 3组合式API的滥用:初期我们过度使用了组合式函数,导致代码难以维护。后来发现,对于简单逻辑,还是应该优先使用选项式API。
-
MySQL 8.0认证插件问题:如果客户端驱动较旧,连接MySQL 8.0可能会报"caching_sha2_password"错误,解决方案是:
sql复制ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY 'password';
- 考试提交的幂等性设计:网络不稳定时考生可能会重复提交,我们通过Redis分布式锁+唯一事务ID保证了幂等性。
