公司动态

Spring Boot在线考试系统毕设源码解析:从架构设计到部署避坑

📅 2026/8/31 17:01:35
Spring Boot在线考试系统毕设源码解析:从架构设计到部署避坑
简介这是一套基于SpringBoot开发的在线考试系统毕设项目源码面向计算机、通信、人工智能等专业的本科生及课程设计学习者解决传统考试管理效率低、流程不闭环、缺乏实时监控等实际问题适用于毕业设计、期末大作业及教学实践场景。压缩包共152个文件含74个Java后端业务与控制器类、26个HTML前端页面、14个XML配置与Mapper映射文件、8个PNG图标资源以及CSS、JS、字体文件woff/ttf/svg和基础配置文件properties、gitignore整体仅997KB结构清晰、模块完整涵盖用户管理、题库维护、在线组卷、限时考试、自动阅卷与成绩统计等核心功能。已有231人学习下载代码经答辩实测调试评审分达95分附带完整可运行环境配置与典型业务流程注释特别适合初学者理解MVC分层架构与前后端交互逻辑也便于进阶者基于现有结构扩展人脸识别监考、AI智能组卷等新功能。1. 项目整体设计与技术选型思路1.1 为什么Spring Boot在线考试系统成了毕设首选在线考试系统大概是Spring Boot毕设里出场率最高的题目。我拿到这份“基于springboot的在线考试系统源码.zip”解压一看和预想中的结构差不多一套标准的三层架构、一份完整的前端页面、外加规范化的数据库脚本。这毕业生选这个题聪明因为它的业务逻辑足够清晰——用户登录、题库管理、组卷、在线答题、自动阅卷、成绩统计每一块都有明确的业务含义又覆盖了Web开发里最常用的技术点答辩老师问起来既有内容可挖每个模块又能往深了聊。这个系统解决的核心问题很实际传统线下考试的组织成本太高。老师要手动出卷、印卷、安排考场、批改试卷、统计成绩一套流程下来几天时间就没了。而在线考试系统把这一整套流程搬到了线上考生登录后随机抽题、限时答题、交卷即出分客观题完全自动批改主观题老师在线评阅成绩自动汇总。对于毕设而言它最大的优势是“麻雀虽小五脏俱全”你能在几十张表、十几个接口里把Web开发的核心知识全部过一遍。适合拿这个项目来学习的人分三类第一类是选了这个题目的应届毕业生需要一个完整可改造的框架打底第二类是刚学完Spring Boot的初学者想找一个中等规模项目练手第三类是工作中需要快速搭一套内部考试/培训考核系统的开发人员这套架构只需少量改动就能直接上生产。1.2 技术栈选型与版本取舍从源码的pom文件能看到这套系统用的是Spring Boot 2.x MyBatis Plus MySQL Thymeleaf部分同学改成了前后端分离版后面会单独说。这套组合是近五年Java毕设的“标准答案”选它有三个原因。第一Spring Boot简化了Spring的配置地狱。传统SSM项目光配置XML就要写一整天Spring Boot通过自动配置把项目跑起来的成本降到了最低对毕设来说你能把更多的精力放在业务代码上而不是消耗在环境搭建里。第二MyBatis Plus比原版MyBatis更友好。它内置了单表CRUD的通用方法写Mapper接口的时候连SQL都不用写对于考试系统中那些高频的简单查询——比如按用户ID查考试记录、按试卷ID查试题列表——几乎就是一行代码的事。复杂查询再写XML有分有合清晰不臃肿。第三MySQLThymeleaf的组合最稳妥。MySQL不用多说开源免费部署简单是个人项目的不二选择。Thymeleaf的优势是服务端渲染页面模板和后端代码放在同一个工程里不需要额外开一个前端服务这对于单人完成的毕设来说维护成本最低。关于版本取舍这里有个实际的经验问题。当前Spring Boot已经出到3.x甚至更高很多同学新拉项目时看到版本号直接选最新版结果发现3.x要求Java 17起步很多老师机子上的JDK还是1.8代码编译不过去。在这份源码的schema里如果对方用的是Spring Boot 2.7.xJava 8就能跑千万别乱升。具体对比如下版本JDK要求MyBatis Plus版本Spring Cloud兼容Spring Boot 2.4.xJava 83.4.x / 3.5.x兼容Spring Cloud 2020.xSpring Boot 2.7.xJava 83.5.x兼容Spring Cloud 2021.xSpring Boot 3.0.xJava 173.5.5需升级至Spring Cloud 2022.xSpring Boot 3.4.xJava 174.x需升级至Spring Cloud 2024.x听我一句劝毕设项目选Spring Boot 2.7.x是一个“安全区”既能用上较完整的新特性又不会因为版本太新导致配套依赖大量报错。线上教程多、问题解决方案也多遇到BUG能查到现成的答案。1.3 前端方案Thymeleaf还是前后端分离这份源码原始版如果是单体的Thymeleaf版本那么解压后你会看到src/main/resources/templates目录下全是HTML模板。这种方案的优点是答辩演示时不需要启动两个服务一个Java进程把所有页面全包了逻辑闭环很完整。但如果你打算二开升级我也会推荐看一眼前后端分离的方案。Spring Boot写RESTful接口、前端用Vue管理页面是现在企业开发的主流模式放在简历和答辩展示上是明显的加分项。缺点是你要额外维护一套Vue工程毕设周期要留出至少两到三周的富余。无论选哪种后端核心逻辑是不变的Controller层负责接口收发、Service层负责业务、Mapper负责数据库交互。我通常建议学生把Controller里的代码写得薄一些验证逻辑和业务逻辑全部下沉到Service层这样既能保持controller美观也为后续做单元测试留好了入口。2. 核心业务模块与数据库设计2.1 六步拆解在线考试系统核心模块这份系统的业务模块大体可以拆成六大块每一块的职责都很集中。第一用户模块。包含用户注册、登录、个人信息维护、密码修改。其中登录是整系统的入口从安全角度考虑需要加上密码加密存储BCrypt、登录状态维护Session或JWT、接口访问拦截。很多毕设在这一步会偷工减料密码直接明文存库答辩时一旦被问到安全问题很容易卡壳。如果你拿到的源码里是明文密码建议至少改成MD5加盐或者直接用Spring Security里的BCryptPasswordEncoder。第二题库管理模块。管理员对试题做增删改查一般按题型分类单选题、多选题、判断题、填空题、简答题。每道题绑定知识点分类和难度等级为后面的智能组卷提供数据支撑。这里的关键点是字段设计时要把“正确答案”单独存一个字段并且前端编辑时不应把正确答案直接显示给普通考生。第三考试管理模块。创建一场考试设置考试名称、考试时间开始-结束、总时长、总分、及格分关联试卷指定参加考试的用户可以直接选全专业或按班级批量导入。这块的细节在于考试状态机的设计——未开始、进行中、已结束不同状态下操作的权限完全不同。第四在线考试模块。考生进入考试后系统按规则分配试卷要么一进考场直接固定试卷要么从题库随机抽题页面显示题目和倒计时定时器到点自动交卷考生可以逐题作答、标记待定题类似驾照科目一那种、上一题下一题切换。第五自动阅卷模块。交卷后系统自动对客观题单选、多选、判断进行比对计分。多选题要支持“少选给一半分、多选错选不给分”这种常见策略。填空和简答要么做关键词匹配要么转人工阅卷。第六成绩与统计模块。考生查看自己的考试成绩与每道题的作答详情包括正确答案、得分情况管理员查看成绩分布、平均分、及格率、每个知识点的得分率导出Excel格式的成绩单。六个模块串起来就是一个完整的考试闭环准备题库 → 设置考试 → 分配考生 → 在线作答 → 自动判分 → 成绩分析。任何一个正规的毕设答辩必然围绕这个流程逐一提问提前把这条线理清答辩就稳了。2.2 数据库表结构设计要点数据库是这个项目的灵魂表设计得好后面代码能省一半力气。这套系统大概要建这几张核心表表名用途关键字段sys_user用户表id, username, password, real_name, role_id, dept_id, create_timesys_role角色表id, role_name, role_codeADMIN/TEACHER/STUDENTexam考试表id, exam_name, start_time, end_time, duration, total_score, pass_score, status, exam_codeexam_paper试卷表id, paper_name, exam_id, total_score, question_countquestion题库表id, question_type, content, options(JSON), answer, analysis, difficulty, knowledge_point, scoreexam_question试卷试题关联表id, paper_id, question_id, score, sort_orderexam_record考试记录表id, user_id, exam_id, paper_id, start_time, submit_time, duration, score, statusanswer_detail答题明细表id, record_id, question_id, user_answer, is_correct, score其中两个细节值得重点提一下。第一个question表的options字段。对单选题和多选题四个选项如果用四个独立字段存option_a, option_b, option_c, option_d再往后要扩展为六个选项比如多选题的六选三就得改表结构。用JSON类型存选项列表最灵活MyBatis Plus里也有TypeHandler可以自动转换取出来就是List页面渲染时直接遍历。题目选项配置在JSON里是一个实用且专业的做法。第二个exam_record和answer_detail是一对多的关系。一场考试的一条记录含几十条答题明细统计分析时必须根据这两张表做关联聚合。例如计算平均分就是对exam_record中的score字段取AVG计算某知识点的平均正确率就要把answer_detail JOIN question再按knowledge_point分组统计。另外几乎所有核心表都要带create_time和update_time两个字段一方面方便排查问题另一方面答辩时“逻辑删除、自动填充”都是可以展示的加分细节。MyBatis Plus的MetaObjectHandler可以在插入和更新时自动填充这些字段减少重复代码。2.3 三种角色权限模型在线考试系统里的角色权限设计是这个项目里最容易被问、也最值得展示的部分。通常拆成管理员Admin、教师Teacher、学生Student三个角色对应三套不同的功能权限。管理员管系统全局用户管理、角色授权、考试开关、数据统计。教师管业务题库录入、试卷配置、考试创建、人工阅卷、导出成绩。学生只使用参加考试、查看成绩、历史记录查询。权限控制的实现在单体的非Security方案中最常见的就是“登录拦截器 注解校验角色”。放一个示例这种写法比零散的session判断专业得多Target(ElementType.METHOD) Retention(RetentionPolicy.RUNTIME) public interface RequireRole { String[] value(); }然后在拦截器的preHandle方法里判断当前登录用户的角色是否在注解允许的范围内Component public class AuthInterceptor implements HandlerInterceptor { Autowired private UserService userService; Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 放行登录接口 if (request.getRequestURI().contains(/login) || request.getRequestURI().contains(/register)) { return true; } HttpSession session request.getSession(); Object userId session.getAttribute(userId); if (userId null) { response.setStatus(401); request.getRequestDispatcher(/login).forward(request, response); return false; } // 处理注解权限 if (handler instanceof HandlerMethod) { HandlerMethod handlerMethod (HandlerMethod) handler; RequireRole requireRole handlerMethod.getMethodAnnotation(RequireRole.class); if (requireRole ! null) { SysUser user userService.getById((Integer) userId); String roleCode user.getRoleCode(); if (!Arrays.asList(requireRole.value()).contains(roleCode)) { response.setStatus(403); return false; } } } return true; } }注册拦截器把需要放行的路径排除掉比如静态资源/css/**、/js/**、/images/**以及登录页本身。这样一套清晰的权限链就建立起来了。如果项目中引入了Spring Security则用SecurityFilterChain配合PreAuthorize(hasRole(ADMIN))注解也行但核心判断逻辑其实一样。3. 核心流程实现与关键代码解析3.1 登录会话与安全拦截先说登录接口。前端把用户名和密码POST到后端后端先做参数校验再调Service层查询用户比对加密密码如果是明文密码建议改成BCrypt验证通过后把用户信息放进Session返回用户基本信息和角色标识。这里有一个很现实的坑很多系统直接把密码放到JSON里返回给前端这是不对的。即使密码经过了加密存储也不应该出现在返回结果里。正确做法是构建一个不含敏感字段的VO对象UserVO只返回id、用户名、姓名、角色、头像密码和token这样敏感信息不放在响应体里只在服务端往Session里存。Data public class UserVO { private Integer id; private String username; private String realName; private String roleCode; private String avatar; }写一个工具方法public static UserVO transToVO(SysUser user) { UserVO vo new UserVO(); BeanUtils.copyProperties(user, vo); // 确保敏感字段不被复制 vo.setPassword(null); vo.setSalt(null); return vo; }这能杜绝“密码泄露”这种低级问题。登录后的所有请求都通过拦截器校验Session里是否有用户信息。这里再强调一次登录接口一定在拦截器的排除名单里否则就变成死循环了新手常在这里绕圈。3.2 智能组卷随机抽题的几种实现方式组卷是整个系统最有技术含量的部分。考试场景中“一场考试每人拿到一套局部不同的题目”能有效防止考生之间互相抄答案所以这个功能几乎是评委必问。常见的抽题方式有三种难度递增。最简单的是ORDER BY RAND()。写一条SQL按题型过滤后直接随机排序取前N条SELECT * FROM question WHERE question_type SINGLE AND difficulty 2 ORDER BY RAND() LIMIT 10;优点是代码量最少缺点也很明显数据量一旦上万ORDER BY RAND()需要把整张表加载到内存再做全表排序极耗性能而且并发高时数据库压力飙升。这套源码如果在数据量较小几百道题时完全能胜任答辩时可主动说明这个问题引到更优方案。更稳的方式是先把符合条件的试题ID查出来在Java里做随机再用ID查详细信息public ListQuestion randomPickQuestions(Integer type, Integer difficulty, int count) { // 1. 查出该类型符合条件的全部编号 ListInteger ids questionMapper.selectIdsByTypeAndDifficulty(type, difficulty); // 2. 随机打乱 Collections.shuffle(ids); // 3. 截取前 count 个 ListInteger pickedIds ids.stream().limit(count).collect(Collectors.toList()); // 4. 用编号批量查题目详情 return questionMapper.selectBatchIds(pickedIds); }这种做法的好处是随机性完全掌握在业务层数据库层面只做一次索引扫描性能开销可控。同时还能在选完题后做一次可用性检查如果pickedIds的数量少于考试要求的题量就提示“题库库存量不足”。到了工程化程度更深一层可以给每个知识点设置抽题占比。比如考试总分100分单选题40分、多选题30分、判断题30分单选题里“基础”占50%、“进阶”占30%、“困难”占20%。那么这个配置就可以做成一张paper_rule表考试创建者在前端配置后后端按权重分配去题库抽取这基本就达到了市场上商业考试系统的水平。这份毕设如果做到这一步可以归类为“优秀档次”的差异化设计。3.3 自动阅卷与计分策略自动阅卷是高频考点逻辑本身不复杂关键是选择题的各种判分策略要把细节想全。答题明细表answer_detail中的每条记录都要算一遍分数然后汇总到exam_record的score字段。单选题用户答案和正确答案完全一致得满分否则0分。多选题这是最容易产生歧义的部分。有些多选题漏选得一半分有些漏选也得0分。常用的计分策略如下public BigDecimal markChoice(Integer questionType, String correctAnswer, String userAnswer, BigDecimal fullScore) { // 单选 if (questionType 1) { // 没有作答 if (StringUtils.isBlank(userAnswer)) { return BigDecimal.ZERO; } return correctAnswer.equalsIgnoreCase(userAnswer) ? fullScore : BigDecimal.ZERO; } // 多选 if (questionType 2) { char[] corrects correctAnswer.toUpperCase().toCharArray(); char[] users userAnswer null ? new char[0] : userAnswer.toUpperCase().toCharArray(); SetCharacter correctSet new HashSet(); SetCharacter userSet new HashSet(); for (char c : corrects) correctSet.add(c); for (char c : users) userSet.add(c); // 有错选或漏选统一给0分 if (!correctSet.equals(userSet)) { return BigDecimal.ZERO; } return fullScore; } // 判断 if (questionType 3) { return correctAnswer.equalsIgnoreCase(userAnswer) ? fullScore : BigDecimal.ZERO; } return BigDecimal.ZERO; }判断题与单选题雷同。填空题和简答题可以走“人工阅卷”流程交卷后客观题立刻得分主观题状态置为待批改老师在后台逐题给分最后更新考试记录的总分并重算及格状态。交卷时要处理一个并发边界场景考试时间到了系统自动交卷考生自己提前交卷这两种入口都必须调用同一个交卷逻辑。我的建议是把交卷逻辑单独抽出定时任务兜底定时扫描超过end_time仍未提交的exam_record强制回收并自动判分防止考生卡在页面不退导致状态永远悬挂。3.4 成绩统计与Excel导出成绩统计的功能点包括按考试场次统计平均分、最高分、最低分、及格率、优秀率通常把80分及以上定义为优秀、分数段分布0-59、60-69、70-79、80-89、90-100按知识点统计正确率生成“雷达图”或“柱状图”给考生做能力画像。这一部分由于数据量不大直接用Java 8 Stream在内存中汇聚即可不需要上聚合框架。答辩时建议带上Excel导出功能因为这是企业开发中几乎躲不掉的功能点。用Apache POI简单生成Excel文件的代码可以这样写public void exportExamScores(OutputStream out, Integer examId) throws IOException { ListScoreVO list examRecordMapper.selectScoreByExam(examId); XSSFWorkbook workbook new XSSFWorkbook(); Sheet sheet workbook.createSheet(成绩单); Row header sheet.createRow(0); header.createCell(0).setCellValue(姓名); header.createCell(1).setCellValue(学号); header.createCell(2).setCellValue(得分); header.createCell(3).setCellValue(用时); header.createCell(4).setCellValue(状态); int rowNum 1; for (ScoreVO vo : list) { Row row sheet.createRow(rowNum); row.createCell(0).setCellValue(vo.getRealName()); row.createCell(1).setCellValue(vo.getUserNo()); row.createCell(2).setCellValue(vo.getScore().doubleValue()); row.createCell(3).setCellValue(vo.getDuration()); row.createCell(4).setCellValue(vo.getStatus()); } workbook.write(out); workbook.close(); }实现容易但安全要注意两点文件流要记得关闭建议用try-with-resources生成文件直接写到HttpServletResponse输出流即可不需要在服务器上长期落盘防止垃圾文件堆积。4. 从源码到可运行部署与演示4.1 解压源码后的目录结构解读拿到zip先别急着启动。先花十分钟把项目结构过一遍做到心里有数。典型的Spring Boot Maven工程长这样course-exam/ ├── src/main/java/com/example/exam/ │ ├── controller/ │ ├── service/ impl/ │ ├── mapper/ │ ├── entity/ │ ├── config/ │ └── common/ ├── src/main/resources/ │ ├── mapper/ # MyBatis XML文件 │ ├── templates/ # Thymeleaf页面 │ ├── static/ # JS/CSS/图片 │ └── application.yml # 核心配置 ├── sql/ # 数据库初始化脚本 └── pom.xml先看application.yml再打开sql目录下的建表脚本。如果源码里没有sql目录就要在项目里搜索.sql文件很多项目的建表语句放在db或doc目录下。4.2 本地环境配置与启动全流程本地跑起来需要准备几个环境JDK 1.8或更高、Maven 3.6、MySQL 5.7、IDE推荐IDEA。如果机器上没装先装齐缺一个后面都会卡壳。第一步创建数据库并导入初始化数据。打开MySQL客户端执行以下命令CREATE DATABASE IF NOT EXISTS exam_system DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_general_ci; USE exam_system; SOURCE /你的路径/sql/exam_system.sql;导入成功后用SHOW TABLES;检查一下表是否齐全重点看各个表的记录数。题库表里有几百条示例数据就对了如果全是空的说明脚本不全后面考试跑不起来。第二步修改application.yml里的数据库连接信息server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/exam_system?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/Shanghai username: root password: 你的数据库密码 driver-class-name: com.mysql.cj.jdbc.Driver mvc: # 如果没有配置静态资源路径可以省略这段 jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT8 mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl type-aliases-package: com.example.exam.entity这段配置里的两个“坑位”需要说清楚。第一个是连接串上的serverTimezoneAsia/Shanghai不加的话本地和MySQL时区不一致会出现“差8小时”的日期错乱。第二个是useSSLfalse本地开发不必开启SSL加了反而可能在MySQL 8.x上由于证书问题连不上。第三步IDEA导入项目。直接Open选中根目录等Maven自动拉依赖。如果发现下载缓慢检查一下Maven的settings.xml是否配置了国内镜像。拉完依赖后跑ExamApplication的main方法看到“Started ExamApplication”字样就成功了。浏览器敲http://localhost:8080就能看到登录页。4.3 Docker部署一人开发、组内评审的便捷方案如果你的演示环境不是本地电脑而是要发给老师评审或者放到组内云服务器上Docker是最高效的部署方式。在源码根目录下新建一个Dockerfile内容简洁安全# 构建阶段 FROM maven:3.8.5-openjdk-8 AS builder WORKDIR /app COPY pom.xml . COPY src ./src RUN mvn clean package -DskipTests # 运行阶段 FROM openjdk:8-jdk-slim WORKDIR /app COPY --frombuilder /app/target/exam-system-0.0.1-SNAPSHOT.jar app.jar EXPOSE 8080 ENTRYPOINT [java, -jar, app.jar]然后一个docker-compose.yml把MySQL和应用编排在一起这样检测方不用动本机环境。version: 3 services: mysql: image: mysql:5.7 container_name: exam-mysql environment: MYSQL_ROOT_PASSWORD: root123456 MYSQL_DATABASE: exam_system ports: - 3306:3306 volumes: - ./sql/init.sql:/docker-entrypoint-initdb.d/init.sql:ro exam: build: . container_name: exam-app ports: - 8080:8080 depends_on: - mysql environment: SPRING_DATASOURCE_URL: jdbc:mysql://mysql:3306/exam_system?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/Shanghai SPRING_DATASOURCE_USERNAME: root SPRING_DATASOURCE_PASSWORD: root123456执行docker-compose up -d --build等几分钟打开http://服务器IP:8080就能访问。到这里一步部署就完成了。这种方式用在团队评审或远程答辩演示会非常加分它证明你不只会本地跑通还具备基本的容器化交付意识。4.4 答辩演示前必须准备的演示脚本在线考试系统这类项目管理性功能多演示时最容易出现“现场点错按钮找不到功能在哪”。提前把演示路径梳理成脚本先讲场景、再讲操作、最后讲数据变化远比临时上手更稳当。具体的演示节奏可以按“三段式”来设计。首先是管理员视角登录管理员账号 → 进入题库管理添加一道新的单选题现场录入一道直接展示数据变化→ 创建一场考试选择试卷、配置考试时间 → 设置考生名单 → 发布考试。这一段的核心是把“管理员能做什么”讲透。然后切换到学生视角重新开一个浏览器隐身窗口登录一个考生账号 → 进入待考列表选择刚创建的考试 → 开始答题现场做掉两三题故意做错一题 → 交卷。交卷后立刻展示成绩页指出那道错题的正确答案和解析。这一段是全场的高潮因为即时判分的效果非常直观。最后回到管理员视角进入成绩统计页Excel导出成绩单 → 人工评阅主观题 → 给打分 → 回到考生端刷新查看总分变化。到这里业务闭环完全展示结束整场演示控制在10分钟以内比较合适。5. 常见问题与避坑实录5.1 环境与启动类问题速查问题现象可能原因解决思路启动时提示“端口被占用”8080已被其他程序占用netstat -ano连接MySQL报Access denied密码错误或远程权限不足用root登录MySQL执行ALTER USER rootlocalhost IDENTIFIED BY 新密码;刷新配置启动成功但访问404没有扫描到Controller检查启动类的位置是否在最外层包SpringBootApplication默认扫描其所在包及子包页面中文乱码数据库或连接串未指定UTF-8建库时用utf8mb4连接串加characterEncodingutf8检查模板文件编码为UTF-8日期字段差8小时时区未配置连接串加serverTimezoneAsia/ShanghaiIDEA导入后Maven依赖报红依赖下载失败或版本冲突先mvn clean再mvn package -DskipTests若慢则配置阿里云镜像页面有数据但接口报500Mapper XML映射错误打开控制台看SQL执行的完整报错重点检查resultMap配置与实体属性对应5.2 开发过程中最常见的三宗罪第一宗罪密码不加密。源码里要是明文密码即使不是出于毕设的要求也建议把注册和登录改成BCrypt加密。这一步很简单但安全提升巨大属于“用一天改造、终身受益”的事。第二宗罪删数据用DELETE。正确的做法是给表加deleted字段0正常、1删除查询时自动过滤这样误删之后还能快速恢复。演示时当着评委的面“把一条题目删掉再复原”这是能让人印象深刻的细节。第三宗罪前端页面直接提交答案。考试页面如果直接把answer字段POST到后端随便一个会看F12的人都能改分数。正确的设计是后端交互只传questionId和userAnswer用户答案在前端做脱敏后端存储通过answer_detail关联处理不给用户直接干预的机会。5.3 源码中关于Spring Boot版本太高问题的补充刚才在技术选型里提过版本问题实战中还会遇到另一个常见情况从网上下载的开源项目里pom用是Spring Boot 2.7.x但你写的是“idea新建项目没有springboot 3.4.3选项”这种新版本。其实IDEA新建项目时的Spring Initializr默认连的官网地址如果官网服务临时有问题可以换成阿里云的初始化地址或者手动在pom里声明父工程parent groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-parent/artifactId version2.7.18/version relativePath/ /parent这个版本已经是2.x的最末代各兼容库投入市场时间足够久稳定性极佳。反过来如果你就是要用最新版3.4.x那要同时把Java切到17、MyBatis Plus换到mybatis-plus-spring-boot3-starter3.5.5以及检查其它中间件依赖一个都不能落下。升级过程中最容易报错的是javax.* 到 jakarta.* 包名的迁移问题这个不做完整体验很难一次通过所以我的建议依旧是毕设优先求稳。5.4 小组协作与二次开发的流程建议如果是和小组成员一起改造这套源码建议先跑通完整链路再分模块开发。每人的分工可以按照“题库/考试管理”“在线考试/阅卷”“统计分析/系统管理”去切每个人负责独立的一条业务线减少合并冲突。代码管理用Git一个分支放在main上稳定版本功能分支完成后合回主分支。为了保证每次修改不破坏现有功能改动前先手写简单的测试用例——不用引入JUnit全套只需要在对应功能上手工回归重要的核心流程比如交卷自动判分每次改动后必须跑一遍。最后再分享一个小技巧做在线考试系统这类项目代码写得再漂亮如果数据量看不出效果也很吃亏。我建议在你的题库表里灌入300道左右的模拟题覆盖不同难度再给用户表里造出20-30个测试账号、几十条考试记录。这样打开成绩统计页折线图、柱状图、分数分布都有数据可看联调时的页面效果和答辩说服力完全不“空城”。我在实际带毕设时经常提到一个判断项目完成质量的土办法把管理员、教师、学生三个角色的账号全部各登一遍按业务流程从头走到尾中途不修改代码不重启服务只要全程无感知顺畅说明这个系统的状态管理和数据流转是过关的。这个办法不复杂但比看一堆页面截图有效得多上线前照着跑一遍心里会很踏实。这套源码是一个很好的起点不是终点。把核心的登录、组卷、阅卷逻辑吃透了往里面加“考试防切屏监控”“成绩导出到PDF”“按部门/班级的权限树”都是很自然的扩展方向。毕竟技术的价值从来不在于运行一个Demo而在于你能在这个基础上造出属于自己的一套东西。本文还有配套的精品资源点击获取