1. 这个项目到底是什么,为什么值得做
很多准备毕业设计或者课程设计的同学都会面临同一个问题:题目看起来都差不多,但真正动手做的时候才发现坑一个接一个。今天我想复盘一个非常经典、也特别适合拿来当毕设或课设的完整源码项目——SpringBoot + Vue 的在线考试与学习交流网页平台。这个项目我在实际带学生的过程中反复推荐过,自己也完整走通过一套,从数据库设计到前后端联调,踩了不少坑,积累了不少经验,这里一次性整理出来。
先一句话说清楚这个东西是什么:它是一套基于 Java 技术栈的全栈 Web 管理系统,后端用 SpringBoot 搭 RESTful API,前端用 Vue 框架做单页应用,数据存储用 MySQL,通过 MyBatis-Plus 做持久层操作。系统核心解决三个问题:在线考试(创建试卷、答题、自动判分)、学习交流(发帖、回帖、互动)、后台管理(用户、题库、试卷、公告、权限)。如果你正在准备毕设,这套系统的功能量、技术深度、扩展空间都卡在了一个非常合适的档位上——不会简单到让答辩老师觉得没含量,也不会复杂到让新手在一个月内做不完。
适合谁来参考?我建议这几类人可以重点关注:一是正在选毕设题目的大四学生,二是做 Java 课程设计的在校生,三是想从前端或者纯后端转向全栈的初学者。你会从这套源码里学到的不只是一个“能跑的系统”,而是 SpringBoot 项目结构怎么划分、Vue 路由和状态管理怎么组织、MySQL 表结构怎么满足业务需求、前后端联调时常见的坑有哪些。
和那些纯 CRUD 的管理系统不同,在线考试平台有两个比较有代表性的业务难点:一个是试卷生成的随机策略,一个是自动评分的判定逻辑。这两个功能做扎实了,整个系统的含金量立刻不一样。而且这个题目天然带有“学习交流”模块,比单纯考试系统多了一层互动性,这也符合现在很多高校对线上教学平台的现实需求。
我在梳理这套源码的时候有一个很深的体会:它的技术选型非常“主流”。SpringBoot + Vue + MySQL 这套组合在当前 Java 就业市场里几乎算是入门标配,面试时候聊项目,面试官大概率不会陌生。换句话说,写完这个项目,你不仅交了毕业设计,还顺带给自己攒了一个能写进简历的项目经历。
2. 整体设计和模块拆解,先想清楚再动手
2.1 角色权限是地基,一开始就要定好
在线考试平台最少要区分三种角色:管理员、教师、学生。有些系统会再拆出“超级管理员”,但我建议初期三种就够。管理员负责全局配置,包括用户管理、公告发布、数据统计;教师负责题库维护、试卷创建、阅卷与成绩查看;学生负责在线考试、查看成绩、参与学习交流。权限设计直接决定路由和接口的拦截策略,如果一开始不规划好,后面会改得非常痛苦。
前端路由需要按角色做动态权限控制,我的做法是登录成功之后,后端返回当前用户的角色标识,前端根据角色动态生成可访问的路由表,而不是把全部路由一股脑注册进去。比如管理员才有用户管理页面,教师才有题库管理页面,学生只有考试中心、成绩查询、交流社区这些入口。后端的拦截器负责二次校验,每个接口都校验当前登录用户的角色是否匹配,防止有人绕过前端直接调接口。
角色权限这块我建议用一个简单的 RBAC 模型,不要搞太复杂。三张表:用户表、角色表、用户角色关联表。虽然 SpringSecurity 或者 Sa-Token 都能做细粒度权限,但毕设项目里用拦截器 + 注解结合的方式完全够用,而且代码更好理解。答辩时老师问你“权限是怎么实现的”,你能把拦截和角色判断讲清楚,比甩一个框架配置更有说服力。
2.2 核心业务流程,考前考中考后三个环节
在线考试的业务流程可以拆成三段来理解,每一段都有它要处理的特殊情况。
考前的核心是组卷。教师从题库里按题型和难度选题,系统要支持手动选题和自动抽题两种模式。手动选题就是教师一道一道勾选;自动抽题需要设计规则,比如“单选题 20 道、每题 2 分”“多选题 10 道、每题 3 分”“判断题 10 道、每题 1 分”,系统从题库里随机抽取对应数量的题目组成一张试卷。这里的关键点是:抽过的题和没抽过的题要区分清楚,还要防止同一张试卷里出现重复题目。
考中的核心是答题和计时。学生进入考试页面后,前端开始倒计时,后端在发起考试时生成一条考试记录,记录开始时间。交卷方式有两种,主动交卷和到时自动交卷。主动交卷前端把答题数据一次性提交给后端;自动交卷需要后端定时任务扫描超时的考试记录,把未交卷的强制置为交卷状态。这个逻辑看起来简单,但涉及并发场景,后面我会专门说坑在哪。
考后的核心是判分和成绩分析。客观题(单选、多选、判断)由系统自动判分,主观题需要教师手动阅卷。自动判分的逻辑不复杂,就是拿学生的答案和标准答案比对,但多选题的比对有一些细节处理:全对才得分,漏选、错选都不得分,或者漏选得一半分,具体规则要根据题目设置来定。成绩分析就是简单的统计:平均分、及格率、分数段分布,这些可以用 SQL 聚合查询直接做。
2.3 学习交流模块,别做得太复杂但要有互动性
学习交流模块本质是一个轻量论坛,功能上只需要四个东西:帖子列表(支持分页和关键词搜索)、发帖、回帖、帖子详情。如果能再加一个“我的帖子”和“帖子分类”,体验会更好。回复里可以设计一个楼层概念,展示“第几楼”,这个对新手理解 Map 或 List 的排序逻辑有点帮助。
为什么我建议学习交流模块不要做太重?因为这个模块的定位是辅助功能,它的存在意义是让平台不只是“做题工具”,还能承载学习氛围。你把发帖、回帖、搜索做扎实,已经完全能满足毕设的功能要求。如果时间充裕,可以再加点赞或置顶,但优先级排在考试核心功能后面。
2.4 数据库表设计,体现实力的关键部分
数据库设计是整个项目里最体现基本功的地方。我建议核心表不少于十张:用户表、角色表、用户角色关联表、公告表、题目表(可以按题型分表,也可以单表加题型字段)、试卷表、试卷题目关联表、考试记录表、答题详情表、帖子表、回复表。这里有一个设计决策值得展开讲讲:题目类型用单表加字段,还是每种题型单独建表。
我测试过两种方案,单表加类型的做法在毕设级别是完全够用的。你把字段设计齐全:题目类型(单选、多选、判断、简答)、难度等级、所属科目、题目内容、选项内容(用 JSON 字符串存储四个选项)、标准答案、分值。用 JSON 存储选项的好处是前端渲染灵活,坏处是查询时如果想按选项内容搜索会麻烦一点,但在我们这个业务场景下,几乎没有那种搜索需求,所以取 JSON 是划算的。
试卷表的核心设计思路是“一表一试卷,一表一关联”。试卷表只存试卷的基本信息,比如标题、总分、考试时长、创建人;试卷题目关联表存的是这份试卷包含了哪些题目,每个题目的分值在关联表里单独记录。为什么要单独记录?因为同一个题目可以被不同试卷引用,而且在不同试卷里分值可能不同。这个设计直接体现你有没有理解关系型数据库的建模思路。
考试记录表和答题详情表是联动的。考试记录表一条数据代表学生的一次考试进程,包含学生 ID、试卷 ID、开始时间、交卷时间、最终得分、状态;答题详情表每条数据对应一道题目的学生作答记录,包含考试记录 ID、题目 ID、学生答案、是否得分。成绩查询的时候,先查考试记录,再按记录 ID 去查答题详情,这样数据的层次感就出来了。
3. SpringBoot 后端核心实现,从项目搭建到业务落地
3.1 项目分层和依赖选型
SpringBoot 项目的目录结构我比较推荐这样分:controller 层接收请求、service 层处理业务逻辑、mapper 层操作数据库、entity 实体类、dto 传输对象、vo 视图对象、config 配置类、common 存放通用返回结果和异常处理。这个分层方式非常标准,答辩时老师一看就知道你受过正规训练。很多新手喜欢把所有代码写进 controller,当时看起来快,后面维护任何一个功能都要改动 controller,项目越改越乱。
依赖方面我和大家说一下我实际用的版本组合,这是踩过坑之后固定下来的:SpringBoot 2.7.x、MyBatis-Plus 3.5.x、MySQL 8.0、JWT 用 jjwt 0.9.1(或者 java-jwt 3.x 也行)、Hutool 工具包、Lombok。SpringBoot 2.7 是目前比较稳妥的版本,网上资料多,遇到问题很容易搜到解决方案。MyBatis-Plus 帮我们把单表 CRUD 全部省掉了,你不用再写基础的增删改查 XML,只需要写复杂 SQL。Lombok 用 @Data 注解省掉 getter/setter,代码量肉眼可见地减少。
3.2 统一返回格式和全局异常,越早做越省事
如果你不想所有接口的返回结构乱七八糟,一定要在第一行代码之前定义统一返回对象。我的做法是一个 Result 类,里面三个字段:code、msg、data。成功时 code 是 200,失败时是 4xx 或 5xx。前端 axios 拦截器判断 code 来决定是走成功回调还是弹出错误提示。这个类虽然简单,但它统一了整个项目的信息交换格式,所有接口的返回都是同一个模板。
全局异常处理用 @RestControllerAdvice 注解实现。它的作用是:当 service 层抛出业务异常时,不需要每个 controller 都写 try-catch,而是集中到一个地方处理。比如用户注册时用户名已存在,service 层直接抛一个 BusinessException("用户名已存在"),全局异常处理器捕获后返回给前端一个 code 为 400 的 Result。这样代码干净,逻辑也统一。
3.3 JWT 登录认证,自己动手实现一次就懂了
登录认证我强烈建议用 JWT,不要用 Session。虽然 Session 更简单,但前后端分离项目里 JWT 是更合理的方案。流程是这样的:用户提交用户名密码,后端校验通过后生成一个 token 字符串,把它返回给前端,前端存在 localStorage 里,之后每次请求都在请求头带上 Authorization 字段。后端有一个拦截器,在请求进入 controller 之前先取 token,解析 token 得到用户 ID 和角色,如果解析失败就返回 401 未登录。
这里有很多新手容易犯的错误是:拦截器写好了但忘记放行登录接口,结果用户还没登录就被拦截了,登录接口本身都访问不了。所以白名单一定要配好,比如 /api/auth/login、/api/auth/register 这些接口要放行,前端静态资源如果有的话也要放行。
我在源码里还会顺手实现一个注解叫 @RequireRole,可以加在 controller 的方法上,用来标记这个接口需要什么角色才能访问。拦截器在 JWT 解析完成之后,再检查当前用户角色是否满足注解要求,不满足就返回 403。这个设计比每个接口自己写 if 判断要优雅很多。
3.4 自动组卷的随机策略,核心代码拆开讲
自动组卷是我认为最能体现逻辑思维的模块。教师提交一个组卷规则,比如单选题 20 道每题 2 分、多选题 10 道每题 3 分。后端要做的事情是:从题库中把符合“科目 + 题型”条件的题目全部查出来,然后随机抽取指定数量,最后把选中的题目写入试卷题目关联表。
随机抽取我建议用 MyBatis-Plus 的 LambdaQueryWrapper 配合 ORDER BY RAND() 实现。简洁的做法是这样:
List<Question> questionList = questionMapper.selectList( new LambdaQueryWrapper<Question>() .eq(Question::getSubjectId, subjectId) .eq(Question::getType, type) .orderByAsc(RAND()) .last("limit " + count) );这里有两个需要注意的细节:第一个是.last("limit " + count)的写法,MyBatis-Plus 提供了 last 方法拼接 SQL 片段,但如果有用户输入的 count 参与拼接,必须要做好类型校验防止 SQL 注入;第二个更关键,如果题库里符合条件的题目数量少于要求数量,程序要提前处理,抛出“题库数量不足”的异常,而不是让 MySQL 返回一个奇怪的错误。
抽完题之后还有一个隐藏步骤——校验总分。组卷规则里写的分数加起来应该等于试卷总分,但老师在配置时可能粗心大意配错了,所以后端在创建试卷前一定要做一遍总分校验,如果不一致直接返回提示:当前配置总分是 X,应为 Y。这个校验能避免学生在考试时看到的试卷总分是错的。
3.5 在线考试自动评分逻辑,细节决定成败
自动评分看起来就是比对两个字符串,但实际上有几个细节很容易被忽略。单选题和判断题是最简单的,学生提交的答案和标准答案完全一致即得分。多选题要复杂一些,我的实现是:先判断学生答案的字符集合和标准答案是否完全一致,一致满分;如果不一致,检查学生答案是否是标准答案的子集,如果是就按规则给一半分。这里我用了一个简单函数来比对字符串集合:
public int judgeMulti(String studentAnswer, String correctAnswer, int score) { if (correctAnswer.equals(studentAnswer)) { return score; } Set<String> studentSet = new HashSet<>(Arrays.asList(studentAnswer.split(","))); Set<String> correctSet = new HashSet<>(Arrays.asList(correctAnswer.split(","))); if (correctSet.containsAll(studentSet)) { return score / 2; } return 0; }注意字符串答案的分隔符前后端要严格统一,我这边统一用英文逗号分隔,前端在多选题拼接答案时也是用英文逗号join(",")。这个统一约定看似是小事情,但出现全对判零分的 bug 时,检查这里通常能发现问题。简答题不能走自动判分,它的审核状态要标记为“待人工阅卷”,教师可以从后台看到待阅卷列表,手动打分。
3.6 考试过程中“防作弊”和异常处理,能加分的设计
考试过程中常见的异常情况有三个:学生考试中途刷新页面、考试期间断网、到时未交卷。刷新页面会导致前端状态丢失,所以我的做法是:学生每次进入考试页面时,先查询后端,如果当前用户对该试卷已经存在未交卷的考试记录,就恢复到那条记录继续答题,而不是重新创建。这个设计叫“断点续考”,答辩的时候讲出来是加分项。
到点未交卷的处理方式有两种:一种是前端倒计时结束后自动提交,另一种是后端启动一个定时任务扫描超时记录强制交卷。我建议两个一起做:前端自动提交保证正常情况下的体验,后端定时任务保证容错。定期任务我用的 Spring 自带的 @Scheduled 注解,每 30 秒扫描一次考试记录表,找到超过截止时间但状态还是“进行中”的记录,把答案置为“超时未答”,状态改为“已交卷”。
3.7 MySQL 连接配置和分页插件
数据库连接配置我建议把关键参数都写清楚,尤其是时区这一项。MySQL 8.0 的连接串如果不加 serverTimezone 参数,大概率会报时区错误。我的 JDBC 连接串是这样的:
spring.datasource.url=jdbc:mysql://localhost:3306/exam_platform?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai spring.datasource.username=root spring.datasource.password=你的密码 spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver分页插件用的是 MyBatis-Plus 自带的分页插件,在 config 里声明一个 MybatisPlusInterceptor Bean,添加 PaginationInnerInterceptor。之后所有分页查询只需要在 service 层传入 pageNum 和 pageSize,配合 Page 对象,不用手动写 LIMIT 了,非常省事。
4. Vue 前端实现,从页面搭建到考试交互
4.1 项目搭建和目录组织
前端我用 Vue CLI 创建的 Vue 2 项目。为什么不用 Vue 3?说实话,Vue 3 的组合式 API 确实更现代,但很多毕设参考代码还是 Vue 2 的写法,而且 Vue 2 的生态资料丰富,遇到问题好搜。但如果你已经会 Vue 3,直接用 Vue 3 也完全没问题,核心思路是一样的。我这里的源码是 Vue 2 的写法,但把它改成 Vue 3 的 setup 语法也不难。
src 目录建议这样分:api 目录放所有接口请求方法,每个模块单独一个文件,比如 exam.js、user.js、post.js;router 目录放路由配置,要做动态路由;store 目录用 Vuex 管理登录状态和用户信息;views 目录放页面组件,按角色分子目录 admin、teacher、student;components 目录放公共组件,比如顶部导航栏、侧边栏、分页组件。
4.2 Axios 封装和请求拦截,让接口调用有统一入口
Axios 一定要做封装,不要每个页面直接 import axios 就发请求。封装的核心是两件事:设置 baseURL 以及请求拦截器里自动加 token。我的封装思路大概是这样:
import axios from 'axios' import router from '@/router' const service = axios.create({ baseURL: '/api', timeout: 10000 }) service.interceptors.request.use(config => { const token = localStorage.getItem('token') if (token) { config.headers['Authorization'] = token } return config }) service.interceptors.response.use( response => { const res = response.data if (res.code !== 200) { if (res.code === 401) { router.push('/login') } return Promise.reject(new Error(res.msg)) } return res }, error => { return Promise.reject(error) } )这样封装之后,每个业务接口只需要关心成功的数据,不需要每个页面都处理 401 跳转登录这些逻辑。特别是毕业论文里的代码展示环节,你把这种“统一处理”的设计讲清楚,老师会觉得你有工程意识,而不仅仅是会调用 axios。
4.3 前端路由和菜单权限
前端动态路由的做法是:登录成功后,把后端返回的角色信息存在 Vuex 里,然后根据角色的不同,调用不同的路由注册方法。具体思路是:routes 里先只放公共路由,比如登录页、注册页;登录后前端判断角色,再通过 router.addRoutes 方法把该角色对应的业务路由动态加上。菜单栏的显隐也是根据角色控制,学生登录看不到题库管理入口,教师登录看不到用户管理入口。
这里有个我去实验室帮人调过的常见bug:动态路由加上了,但页面一刷新就 404。原因很简单:路由是 JS 动态加的,刷新后 Vuex 状态重新初始化,动态路由就没了,页面自然找不到。解决办法是在路由守卫里做一个“已登录但路由还没初始化”的状态判断,每次刷新后重新拉取用户信息并且重新 addRoutes。这个坑几乎每个做动态路由的人都会遇到,提前知道可以省下很多时间。
4.4 考试页面核心交互设计
考试页面是整个前端最复杂的页面,在实现的时候要考虑到防误触和实时计时。我的布局是:顶部显示试卷标题和倒计时,左侧是题目列表(可以快速跳转到任意题目),中间是题目内容和作答区域。这样分栏的好处是考生能看到整体答题进度,哪些题没做一目了然。
倒计时用 setInterval 实现,每秒钟减一秒,剩余时间显示为“分:秒”格式。时间到了自动触发交卷确认弹窗,然后调用后端交卷接口。这里要注意一个问题:setInterval 在页面被切换或浏览器标签页切走时,定时器的精度会下降,所以前端倒计时可能不准。我的方案是:后端在考试记录里存了开始时间和考试时长,前端在拿到试卷信息时算出截止时间戳,然后用截止时间戳减当前时间戳来刷新倒计时,不依赖 setInterval 的次数累加。这样即使定时器偶尔延迟,倒计时依然是准的。
交卷前一定要做二次确认弹窗,防止学生手抖点了交卷。交卷按钮触发后弹一个 Dialog,提示“你还有 X 道题未作答,确认交卷吗”,确认后再提交数据。这个防误触设计在答辩演示时很有用,可以当作一个用户体验的亮点来讲。
4.5 考试数据提交方式,一次性提交还是逐题保存
答题数据的保存有两种常见方案。方案一是每答一道题就调一次接口保存,优点是不会丢数据,缺点是频繁请求后端压力大;方案二是所有答案在前端暂存,交卷时一次性提交,优点是接口调用少,缺点是中途系统崩溃数据不保。如果你的项目想要兼顾两者,可以做一个中间态:答题时把数据存在 localStorage,每 30 秒自动同步一次到后端,交卷时再提交一次。但我实测下来,毕设级别用方案二就够了,把最终提交的数据结构设计好即可。
提交的数据结构我定义成 JSON 数组,每个元素包含题目 ID 和学生答案。交卷接口改成接收一个 List,后端循环判断。要注意的是,数组中的题目 ID 必须是这张试卷里的题目,后端要做一个校验,防止学生篡改请求,把其他题目的答案塞进去。这个“前后端数据可信度”的问题,可以在文档里当做一个安全点来说,也是答辩亮点。
5. 常见问题排查与避坑指南
5.1 跨域问题,新手必遇的拦路虎
前后端分离项目百分之百会遇到跨域问题。表现是:前端请求后端接口,浏览器控制台报错 CORS,接口完全不返回数据。解决方式有两种:一种是后端配置全局跨域,另一种是前端通过 devServer 的 proxy 代理转发。我推荐开发阶段用前端代理,因为配置 proxy 之后你只需要理会 baseURL,而且浏览器看起来还是“同源”请求,更干净。
// vue.config.js module.exports = { devServer: { proxy: { '/api': { target: 'http://localhost:8080', changeOrigin: true } } } }如果你一定要后端跨域,用 @CrossOrigin 注解或者写一个 CorsFilter 也行。但我建议毕设项目直接用代理方案,因为部署到服务器后,你用 Nginx 反代也能复用这个思路,学一次能顶两处用。
5.2 时区问题和日期显示
MySQL 和 Java 的时区不一致会导致两个典型问题:一个是存入数据库的时间比实际时间少 8 小时,另一个是查询出来的时间在前端显示不对。解决办法是连接串加serverTimezone=Asia/Shanghai,同时实体类里的日期字段统一用LocalDateTime,前端拿到时间后格式化显示。另外要注意,如果你用了 Jackson 的日期格式化配置,记得确认一下spring.jackson.date-format和time-zone配置,不然接口返回的日期格式可能不符合前端预期。
5.3 并发交卷和超时自动交卷的冲突
考试平台在毕设答辩场景下并发量不高,但代码逻辑上还是要处理极端情况:学生在最后几秒手动交卷,同时后台定时任务也发现这条记录超时了,两个操作同时去修改考试记录状态,就可能导致状态混乱。我的处理方式很直接:修改考试记录状态时加一个条件判断,比如用 MyBatis-Plus 的 UpdateWrapper 设置eq("status", "进行中"),这样两个操作只有一个能成功更新。这其实就是乐观锁思想,你不用真去配 @Version 也能达到目的,但把这个思路写进文档是加分的。
5.4 JWT 过期和用户状态异常
JWT 默认有个有效期,但有时候学生考试时间超过了 token 有效期,中途就会因为 token 过期被拦截器踢回登录页。这个问题在考试类系统里很现实。解决方法有两个方向:一个是把 token 有效期设置得比最长考试时长更长,简单但不够优雅;另一个是后端放宽考试相关接口的拦截,比如只校验 token 是否存在,不校验是否过期。我还是建议做成“刷新 token”机制:拦截器发现 token 快过期时,在响应头里返回一个新 token,前端拦截器检测到后替换本地存储。这种实现有助于你理解 token 的工作原理,放在简历上也是亮点。
5.5 源码学习建议:拿到一套源码应该怎么看
这套平台源码在我手里,但我还是要提醒你:拿到源码不是跑起来就完事了,你要能讲清楚每一块是干什么的。我的建议是先跑起来看整体功能,再追核心链路。整体功能就是注册登录、创建试卷、参加考试、查看成绩这一条线走通。核心链路是指:你从点击“开始考试”这个按钮开始,前端发送什么请求、后端哪个 controller 接收、调用了哪些 service、查了哪些表、返回了什么数据,这整条链路追踪下来,你对项目的理解就基本到位了。然后再去改一些细节,比如把多选题判分规则改成“漏选不得分”,改完跑测试验证,这种小改造会让源码真正变成你的东西。
6. 部署上线和扩展方向
6.1 本地打包和部署
项目开发完,最终要能打包部署。前端在项目根目录执行npm run build,会在 dist 目录生成静态文件;后端用 Maven 打包,执行mvn clean package -DskipTests,生成一个 jar 文件。如果你有服务器,把 dist 里的文件交给 Nginx 托管,把 jar 用java -jar运行,然后 Nginx 里配置一个反向代理,把/api路径转发到后端端口,就完成了最简单的部署。有些同学可能觉得部署是“加分项”不想做,但我建议至少自己在本地用前后端分离方式跑通一次,因为答辩演示时你可能要用到部署后的系统。
6.2 在线考试平台的合理扩展方向
毕设做完不是终点,如果你想把它继续发展成一个更有竞争力的项目,可以往三个方向扩展。第一个是统计分析:当前成绩分析只是简单汇总,可以加 ECharts,把分数分布、科目对比、班级对比做成图表,视觉效果立刻提升,也显得更贴合“智慧教学”概念。第二个是导入导出:用 EasyExcel 或者 POI 把题库批量导入 Excel,把考试成绩导出成 Excel,这是企业里非常现实的需求,顺手也能在简历里写“熟悉 EasyExcel 的操作”。第三个是消息通知:考试创建后自动通知学生、成绩发布后通知学生,用 WebSocket 或者简单的站内信功能都可以实现。
6.3 文档和答辩资料准备,别忽略软实力
最后提醒一句,源码和项目本身只是一个部分,毕业设计还有文档和答辩这些环节。写文档的时候,重点把数据库设计的 E-R 图、核心业务流程的时序图、接口设计的表格放进去,图多字少是技巧。答辩的时候,不要全程念 PPT,而是先现场演示系统功能,演示完再讲技术点,老师一般会对你实现过程中遇到过的难点更感兴趣。你在 JWT 认证、自动组卷、自动判分、断点续考这几个点上的经验,就是最好的答辩素材。
我个人在实际带项目的过程中最深的体会是:这类 SpringBoot + Vue 的全栈项目,真正难的不是某一个单独的技术,而是把前后端两套体系串起来时出现的各种“连接性”问题。你只要完整走完一遍组卷、考试、判分这条链路,踩过一遍这些坑,后面再遇到其他全栈项目,基本都能触类旁通。如果时间充裕,建议在跑通源码之后,自己动手从零写一遍后端核心接口,哪怕只是照着源码的流程仿写,收获都会非常大。