基于Spring Boot+Vue+MySQL的在线考试系统毕业设计实战指南
在线考试系统是高校、培训机构和企业内部考核的常见需求它需要同时处理高并发访问、实时性、数据一致性和安全性问题。一个典型的系统会涉及用户认证、题库管理、试卷生成、在线答题、自动判卷、成绩统计和权限控制等多个模块。对于计算机专业的毕业设计而言选择 Java 作为后端Vue 作为前端Spring Boot 作为应用框架MySQL 作为数据库是一个技术栈成熟、社区资源丰富、易于上手且能充分展示综合能力的方案。本文将围绕如何从零开始构建一个具备完整流程的在线考试系统详细讲解后端 API 设计、前后端交互、数据库表结构以及关键业务逻辑的实现并提供可运行的代码片段、配置说明和常见问题排查路径目标是让你能独立完成一个可演示、可答辩的毕业设计项目。1. 理解在线考试系统的核心模块与业务流程在动手编码之前必须先理清系统的业务边界和数据流转。一个最小可用的在线考试系统至少包含以下六个核心模块它们共同构成了系统的主干。1.1 用户与权限管理模块这是所有业务的基础。系统通常有三种角色管理员、教师和学生。管理员负责系统基础配置和用户管理教师负责题库、试卷、考试安排和成绩管理学生则参与考试和查看成绩。权限控制需要贯穿始终例如学生不能访问教师的管理页面教师只能管理自己创建的考试。在技术实现上这通常通过基于角色的访问控制RBAC模型来实现Spring Security 或 Shiro 是常用的框架。1.2 题库与试卷管理模块题库是系统的知识库支持单选题、多选题、判断题、填空题、简答题等题型。每道题目需要包含题干、选项对于选择题、正确答案、分值、难度、所属知识点等属性。试卷管理则基于题库支持手动组卷教师逐题选择和自动组卷根据题型、难度、知识点等规则随机抽题。生成的试卷需要被持久化形成一个独立的试卷快照即使后续题库中的题目被修改已生成的试卷内容也应保持不变。1.3 在线考试与实时交互模块这是系统的核心体验环节。学生进入考试后界面需要展示倒计时、题目列表、答题区域。答题过程需要实现自动保存功能防止浏览器意外关闭导致答案丢失。对于客观题选择、判断系统可以在交卷时或后台自动批改对于主观题填空、简答则需要教师手动批阅。此模块对前端实时性和后端会话状态管理要求较高。1.4 自动判卷与成绩统计模块考试结束后系统需要根据试卷快照中的标准答案与学生的作答记录进行比对计算客观题得分。成绩统计不仅包括个人分数还应包括班级/考试的平均分、最高分、最低分、分数段分布以及每道题的正确率分析为教学评估提供数据支持。1.5 数据持久化与数据库设计MySQL 作为关系型数据库需要合理设计表结构来支撑上述业务。表之间通过外键关联保证数据完整性。关键表包括用户表sys_user、角色表sys_role、权限表sys_menu、题目表exam_question、试卷表exam_paper、考试记录表exam_record、学生答案表exam_answer等。索引的建立对查询性能至关重要特别是在查询成绩、筛选题目时。1.6 前后端分离架构与 API 设计采用 Spring Boot 提供 RESTful APIVue 作为 SPA单页应用消费这些 API是现代 Web 开发的典型模式。前后端通过 JSON 格式交换数据。API 设计要遵循 REST 规范清晰定义资源路径如/api/exam/paper、HTTP 方法GET/POST/PUT/DELETE和状态码。同时需要考虑跨域问题CORS、身份认证如 JWT和 API 文档如 Swagger的集成。2. 项目环境准备与依赖配置一个稳定、一致的开发环境是项目顺利推进的前提。下面将列出从零开始搭建开发环境所需的全部工具、软件及其版本建议。2.1 后端开发环境Java Spring Boot后端环境的核心是 JDK、构建工具和 IDE。JDK: 推荐使用 OpenJDK 17 或 Oracle JDK 17。Spring Boot 3.x 要求最低 JDK 17。确保JAVA_HOME环境变量正确配置并在命令行中通过java -version验证。构建工具: Maven 3.6 或 Gradle 7.x。本文以 Maven 为例它是 Spring Boot 项目的默认选择。集成开发环境IDE: IntelliJ IDEA社区版或旗舰版或 Eclipse需安装 Spring Tools 插件。IDEA 对 Spring Boot 的支持更为友好。数据库: MySQL 8.0 或 5.7。建议使用 8.0 以获得更好的性能和功能。需要提前安装并创建好一个用于本项目的数据库如exam_system。API 测试工具: Postman 或 Insomnia用于测试后端接口。2.2 前端开发环境Vue.js前端环境依赖于 Node.js 和包管理器。Node.js: 推荐安装 LTS 版本如 18.x。Node.js 自带了 npm 包管理器。安装后通过node -v和npm -v检查版本。Vue 脚手架: 使用 Vue CLI 或 Vite 快速创建项目。本文使用 Vue CLI可通过npm install -g vue/cli全局安装。前端 IDE: Visual Studio Code 是主流选择配合 Vetur 或 Vue Language Features (Volar) 插件。2.3 创建 Spring Boot 项目并配置核心依赖使用 Spring Initializr start.spring.io 或 IDEA 内置的 Spring Initializr 创建项目。项目元信息: Group如com.yournameArtifact如exam-system打包方式选 JarJava 版本选 17。依赖选择: 在创建时勾选以下依赖Spring Web: 用于构建 RESTful API。Spring Data JPA: 用于简化数据库操作。MySQL Driver: MySQL 数据库连接驱动。Lombok: 通过注解简化 Java Bean 的编写如自动生成 getter/setter。Spring Security(可选): 如果计划实现完整的登录认证和授权可以勾选。初期为简化可先不选使用简单拦截器或手动校验。Spring Boot DevTools: 开发工具支持热重启。生成并导入: 下载生成的项目压缩包解压后用 IDEA 打开等待 Maven 自动下载依赖。项目初始化后关键的pom.xml依赖部分大致如下dependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-data-jpa/artifactId /dependency dependency groupIdcom.mysql/groupId artifactIdmysql-connector-j/artifactId scoperuntime/scope /dependency dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId optionaltrue/optional /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-test/artifactId scopetest/scope /dependency /dependencies2.4 配置数据库连接与 JPA在src/main/resources/application.properties或application.yml中配置数据库连接。这里使用application.yml因为它结构更清晰。spring: datasource: url: jdbc:mysql://localhost:3306/exam_system?useUnicodetruecharacterEncodingutf-8serverTimezoneAsia/Shanghai username: root password: yourpassword # 替换为你的数据库密码 driver-class-name: com.mysql.cj.jdbc.Driver jpa: hibernate: ddl-auto: update # 开发环境可用 update生产环境应改为 validate 或 none show-sql: true # 在控制台打印 SQL便于调试 properties: hibernate: dialect: org.hibernate.dialect.MySQL8Dialect format_sql: true # 格式化打印的 SQL关键配置解释:ddl-auto: update: Hibernate 启动时会根据实体类自动更新数据库表结构。这在开发初期非常方便但严禁在生产环境使用因为可能导致数据丢失。生产环境应使用 Flyway 或 Liquibase 进行数据库版本管理。show-sql: true: 在控制台输出执行的 SQL 语句是调试和优化查询的重要工具。3. 数据库设计与核心实体类映射根据第一部分的模块分析我们设计核心表结构并创建对应的 Java 实体类。这里使用 JPA 注解进行对象-关系映射ORM。3.1 用户与角色实体设计用户和角色是多对多关系。一个用户可以有多个角色如既是教师又是管理员一个角色也可以分配给多个用户。// User.java Entity Table(name sys_user) Data // Lombok 注解自动生成 getter, setter, toString 等 public class User { Id GeneratedValue(strategy GenerationType.IDENTITY) private Long id; Column(unique true, nullable false) private String username; // 登录名 private String password; private String realName; // 真实姓名 private String email; ManyToMany(fetch FetchType.EAGER) // 急加载获取用户时同时获取角色 JoinTable(name sys_user_role, joinColumns JoinColumn(name user_id), inverseJoinColumns JoinColumn(name role_id)) private SetRole roles new HashSet(); // 省略其他字段如状态、创建时间等 } // Role.java Entity Table(name sys_role) Data public class Role { Id GeneratedValue(strategy GenerationType.IDENTITY) private Integer id; Column(unique true, nullable false) private String name; // 角色名如 ROLE_ADMIN, ROLE_TEACHER, ROLE_STUDENT private String description; ManyToMany(mappedBy roles) private SetUser users new HashSet(); }3.2 题目与试卷实体设计题目Question和试卷Paper是系统的核心。一道题目可以属于多张试卷在组卷时被选中一张试卷包含多道题目。我们通过一个中间表paper_question来存储试卷与题目的关联关系并记录该题目在本次试卷中的分值。// Question.java Entity Table(name exam_question) Data public class Question { Id GeneratedValue(strategy GenerationType.IDENTITY) private Long id; private String title; // 题干 private String type; // 题型SINGLE_CHOICE, MULTI_CHOICE, TRUE_FALSE, FILL_BLANK, SHORT_ANSWER Column(columnDefinition TEXT) private String options; // 选项JSON 字符串存储如 [A.选项1, B.选项2, ...] private String answer; // 标准答案。选择题存选项字母如A或AB判断题存T/F填空/简答存文本。 private Integer score; // 标准分值 private String difficulty; // 难度EASY, MEDIUM, HARD private String knowledgePoint; // 知识点标签 ManyToOne JoinColumn(name creator_id) private User creator; // 题目创建者教师 // 省略其他字段 } // Paper.java Entity Table(name exam_paper) Data public class Paper { Id GeneratedValue(strategy GenerationType.IDENTITY) private Long id; private String name; // 试卷名称 ManyToOne JoinColumn(name creator_id) private User creator; // 试卷创建者 private Integer totalScore; // 试卷总分 private Integer timeLimit; // 考试时长分钟 OneToMany(mappedBy paper, cascade CascadeType.ALL, orphanRemoval true) private ListPaperQuestion paperQuestions new ArrayList(); // 试卷包含的题目列表 // 省略状态、创建时间等字段 } // PaperQuestion.java (中间实体) Entity Table(name exam_paper_question) Data public class PaperQuestion { Id GeneratedValue(strategy GenerationType.IDENTITY) private Long id; ManyToOne JoinColumn(name paper_id, nullable false) private Paper paper; ManyToOne JoinColumn(name question_id, nullable false) private Question question; private Integer assignedScore; // 该题目在本试卷中的分值可与题库中标准分值不同 }设计要点PaperQuestion中间表的设计至关重要。它解耦了试卷和题库使得试卷生成后即使原题目被修改或删除试卷内容也不会受影响。assignedScore字段允许教师针对不同试卷调整同一题目的分值。3.3 考试记录与答案实体设计学生参加一次考试会产生一条考试记录ExamRecord以及针对试卷中每一道题目的作答答案Answer。// ExamRecord.java Entity Table(name exam_record) Data public class ExamRecord { Id GeneratedValue(strategy GenerationType.IDENTITY) private Long id; ManyToOne JoinColumn(name student_id) private User student; // 参考学生 ManyToOne JoinColumn(name paper_id) private Paper paper; // 参加的哪份试卷 private LocalDateTime startTime; // 开始时间 private LocalDateTime submitTime; // 交卷时间 private Integer totalScore; // 最终得分客观题自动主观题教师批改 private String status; // 状态IN_PROGRESS, SUBMITTED, MARKED OneToMany(mappedBy examRecord, cascade CascadeType.ALL) private ListAnswer answers new ArrayList(); // 所有作答记录 } // Answer.java Entity Table(name exam_answer) Data public class Answer { Id GeneratedValue(strategy GenerationType.IDENTITY) private Long id; ManyToOne JoinColumn(name record_id) private ExamRecord examRecord; ManyToOne JoinColumn(name paper_question_id) private PaperQuestion paperQuestion; // 关联到试卷中的具体题目 Column(columnDefinition TEXT) private String studentAnswer; // 学生提交的答案 private Integer score; // 该题得分自动判卷或教师批改后填入 private Boolean isCorrect; // 客观题是否正确 }设计要点Answer关联的是PaperQuestion而非原始的Question这保证了批卷时使用的是试卷生成时的题目快照信息。status字段用于跟踪考试流程控制学生是否可以重复进入考试、教师是否可以批改等。4. 核心业务逻辑与 RESTful API 实现实体类定义好后接下来实现核心的业务逻辑层Service和数据访问层Repository并通过控制器Controller暴露 RESTful API。4.1 数据访问层RepositorySpring Data JPA 只需定义接口无需实现。它提供了基础的 CRUD 方法。// QuestionRepository.java Repository public interface QuestionRepository extends JpaRepositoryQuestion, Long { // 根据题型和创建者查询 ListQuestion findByTypeAndCreator(String type, User creator); // 根据知识点模糊查询 ListQuestion findByKnowledgePointContaining(String knowledgePoint); } // PaperRepository.java Repository public interface PaperRepository extends JpaRepositoryPaper, Long { ListPaper findByCreator(User creator); } // ExamRecordRepository.java Repository public interface ExamRecordRepository extends JpaRepositoryExamRecord, Long { ListExamRecord findByStudentAndStatus(User student, String status); ListExamRecord findByPaperAndStatus(Paper paper, String status); }4.2 业务逻辑层ServiceService 层封装复杂的业务规则。以组卷和自动判卷为例。// PaperService.java Service Transactional public class PaperService { Autowired private QuestionRepository questionRepository; Autowired private PaperRepository paperRepository; /** * 自动组卷 * param rule 组卷规则包含各题型数量、难度分布、知识点等 * return 生成的试卷 */ public Paper generatePaperAuto(PaperGenerationRule rule) { Paper paper new Paper(); paper.setName(rule.getPaperName()); paper.setCreator(getCurrentTeacher()); // 获取当前登录教师 paper.setTimeLimit(rule.getTimeLimit()); ListPaperQuestion paperQuestions new ArrayList(); int totalScore 0; // 遍历规则中的每种题型要求 for (QuestionRule qRule : rule.getQuestionRules()) { // 根据题型、难度、知识点从题库随机抽取指定数量的题目 ListQuestion questions questionRepository.findRandomQuestions( qRule.getType(), qRule.getDifficulty(), qRule.getKnowledgePoint(), qRule.getCount() ); // 自定义查询方法需要借助 Query 注解实现 for (Question q : questions) { PaperQuestion pq new PaperQuestion(); pq.setPaper(paper); pq.setQuestion(q); pq.setAssignedScore(qRule.getScorePerQuestion()); paperQuestions.add(pq); totalScore qRule.getScorePerQuestion(); } } paper.setPaperQuestions(paperQuestions); paper.setTotalScore(totalScore); return paperRepository.save(paper); } }自动判卷服务// MarkingService.java Service Transactional public class MarkingService { /** * 自动批改客观题 */ public void autoMarkObjectiveQuestions(ExamRecord record) { int objectiveTotalScore 0; for (Answer answer : record.getAnswers()) { PaperQuestion pq answer.getPaperQuestion(); Question question pq.getQuestion(); // 只处理客观题 if (SINGLE_CHOICE.equals(question.getType()) || MULTI_CHOICE.equals(question.getType()) || TRUE_FALSE.equals(question.getType())) { boolean isCorrect question.getAnswer().equalsIgnoreCase(answer.getStudentAnswer()); answer.setIsCorrect(isCorrect); answer.setScore(isCorrect ? pq.getAssignedScore() : 0); objectiveTotalScore answer.getScore(); } } // 更新考试记录的总分先加上客观题分数主观题分数后续由教师添加 record.setTotalScore(objectiveTotalScore); // 如果试卷没有主观题状态可直接变为 MARKED if (!hasSubjectiveQuestions(record.getPaper())) { record.setStatus(MARKED); } } }4.3 控制器层Controller与 API 设计控制器接收 HTTP 请求调用 Service并返回 JSON 响应。需要处理参数校验、异常和统一的响应格式。// PaperController.java RestController RequestMapping(/api/papers) public class PaperController { Autowired private PaperService paperService; PostMapping(/generate) public ResponseEntityApiResponsePaper generatePaper(RequestBody Valid PaperGenerationRule rule) { // Valid 会触发规则对象内的校验注解如NotNull, Size Paper paper paperService.generatePaperAuto(rule); return ResponseEntity.ok(ApiResponse.success(paper)); } GetMapping(/{id}) public ResponseEntityApiResponsePaper getPaper(PathVariable Long id) { Paper paper paperService.findById(id) .orElseThrow(() - new ResourceNotFoundException(试卷不存在)); return ResponseEntity.ok(ApiResponse.success(paper)); } GetMapping(/my) public ResponseEntityApiResponseListPaper getMyPapers() { // 获取当前登录教师创建的试卷 ListPaper papers paperService.findPapersByCurrentUser(); return ResponseEntity.ok(ApiResponse.success(papers)); } } // 统一的 API 响应包装类 Data public class ApiResponseT { private boolean success; private String message; private T data; private long timestamp; public static T ApiResponseT success(T data) { ApiResponseT response new ApiResponse(); response.setSuccess(true); response.setMessage(操作成功); response.setData(data); response.setTimestamp(System.currentTimeMillis()); return response; } // 静态方法 fail 略 }4.4 前端 Vue 组件与 API 调用示例在前端 Vue 项目中使用 Axios 调用后端 API。以“我的试卷”列表页面为例。!-- PaperList.vue -- template div h2我的试卷/h2 el-button typeprimary clickhandleCreate创建新试卷/el-button el-table :datapaperList stylewidth: 100% el-table-column propid labelID width80/el-table-column el-table-column propname label试卷名称/el-table-column el-table-column proptotalScore label总分 width100/el-table-column el-table-column proptimeLimit label时长(分钟) width120/el-table-column el-table-column label操作 width200 template #defaultscope el-button sizesmall clickhandleEdit(scope.row)编辑/el-button el-button sizesmall typedanger clickhandleDelete(scope.row)删除/el-button /template /el-table-column /el-table /div /template script import axios from axios; import { ElMessage } from element-ui; export default { name: PaperList, data() { return { paperList: [] }; }, mounted() { this.fetchPapers(); }, methods: { fetchPapers() { // 从本地存储获取 token const token localStorage.getItem(token); axios.get(/api/papers/my, { headers: { Authorization: Bearer ${token} } }) .then(response { if (response.data.success) { this.paperList response.data.data; } else { ElMessage.error(response.data.message); } }) .catch(error { console.error(获取试卷列表失败:, error); ElMessage.error(网络请求失败); }); }, handleCreate() { this.$router.push(/paper/create); }, handleEdit(paper) { this.$router.push(/paper/edit/${paper.id}); }, async handleDelete(paper) { try { await this.$confirm(确定删除试卷 ${paper.name} 吗, 提示, { type: warning }); const token localStorage.getItem(token); await axios.delete(/api/papers/${paper.id}, { headers: { Authorization: Bearer ${token} } }); ElMessage.success(删除成功); this.fetchPapers(); // 刷新列表 } catch (error) { if (error ! cancel) { ElMessage.error(删除失败); } } } } }; /script关键点前端需要处理认证在请求头中添加 JWT Token、错误处理拦截器统一处理 401、403、500 等状态码和用户交互反馈。5. 系统运行验证与关键功能测试完成核心代码开发后需要进行系统性的验证确保各模块功能正常数据流转正确。5.1 后端启动与基础 API 测试启动 Spring Boot 应用在 IDEA 中运行ExamSystemApplication的 main 方法或在项目根目录执行mvn spring-boot:run。观察控制台日志确认无启动错误并看到类似Tomcat started on port(s): 8080的信息。数据库表检查连接 MySQL查看exam_system数据库应自动创建了sys_user,exam_question,exam_paper,exam_record等表。使用 Postman 测试 API用户注册/登录POST/api/auth/register和/api/auth/login应能成功返回用户信息和 Token。题库管理以教师身份登录后POST/api/questions创建题目GET/api/questions查询题目列表。试卷生成POST/api/papers/generate传入组卷规则应返回一个包含题目列表的试卷对象。开始考试学生 POST/api/exam/start/{paperId}应创建一条状态为IN_PROGRESS的考试记录。提交答案学生 POST/api/exam/submit-answer提交某道题的答案后端应能正确保存到exam_answer表。交卷与判卷学生 POST/api/exam/submit/{recordId}交卷系统应触发自动判卷更新客观题分数和考试状态。5.2 前端项目启动与界面联调启动 Vue 开发服务器在 Vue 项目根目录下运行npm run serve。默认访问地址为http://localhost:8081注意端口可能与后端 8080 不同。配置代理解决跨域在 Vue 项目的vue.config.js文件中配置代理将 API 请求转发到后端。// vue.config.js module.exports { devServer: { proxy: { /api: { target: http://localhost:8080, // 后端地址 changeOrigin: true, pathRewrite: { ^/api: } } } } };功能页面遍历测试登录页输入不同角色账号能否正确跳转到对应主页管理员后台、教师控制台、学生考试中心。教师端创建题目、手动/自动组卷、发布考试、查看考试列表、批改主观题、查看成绩统计图表。学生端查看可参加的考试、进入考试界面倒计时、题目导航、自动保存、交卷、查看成绩和答案详情。管理员端用户管理、角色权限分配、系统日志查看。5.3 核心业务流程端到端测试模拟一个完整的考试流程检查数据一致性教师 T1 登录创建 10 道单选题难度中等属于“Java基础”知识点。教师 T1 使用自动组卷规则为从“Java基础”知识点中随机抽取 5 道单选题每题 10 分生成试卷 P1。学生 S1 登录在考试列表中找到试卷 P1点击“开始考试”。学生 S1 答题系统每隔 30 秒自动保存答案或手动保存。学生 S1 提交试卷。检查数据库exam_record表应有一条 S1 关于 P1 的记录状态为SUBMITTED或MARKED。exam_answer表应有 5 条记录对应 S1 对 P1 中 5 道题的作答。客观题分数应已自动计算并填入exam_answer.score和exam_record.total_score。教师 T1 登录在批改列表中应能看到 S1 的试卷总分应为客观题得分之和。学生 S1 刷新成绩页面应能看到自己的分数和试卷详情。6. 常见问题排查与解决方案在开发部署过程中你几乎一定会遇到以下问题。这里提供清晰的排查路径。6.1 数据库连接失败现象应用启动时报错Communications link failure或Access denied for user。排查步骤检查application.yml中的url,username,password是否正确。确认 MySQL 服务是否已启动sudo systemctl status mysql或查看服务列表。检查 MySQL 是否允许远程连接如果非本地。默认只允许localhost。如需远程需修改bind-address并授权用户。确认数据库exam_system是否存在。检查连接字符串中的时区设置serverTimezone建议使用Asia/Shanghai。解决方案修正配置重启 MySQL 服务和应用。6.2 JPA 表未自动创建或字段映射错误现象启动无报错但数据库中无表或运行时报Unknown column xxx in field list。排查步骤确认ddl-auto设置为update或create仅开发环境。检查实体类Entity和Table注解是否正确。检查字段名是否与数据库保留字冲突如order,desc如有冲突需使用Column(name order)转义或改名。查看启动日志中 Hibernate 生成的create tableSQL 语句与预期对比。解决方案根据日志修正实体类注解。生产环境务必使用validate并配合数据库迁移工具。6.3 前端调用 API 返回 404 或 500现象Vue 页面网络请求报错后端控制台无相关日志或有异常日志。排查步骤404: 检查请求 URL 是否正确特别是代理配置vue.config.js。打开浏览器开发者工具F12的 Network 面板查看请求的实际 URL。500: 查看后端控制台异常堆栈。常见原因空指针、数据库查询异常、JSON 序列化失败如循环依赖。跨域问题CORS如果未配置代理且前端直接访问后端端口浏览器会报 CORS 错误。Spring Boot 可通过CrossOrigin注解或全局配置解决。解决方案确保代理配置正确后端 Controller 路径匹配并处理后端业务逻辑中的异常。6.4 考试过程中答案丢失或提交失败现象学生答题后刷新页面答案没了或交卷时提示失败。排查步骤自动保存未生效检查前端定时器或防抖函数是否正常工作网络请求是否成功。查看浏览器控制台有无 JS 错误。后端保存逻辑错误检查Answer实体与ExamRecord的关联关系ManyToOne是否正确保存Answer时是否设置了关联的examRecord。事务问题确保保存操作在Transactional注解的方法内避免部分成功部分失败。并发问题同一学生同时提交多道题答案可能导致数据覆盖。考虑使用乐观锁或更细粒度的保存策略。解决方案增强前端保存提示如“保存成功”Toast后端做好日志记录和数据校验。6.5 性能问题组卷或查询列表缓慢现象当题库题目数量很大如 10000时组卷或分页查询变慢。排查步骤检查涉及查询的 SQL 语句通过show-sql: true是否使用了select *查询了不必要字段。检查是否缺少索引。例如exam_question表的type,difficulty,knowledge_point字段如果常用于查询条件应建立复合索引。自动组卷的“随机抽取”逻辑如果使用ORDER BY RAND()性能极差。解决方案为高频查询条件添加数据库索引。随机抽取改用应用层逻辑先通过条件查询出符合条件的 ID 列表然后在 Java 中随机选取。对大列表查询进行分页使用 JPA 的Pageable接口。7. 生产环境部署与安全最佳实践毕业设计演示通常在本机进行但了解生产环境的要求是加分项。以下是一些关键考量点。7.1 应用部署后端打包使用mvn clean package生成可执行的 Jar 文件target/exam-system-0.0.1-SNAPSHOT.jar。该 Jar 包包含了内嵌的 Tomcat 服务器。前端构建运行npm run build生成静态资源文件在dist目录。可以将这些文件放到后端项目的src/main/resources/static/目录下由 Spring Boot 统一服务更常见的做法是使用 Nginx 作为 Web 服务器托管前端并反向代理后端 API。进程管理生产环境不要直接用java -jar启动。推荐使用系统服务如 systemd或进程管理工具如 Supervisor来管理应用进程实现开机自启、自动重启。环境配置将数据库密码、密钥等敏感信息从application.yml移到环境变量或外部配置中心。使用spring.profiles.activeprod指定生产环境配置。7.2 数据库安全与优化权限最小化为应用创建独立的数据库用户只授予exam_system数据库的必要权限SELECT, INSERT, UPDATE, DELETE, CREATE INDEX 等禁止 GRANT、DROP 等权限。连接池配置在application-prod.yml中配置合理的数据库连接池参数如 HikariCP控制最大连接数防止连接耗尽。spring: datasource: hikari: maximum-pool-size: 10 connection-timeout: 30000 idle-timeout: 600000 max-lifetime: 1800000定期备份建立 MySQL 数据库的定期备份机制如使用mysqldump或xtrabackup。7.3 API 安全增强HTTPS为域名申请 SSL 证书在 Nginx 或 Spring Boot 中启用 HTTPS防止数据在传输中被窃听或篡改。认证与授权毕业设计中使用简单的 Session 或 JWT 即可。生产环境应考虑更完善的方案如 Spring Security OAuth 2.0。关键点密码必须加盐哈希存储使用 BCrypt切勿明文存储。输入验证与防注入后端对所有用户输入进行验证使用Valid和自定义校验器使用 JPA 的参数化查询防止 SQL 注入对输出进行编码防止 XSS。速率限制对登录、提交答案等关键接口实施速率限制Rate Limiting防止暴力破解或刷接口。7.4 监控与日志应用日志配置 Logback 或 Log4j2将日志按级别INFO, ERROR输出到文件并设置日志滚动策略。关键业务操作如登录、交卷必须记录操作日志。健康检查Spring Boot Actuator 提供/actuator/health端点可用于监控应用状态。异常监控不要吞掉异常确保所有未处理异常都能被日志记录并考虑集成 Sentry 等异常监控平台。完成以上所有步骤你将拥有一个功能完整、结构清晰、代码规范的在线考试系统。这个项目不仅能够满足毕业设计答辩的要求其模块化设计和实现思路也能为你后续开发更复杂的 Web 应用打下坚实基础。在答辩时你可以重点阐述系统架构设计尤其是前后端分离和数据库设计、核心业务逻辑如自动组卷和判卷算法以及你遇到并解决的技术难点。