资讯详情

资讯详情

SpringBoot2+Vue3+MyBatis-Plus在线考试系统开发实战详解

考试系统这个选题在 Java Web 方向里算是“常青树”了。从十多年前的 JSP/Servlet 时代到现在的 SpringBoot 前后端分离它始终是毕业设计、课程设计里出镜率最高的项目之一。原因其实不复杂它虽然看起来很教学但业务边界非常清晰从用户登录、权限控制、题库管理到在线答题、自动计时、评分统计一条线走下来前端和后端该处理的真实问题一个都不少非常适合用来检验一个人是否具备完整的 Web 开发能力。标题里这套 SpringBoot2 Vue3 MyBatis-Plus MySQL8.0 的组合是这两年最常见的搭配几乎成了课设项目的“标准答案”。SpringBoot2 负责后端接口Vue3 负责页面交互MyBatis-Plus 把 CRUD 从体力活变成半自动流水线MySQL8.0 作为数据底座稳定托底。再加上项目自带文档这套源码非常适合两类人一类是正在做毕设或课设的同学需要快速理解全流程并且真正跑起来一套能演示的代码另一类是学校、培训机构或企业内部想搭一套在线考试系统希望找一个可靠基线去做二次开发的工程师。这篇文章不贴完整源码而是把这个项目里面真正值得下功夫的地方讲透数据库表该建几张、每张表的关键字段怎么设计、JWT 登录怎么做、随机组卷和自动评分如何实现、前端哪些地方容易踩坑、部署到服务器又要改哪几个配置。把这些弄明白了你能答辩顺利也能快速把它魔改成自己想要的版本。1. 项目概述与需求拆解1.1 考试系统的核心角色与业务流程在线考试系统本质上是一个“人—题—成绩”的管理系统。人分三种管理员、教师、学生。题就是题库里的各类题目。成绩是考试结束之后产生的记录和统计。这个定位看着简单但如果你一上来就打开 Navicat 建表大概率会在第三张表开始反复删改因为很多关联关系没有提前理清楚。先看角色。管理员负责系统级配置比如用户管理、科目管理、角色分配教师负责业务内容比如维护题库、配置试卷、查看成绩统计学生是系统的主要使用者登录后选择考试、答题、提交、查看成绩。三种角色的权限边界必须在立项时就明确不然后端接口会变成一团浆糊。再看业务流程。核心链路就是登录认证 → 选择试卷 → 进入考试 → 逐题作答 → 倒计时结束或主动提交 → 客观题自动评分 → 主观题教师批改 → 成绩入库 → 成绩查询和统计。整条链路里状态流转是最容易忽略的。一张试卷或一场考试至少要区分“未开始”“进行中”“已结束”三种状态前端按钮的显示、后端接口的可调用性都要跟着状态走。1.2 搞清需求边界比写代码更早我见过不少同学拿到需求就急着写代码结果做到一半发现组卷方式跟数据库设计冲突或者权限漏洞百出。这类项目最忌讳“上来就写”。你要先画两张图一张是角色用例图一张是核心流程的时序图。不用画得多标准哪怕是白纸上手绘都行能帮你把“谁做什么数据怎么流”梳理清楚。需求边界里要重点确认的问题包括题目类型有哪些。单选、多选、判断是最基础的如果加简答题就要有主观题评分机制这意味着教师端要出现一个“批改”页面。组卷模式是手动组卷还是随机抽题。随机抽题需要题库表里存难度系数和知识点字段抽题逻辑要按难度和知识点分布去取。是否允许考试中途退出再进入。如果不允许前端要做二次确认后端要处理重复提交的幂等。是否要做防舞弊。简单版本可以记录切屏次数进阶版本可以做摄像头抓拍但这些都会增加大量复杂度毕设做基础版就够了。把这些边界问题确认清楚你后面所有表设计和接口设计都会顺畅很多。经验之谈这时候花 1 天梳理需求后面能省 3 天返工。2. 技术选型深度解析2.1 SpringBoot2它依然是后端主力很多人纠结为什么不是 SpringBoot3。答案很简单生态兼容性。SpringBoot3 要求 JDK17 起步很多老牌库还没有完全跟上而 SpringBoot2 天生配合 JDK8 或 JDK11网上能查到的资料、踩坑帖子、毕设参考案例都最多。对一个需要快速上线、快速被评审老师看明白的项目来说SpringBoot2 是最稳妥的。SpringBoot2 的价值在于“约定大于配置”。内嵌 Tomcat不用你再去部署一个独立的 Web 容器自动配置能力让你只需要在 application.yml 里写少量配置就能把数据源、MyBatis、事务管理全部拉起来。考试系统里大量接口是标准的增删改查SpringBoot 的 Controller Service Mapper 三层结构非常规整新同学能看懂有经验的人也不觉得别扭。用 SpringBoot2 还有一个实际好处Spring Security 或 Shiro 这类安全框架都有成熟版本。不过说实话毕设级别项目我建议自己用 JWT 拦截器实现认证权限代码量不大而且每个细节你都能在答辩时讲清楚。用了重量级安全框架反而容易在配置上翻车。2.2 Vue3 与组合式 API前端体验的升级Vue3 出来之后前端代码组织方式发生了非常大的变化。最核心的就是 Composition API。以前用 Options API代码是按 data、methods、computed、watch 这些选项分块的一个功能相关的代码可能被拆散到不同位置现在用组合式 API你可以把一个考试倒计时、答题状态、提交逻辑全部收拢在一起维护性明显提升。现在 Vue3 项目基本都是写script setup语法糖配合 ref、reactive、computed、watchEffect 这几个核心 API。ref 和 reactive 处理响应式数据computed 处理派生状态watch 监听变化。考试系统里最适合展示组合式 API 优势的场景就是答题页面当前题目索引、用户答案列表、剩余时间、答题进度这些状态相互关联用原生ref和computed可以写得很简洁。还要提一下构建工具。Vue3 的默认配套是 Vite而不是 Vue2 时代的 Webpack。Vite 开发环境下启动几乎秒开热更新也快对于反复调样式、调接口的联调阶段非常友好。唯一要注意的是 Vite 构建后的资源路径问题默认 base 是根路径/部署到二级目录时要把base改成相对路径或实际子路径不然页面会白屏。2.3 MyBatis-Plus 与 MySQL8.0省心省力的数据层组合MyBatis-Plus 不是给“懒人”用的而是把单表 CRUD 的效率提到极致。继承一个BaseMapperTselectById、insert、updateById、deleteById这些方法直接就能用你用不着为每张表写一大堆 XML 映射文件。考试系统里像题库增删改查、考试记录查询、用户管理这些接口大量就是单表操作用 MyBatis-Plus 能省一半代码量。更实用的是条件构造器。比如“按科目查题目并按难度排序”用LambdaQueryWrapper一行就写出来不用拼 SQL 字符串。动态条件判断用condition参数传值为 null 的时候自动跳过条件这个特性在处理查询条件时太好用了。MySQL8.0 相比 5.7 有几个实际改进。第一是默认字符集为utf8mb4可以把 emoji 和生僻字完整存下来现在用户名备注里出现特殊符号是很常见的事用 5.7 老配置会报字符集错误。第二是支持窗口函数如果要写“学生成绩排名按班级分组取前三名”这种统计 SQL会用窗口函数可以简短很多。第三是持久化配置更规范如果要用 MySQL8.0注意 JDBC 驱动要配com.mysql.cj.jdbc.Driver而且 URL 里强烈建议加上serverTimezoneAsia/Shanghai否则日期时间不同步会让你排查到崩溃。3. 核心功能模块设计与实现3.1 JWT 认证与角色权限控制这个模块是考试系统的安全底线。登录成功后后端生成一个 JWT token 返回给前端前端存在本地存储里后续每次请求前端都要在请求头里带上Authorization: Bearer token。后端写一个拦截器统一校验 token 是否有效、是否过期再把当前用户信息塞到 ThreadLocal 里供后续业务代码直接读取。JWT 生成建议用 java-jwt 或 hutool 里的 JWT 工具都可以。Payload 里放 userId、userName、role 这三个核心字段就行密码绝不能放。过期时间不要设太长考试系统一般 2 到 4 小时即可如果业务上有“记住我”的需求再考虑 refresh token但毕设阶段做成单 token 就可以了。角色权限方面我不建议引入太重的框架。可以定义一个自定义注解比如RequireRole(teacher)在拦截器里读取请求 HandlerMethod 上的注解比对 JWT 里的角色字段不一致就返回 403。这样管理员、教师、学生的接口权限就能轻松控制住。拦截器放行规则也要留意登录接口、前端静态资源这些必须放行不然会出现死循环。前端同样需要路由守卫。Vue Router 的beforeEach里判断有没有 token没有就跳登录页进入考试页面之前再调后端接口确认当前用户有没有权限参加这场考试。前后端两层都做控制才算一个完整的认证方案。3.2 题库管理、组卷策略与试卷渲染题库管理最核心的实体是“题目”。题目包含的类型、题干、选项、答案、解析、知识点、难度、所属科目这些字段是后面组卷和评分的依据。管理端要支持多选题型的增删改查题目最好支持批量导入我做过一个 Excel 导入版本用 EasyExcel 就能实现方便教师一次性录入几百道题。组卷策略有手动和随机两种。手动组卷是指教师从题库里勾选题目设定分值存进试卷随机组卷是教师设置试卷配置比如单选题 20 道、多选题 10 道、判断题 10 道并且指定难度占比系统从题库里随机抽取。考试系统通常会做随机组卷因为每个学生抽到的题可能不同可以有效减少作弊。随机组卷的实现思路是试卷表存“试卷配置”试卷与题目通过一张关联表记录。抽题时按科目、题目类型、难度分组查题库每组按需要的数量取随机记录然后插入试卷题目关联表。MyBatis-Plus 里可以用last(ORDER BY RAND() LIMIT count)做随机查询数据量在一万道以内性能没有压力这也是一种最常见的实现。试卷渲染要看前端功底。拿到试卷结构后前端要把不同题型渲染成不同组件单选题用 radio多选题用 checkbox判断题用 radio简答题用 textarea。用户选择后答案要实时同步到响应式对象里。我建议用“题目索引 - 用户答案”映射的 Map 结构不要用二维数组否则每道题的值要手动绑定索引代码会很难看。3.3 考试计时、自动交卷与自动评分考试计时是学生端最容易出 bug 的地方。最忌讳的做法是前端倒计时到 0 就本地提交因为用户可以改本地时间。正确做法是后端在考试开始接口里返回服务器当前时间和考试时长前端用这两个值计算剩余秒数只做展示倒计时结束或用户主动提交时统一走后端提交接口后端校验时间是否超时。这才是可靠的计时方案。自动交卷有两层前端倒计时归零时调用提交接口同时把本地已答的题发送过去后端接口里还要做兜底判断当前时间如果已经超过了考试截止时间就强制按已提交处理。这里要处理好重复提交的问题最简单的方案是考试记录表给user_id exam_id建唯一索引第二次插入或者更新直接返回冲突。自动评分针对客观题单选题、判断题、多选题都是比对答案字符串。单选题判断如果用户答案等于正确答案就对多选题要注意答案顺序存储时统一用固定分隔符排序比如“A,B,C”存好比对时拆开排序再拼回去避免因顺序不同误判。答对得分、答错 0 分部分多选要不要给一半分这个是要跟需求确定的细节推荐做成配置项。主观题评分需要教师角色介入。学生提交答卷后记录状态是“待批改”教师端可以看到简答题列表逐题打分最终成绩 客观题得分 主观题得分。这个过程要实现得完整就又牵连出一道题“成绩合不算”其实逻辑不复杂关键是要把状态机设计好。3.4 成绩统计与教师批改成绩统计这块很多项目会低估它的工作量。最基础的是“成绩查询”学生能看到自己的历史成绩教师能看到某一场考试所有学生成绩的列表并且可以导出 Excel。再往上一点是班级平均分、最高分、最低分、及格率、分数段分布。这些 SQL 写熟练了其实不难难点是要在前面数据库设计里把考试记录表和班级信息表预留好关联字段。教师批改功能的实现简单说就是先把客观题分数统计出来然后把一考生的简答题答案展示给教师教师输入分数后后端更新考试记录的总分和状态。这里推荐做一个小优化批改时直接用事务把客观题分数和主观题分数一起锁住更新避免两个人同时批改同一份答卷。不用搞得很复杂select ... for update就能解决问题。4. 数据库设计与核心表结构4.1 整体表设计与关联关系这一节是很多同学最头疼的地方因为表一多就乱。我先给你一个经过实践检验的表清单用户表 users、科目表 subjects、题库表 questions、试卷表 exam_papers、试卷题目关联表 exam_paper_questions、考试记录表 exam_records、学生答题详情表 answer_details、操作日志表 sys_logs。一共八张表核心业务六张日志表看需求加。关联关系是这样走users 和 subjects 是多对多一个学生可以选多门课一门课有多个学生但如果你不打算做选课系统可以直接在 users 表里加一个subject_id字段简化成一对多。questions 属于某个 subjectexam_papers 也属于某个 subject。exam_papers 和 questions 是多对多通过 exam_paper_questions 关联并且这个关联表要存“该题目在本张试卷中的分值”和“题目顺序”。exam_records 记录某学生参加某场考试的整体信息answer_details 记录该学生每一道题的具体作答。我强烈建议你在建表时就把外键逻辑写在注释里但物理外键可以不加。用逻辑外键的好处是数据导入导出时不怕外键约束卡壳查询时用连接查询或应用层组装即可性能可控。物理外键在并发写入量大时还会产生锁开销考试系统这种中等压力场景用逻辑外键完全足够。4.2 exam_records 与 answer_details 的建表思路我直接给两个核心表的建表参考。考试记录表是整条业务线的关键表它的字段决定了成绩统计好不好写。CREATE TABLE exam_records ( id bigint NOT NULL AUTO_INCREMENT, exam_id bigint NOT NULL COMMENT 试卷ID, user_id bigint NOT NULL COMMENT 学生ID, start_time datetime DEFAULT NULL COMMENT 开始时间, submit_time datetime DEFAULT NULL COMMENT 提交时间, objective_score decimal(5,1) DEFAULT 0.0 COMMENT 客观题得分, subjective_score decimal(5,1) DEFAULT 0.0 COMMENT 主观题得分, total_score decimal(5,1) DEFAULT 0.0 COMMENT 总分, status tinyint DEFAULT 0 COMMENT 0考试中 1待批改 2已完成, PRIMARY KEY (id), UNIQUE KEY uk_exam_user (exam_id,user_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT考试记录表;注意这里exam_id user_id的唯一索引就是用来解决重复提交问题的。答题详情表更细一张记录对应学生在一次考试中的一题CREATE TABLE answer_details ( id bigint NOT NULL AUTO_INCREMENT, record_id bigint NOT NULL COMMENT 考试记录ID, question_id bigint NOT NULL COMMENT 题目ID, question_type tinyint DEFAULT 0 COMMENT 0单选 1多选 2判断 3简答, user_answer text COMMENT 学生作答, correct_answer text COMMENT 正确答案, is_correct tinyint DEFAULT NULL COMMENT 是否答对 0错 1对, score decimal(5,1) DEFAULT 0.0 COMMENT 本题得分, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT答题详情表;这两张表设计好后成绩统计就非常丝滑按 record_id 聚合 detail按 exam_id 和 user_id 分组算平均分都能用简单的 SQL 完成。这里有个经验answer_details不要存学生答案的文本镜像之外多余的东西例如不要冗余存题目内容因为题目可能被编辑成绩是当时的快照如果需要留存“考试时的题目快照”建议单独用 JSON 字段存。5. 关键代码实现与踩坑实录5.1 JWT 拦截器的完整写法后端拦截器是整个系统安全的核心写的时候要考虑三个点token 取出来之后是否合法、过期时间是否有效、这次请求的操作是否有权限。代码骨架大致是public class JwtInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { if (!(handler instanceof HandlerMethod)) { return true; } String token request.getHeader(Authorization); if (token ! null token.startsWith(Bearer )) { token token.substring(7); } // 校验token解析出 userId 和 role Long userId JwtUtil.getUserId(token); String role JwtUtil.getRole(token); if (userId null) { response.setStatus(401); return false; } UserContext.set(userId, role); return true; } Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { UserContext.clear(); } }在 SpringBoot 里注册拦截器时要注意排除登录接口registry.addInterceptor(jwtInterceptor) .addPathPatterns(/**) .excludePathPatterns(/api/auth/login, /error, /api/auth/register);踩坑点注意如果不排除 Swagger 文档和上传下载接口联调时经常会被 401 卡住。如果前端上传文件用的是 multipart 请求token 不能放在 body 里必须放 header。还有afterCompletion里清理 ThreadLocal 千万别忘否则线程池复用线程时下一次请求可能读到旧用户数据这是很隐蔽的内存泄漏问题。5.2 MyBatis-Plus 条件构造器与分页插件MyBatis-Plus 的LambdaQueryWrapper是写业务查询的利器。比如教师后台要查“某个科目下难度为困难的选择题”可以这样写LambdaQueryWrapperQuestion wrapper new LambdaQueryWrapper(); wrapper.eq(Question::getSubjectId, subjectId) .eq(Question::getType, 0) .eq(Question::getDifficulty, 3); ListQuestion list questionMapper.selectList(wrapper);LambdaQueryWrapper最大的好处是类型安全字段名写错了编译期就报错不会等到 SQL 执行才暴露。动态条件时用wrapper.eq(StringUtils.hasText(name), User::getName, name)这种重载可以自动跳过空值条件非常实用。分页是考试系统列表页的刚需。MyBatis-Plus 分页需要配置一个分页插件Configuration public class MybatisPlusConfig { Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor new MybatisPlusInterceptor(); PaginationInnerInterceptor pagination new PaginationInnerInterceptor(DbType.MYSQL); pagination.setMaxLimit(500L); interceptor.addInnerInterceptor(pagination); return interceptor; } }分页查询时直接用PageUser page new Page(pageNum, pageSize)作为第一个参数传给 mapper 方法即可返回的页对象里带了总数、总页数等属性。实际踩坑不配置分页插件时selectPage并不会报错但分页不生效它会查出全表然后内存分页数据量一大就直接卡死。所以项目跑起来后一定要先验证控制台打印的 SQL 里有没有LIMIT。5.3 Vue3 前端状态管理与试卷提交逻辑Vue3 考试页面的核心状态我建议做法是试卷结构存在一个ref里用户答案存在一个reactive的 Map 对象里倒计时单独用ref。用户切换题目或者选择答案时同步更新 Mapscript setup import { reactive, ref, computed } from vue const paper ref(null) const userAnswers reactive({}) const currentIndex ref(0) function handleAnswer(questionId, answer) { userAnswers[questionId] answer } const answeredCount computed(() Object.keys(userAnswers).length) /script提交时把userAnswers转成一个数组连同examId、recordId一起 POST 给后端。这里最容易踩的坑是用户在多选题里勾选答案后需要把数组join(,)转成字符串再存不然传到后端的数据结构是不一致的。还有提交按钮要加防重复提交最简单的方案是提交后把按钮 disabled并加一个 loading 状态。考试中断的处理也要考虑。如果学生做题中途刷新页面答案是存在前端内存里的刷新就没了。可靠的方案是每做一题就把答案备份到 localStorage只有提交成功后清除。或者更强一点每次切换题目时调后端接口保存草稿。但这就引入草稿表和更多接口课设阶段用 localStorage 就够了但要在文档里说明这个限制。5.4 联调时最容易踩的 5 个坑前后端分离后联调期基本每天都能遇到新问题。我把最常见的五个坑列出来你遇到直接对照排查。第一个是跨域。SpringBoot 里要配置 CORS最简单的方式是写一个WebMvcConfigurer的跨域配置类允许来源*、允许所有方法。不想每个方法都加注解就在配置类统一处理。第二个是 Long 型 ID 精度丢失。后端主键是雪花 ID 或自增 Long前端 JS 的 Number 只能安全表示到 2 的 53 次方超过就会丢精度导致列表最后几位变成 0。解决办法是让后端序列化 Long 为 String要么加JsonSerialize(using ToStringSerializer.class)要么全局配置 Jackson 的 Long 转换规则。第三个是时区和日期格式问题。后端返回Date给前端经常出现差 8 小时或变成一串时间戳。统一做法是后端序列化时指定yyyy-MM-dd HH:mm:ss类型用LocalDateTime数据库连接串配好serverTimezoneAsia/Shanghai。第四个是空字符串转枚举报错。比如前端传一个空字符串给后端枚举字段Jackson 反序列化会直接 500。处理方式是在枚举字段上做空值转换或者全局配置允许空字符串转 null。第五个是前端代理配置。联调时前端不在服务器环境很容易直接用 axios 请求后端http://localhost:8080然后被跨域拦住。开发阶段最好用 Vite 的 server.proxy 配置代理把/api转发到后端地址这样浏览器里的请求都是同源的。到了生产环境再用 Nginx 做反向代理原理是一样的。6. 部署与运行环境配置6.1 本地环境从头跑通的步骤很多同学拿到源码后第一个卡点不是代码而是环境。这里给一个从零到一的标准流程。第一步装 JDK、Maven、MySQL8.0 和 Node。JDK 我建议用 8 或 11Maven 用 3.6Node 用 16 或 18版本不要太新新版本有时跟旧依赖会有兼容性问题。MySQL8.0 安装完一定要确认服务能启动用命令行能登进去。第二步是建库。执行项目里的 SQL 脚本比如init.sql把数据库、表和测试数据一次导入。导入完成后先查一遍关键表的数据条数。如果脚本执行报错优先检查 MySQL 版本是否低于 8.0、编码集是否为 utf8mb4。第三步改配置文件。application.yml里重点是数据库地址、用户名、密码和 Redis 配置。如果是本地单机环境没有 Redis 就用不了缓存相关的东西但很多基础版考试系统并不强制 Redis直接把缓存配置注释掉即可。启动后端前先确认 Maven 依赖能拉下来国内网络建议配阿里云镜像不然卡在下载依赖这一步能急死人。第四步启动前端。进入前端目录npm install安装依赖然后npm run dev。这里常见问题是依赖版本冲突如果项目锁定了版本建议直接用npm ci代替npm install能严格按 package-lock.json 安装。启动成功后浏览器打开前端地址先跑一遍登录、考试、提交的主流程确认整个链路通不通。6.2 服务器部署与 Nginx 反向代理本地跑通不算完考试系统最终是要给老师演示或真实使用的部署到服务器是大概率需求。后端打包用 Maven 的mvn clean package -DskipTests生成 jar 包后传到服务器用nohup java -jar xxx.jar --spring.profiles.activeprod app.log 21 启动。如果服务器内存不大可以加-Xms256m -Xmx512m限制堆内存。前端先执行npm run build生成 dist 目录然后把它放到 Nginx 的 html 目录下。Nginx 配置里最关键的是 location 转发server { listen 80; server_name yourdomain.com; location / { root /usr/share/nginx/html; index index.html; try_files $uri $uri/ /index.html; } location /api/ { proxy_pass http://127.0.0.1:8080/api/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }这个配置里try_files是 Vue Router 的 history 模式必需项否则用户直接访问某个路由地址会 404反而刷新就白屏。生产环境的 MySQL 建议单独开一台或至少配置个复杂密码不要用项目默认账号密码裸露在公网这个细节虽然老套但真能帮你避免不必要的麻烦。服务器部署还有一个容易被忽略的地方防火墙和云服务器安全组。你本地怎么都访问不了第一反应应该去检查 80 端口和 8080 端口有没有放行而不是反复重装服务。另外如果数据库是单独部署的要允许后端应用服务器的 IP 访问 3306 端口否则连不上库会一直报超时。7. 项目文档里必须写清楚的内容标题里带着【含文档】这个文档其实是整套源码里很容易被忽略分量的部分。答辩的时候老师的关注点往往不只是代码能不能跑更在于你对系统设计思路的把握。项目文档至少要包含三块需求文档、数据库设计文档、接口说明文档。需求文档不要写一堆“系统是先进的”这种空话而是写角色和流程。画清楚用例图把每个角色的可操作用例列出来再把核心流程用文字描述一遍。这部分能直接变成答辩 PPT 里的“需求分析”那页。数据库设计文档要给出每一张表的字段说明包括字段类型、含义、是否可空、默认值以及表之间的关联关系。我习惯把建表脚本直接放进文档里加一些文字注释。如果用了外键逻辑一定要画一张简单的 ER 关系图不需要用专业工具PowerPoint 就能画。接口说明文档可以用 Swagger 自动生成代码里加好注解启动项目后访问 swagger-ui 页面就能看到完整的接口列表。如果不想引入 Swagger也可以把接口整理成 Markdown 表格列出接口地址、请求方式、参数、返回结果。对课设项目来说一份清晰的 Markdown 接口文档已经足够。文档还有一个作用是帮你“复习”项目。你隔一个月再回头看自己的代码往往很多地方都忘了但如果文档写得规范你很快就能把项目逻辑捡回来这对答辩前突击准备非常有利。8. 二次开发的方向与建议如果你拿到这套源码之后不是直接交差而是想让它更有竞争力有几个性价比很高的扩展方向。第一个是支持 Excel 批量导入题库。教师手敲一道题一道题录入太低效了做导入功能等于帮教师节省大量时间。后端用 EasyExcel 解析 Excel前端提供模板下载和上传入口导入时校验题型、分值、答案字段。这个功能做完系统在老师心目中的评价会高出不少。第二个是增加考试防舞弊措施。前端定时截图或统计失去焦点次数记录到后端考试结束后教师可以查看某学生的切屏记录。实现不算复杂前端用visibilitychange事件后端存日志表但答辩时讲出来会很加分。第三个是增加一个简单的系统公告模块。管理员发布考试通知学生和教师在登录后可以看到最新公告。这是纯基础的 CRUD锻炼价值有限但实用性强适合给系统做内容填充。第四个是数据可视化。用 ECharts 画出成绩分布图、班级对比图、题目正确率图。这些图表放在首页仪表盘上整个系统的展示效果会立刻提升一个档次而且 ECharts 是纯前端库接入成本很低。这些扩展方向其实都指向同一个判断考试系统这类项目的核心竞争力不在于技术多复杂而在于业务流程闭环是否完整、细节体验是否到位。你每多考虑一个真实使用场景系统的完成度就高一分。关于这套项目最后想再啰嗦一句实际体会。我经手过不少课设和毕设项目最常看到的失败原因不是技术难题而是“版本不一致”。SpringBoot 是 2.3 的工程代码里却用了 2.7 才有的写法MyBatis-Plus 是 3.4 的版本却按 3.5 的手册配置分页插件前端的 Node 版本太高导致 node-sass 安装失败。这些版本错位带来的问题有时候比业务逻辑本身更恼火。所以拿到这套带着文档的源码第一步一定是按文档里标注的版本号逐一对齐环境再谈其他。版本对了项目跑起来就是顺理成章的事版本错了再简单的代码也能把你卡得怀疑人生。
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →