基于 Spring Boot 的在线考试系统设计与实现
一、需求分析与技术选型
1. 核心需求梳理
系统需要实现的核心功能包括:
- 多角色管理:学生(参加考试、查询成绩)、教师(创建题库、组卷、阅卷)、管理员(用户管理、系统设置)。
- 题库管理:支持单选题、多选题、判断题和简答题,可批量导入试题。
- 在线考试:定时交卷、防作弊(禁止切屏)、异常断线后可续考。
- 自动阅卷:客观题自动评分,主观题教师手动评分。
- 成绩统计:按班级、科目统计平均分、及格率等数据。
2. 技术选型考量
选择易于上手且资料丰富的技术栈:
- 后端:Spring Boot 2.7.x(开发效率高,适合快速迭代)。
- 前端:Bootstrap 5 + jQuery(响应式设计,适配电脑和手机)。
- 数据库:MySQL 8.0(关系型数据库,适合存储结构化考试数据)。
- 开发工具:IntelliJ IDEA + Maven。
二、环境准备
1. 下载并安装 IntelliJ IDEA
作为首选 IDE,打开 JetBrains 官网下载 Community 版本 Windows 安装包。安装时注意勾选'Add launchers dir to the PATH'以便命令行启动,以及'Create Desktop Shortcut'。
2. 配置开发环境
确保已安装 JDK 1.8+ 或 JDK 17,并配置好 MySQL 8.0 数据库。初始化项目结构,引入 Spring Boot Starter Web, MyBatis-Plus, Lombok 等依赖。
三、模块设计与编码
在开发过程中,采用 MVC 架构进行分层设计。以下是核心模块的代码示例:
1. 实体类设计 (Entity)
Question.java(试题实体)
package com.student.exam.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;
@Data
@NoArgsConstructor
@AllArgsConstructor
@TableName("question")
public class Question {
@TableId(type = IdType.AUTO)
private Long id;
private String content;
private Integer type;
private String optionA;
private String optionB;
private String optionC;
private String optionD;
private String correctAnswer;
private Integer score;
private Integer difficulty;
private String subject;
private LocalDateTime createTime;
private Integer status;
}
ExamRecord.java(考试记录实体)
package com.student.exam.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@Data
@NoArgsConstructor
@AllArgsConstructor
@TableName("exam_record")
public class ExamRecord {
@TableId(type = IdType.AUTO)
private Long id;
private Long studentId;
private Long paperId;
private Integer status;
private LocalDateTime startTime;
private LocalDateTime endTime;
private BigDecimal score;
private Integer totalScore;
private Integer screenChangeCount;
private LocalDateTime lastOperateTime;
}
2. 数据传输对象 (DTO)
PaperCreateDTO.java(试卷创建请求 DTO)
package com.student.exam.dto;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Positive;
import lombok.Data;
import java.util.List;
@Data
public class PaperCreateDTO {
@NotBlank(message = "试卷名称不能为空")
private String paperName;
@NotNull(message = "考试时长不能为空")
@Positive(message = "考试时长必须大于 0")
private Integer examDuration;
@NotNull(message = "试卷总分不能为空")
@Positive(message = "试卷总分必须大于 0")
private Integer totalScore;
private List<Long> questionIds;
}
3. 业务逻辑层 (Service)
ExamServiceImpl.java(考试服务实现类)
package com.student.exam.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.student.exam.dto.ExamSubmitDTO;
import com.student.exam.entity.*;
import com.student.exam.mapper.*;
import com.student.exam.service.ExamService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
import java.util.stream.Collectors;
@Slf4j
@Service
@RequiredArgsConstructor
public class ExamServiceImpl extends ServiceImpl<ExamRecordMapper, ExamRecord> implements ExamService {
private final ExamRecordMapper examRecordMapper;
private final PaperMapper paperMapper;
private final AnswerSheetMapper answerSheetMapper;
@Override
@Transactional
public void submitExam(ExamSubmitDTO submitDTO) {
log.info("学生提交考试:考试记录 ID={}", submitDTO.getExamRecordId());
// 1. 校验考试记录状态
ExamRecord examRecord = examRecordMapper.selectById(submitDTO.getExamRecordId());
if (!"IN_PROGRESS".equals(examRecord.getStatus())) {
();
}
BigDecimal.ZERO;
List<AnswerSheet> answerSheets = submitDTO.getAnswers().stream().map(answer -> {
();
answerSheet.setExamRecordId(submitDTO.getExamRecordId());
answerSheet.setStudentAnswer(answer.getStudentAnswer());
answerSheet.setScore(question.getScore());
answerSheet;
}).collect(Collectors.toList());
examRecord.setStatus();
examRecord.setScore(totalScore);
examRecordMapper.updateById(examRecord);
}
}
四、网页展示
前端采用 Bootstrap 实现响应式布局,界面简洁无冗余。
1. 学生端 - 考试列表页
- 布局:顶部导航与搜索栏,中间卡片式展示考试列表,底部分页控件。
- 核心功能:显示试卷名称、学科、考试时长、状态标签(未开始/可参加/已截止)。点击'开始考试'跳转至答题页。
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>我的考试</title>
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container mt-4">
<h4>我的考试</h4>
<div class="row">
<div class="col-md-6 col-lg-4">
<div class="card exam-card available">
<h5>Java 编程基础期末测试</h5>
<p>学科:Java 编程 | 时长:90 分钟</p>
<button class="btn btn-primary">开始考试</button>
2. 学生端 - 考试答题页
- 布局:顶部信息栏(剩余时间、切屏次数),左侧试题导航,中间答题区。
- 核心功能:倒计时提醒,答案自动保存,切屏检测警告。
// 考试倒计时功能
function startCountdown() {
let totalSeconds = 5130;
const interval = setInterval(() => {
totalSeconds--;
if (totalSeconds <= 0) {
clearInterval(interval);
autoSubmit();
return;
}
// 更新 UI 显示
}, 1000);
}
// 切屏检测
document.addEventListener('visibilitychange', () => {
if (document.visibilityState === 'hidden') {
screenChangeCount++;
if (screenChangeCount >= 5) {
alert('切屏次数过多,将影响成绩');
}
}
});
3. 教师端 - 试题管理页
- 布局:顶部操作栏(添加/导入),筛选栏,试题列表表格。
- 核心功能:增删改查试题,批量导入 Excel,状态管理(启用/禁用)。
五、总结与展望
1. 开发收获
通过本系统的开发,掌握了 Spring Boot + MyBatis-Plus 的全流程开发模式,深入理解了数据库设计与性能优化。同时学会了使用 Bootstrap 构建响应式前端页面,提升了全栈开发能力。
2. 系统不足与优化方向
- 防作弊功能:目前仅实现切屏检测,未来可增加摄像头监控、随机调整题目顺序。
- 智能组卷:引入知识点分布算法,自动生成更科学的试卷。
- 大数据分析:对学生答题数据进行挖掘,辅助教学改进。
3. 建议
- 善用开发工具提升效率,但核心逻辑需自行把控。
- 重视代码规范,养成良好的注释与命名习惯。
- 遇到问题多查阅文档,通过调试定位根源。
本次开发实践表明,从 0 到 1 构建实用系统虽有挑战,但在解决实际问题中能获得显著成长。


