简介:一份基于Java技术栈的在线考试系统完整源码,后端使用Spring MVC、MyBatis与FreeMarker,前端结合Bootstrap、jQuery和Vue.js实现,适合Java学习者、毕业设计选题学生以及需要快速搭建考试系统的开发者参考。资源共332个文件,压缩包约10.33MB,其中包含47个Java业务类、82个PNG素材、69个CSS样式、58个JS脚本以及数据库SQL脚本和Eclipse工程配置,目录结构清晰,可直接导入IDE并结合SQL初始化数据库后运行。项目覆盖管理员、教师、学生三类角色的登录认证、试题管理、在线答题和成绩统计等核心流程,可作为课程设计或商业项目二次开发的完整起点;同时借助FreeMarker模板和Vue.js数据绑定,能帮助读者理解服务端渲染与前后端交互的混合开发模式。已有1111人学习浏览,适合正在研究在线教育系统或SSM+Vue整合方案的开发者下载学习。
1. 拿到“Java在线考试系统源码.zip”后,第一件事不是解压
很多同学下载了这类课程设计源码,解压后双击 README 就开始配环境,折腾两小时还在报错。原因不是代码有问题,而是没搞懂这个 zip 里打包的到底是个什么架构、什么阶段的项目。Java 在线考试系统源码,从技术栈上分大致两个世代:早期课程设计多是 JSP + Servlet + JDBC,最多套一层 MVC 自己写的;近几年的毕设和课设则以 SSM(Spring + SpringMVC + MyBatis)为主,前端用 Bootstrap 或 layui,数据库是 MySQL。你这个 zip 到底是哪一种,直接决定了你能不能在一小时内跑起来。
这篇文章就围绕“收到一份 Java 在线考试系统源码.zip 之后,怎么从解压到改造成自己的项目”这条线展开。内容覆盖系统的模块划分、本地环境搭建、核心参数的配置方法和几个高频翻车点。适合正在做课程设计、毕业设计,或者想拿一套现成代码做二次开发的从业者。我不讲官方文档里那种面面俱到的话,只讲实际跑项目时真正决定成败的几个点。
2. 在线考试系统的骨架:这三个角色把整个系统撑起来
2.1 为什么所有在线考试系统都长一个样
不管代码写得怎么样,在线考试系统的功能边界是高度固定的。管理员、教师、学生,三个角色基本覆盖了全部业务。管理员管用户、管课程、管公告;教师管题库、组卷、阅卷、成绩导出;学生登录、考试、查分。市面上能下载到的 Java 在线考试系统源码,绝大多数围绕这三条线在写。
先花十分钟确认你手里的源码对应的是哪个角色视角。方法很简单,看 controller 层的包名或 URL 前缀。常见的有admin、teacher、student三段式目录结构,也有按功能拆成exam、question、user的。前者角色清晰,后者功能内聚。两者都能跑通,但改起来差异很大——按角色拆的,加一个权限过滤器很轻松;按功能拆的,容易在多个 Controller 里重复写权限判断。
我一般打开项目后第一眼看web.xml(老项目)或配置类(新项目),确认 Spring 版本和 DispatcherServlet 的映射规则。这决定了 URL 是.do后缀、REST 风格还是纯 JSP 直访。很多报错根源就在这一步,后面会详细说。
2.2 核心数据表与字段:改需求前必须先看这几张表
题库表、考试表、答卷表是三大核心。以我见过的多数 SSM 版本源码为例,题库表字段通常包含:题目类型(单选/多选/判断/简答)、难度等级、所属课程 ID、题目内容、选项存储方式(逗号分隔或 JSON 串)、正确答案、分值。组卷逻辑有固定抽题和随机抽题两种,随机卷多数是靠ORDER BY RAND()或NEWID()实现,这在数据量小时能接受,题目过万后会明显卡顿。
学生提交答卷后,系统怎么判分非常关键。单选题和判断题一般由后端逐题比对,多选题按全对给分或漏选给半分,简答题则进入人工阅卷列表。你如果想把“考试结束立即出分”改成“老师统一阅卷后才发布成绩”,核心改动点就在提交答案的 Service 层,而不是前端页面。
改表结构时的建议:不要动原有字段名,尽量新增字段。比如题目要加“所属知识点”字段,就新增knowledge_point列,而不是把type字段改成字符串数组。理由很简单——Mapper XML 里的 SQL 可能做了SELECT *映射,也可能用resultMap显式映射。前者加字段没事,后者不补映射直接报错。这是 MyBatis 项目里最常见的改表翻车场景。
2.3 技术选型的现实逻辑:SSM 为什么是主流
选 SSM 而不是 Spring Boot,在 2025 年看有点“复古”,但课程设计和毕业设计的评分标准里,很多学校至今要求 SSM 或 SSH。Spring Boot 自动配置太省事,老师反而看不出你对 Bean 生命周期和事务传播的理解。另一个现实原因是:网上能下载到的这类课程设计源码,七八成都是 SSM 工程结构——src/main/java下分controller、service、dao、pojo,src/main/resources下放 Spring 配置和 MyBatis 映射文件。
如果你拿到的源码恰好是 Spring Boot 版本,那恭喜,配置负担小很多。但注意一点:Spring Boot 的在线考试系统源码里,很多是前后端分离的,前端 Vue 打包后丢进 static 目录,或者单独起一个前端工程。这时候要求你额外懂一点 Node 生态。传统 SSM 则多是 JSP 页面,虽然看着老,但调试链路短,打开浏览器就是完整页面,改完重启 Tomcat 就能看到效果,对课程设计来说反而是优势。
3. 跑通这套源码:从 JDK 到 Tomcat 的完整参数设置
3.1 环境版本怎么配才不吵架
拿到源码后,第一步不是打开 IDE,而是看项目依赖配置文件确认版本。以最常见的 Maven 工程为例,打开pom.xml看<properties>节点。Java 版本、Spring 版本、MyBatis 版本、MySQL 驱动版本这四项先确认。
这里有一个非常实用的经验法则:Java 版本不要追新。即使你电脑上装的是 JDK 17,如果源码是五年前写的,pom.xml里source和target大概率是 1.8,那你应该装一个 JDK 8 专门跑这个项目,而不是用 JDK 17 硬编译。原因在于老版本 Spring 对高版本 JDK 的反射访问限制没有适配,最典型的就是 CGLIB 代理在 JDK 17 上直接抛InaccessibleObjectException。虽然可以加--add-opens参数绕过去,但课程设计没必要跟自己过不去。
MySQL 版本同样要克制。源码里 JDBC 连接串如果长这样:
jdbc:mysql://localhost:3306/exam?useUnicode=true&characterEncoding=utf8那对应的 MySQL 驱动一般是 5.x,需要连 MySQL 5.7 或 8.0(得配useSSL=false)。如果驱动是com.mysql.cj.jdbc.Driver,那是 8.x 驱动,连 MySQL 8.0 即可。最怕的是代码里写com.mysql.jdbc.Driver(5.x 驱动)去连 MySQL 8.0,这时候会直接报Public Key Retrieval is not allowed,需要在连接串末尾加allowPublicKeyRetrieval=true。
3.2 数据库初始化:SQL 脚本导入的三个细节
源码包里的db或sql目录下通常有.sql文件。用 Navicat 或命令行导入时,注意以下三个细节。
第一,字符集。新建数据库时指定:
CREATE DATABASE exam_system DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;不要直接用默认字符集。很多源码里的建表语句没写DEFAULT CHARSET,如果你的 MySQL 服务端默认是latin1,导进去后所有中文都变乱码,等发现时已经晚了。utf8mb4 兼容 utf8,还能存 emoji(虽然考试系统用不到,但学生表里偶尔有人填了表情符号)。
第二,导入顺序。如果 SQL 文件有外键约束,先导主表(用户表、课程表),再导从表(考试表、答卷表)。不过多数课程设计源码不会写外键,都是靠 Service 层逻辑保证引用关系。但保险起见,单独用一个文件执行视图或存储过程时,确认它在表结构之后执行。
第三,确认 SQL 文件里的数据库名和连接串里的库名一致。这是最基础的,但也是我见过翻车率最高的。源码里写exam,你建库叫exam_system,然后所有登录验证都报“用户名密码错误”——因为用户表根本没查出来。用命令行导入的核心命令:
source /path/to/exam.sql;导入后顺手执行一条:
SELECT COUNT(*) FROM sys_user;确认有数据,再关终端。否则你只是把 SQL 文件“跑完”了,到底有没有数据完全不知道。
3.3 从解压到浏览器出现登录页:四步操作流程
配好环境后,我习惯用下面的顺序跑通整个项目,每一步都能快速验证成功与否。
第一步,把源码导入 IDEA,用 Maven 刷新依赖。注意 IDEA 里要配置好本机 Maven 的settings.xml,否则默认走内置 Maven 和中央仓库,下载慢不说,还可能因为网络问题失败。如果pom.xml里依赖下载不下来,优先检查本地仓库路径下有没有上次破的.lastUpdated文件,有就删掉再重新刷新。
第二步,修改配置文件。SSM 项目的配置集中在jdbc.properties(或db.properties),内容类似这样:
jdbc.driver=com.mysql.cj.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/exam?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai jdbc.username=root jdbc.password=123456serverTimezone=Asia/Shanghai是必需品,不写会报时区错误。密码填你本机 MySQL 的实际密码,别照抄源码里的默认值。
第三步,配置 Tomcat。IDEA 里添加 Tomcat Server,选择 Local,Application server指向你本机的 Tomcat 目录。如果编译版本是 JDK 8,Tomcat 用 8.5 或 9.x 都行,不要用 Tomcat 10——Tomcat 10 把javax.*换成了jakarta.*,老代码直接编译不过。
部署方式有两种:war包部署和exploded目录部署。课程设计阶段用 exploded 就行,改完 Java 代码热部署时速度更快。Application context建议直接设成/exam,这样访问路径是http://localhost:8080/exam。改完记得到 Deployment 页签确认 Artifact 已经选上。
第四步,启动后打开浏览器。登录页能正常出来,就直接用源码 README 里给出的测试账号登录,先走一遍学生流程——做一份试卷、提交、看成绩。这一步通过,整个链路就通了。
3.4 登录后白屏或 404?先查这三处
这里先把最常见的现象和原因说清楚,后面避坑章还会展开。
现象一:登录成功跳转后 404。大概率是 SpringMVC 配置里的view-resolver前缀路径错了,比如 JSP 放在/WEB-INF/views/下,配置里写成了/WEB-INF/page/。
现象二:页面出来了但 CSS/JS 全丢。打开浏览器开发者工具,看到一堆.css文件报 404,这是静态资源映射问题。SpringMVC 配置里需要加:
<mvc:resources location="/static/" mapping="/static/**" />如果源码没有这段,加上就好。
现象三:登录后跳转去http://localhost:8080/exam/index显示 404 且 Tomcat 控制台报 404 的详细路径。这里查前台 Controller 的@RequestMapping有没有写全。常见错误是在类上写了@RequestMapping("/exam"),方法上又写@RequestMapping("/index"),实际访问路径就变成/exam/index,这是对的。如果跳转链接里多写了一层,就会 404。看后台日志找到“Not mapped”字样,把实际请求路径和映射路径对比一下就行。
4. 在线考试系统的核心链路:组卷、考试、判分、查成绩
4.1 手动组卷和随机组卷的实现差异
这是在线考试系统的关键业务。源码里手动组卷通常是教师登录后,按课程筛选题目,勾选后点击“加入试卷”,最后保存试卷。随机组卷则是选择课程、题型、每种题型数量、分值,系统自动从题库里抽。
先说随机组卷的 SQL 写法。多数源码用下面这类方式:
SELECT * FROM exam_question WHERE course_id = #{courseId} AND type = #{type} ORDER BY RAND() LIMIT #{count}这个写法在小数据量(几千道题)时没问题,但有两个隐患。第一,ORDER BY RAND()会对全表扫描并按随机值排序,题目到了五万条以上,接口响应能到三秒以上。第二,同一学生反复进入考试页面,如果前端没有把试卷固定住,每次刷新都会重新抽题,导致学生前面做过的答案跟后面试卷对不上。
常见做法是对试卷做“一次性生成、多次使用”的处理。第一次进入考试时生成试卷明细(即考卷和题目的关联记录),之后刷新都读取同一份试卷明细。改成这样后,后端代码要动的点主要是:原来是每次请求都随机抽题,现在要改成先查询是否已有试卷明细,没有才生成。
改法示例:
public ExamPaper generatePaper(Exam exam, Student student) { // 先查是否已有试卷 ExamPaper paper = examPaperMapper.selectByExamAndStudent(exam.getId(), student.getId()); if (paper != null) { return paper; } // 没有则生成新试卷 paper = new ExamPaper(); paper.setExamId(exam.getId()); paper.setStudentId(student.getId()); // 随机抽题、装配 List<ExamQuestion> questions = questionMapper.selectRandomByCourse( exam.getCourseId(), exam.getQuestionCount(), exam.getQuestionType()); examPaperMapper.insert(paper); // 生成试题关联记录 for (ExamQuestion q : questions) { examPaperDetailMapper.insert(paper.getId(), q.getId()); } return paper; }参数说明:selectRandomByCourse里的LIMIT数量直接决定了试卷题量,count的值来自考试的配置字段,不要硬编码在 SQL 里。另外,如果考试配置里单选、多选、判断各有不同数量,这个 Mapper 得写成按题型和数量分别查询,再在 Service 层拼接。
这里要特别提醒:改完随机组卷逻辑后,旧数据里的考试记录如果已经生成了试卷,代码升级后可能出现“老考试试卷读取不到”的情况。迁移方案是给exam_paper表加一个version字段,老数据默认为 0,新生成的试卷写 1。查询时优先查当前版本的试卷,查不到再走生成逻辑。
4.2 考试倒计时与自动交卷:前端定时器和后端兜底的配合
考试倒计时是另一个高频需求。前端实现常用setInterval每秒减一次倒计时,时间归零后触发交卷。这里的第一个坑是刷新页面倒计时重置,解决方法是考试开始时把截止时间存到服务端 Redis 或数据库里,前端每次加载页面时取剩下的时间。
源码里如果没有 Redis,就用数据库字段存end_time。每次进入考试页时查询:
SELECT end_time FROM exam_record WHERE exam_id = #{examId} AND student_id = #{studentId}前端倒计时计算公式:
const remainMs = endTime.getTime() - Date.now(); const remainMinutes = Math.floor(remainMs / 60000); const remainSeconds = Math.floor((remainMs % 60000) / 1000);前端倒计时归零后,触发的是提交表单的 JS,如果此时学生网络断了,请求没发到后端,这门考试就成了“没交卷”。所以后端必须加一道兜底:考试结束时,批量把status还是doing的考卷标记为submitted,未作答的题目按 0 分处理。这个逻辑写在定时任务里,课程设计阶段用 Spring 的@Scheduled注解最方便:
@Component public class ExamAutoSubmitTask { @Scheduled(cron = "0 0/5 * * * ?") public void autoSubmitExpiredExams() { // 查询所有已过期但状态为进行中的考试记录 List<ExamRecord> records = examRecordMapper.selectExpiredUnsubmitted(); for (ExamRecord record : records) { record.setStatus("submitted"); examRecordMapper.updateStatus(record); } } }注意cron表达式:0 0/5 * * * ?表示每 5 分钟执行一次。如果考试是整点结束,建议用0 0/1 * * * ?每分钟扫一次,否则学生 10:00 交卷,定时任务 10:04 才标记,中间这四分钟学生查成绩可能报错。性能上这个定时任务每五分钟全表扫一次,数据量几万条没问题,但注意给status字段加索引,否则全表扫描会越来越慢。
4.3 客观题判分和主观题人工阅卷:成绩从哪来
判分逻辑在不同的源码里质量和完成度差异很大。完整版本的同学应该能看到ExamJudgeService或类似类,负责标准答案比对和得分计算。不完整的源码可能会把判分逻辑写死在 Controller 里,一坨代码既查答案又算分,后面查 bug 极其痛苦。
判断一道多选题的得分逻辑,核心在于正确答案的存储形式。源码里多选的答案字段常见两种存法:
- 逗号分隔字符串:
"A,B,D" - JSON 数组:
["A","B","D"]
如果是逗号分隔,判分时注意顺序问题。学生作答是"B,A,D",与标准答案比对时不能用equals,得先拆分、排序、再拼接比对,或者用集合比较:
public boolean checkMultiAnswer(String studentAnswer, String correctAnswer) { // studentAnswer 和 correctAnswer 都是逗号分隔字符串 Set<String> studentSet = new HashSet<>(Arrays.asList(studentAnswer.split(","))); Set<String> correctSet = new HashSet<>(Arrays.asList(correctAnswer.split(","))); if (studentSet.size() != correctSet.size()) { return false; } return studentSet.containsAll(correctSet); }逻辑说明:先比较数量,再比较包含关系。数量不等直接判错,数量相等但包含关系不成立也是错。这里有个边界:学生答案有空值的情况,比如只选了 A 和 B,传上来可能是"A,B,",最后的空字符串会被拆出来变成一个空元素,导致size多 1。处理办法是在拆分前先去掉末尾逗号:
studentAnswer = studentAnswer.replaceAll(",+$", "");主观题(简答、论述)一般由教师登录后进入待阅卷列表,逐题打分。这个模块的核心是前端展示的学生答案不要被 HTML 转义掉。源码如果用了 JSTL 的<c:out>输出答案内容,默认会转义换行符,学生答案里的分段全没了,老师阅卷时看到的是“一段长文字”。解决方法是把答案里的换行符替换成<br>,并关掉escapeXml:
<c:out value="${fn:replace(answer.content, newline, '<br>')}" escapeXml="false" />注意:escapeXml="false"有 XSS 风险,如果学生答案里嵌了<script>标签,会在阅卷页面直接执行。稳妥做法是对学生的原始输入做白名单过滤,只允许文本和换行,去掉 HTML 标签。课程设计阶段可以不做,但答辩时如果老师问起,能说清楚这一点是被加分的。
5. 六条高频翻车记录:每一条都是拿真金白银换的
5.1 启动 Tomcat 后页面能开但接口报 404
现象:Tomcat 正常启动,浏览器能访问 index.jsp,但点登录按钮后 URL 变成 404。
原因:SpringMVC 的 DispatcherServlet 映射配置不对。如果web.xml里这样写:
<servlet-mapping> <servlet-name>dispatcher</servlet-name> <url-pattern>*.do</url-pattern> </servlet-mapping>那所有 Controller 的@RequestMapping必须带.do后缀,比如/login.do。但前端表单的action写的是/login,两者对不上,直接 404。反过来,url-pattern是/,而 Controller 映射写的是/login.do,也同样 404。
解决:打开web.xml看一眼映射规则,再打开登录页 JSP 或 JS 看表单提交地址,保证两者一致。这是配置问题,不是代码问题。如果地址完全对上了还 404,打开 IDEA 的 Console 找到“RequestMapping”相关日志,确认 Controller 有没有被 Spring 扫到。context:component-scan的 base-package 配错的话,Controller 不会注册,接口不存在。
5.2 MySQL 8.0 连接报 Public Key Retrieval is not allowed
现象:启动项目时数据源初始化失败,报Public Key Retrieval is not allowed。
原因:MySQL 8.0 默认使用caching_sha2_password认证插件,客户端首次连接需要从服务端获取公钥。JDBC 驱动为了安全默认禁止了自动获取公钥。
解决:在 JDBC 连接串末尾加参数:
jdbc.url=jdbc:mysql://localhost:3306/exam?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&useSSL=false另一种解决方案是把 MySQL 用户的认证插件改回mysql_native_password:
ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '你的密码';第一个方案改一处就生效,第二个方案要动 MySQL 用户配置。我建议用第一个,因为后续换机器部署时,连接串里带着参数,省心。
5.3 导入 SQL 后登录报用户名密码错误
现象:导入 SQL 脚本成功,用 README 里写的 admin/admin123 登录,提示“用户名或密码错误”。
原因:要么是 README 里的密码和sys_user表里password字段的密文不对应,要么是数据库连错了。可以先试 SQL 文件里的原始密码,通常是123456或admin。
解决:直接查数据库看这个密码字段是什么样的。如果是明文(比如admin123),说明源码里的登录校验逻辑是明文比对。如果是 32 位 MD5 字符串,用在线工具或本地命令算一下已知密码的 MD5,对比是否一致。如果数据库里存的 MD5 是admin的加密结果,那登录密码就应该是admin。这一步不是改代码,而是确认源码当初填的测试数据是什么。
5.4 部署后中文乱码,改了三处 encoding 才解决
现象:页面上所有中文显示为??,或者数据库中中文变成乱码。
原因:Tomcat 请求编码、页面响应编码、数据库连接编码三处没有统一。缺失任何一环都可能在某个环节出现问题。
解决:三处都改成 UTF-8。
第一处,Tomcat 的server.xml里给 Connector 加上:
<Connector port="8080" protocol="HTTP/1.1" URIEncoding="UTF-8" />第二处,SpringMVC 的字符编码过滤器:
<filter> <filter-name>characterEncodingFilter</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> </filter>注意这个 filter 要放在所有 filter 的最前面,否则它对后的请求参数不起作用。
第三处,在 JSP 页面头部确认:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>如果三处都改了还乱码,检查数据库表字段的collation是不是utf8mb4_general_ci,以及 MySQL 服务端character_set_server变量值是不是utf8mb4。查完就结束了,编码问题八成都在这几个位置里。
5.5 Redis 缓存数据导致成绩被“记住”了,学生重考无法刷新
现象:学生第一次考试后成绩没问题,但第二次重考时成绩没有更新,或者第一次作答的痕迹再次出现。
原因:源码集成了 Redis 做考试过程缓存。学生作答期间,答案缓存在 Redis 中,交卷时部分源码只在 Redis 里更新了状态,数据库里的遗留记录没有清干净。第二次进入考试时先读缓存,读到旧的作答记录。
解决:定位到缓存 key 的生成规则,确认里面包含考试 ID、学生 ID 和场次编号。如果只有考试 ID 和学生 ID,重考时新旧数据就会冲突。改法是在缓存 key 里追加一个自增的尝试次数:
String cacheKey = "exam:answer:" + examId + ":" + studentId + ":" + attemptNo;attemptNo是第几次参加同一场考试,从数据库里查。同时,交卷成功后的方法里显式删除缓存:
redisTemplate.delete(cacheKey);如果源码没集成 Redis,跳过这条。
5.6 文件上传功能报错:javax.servlet.ServletException: NoClassDefFoundError
现象:教师上传题目附件(比如批量导入 Excel)时,点上传直接报错,Tomcat 日志有一堆 ClassNotFound。
原因:源码用到了 Commons FileUpload 库,但pom.xml只引了commons-fileupload,没引commons-io依赖。这个依赖是传递依赖,某些情况下 Maven 没有正确拉全。
解决:在pom.xml里手动补上:
<dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.11.0</version> </dependency>同时检查 Commons FileUpload 版本和你的 Servlet 版本兼容性。老项目用的 1.3.x 配 Tomcat 8 没问题,但在 Tomcat 9 上偶尔显示上传文件丢失。这两个点都排查完之后,上传功能基本能恢复。
6. 把这份源码变成课程设计答辩作品的最后一公里
前面的步骤做完,系统已经能跑能用了,但课程设计和毕业设计的评分通常看的不只是“能运行”。这份源码是下载来的,老师也知道课设源码可以借鉴,关键是把位移量和差异化做出来。
最值得做的扩展有几个方向,工作量从小到大排:一是给题库加批量导入导出的 Excel 功能,老师对这个功能有天然好感,但实现起来需要引入 EasyPOI 或 POI 依赖,注意数据校验和错误反馈;二是给成绩单做可视化报表,用 ECharts 画柱状图、折线图和雷达图,难点在于后端要按班级、按课程、按考试场次聚合成绩数据;三是加一个防切屏检测,前端监听visibilitychange事件,切换页面超过三次自动交卷,这条能在答辩现场直接演示,而且防切屏是真实场景里老师非常关心的问题。
以防切屏检测为例,核心实现逻辑是在考试页面监听两个事件:
document.addEventListener('visibilitychange', function() { if (document.hidden) { // 切出页面,次数加一 switchCount++; if (switchCount >= 3) { // 超过三次,自动交卷 document.getElementById('examForm').submit(); } } });这段代码要配合后端在交卷接口里记录切换次数,否则刷新页面计数器就重置了。答辩时老师问“这个防切屏能不能防住手机”,你就说“当前实现是 PC 端浏览器方案,配合后端的摄像头抓拍和人脸比对可以防双端”即可,不需要真的做出来,但要有思路。
答辩演示环节有一个长期积累下来的经验:用真实课堂场景的演示数据,不要用源码自带的测试数据。老师们往往会对“试题内容和实际课程是否相关”产生兴趣。建一张容量为 20 人的班级名单,把其中 5 人设为“缺考”状态,另外 15 人设定不同的得分分布,演示成绩导出时用 Excel 打开,让老师看到“张三缺考、李四 85、王五 62”这种真实观感,比空荡荡的学生名更有说服力。
最后说一个亲身经历:我曾帮一个朋友调过一份在线考试系统源码,前端页面用的是当年比较经典的蓝色 Bootstrap 模板,功能完全正常,但答辩时老师第一句话是“你这界面是不是网上下的”。朋友临时要改换主题颜色和 logo,那一次真的措手不及。现在再拿到这类源码,我会先花半小时把导航栏的 logo、标题、右下角版权都改掉,再把登录页换成一张课程的封面图,把系统名称从“在线考试系统”改成“XX 课程在线考核平台”,同时顺手把 JSP 页面里默认的页面标题改掉。这些小改动不涉及业务逻辑,纯改前端模板,半小时内能全部完成,但答辩现场给老师的第一印象完全不同。
把最后一步做得踏实的通用顺序是:改系统名称和 logo、替换登录页背景和提示文案、删除源码里写死的演示公告和新闻列表、检查数据库里的初始化管理员密码是否安全。然后才是功能层面的扩展。按这个顺序走完,得到的是一个能运行、看着像自己做的、功能有差异化亮点的系统。希望这份指南能帮到你,少走几步弯路。
本文还有配套的精品资源,点击获取