做毕业设计这几年,身边不少同学都在“在线考试系统”这个题目上撞车。每年选题阶段,这个题目几乎都是秒没的状态,原因很简单:题库、组卷、在线答题、自动判分、成绩统计,一听就是一套完整业务闭环,和那种纯增删改查的“管理系统”完全不是一个含金量。但正因为太常见,很多人反而把它做成了普普通通的“选择题网页”,答辩被问两句就露馅。
我最近完整整理了一套高分毕业设计:在线考试系统(源码+数据库+Word+PPT),今天直接把整条链路的思路、数据库细节、核心功能实现、答辩准备和踩过的坑全部写出来。这篇内容不是教你“抄一套代码”,而是把为什么这么设计、哪些地方最容易翻车、怎么让老师在答辩时点头,一次讲透。
1. 整体设计与技术选型:先定边界,再动手写代码
1.1 业务闭环才是毕业设计的核心
很多同学一上来就写注册、登录、加试题、考试、出成绩,功能倒是全,但串不成一个闭环。毕业设计评分时,老师第一眼看的不是界面多漂亮,而是你的系统能不能像真实产品一样跑通完整链路:老师维护题库、发布试卷,学生参加考试,系统自动判分,最后老师和学生都能看到成绩和分析结果。
我建议整个系统收敛成三个端:管理员端、教师端、学生端。可能有人问,管理员和教师功能不是重叠吗?实际做课题时,这两类角色的数据操作边界差异很大——管理员管用户、班级、系统配置,教师管题库和试卷,绝对不能混在一个页面里。
用户体系解决“谁在用”,题库体系解决“考什么”,试卷与考试体系解决“怎么考”,判分与统计体系解决“考完怎么办”。四个体系扣在一起,就是完完整整的项目叙事线。答辩时,你就按照这条线去讲,老师顺着你的逻辑走,基本不会打断你。
1.2 技术栈选型的实际考量
我见过太多人做毕设时在技术选型上栽跟头,要么用特别冷门的框架,要么用自己根本说不清楚原理的东西。毕业设计的评分逻辑不是越难越好,而是“你选的东西能自圆其说,并且你能扛住追问”。
以这套在线考试系统为例,最佳的组合就是后端、前端、数据库三件套:
| 层级 | 推荐选型 | 核心理由 |
|---|---|---|
| 后端 | Spring Boot 2.7 + MyBatis-Plus | 生态成熟、配置简单、资料多,答辩时任何问题都能搜到答案 |
| 前端 | Vue 2 + Element UI | 组件化开发快,表格、表单、弹窗都是现成的,不用自己写CSS |
| 数据库 | MySQL 5.7 / 8.0 | 行业标配,支持事务、索引、存储过程,毕设场景完全够用 |
| 权限控制 | Spring Security + JWT | 无状态鉴权,前后端分离友好,解释起来也清晰 |
| 模板/文档 | Apache POI + EasyExcel | 用于Excel导入试题、导出成绩单,这是加分项 |
为什么不推荐Python Flask或Django?不是不行,而是Spring Boot在Java方向的学生里占有率高,查错成本低。如果你导师明确要求Python,也可以做,但下面的数据库设计和业务逻辑思路完全一样,换语言不换思想。
前端千万别上太复杂的东西。有人喜欢用Vue 3 + TypeScript + Vite,确实新潮,但你问问自己:毕设答辩时被问到“Vite和Webpack的区别”,你能讲清楚吗?讲不清楚就老老实实用Vue 2 + Element UI,够用、好解释、出了问题百度一抓一大把。
1.3 目录结构与数据库初始化脚本的规划
拿到源码后,第一步不是急着打开idea,而是先看懂整个项目目录。一个规范的Spring Boot项目,分包方式就是给老师看的“第一印象”。
通常我会这样组织后端代码:
com.example.exam ├── controller // 接口层:接收前端请求,返回JSON ├── service // 业务层:核心业务逻辑都写在这里 │ └── impl // 业务实现类 ├── mapper // 数据访问层:MyBatis-Plus的Mapper接口 ├── entity // 实体类:对应数据库表结构 ├── dto // 入参和返回对象:避免直接把实体类暴露给前端 ├── config // 配置类:CORS跨域、JWT拦截器等 ├── common // 公共类:统一返回值、异常处理、工具类 └── ExamApplication.java // 启动类数据库初始化脚本(exam.sql)必须在项目根目录下单独放一份,里面包含建库语句、建表语句和测试数据。我见过不少人把SQL文件丢在某个犄角旮旯的文件夹里,老师找数据库脚本找了半天,体验极差。正确做法是放在doc/sql/exam.sql,或者在README里明确指出“导入doc/sql/exam.sql即可完成建库”。
2. 数据库设计:在线考试系统的高分根基
数据库是毕业设计里最容易被扣分也最容易拿分的地方。很多人的表设计就是随便建个用户表、试题表、考试记录表,完全没有主外键关联和约束,答辩时老师一看ER图就问“关联关系在哪”。所以我花大篇幅讲数据库。
2.1 核心表结构:九张表撑起整个系统
我的这套设计中,一共规划了9张核心表。不多也不少,少了说明功能覆盖不全,多了容易让人看不出你的主次。
| 表名 | 中文名 | 核心作用 |
|---|---|---|
| sys_user | 用户表 | 存学生、教师、管理员三类账号,用role字段区分 |
| sys_class | 班级表 | 学生归属的班级,方便按班级维度统计成绩 |
| exam_subject | 科目表 | 课程或科目维度,一门科目一套题库 |
| exam_question | 试题表 | 单选、多选、判断题,题目与选项统一存储 |
| exam_paper | 试卷表 | 一张试卷的基本信息:名称、总分、时长、考试时间范围 |
| exam_paper_question | 试卷试题关联表 | 多对多关系,一张试卷包含多道题,含单题分值 |
| exam_record | 考试记录表 | 每个学生某次考试的状态和总得分 |
| exam_record_answer | 答题明细表 | 记录学生每道题的答案和对错 |
| sys_notice | 公告表 | 发布考试通知或系统公告,作为辅助功能 |
特别注意:试卷和试题之间千万别用“在试卷表里存一堆题目ID,逗号分隔”这种设计。第一,无法用SQL做关联查询;第二,修改一道题时,你得去字符串里定位替换,逻辑一塌糊涂;第三,答辩时老师看到这种设计,直接判定“数据库设计不合格”。一定要建关联表exam_paper_question,这是最基础的关系型数据库素养。
2.2 试卷、试题、答题明细三张表的设计细节
每张表的关键字段都要“能讲出道理”,这是答辩时拉开差距的地方。
试题表(exam_question)的核心字段,我建议这样设计:
CREATE TABLE exam_question ( id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '试题ID', subject_id BIGINT NOT NULL COMMENT '所属科目ID', question_type TINYINT NOT NULL COMMENT '题型:1单选 2多选 3判断', content TEXT NOT NULL COMMENT '题干', options TEXT COMMENT '选项内容,JSON格式:{"A":"xx","B":"xx"}', answer VARCHAR(10) NOT NULL COMMENT '正确答案:单选存A,多选存A,C, 判断存T/F', analysis VARCHAR(500) COMMENT '答案解析', difficulty TINYINT DEFAULT 3 COMMENT '难度等级:1-5,5最难', create_time DATETIME DEFAULT CURRENT_TIMESTAMP, update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='试题表';几个容易被忽视的细节,我踩过坑,帮你标出来:
题型字段用TINYINT而不是VARCHAR。有人喜欢存“单选”“多选”,纯中文,看着直观,但你写判分逻辑的时候就要用switch-case去匹配字符串,改起来巨烦。用1、2、3这种数字枚举,写代码时一个if判断搞定,解释起来也清晰。
选项用JSON格式统一存储。有人给每道题建四个字段:option_a、option_b、option_c、option_d。对于单选没问题,但判断题只有“对/错”两个选项,填空题甚至没有选项,表结构就被撑死了。用JSON存所有选项,兼容一切题型,代码里用ObjectMapper解析一下就行。
答案字段格式必须统一。单选存“A”,多选存“A,C,D”,判断存“T”或“F”,这种约定只要写进开发规范里,后面的自动判分逻辑会非常清爽。问题就出在有人输入法切到中文,把“A,D”写成“A,D”,判分时String.split(",")得到乱数据,就是你查半天查不出来的灵异bug。
考试记录表(exam_record)和答题明细表(exam_record_answer)是自动判分的基石:
CREATE TABLE exam_record ( id BIGINT PRIMARY KEY AUTO_INCREMENT, paper_id BIGINT NOT NULL COMMENT '试卷ID', student_id BIGINT NOT NULL COMMENT '学生ID', total_score DECIMAL(5,1) DEFAULT 0 COMMENT '总分', start_time DATETIME COMMENT '开始时间', submit_time DATETIME COMMENT '交卷时间', status TINYINT DEFAULT 0 COMMENT '0进行中 1已交卷 2异常中断', UNIQUE KEY uk_paper_student (paper_id, student_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='考试记录表';这里加唯一索引uk_paper_student的意义在于:防止同一个学生对同一张试卷重复提交。如果不加,学生交卷按钮多点两下,数据库里就会出现两条记录,后面的判分和统计全乱套。
2.3 数据库设计的常见扣分点
这里我直接列一个“老师看了就想扣分”的清单,你做完之后自查一遍:
- 没有外键或者逻辑外键关系,表之间完全孤立。
- 字符集用latin1或者utf8,导致存不了表情符号和特殊字符,建议统一utf8mb4。
- 金额和分数用float。浮点数有精度问题,成绩统计时会出现74.99999这种笑话,统一用DECIMAL(5,1)。
- 没有create_time和update_time字段。这是MySQL的通用审计字段,缺了就说明你没写过真实项目。
- 时间字段用VARCHAR存储。有人图省事把时间存成字符串“2025-04-10 10:30”,SQL查询时没法做时间范围比较,排序也会出问题。用DATETIME,这是底线。
3. 核心功能实现:从搭建到跑通的完整流程
3.1 环境准备与项目初始化
实际操作时,我建议把环境分两段准备。
第一段是数据库环境:打开MySQL命令行或Navicat,新建数据库exam_db,字符集选utf8mb4,排序规则utf8mb4_general_ci,然后导入项目里的exam.sql。这里有个高频坑:如果你的MySQL是8.0以上,连接驱动必须用com.mysql.cj.jdbc.Driver,老项目里写的com.mysql.jdbc.Driver在MySQL 8.x下会直接报ClassNotFoundException。同时记得在连接URL后加上?useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf8,不然时区报错能卡掉你一上午。
第二段是后端启动:用IDEA打开项目,等待Maven下载依赖,修改application.yml里的数据库用户名密码,然后启动启动类。启动成功后访问http://localhost:8080/api/health,能返回JSON说明后端起来了。前端项目在vscode里打开,依次执行:
npm install npm run serve访问http://localhost:8085,看到登录页就说明前后端联调成功。记住前端端口和后端端口一定要分开,并且在后端配置CORS跨域允许,不然浏览器会拦截所有请求。
3.2 试题批量导入:Excel解析的完整代码
题库是考试系统的灵魂。人工一道一道录入不仅慢,而且容易出操作失误,所以我给这个系统做了Excel批量导入。这部分在答辩时特别出彩,因为大多数同学的系统都是“手动录入试题”的玩具逻辑。
先说Excel模板格式,第一行是表头:题型、题干、选项A、选项B、选项C、选项D、正确答案、解析、难度。第二行开始填数据,题型用“单选/多选/判断”。判断题的选项固定填“正确/错误”,答案填“T”或“F”。
后端解析这块,核心就是EasyExcel的事件监听器。我写了一个简化版的处理逻辑:
public class QuestionDataListener extends AnalysisEventListener<QuestionExcelDTO> { private final QuestionService questionService; private List<QuestionExcelDTO> cache = new ArrayList<>(); private static final int BATCH_COUNT = 100; public QuestionDataListener(QuestionService questionService) { this.questionService = questionService; } @Override public void invoke(QuestionExcelDTO data, AnalysisContext context) { // 数据校验 if (StringUtils.isBlank(data.getContent())) { throw new RuntimeException("第" + context.getCurrentRowNum() + "行题干为空"); } cache.add(data); if (cache.size() >= BATCH_COUNT) { saveData(); cache.clear(); } } @Override public void doAfterAllAnalysed(AnalysisContext context) { saveData(); } private void saveData() { // 将Excel DTO转换为试题实体,插入数据库 List<ExamQuestion> questions = cache.stream() .map(dto -> convertToQuestion(dto)) .collect(Collectors.toList()); questionService.saveBatch(questions); } }这段逻辑里,我最想强调的其实是invoke方法里那行校验代码。很多导入功能的Bug都出在校验不严上:题干为空、题型填了“不定项”这种系统不支持的枚举、答案填了“AB”但选项里根本没有B。答辩前一定要用一批脏数据测试导入功能,让老师看到你的系统“能拒绝脏数据”,这比功能本身更让人信服。
3.3 组卷策略:固定试卷和随机试卷双策略
组卷是考试系统的核心亮点。我实现了两种模式:
固定组卷:教师从题库中手动逐题勾选,每题设置分值。实现起来最简单,就是往exam_paper_question表里不断插入数据。
随机组卷:教师设置“单选题数量、多选题数量、判断题数量”、每种题型的分值、难度范围,系统自动从题库随机抽题。这里最有技术含量的是保证“同一场考试中学生拿到的题目不完全一样”,避免学生互相传答案。
随机组卷的核心SQL用了MyBatis-Plus的查询构造器,也可以直接用原生SQL:
public List<ExamQuestion> selectRandomQuestions(Long subjectId, Integer questionType, Integer count, Integer minDifficulty, Integer maxDifficulty) { // 使用原生SQL查询,在MySQL中ORDER BY RAND()实现随机抽取 // 注意:RAND()在大数据量下性能较差,毕设场景完全够用 return questionMapper.selectRandomQuestions(subjectId, questionType, count, minDifficulty, maxDifficulty); }对应Mapper XML里的SQL是:
<select id="selectRandomQuestions" resultType="com.example.exam.entity.ExamQuestion"> SELECT * FROM exam_question WHERE subject_id = #{subjectId} AND question_type = #{questionType} AND difficulty BETWEEN #{minDifficulty} AND #{maxDifficulty} ORDER BY RAND() LIMIT #{count} </select>交叉组卷时,我建议每次按“单选题抽完,抽多选题,再抽判断题”的顺序执行,每一轮单独用一次RAND()排序,不要一次性抽完再切分。这样代码逻辑清晰,以后扩展“填空题”、“简答题”也容易,只要加个枚举类型和对应SQL即可。
这里有个实战细节:组卷完成后,一定要校验“实际抽到的题目数量”是否等于“教师要求的数量”。如果题库里符合条件的题不够,系统必须弹窗提示“题库数量不足,无法组卷”,而不是静默生成一份缺题目的试卷。这种细节控制能让答辩时的演示顺畅度提升一个档次。
3.4 在线考试与自动判分:别让逻辑漏洞坑了自己
在线考试模块是“翻车重灾区”,很多人代码写完、能跑,但就是经不起细问。核心问题集中在三个方面。
时间控制问题。考试时长是教师创建试卷时设置的,比如90分钟。学生点击“开始考试”后,前端要倒计时显示剩余时间,时间归零自动交卷。这里有个关键:倒计时的起点到底是从学生点击开始算,还是从教师设定的统一开考时间算?我建议用“学生点击开始时服务端时间戳 + 试卷时长”作为截止时间,每次答题时前端向后端同步一次当前时间。别用前端本地时间,学生改一下电脑系统时间就能无限延长考试,这属于严重安全漏洞。
切屏检测问题。实现并不复杂,前端监听visibilitychange事件,当学生切出浏览器标签页时,记录切屏次数。交卷时把这个次数一并提交,教师端能看到“该学生切屏3次”。代码示意如下:
document.addEventListener('visibilitychange', () => { if (document.hidden) { this.switchCount++; } });这个功能在演示时是“哇”点,成本却很低。但注意:不要在切屏超过一定次数后强制交卷,因为这有可能误伤正常操作(比如学生切出去看一眼计算器)。记录但不动手处理,是更稳的产品策略。
自动判分逻辑。这是后端最容易写错的地方。判分时一定要注意题型差异:
- 单选题:答案完全匹配才得分。
- 多选题:严格模式,多选、少选、错选都不得分;宽松模式,少选得一半分。我建议毕设里用严格模式,因为逻辑简单、容易解释。
- 判断题:答案完全匹配得分。
判分代码核心逻辑:
public double calculateScore(ExamQuestion question, String studentAnswer) { double score = 0; String correctAnswer = question.getAnswer(); // 对答案字符串进行标准化:统一大写、去掉空格 studentAnswer = studentAnswer == null ? "" : studentAnswer.toUpperCase().trim(); correctAnswer = correctAnswer.toUpperCase().trim(); if (question.getQuestionType() == 1 || question.getQuestionType() == 3) { // 单选和判断:完全匹配 if (correctAnswer.equals(studentAnswer)) { score = question.getScore(); } } else if (question.getQuestionType() == 2) { // 多选:多选少选错选均不得分 if (correctAnswer.equals(studentAnswer)) { score = question.getScore(); } } return score; }扪心自问一下:如果你把多选题的判分写成“只要有交集就给一半分”,答辩时是不容易解释清楚的,因为“一半分”的产生规则很模糊。严格匹配是最好讲的方案。
3.5 成绩统计:让分数“说话”的加分功能
成绩统计在很多复制来的源码里是一笔带过的,但恰恰是这块最容易被答辩老师关注。我只写了三个统计图,但每个都能展开讲:
- 班级平均分对比柱状图:按班级维度,展示本次考试的平均分。
- 分数段分布饼图:90-100、80-89、70-79、60-69、60以下五个区间,一眼看出试卷难度是否合理。
- 每道题的错误率条形图:统计每道题的错误次数/作答人数,这是“试卷分析”功能的雏形。
前端用ECharts实现,后端提供统计数据JSON接口。核心SQL是根据答题明细表关联试题表:
SELECT q.content AS question_content, SUM(CASE WHEN ra.is_correct = 0 THEN 1 ELSE 0 END) AS wrong_count, COUNT(ra.id) AS answer_count FROM exam_record_answer ra JOIN exam_question q ON ra.question_id = q.id WHERE ra.paper_id = #{paperId} GROUP BY ra.question_id ORDER BY wrong_count DESC注意is_correct字段,在exam_record_answer表里存了每题的对错标记(1对0错),这样统计错误率时不用再回表计算答案,省去大量查询时间。这个字段在一开始建表时就要预留好。
4. Word文档与PPT:毕业设计的“面子工程”
很多同学写代码能熬夜到两点,写文档却只有一个星期凑字数。实际上,论文文档和答辩PPT对最终成绩的影响占比相当高,甚至不比代码低。
4.1 毕业设计Word文档的标准结构
一个高分的在线考试系统论文,章节结构已经高度模板化了,你只需要把内容填扎实:
| 章节 | 核心内容 | 建议页数 |
|---|---|---|
| 第一章 绪论 | 研究背景、国内外现状、研究内容 | 5-8页 |
| 第二章 相关技术介绍 | Spring Boot、Vue、MySQL、JWT | 6-8页 |
| 第三章 系统分析 | 可行性分析、需求分析、用例图 | 8-12页 |
| 第四章 系统设计 | 总体架构、功能模块图、数据库ER图 | 10-15页 |
| 第五章 系统实现 | 每个功能模块的截图+核心代码+实现说明 | 15-20页 |
| 第六章 系统测试 | 测试环境、测试用例、测试结果 | 6-10页 |
| 第七章 总结与展望 | 做的什么、收获、不足与展望 | 2-3页 |
每章都有明确的写作重点。比如第四章系统设计,你必须包含两张图:一张是系统功能模块图(树状结构,列出所有功能点),一张是数据库ER图(用PowerDesigner或draw.io绘制)。第五章系统实现必须做到“一到两个截图 + 一段核心代码 + 一段文字说明”的组合,光贴代码不截图、光截图不给实现逻辑,都要扣分。
这里有一个我多年的经验:论文里的代码不要大段粘贴。全文所有核心代码控制在80行以内,选逻辑最核心的那一段(比如自动判分逻辑),其他代码用文字描述,配合流程图说明。老师看论文看得是逻辑,不是代码量。
4.2 答辩PPT的设计思路
答辩PPT不用花哨,但要逻辑严密、节奏对。一般20张左右,时间控制在10到15分钟。我的PPT结构如下:
- 封面(1张):题目、姓名、学号、指导教师。
- 目录(1张):简洁列4个部分。
- 选题背景(2张):为什么要做在线考试系统。
- 技术栈(2张):每个技术一句话+选择理由。
- 系统功能模块(2张):模块图+核心功能点。
- 数据库设计(3张):ER图+核心表设计说明。
- 核心功能演示(5张):登录、题库导入、组卷、在线考试、成绩统计,每个功能截图+讲一句话。
- 测试与总结(2张):测试结果+总结与展望。
- 致谢(1张)。
PPT的坑我踩过无数次,这里单独提醒:尽量不要放代码。PPT里的代码既是字体灾难又是讲解灾难,老师根本看不清。核心功能展示用截图配合箭头标注效果远好于贴代码。如果需要说明逻辑,画流程图比放代码强十倍。
4.3 答辩演示环境的准备工作
答辩翻车大多翻在演示环节。环境问题引发的尴尬,我见得实在太多了。提前做好这几件事:
第一,用本地环境演示,别用校园网在线演示。在线环境万一断电、断网、服务器崩了,你全程对着一个500页面解释,老师再好说话也救不了你。
第二,演示数据要“有戏”。提前准备5个学生账号、1个教师账号、1个管理员账号,题库里放100道题以上(单多判各30题以上),成绩统计里要有真实数据和图表。老师随手点开一个功能都能看到数据,比临时录入假数据顺畅得多。
第三,把数据库重置脚本准备好。演示前一键导入exam.sql,把所有数据恢复到初始状态,避免上一次测试留下的脏数据干扰演示效果。
5. 常见问题与排查技巧:实测中踩过的坑
5.1 数据库相关问题
MySQL启动不了。Windows上最常见的是端口被占用。用命令行执行netstat -ano | findstr "3306",看是什么进程占用了3306端口,多半是之前装了旧版MySQL没卸载干净,杀掉进程或者改端口即可。
中文乱码。连接URL里加characterEncoding=utf8只是第一步,建库时字符集也要选utf8mb4,两处都对了才会正常显示。如果已经建错库了,不要手贱去修改字符集,直接用Navicat将整个库的转储导出后重新建库导入,这是最省事的方法。
日期显示少了8小时。时区问题,连接URL加上serverTimezone=Asia/Shanghai,同时检查服务器(本机)的系统时区是否是中国标准时间。
5.2 前后端联调问题
跨域请求被拦截。后端写一个CORS配置类,允许前端地址访问。这道题是面试常考也是答辩高频,一定要背下这段代码:
@Configuration public class CorsConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOrigins("http://localhost:8085") .allowedMethods("GET", "POST", "PUT", "DELETE") .allowedHeaders("*") .allowCredentials(true) .maxAge(3600); } }登录后刷新页面就跳回登录页。JWT存在哪里?我见过有人把Token放在localStorage,浏览器开着没问题,一刷新就丢失。建议放在sessionStorage或者配合Vuex保存。但如果做了记住登录状态,可以用localStorage,配合路由守卫判断。
5.3 在线考试模块的隐蔽Bug
学生没点交卷,考试时间到了,成绩却一直显示“进行中”。原因是交卷逻辑只依赖前端倒计时,前端页面关了倒计时就停了。解决方法是后端加一个定时任务或监听器,每分钟扫描一次exam_record表,将start_time + duration小于当前时间且仍未交卷的记录自动置为“已交卷”并按已答题目判分。这个细节在答辩时很加分,因为说明你考虑到了异常退出场景。
同一账号双端登录造成数据覆盖。简单粗暴的解决方案是登录时把原来的Token作废,只允许最新的登录态生效。毕设阶段用HashMap维护Token和用户的映射即可。
5.4 答辩时的高频追问清单
这部分我以“被问过无数次”的经验,直接帮你给答案。
老师问:为什么选择Spring Boot而不是Spring?
答:Spring Boot内置了Tomcat、自动配置极简起步依赖,让我能在更短时间内专注于业务逻辑实现,避免大量XML配置,提升了开发效率。
老师问:你的权限控制是怎么实现的?
答:基于JWT无状态鉴权。登录成功后后端签发Token,前端每次请求携带Token,后端通过拦截器统一校验Token中的角色信息,实现管理员、教师、学生三种角色的访问控制。
老师问:如果同时有1000个学生在线考试,你的系统会怎样?
答:当前架构基于单机部署,1000并发可能会卡顿。如果要优化,我会在题库查询层面加Redis缓存,考试提交时使用消息队列异步判分,数据库连接池也要做调优。但这个属于架构演进方向,毕设场景我的定位是验证业务闭环。
碰到不会的题怎么办?千万不要硬编。诚实地说:这个问题我在开发时没有深入考虑,但结合我的理解,可能的方向是……,以后可以这样优化。态度诚恳配合一个合理猜测,绝对比瞎编更讨巧。
最后再分享一个实用技巧:答辩前把系统里的测试账号密码打印在一张纸上,放在鼠标旁边。万一老师要试操作,你直接让老师拿学生账号登录考试,全程流程走一遍,比你嘴上讲十分钟都有说服力。很多人不会注意到这个细节,但效果出奇得好。
这套“在线考试系统源码+数据库+Word+PPT”的完整方案,按上面的思路做下来,代码能运行、文档能通过查重、答辩能讲明白。找源码的时候也记得看清楚数据库脚本和文档是不是匹配,很多来源的代码和论文对不上,老师一眼就能看出来是拼凑的。我做毕设这几年最大的体会就是——真正帮你拿高分的,不是源码本身,而是你对这套系统每个设计决策的理解深度。