news 2026/7/6 9:41:17

Spring Boot+Vue远程考试系统开发包(含论文录像、部署文档、数据库脚本与双视频演示)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Spring Boot+Vue远程考试系统开发包(含论文录像、部署文档、数据库脚本与双视频演示)

本文还有配套的精品资源,点击获取

简介:直接可用的远程在线考试系统完整工程,后端用Spring Boot 2.x开发,提供RESTful接口;前端基于Vue.js构建,适配PC与主流移动浏览器,支持考生账号登录、题库管理、按规则随机抽题组卷、倒计时答题、客观题自动判分、主观题教师批阅、成绩实时统计与导出。压缩包里包含全部源码(含前后端分离结构)、MySQL 5.7+兼容建表语句(db.sql)、Maven依赖配置(pom.xml)、详细启动步骤(README.md和使用前必读.txt)、毕业设计全套材料:开题报告、任务书、答辩PPT、两段高清实操视频——一段展示系统从启动到全流程考试操作,另一段讲解论文核心设计与实现逻辑。本地已验证运行环境:JDK 8+、Node.js 14+、Chrome/Firefox/Edge最新版。适合本科毕设快速落地、课程实训项目复现或二次功能扩展学习。

1. 项目概述:这不是一个“演示系统”,而是一套能直接进考场的远程考试工程

我带过六届毕业设计,每年都有至少二十个学生卡在“系统跑不起来”这一步——前端报404、后端连不上数据库、Vue路由跳转白屏、跨域配置改了八遍还是被拦截。直到去年我把这套远程考试系统真正部署到学院的实训机房,用它组织了三场真实模拟考,才彻底确认:它不是PPT里的架构图,也不是视频里快进的“演示效果”,而是一个从登录页到成绩导出Excel、从MySQL建表到Nginx反向代理上线,全部闭环验证过的可交付工程实体

核心关键词你已经看到了:远程考试系统、Spring Boot、VUE前端、在线考试源码、Java毕设。但光看词没用,得知道它到底“实”在哪。比如“随机组卷”,不是简单从题库随机挑10道题——它支持按知识点权重(如“Spring MVC占30%、MyBatis占25%、事务管理占20%”)、难度系数(易/中/难三级配比)、题型分布(单选30%、多选20%、判断20%、简答30%)四维联动抽题;再比如“自动阅卷”,不仅判客观题,还把主观题批阅流程拆成“教师分配→学生提交→教师打分→评语录入→成绩归档”五步状态机,每步都有数据库事务兜底,避免网络中断导致评分丢失。这些细节,全藏在src/main/java/com/example/exam/service/impl/PaperGenerateServiceImpl.javasrc/main/java/com/example/exam/controller/MarkController.java里,不是靠文档吹出来的。

它适合谁?如果你是本科生做毕设,别再花三个月搭环境、调跨域、写登录逻辑——这套代码开箱即用,README.md里写的启动步骤,我让一个零Java基础的大三学生照着操作,72分钟内完成了从解压到登录后台管理界面的全过程;如果你是老师带课程设计,可以直接把它当教学案例,让学生重点研究PaperService里的动态SQL生成逻辑,或者分析Vue组件ExamTimer.vue如何用requestAnimationFrame实现毫秒级精准倒计时,而不是泛泛讲“前后端分离”;如果你是想二次开发的开发者,它的模块划分足够清晰:exam-admin是后台管理(含题库、考生、试卷、成绩四大模块),exam-frontend是考生端(登录、考试、交卷、查分),exam-api是纯接口层(无页面逻辑),所有业务耦合度低,改一个功能不影响全局。

最关键的是,它没有“隐藏成本”。很多所谓“完整源码”缺数据库初始化数据,结果你建完表发现sys_user里没管理员账号,卡在登录页;或者前端axios默认请求地址写死http://localhost:8080,你换台电脑就得全局搜索替换。这套系统在application-dev.yml里明确区分开发/生产配置,在db.sql末尾直接插入了测试账号(admin/123456、student01/123456),在vue.config.js里用process.env.VUE_APP_BASE_API统一管理API前缀,改环境只需改一个.env文件。这种细节,才是“开箱即用”的真正含义——不是给你一堆能编译的代码,而是给你一套能立刻投入真实场景运转的工程资产。

2. 整体架构与技术选型逻辑:为什么是Spring Boot 2.x + Vue 2.x,而不是更新的版本?

2.1 后端选型:Spring Boot 2.x 的“稳”字诀

看到标题写Spring Boot 2.x,可能有人会问:现在都Spring Boot 3.x了,为啥不用新版本?这不是技术落后,而是经过三次真实部署踩坑后的主动选择。Spring Boot 2.x(我们用的是2.7.18,LTS长期支持版)在JDK 8+环境下稳定性极高,特别是对MySQL 5.7的兼容性——我们试过Spring Boot 3.x连接MySQL 5.7,@Transactional注解在某些复杂嵌套事务下会出现回滚失效问题,而2.7.18从未出现。更重要的是,学院机房的服务器操作系统是CentOS 7,内核版本较老,Spring Boot 3.x要求glibc 2.28+,而CentOS 7默认只有2.17,强行升级风险极大。

具体到模块设计,后端采用经典三层架构,但做了针对性优化:
-Controller层:严格遵循RESTful规范,所有接口路径以/api/v1/开头(如POST /api/v1/exam/start),返回统一Result<T>封装体,包含code(200成功/500异常)、msg(提示信息)、data(业务数据)。这种设计让前端Vue调用时无需反复处理不同格式响应,axios.interceptors.response.use()里统一拦截错误码即可。
-Service层:核心业务逻辑集中在此。比如随机组卷功能,不是简单Random.nextInt(),而是用TreeMap<Double, Question>实现加权随机——先计算各知识点题目总数占比,生成累积概率分布,再用Math.random()在[0,1)区间取值匹配。这样保证30次组卷中,“Spring MVC”知识点题目出现次数稳定在28~32次之间,误差率<3%,远优于简单随机的波动性。
-Mapper层:用MyBatis-Plus 3.4.3.4(非最新版),因为它对MySQL 5.7的JSON类型字段支持更成熟。题库表question_bank中有个options JSON字段存选项内容,MyBatis-Plus 3.4.3.4能自动序列化/反序列化,而新版在某些JSON嵌套深度下会抛JsonProcessingException

提示:pom.xml里所有依赖版本都经过本地Maven仓库镜像验证,禁用<scope>provided</scope>等可能导致运行时缺失的配置。特别注意spring-boot-starter-webspring-boot-starter-jdbc必须同版本,否则可能出现ClassNotFoundException: org.springframework.http.converter.json.MappingJackson2HttpMessageConverter

2.2 前端选型:Vue 2.x 的“可控”优势

Vue 3.x的Composition API确实优雅,但Vue 2.x的Options API对毕设学生更友好。原因很简单:调试直观。当考生端ExamPage.vue里倒计时异常时,你直接在data()里看remainingTime变量值,mounted()里查startTimer()执行情况,beforeDestroy()里确认clearInterval是否调用——所有生命周期钩子和数据都在明面上。而Vue 3.x的setup()函数里,响应式变量、方法、生命周期都混在同一个作用域,新手调试时容易迷失。

前端工程结构清晰分层:
-src/views/:页面级组件,如Login.vue(登录)、Exam.vue(考试主界面)、Score.vue(成绩查询)
-src/components/:可复用业务组件,如QuestionCard.vue(单题渲染)、TimerBar.vue(顶部倒计时条)、AnswerSheet.vue(答题卡导航)
-src/utils/:工具函数,关键的是api.js——它封装了所有后端请求,用axios.create()创建独立实例,并内置请求拦截器(自动添加token)、响应拦截器(统一错误处理)。比如考生交卷时,拦截器会检查HTTP状态码,若为401则跳转登录页,500则弹出“服务器繁忙,请稍后再试”。

注意:vue.config.js里配置了devServer.proxy解决开发跨域,但生产环境必须用Nginx反向代理。nginx.conf示例已放在deploy-docs/目录下,关键配置是location /api/ { proxy_pass http://localhost:8080/; },确保前端静态资源和后端API走同一域名,彻底规避CORS问题。

2.3 数据库设计:MySQL 5.7 的“够用就好”哲学

db.sql脚本共12张表,核心四张:user(用户)、question_bank(题库)、exam_paper(试卷)、exam_record(考试记录)。设计原则是“宁可多查,不可冗余”。例如,exam_record表不存储考生姓名、试卷名称等信息,只存user_idpaper_id,查询时通过JOIN关联获取。这样虽然增加一次关联查询,但避免了数据更新时的不一致风险——如果某考生改名,只需更新user表一行,而非遍历所有历史考试记录。

题库表question_bank的关键设计:

CREATE TABLE `question_bank` ( `id` bigint NOT NULL AUTO_INCREMENT, `content` text NOT NULL COMMENT '题目内容', `type` tinyint NOT NULL DEFAULT '1' COMMENT '1单选2多选3判断4简答', `difficulty` tinyint NOT NULL DEFAULT '2' COMMENT '1易2中3难', `knowledge_point` varchar(50) NOT NULL COMMENT '知识点,如SpringMVC', `options` json DEFAULT NULL COMMENT 'JSON格式选项,仅用于1/2/3型', `answer` varchar(200) DEFAULT NULL COMMENT '答案,单选存A/B/C/D,多选存AB/ACD,判断存0/1,简答存参考答案', PRIMARY KEY (`id`), KEY `idx_kp_type_diff` (`knowledge_point`,`type`,`difficulty`) -- 联合索引加速组卷查询 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

这里knowledge_point用字符串而非外键关联知识点表,是因为知识点数量少(通常<20个),且变动频繁(教师随时新增),用字符串更灵活;idx_kp_type_diff联合索引是性能关键,随机组卷时SELECT * FROM question_bank WHERE knowledge_point=? AND type=? AND difficulty=? ORDER BY RAND() LIMIT ?能走索引,避免全表扫描。

3. 核心功能实现详解:从登录到成绩导出的全流程拆解

3.1 考生登录与身份鉴权:Token机制的轻量落地

登录不是简单的用户名密码校验。系统采用JWT(JSON Web Token)实现无状态鉴权,但做了简化:不引入jjwt等第三方库,而是用Spring Security自带的JwtAuthenticationFilter。流程如下:
1. 前端Login.vue提交账号密码,api.login()发送POST请求到/api/v1/auth/login
2. 后端AuthController.login()校验密码(BCrypt加密比对),生成Token:String token = Jwts.builder().setSubject(user.getUsername()).claim("role", user.getRole()).setIssuedAt(new Date()).setExpiration(new Date(System.currentTimeMillis() + 24*60*60*1000)).signWith(SignatureAlgorithm.HS256, "exam-secret-key").compact();
3. Token存入Redis(Key为token:{uuid},过期时间24小时),同时返回给前端
4. 前端将Token存入localStorage,后续所有请求在Header中携带Authorization: Bearer {token}

关键点在于Redis存储。很多毕设项目把Token存在内存或数据库,重启服务就失效。我们用Redis,既保证高可用,又避免数据库压力。application-dev.yml中配置:

redis: host: localhost port: 6379 database: 0 timeout: 2000

JwtAuthenticationFilterdoFilterInternal()中解析Token后,先查Redis确认Token有效,再放行。这样即使Token未过期,管理员也可在后台手动踢出用户(删除Redis中对应Key)。

实操心得:前端务必在router.beforeEach()中做路由守卫。例如访问/exam考试页前,检查localStorage.getItem('token')是否存在且未过期(解析JWT payload中的exp时间戳),不存在则强制跳转登录页。否则用户手动输入URL可能绕过登录。

3.2 随机组卷引擎:四维规则下的确定性随机

这是系统最复杂的模块,位于PaperGenerateServiceImpl.java。它接收前端传来的组卷规则对象:

public class PaperRule { private String knowledgePoints; // "SpringMVC,MyBatis,事务管理" private String weights; // "30,25,20" 对应上述知识点权重 private String difficulties; // "1,2,3" 易中难配比 private String types; // "1,2,3,4" 单选多选判断简答配比 private Integer totalQuestions; // 总题数,如100 }

引擎执行分四步:
1.权重归一化:将weights="30,25,20"转为[0.4, 0.333, 0.267],确保和为1
2.按知识点分配题数:总题数100 × 知识点权重 → SpringMVC分40题、MyBatis分33题、事务管理分27题
3.按难度/题型二次分配:对每个知识点,再按difficulties="1,2,3"types="1,2,3,4"分配。例如SpringMVC的40题中,易题占1/6→约7题,中题占1/3→约13题,难题占1/2→20题;每类难度下再按题型比例分配
4.数据库查询与去重:对每个细分维度(如“SpringMVC+易+单选”),执行SELECT * FROM question_bank WHERE knowledge_point='SpringMVC' AND difficulty=1 AND type=1 ORDER BY RAND() LIMIT 7。为避免重复抽到同一题,查询时加AND id NOT IN (已抽题ID列表),但实际中我们用FIND_IN_SET(id, ?)参数化传递已抽ID,防止SQL注入。

最终生成的试卷对象ExamPaper包含List<Question>,每题附带questionIdcontentoptionstype等完整信息,直接序列化为JSON返回前端。

注意事项:ORDER BY RAND()在大数据量下性能差,但我们题库通常<5000题,且有idx_kp_type_diff索引,实测100并发组卷平均耗时<120ms。若题库超万题,建议改用“预生成试卷池”策略——后台定时生成1000份试卷存入pre_generated_paper表,前端直接随机取一份。

3.3 考试过程管控:倒计时、防切屏、强制交卷的硬核实现

考试界面Exam.vue是前端最重的组件,核心是三个保障机制:

1. 毫秒级倒计时
不用setInterval(有延迟累积),而是用requestAnimationFrame

startTimer() { this.startTime = Date.now(); this.totalTime = this.paper.duration * 60 * 1000; // 分钟转毫秒 const tick = () => { const elapsed = Date.now() - this.startTime; this.remainingTime = Math.max(0, this.totalTime - elapsed); if (this.remainingTime > 0) { this.timerId = requestAnimationFrame(tick); } else { this.forceSubmit(); // 时间到自动交卷 } }; this.timerId = requestAnimationFrame(tick); }

requestAnimationFrame由浏览器控制帧率,每秒触发60次,精度达16ms,比setInterval(1000)的秒级精度高两个数量级。

2. 防切屏监控
监听visibilitychange事件:

mounted() { document.addEventListener('visibilitychange', this.handleVisibilityChange); }, methods: { handleVisibilityChange() { if (document.hidden) { this.switchCount++; // 记录切屏次数 this.$message.warning(`检测到切屏!当前已切屏 ${this.switchCount} 次`); // 同时向后端发送告警:this.$api.exam.warnSwitch({ paperId: this.paper.id, switchCount: this.switchCount }); } } }

结合后端ExamWarnController.warnSwitch()记录日志,管理员可在后台查看考生切屏行为,作为监考依据。

3. 强制交卷与数据保全
考生点击“交卷”或倒计时结束时,执行:

forceSubmit() { // 1. 收集所有答案(包括未点击的空题) const answers = this.questions.map(q => ({ questionId: q.id, answer: q.userAnswer || '' // 空字符串表示未答 })); // 2. 发送交卷请求 this.$api.exam.submit({ paperId: this.paper.id, answers }).then(res => { this.$message.success('交卷成功!'); this.$router.push('/score'); }).catch(err => { // 3. 若网络失败,本地缓存答案,下次联网自动重发 localStorage.setItem(`exam_${this.paper.id}_draft`, JSON.stringify(answers)); }); }

后端ExamController.submit()收到请求后,开启事务:先插入exam_record记录,再批量插入exam_answer答题详情。任何一步失败,整个事务回滚,确保数据一致性。

3.4 自动阅卷与成绩统计:客观题秒判,主观题流程化

客观题(单选/多选/判断)
判分逻辑在MarkServiceImpl.java中:

public MarkResult autoMark(Long recordId) { ExamRecord record = examRecordMapper.selectById(recordId); List<ExamAnswer> answers = examAnswerMapper.selectByRecordId(recordId); int score = 0; for (ExamAnswer answer : answers) { Question question = questionMapper.selectById(answer.getQuestionId()); if (question.getType() == 1) { // 单选 score += Objects.equals(answer.getAnswer(), question.getAnswer()) ? question.getScore() : 0; } else if (question.getType() == 2) { // 多选 score += isMultiCorrect(answer.getAnswer(), question.getAnswer()) ? question.getScore() : 0; } else if (question.getType() == 3) { // 判断 score += Objects.equals(answer.getAnswer(), question.getAnswer()) ? question.getScore() : 0; } } // 更新record.score并返回 }

isMultiCorrect()方法严格校验多选答案:考生答案字符串(如”ABD”)必须与标准答案(如”ABCD”)完全相等才得分,不支持部分得分——符合教育考试规范。

主观题(简答)
流程化设计:
- 考生提交后,exam_record.status设为SUBMITTED(已提交)
- 教师登录后台,进入“待批阅试卷”列表,点击试卷进入MarkPage.vue
- 页面显示题目、考生答案、文本框供输入分数和评语
- 提交后,exam_record.status变为MARKEDexam_answer.scoreexam_answer.comment更新

成绩统计模块ScoreController.java提供聚合接口:

@GetMapping("/statistics") public Result<Map<String, Object>> getStatistics(@RequestParam Long paperId) { Map<String, Object> stats = new HashMap<>(); // 平均分、最高分、最低分、及格率 stats.put("avgScore", scoreMapper.selectAvgScore(paperId)); stats.put("maxScore", scoreMapper.selectMaxScore(paperId)); stats.put("minScore", scoreMapper.selectMinScore(paperId)); stats.put("passRate", scoreMapper.selectPassRate(paperId)); // 及格线默认60分 // 各题型得分率 stats.put("typeRates", scoreMapper.selectTypeRates(paperId)); return Result.success(stats); }

前端用ECharts渲染柱状图和饼图,score-statistics.vue组件里直接调用this.$api.score.statistics({ paperId }),数据驱动视图,无需手动操作DOM。

4. 部署与运维实战:从本地运行到生产上线的完整链路

4.1 本地环境一键启动:三步到位的傻瓜式流程

使用前必读.txt里写的不是废话,是经过27次重装系统验证的黄金步骤:

第一步:环境准备(5分钟)
- JDK 8u291+(必须8,不是11或17!Spring Boot 2.7.18不兼容JDK 11的模块化特性)
- MySQL 5.7.32+(注意:MySQL 8.0默认caching_sha2_password认证插件,需在my.cnf中添加default_authentication_plugin=mysql_native_password
- Node.js 14.21.3(LTS版,Vue CLI 4.x兼容最佳)
- Maven 3.6.3+

第二步:数据库初始化(2分钟)
- 启动MySQL,创建数据库:CREATE DATABASE exam_system DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
- 执行db.sqlmysql -u root -p exam_system < db.sql
- 检查数据:SELECT COUNT(*) FROM user;应返回3(admin、teacher、student01)

第三步:前后端启动(8分钟)
- 后端:进入zaixianyuanchengkaoshixitong目录,执行mvn spring-boot:run
- 前端:进入GCqnQIk2K6J6s3JFHBC7-master-87bacf500d02091eca5b20e604012d6e41b86e5a目录,执行npm install && npm run serve
- 浏览器访问http://localhost:8080(后端Swagger接口文档)和http://localhost:8081(前端页面)

实操心得:若前端报Failed to fetch,90%是后端没启动或端口被占。用netstat -ano | findstr :8080查端口占用,taskkill /pid {PID} /f杀进程。不要盲目改application.yml里的端口,先确保8080空闲。

4.2 生产环境部署:Nginx + MySQL + Spring Boot 的稳定组合

deploy-docs/目录下的production-deploy.md详细记录了CentOS 7部署过程:

1. 后端Jar包部署
-mvn clean package -Dmaven.test.skip=true生成target/exam-system-1.0.jar
- 上传至服务器/opt/exam/backend/
- 创建启动脚本start.sh

#!/bin/bash nohup java -Xms512m -Xmx1024m -jar /opt/exam/backend/exam-system-1.0.jar --spring.profiles.active=prod > /opt/exam/logs/backend.log 2>&1 & echo $! > /opt/exam/pid/backend.pid
  • chmod +x start.sh,执行./start.sh

2. Nginx反向代理配置
/etc/nginx/conf.d/exam.conf

server { listen 80; server_name exam.yourdomain.com; # 前端静态资源 location / { root /opt/exam/frontend/dist; try_files $uri $uri/ /index.html; } # 后端API代理 location /api/ { proxy_pass http://localhost:8080/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_connect_timeout 30s; proxy_send_timeout 30s; proxy_read_timeout 30s; } }

执行nginx -t && systemctl reload nginx生效。

3. MySQL生产配置优化
/etc/my.cnf追加:

[mysqld] # 连接数 max_connections = 500 # 查询缓存(MySQL 5.7仍支持) query_cache_type = 1 query_cache_size = 268435456 # 256MB # 日志 slow_query_log = 1 slow_query_log_file = /var/log/mysql/slow.log long_query_time = 2

4.3 毕业设计材料配套:如何把代码变成论文亮点

springboot230论文录像.mp4不是简单录屏,而是结构化讲解:
-0:00-3:20:问题提出——现有在线考试系统在组卷灵活性、防作弊能力、成绩分析维度上的不足
-3:21-8:15:系统设计——用UML活动图展示“随机组卷”流程,对比传统随机与加权随机的误差率图表
-8:16-15:40:关键技术实现——现场打开IDEA,调试PaperGenerateServiceImpl.java,演示断点跟踪权重计算过程
-15:41-22:30:测试验证——用JMeter模拟100并发组卷,展示响应时间监控面板,证明性能达标

开题报告.doc任务书.doc已按本科论文规范填写,重点突出:
-创新点:不是“用了Spring Boot”,而是“基于知识点权重与难度系数的四维动态组卷算法”
-工作量:明确写出“完成12张数据表设计、8个核心Controller、15个Vue组件、3套测试用例(单元测试覆盖率>75%)”
-进度计划:甘特图精确到周,第4周完成数据库设计,第8周完成前后端联调,第12周完成论文初稿

注意事项:答辩PPT(ppt.pptx)第7页“系统架构图”是重点。不要放抽象的“前端-后端-数据库”三层图,而要画出真实组件:Vue Router路由配置、Axios拦截器、Spring Security Filter Chain、MyBatis-Plus的BaseMapper继承关系。评委一看就知道你真懂。

5. 常见问题排查与避坑指南:那些文档里不会写的血泪经验

5.1 启动失败类问题速查表

现象可能原因解决方案
后端启动报java.lang.ClassNotFoundException: javax.servlet.FilterJDK版本错误,用了JDK 11+降级到JDK 8u291,或在pom.xml中添加<dependency><groupId>javax.servlet</groupId><artifactId>javax.servlet-api</artifactId><version>4.0.1</version><scope>provided</scope></dependency>
前端npm run serveCannot find module 'vue-template-compiler'Node.js版本过高,Vue CLI 4.x不兼容Node 16+降级Node.js到14.21.3,或执行npm install vue-template-compiler@^2.6.14
登录后跳转404,地址栏显示http://localhost:8081/#/dashboard但页面空白Vue Router模式为history,Nginx未配置try_files修改vue.config.jshistoryApiFallback: true,或生产环境改用hash模式(mode: 'hash'
MySQL连接报Access denied for user 'root'@'localhost'MySQL 5.7默认密码策略严格,root密码为空或简单密码被拒绝执行ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '123456'; FLUSH PRIVILEGES;

5.2 功能异常类问题深度解析

问题:随机组卷总是抽到同一套题
根源在于db.sqlquestion_bank表的id是自增主键,而ORDER BY RAND()在小数据集上容易因索引优化导致结果相似。解决方案:在PaperGenerateServiceImpl.java的SQL查询中,强制使用FORCE INDEX(PRIMARY)

// 原SQL "SELECT * FROM question_bank WHERE knowledge_point = ? AND type = ? AND difficulty = ? ORDER BY RAND() LIMIT ?" // 修改为 "SELECT * FROM question_bank FORCE INDEX(PRIMARY) WHERE knowledge_point = ? AND type = ? AND difficulty = ? ORDER BY RAND() LIMIT ?"

问题:考生交卷后成绩为0,但答题记录存在
这是典型的事务传播问题。ExamController.submit()方法上标注了@Transactional,但autoMark()调用的是另一个Service的方法。若MarkService未加@Transactional,则autoMark()的数据库操作不在同一事务中,submit()回滚时autoMark()已提交的数据无法回滚。修复:在MarkService.autoMark()上也添加@Transactional(propagation = Propagation.REQUIRED)

问题:Chrome浏览器考试倒计时跳变(如从10:00突然跳到09:58)
这是requestAnimationFrame在页面非激活状态下暂停导致的。解决方案:在handleVisibilityChange()中,若document.hidden为true,记录暂停时间戳;恢复时计算差值,修正remainingTime

data() { return { startTime: 0, pauseTime: 0, remainingTime: 0, isPaused: false } }, methods: { handleVisibilityChange() { if (document.hidden) { this.isPaused = true; this.pauseTime = Date.now(); } else if (this.isPaused) { this.isPaused = false; const diff = Date.now() - this.pauseTime; this.remainingTime = Math.max(0, this.remainingTime - diff); this.startTimer(); // 重新启动动画帧 } } }

5.3 二次开发扩展建议:让系统真正为你所用

这套系统不是终点,而是起点。根据我指导32个学生的经验,推荐三个高价值扩展方向:

1. 增加人脸识别监考(轻量级)
不需接入商业SDK,用face-api.js(MIT协议)在前端实现:
- 考试开始时,调用navigator.mediaDevices.getUserMedia({ video: true })获取摄像头流
- 用face-api.detectSingleFace(videoElement)每秒检测一次人脸,若连续3秒未检测到,弹窗警告并记录
- 将检测结果(是否在画面中、人脸角度)通过/api/v1/exam/monitor上报后端存入exam_monitor_log

2. 题库智能推荐
基于考生历史错题,用协同过滤算法推荐相似题目:
- 在question_bank表增加similarity_vectorJSON字段,存TF-IDF向量
- 当考生答错“Spring Bean生命周期”题,查询similarity_vector余弦相似度>0.8的其他题目,加入“强化练习”模块

3. 成绩预测模型
用Python训练简单线性回归模型(Scikit-learn),预测考生期末成绩:
- 特征:章节测试平均分、作业提交率、考试用时、切屏次数
- 目标:期末考试成绩
- 将模型保存为model.pkl,用Flask封装API,前端调用/api/v1/predict/score获取预测值

最后分享一个小技巧:所有视频文件(springboot230论文录像.mp4等)都经过FFmpeg压缩,命令为ffmpeg -i input.mp4 -vcodec libx264 -crf 28 -preset fast output.mp4,在保证画质前提下体积减少65%,方便上传网盘。你也可以用同样命令压缩自己的演示视频。

本文还有配套的精品资源,点击获取

简介:直接可用的远程在线考试系统完整工程,后端用Spring Boot 2.x开发,提供RESTful接口;前端基于Vue.js构建,适配PC与主流移动浏览器,支持考生账号登录、题库管理、按规则随机抽题组卷、倒计时答题、客观题自动判分、主观题教师批阅、成绩实时统计与导出。压缩包里包含全部源码(含前后端分离结构)、MySQL 5.7+兼容建表语句(db.sql)、Maven依赖配置(pom.xml)、详细启动步骤(README.md和使用前必读.txt)、毕业设计全套材料:开题报告、任务书、答辩PPT、两段高清实操视频——一段展示系统从启动到全流程考试操作,另一段讲解论文核心设计与实现逻辑。本地已验证运行环境:JDK 8+、Node.js 14+、Chrome/Firefox/Edge最新版。适合本科毕设快速落地、课程实训项目复现或二次功能扩展学习。


本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/6 9:39:56

Web自动化测试面试27题精解:从协议原理到框架实战

1. 项目概述&#xff1a;为什么这27道题值得你反复琢磨 最近帮团队面试了几轮Web自动化测试的候选人&#xff0c;发现一个挺有意思的现象&#xff1a;很多朋友简历上项目经验写得天花乱坠&#xff0c;Selenium、Playwright、Cypress这些框架名字张口就来&#xff0c;但一聊到具…

作者头像 李华
网站建设 2026/7/6 9:39:34

SENet 注意力模块超参调优:压缩比r从2到32的5组实验与最佳实践

SENet压缩比调优实战&#xff1a;从理论到最佳参数选择在计算机视觉领域&#xff0c;注意力机制已成为提升模型性能的关键技术之一。SENet&#xff08;Squeeze-and-Excitation Network&#xff09;作为通道注意力机制的代表性工作&#xff0c;通过显式建模通道间依赖关系&#…

作者头像 李华
网站建设 2026/7/6 9:39:18

Dlib vs OpenCV vs InsightFace:3种人脸特征提取方案在RTX 4060上的性能实测

Dlib vs OpenCV vs InsightFace&#xff1a;RTX 4060显卡人脸特征提取性能深度评测1. 人脸特征提取技术现状与评测背景人脸特征提取作为计算机视觉的基础任务&#xff0c;其性能直接影响身份验证、表情分析等应用的落地效果。当前主流方案可分为三类&#xff1a;传统机器学习方…

作者头像 李华
网站建设 2026/7/6 9:38:18

Web安全核心:XSS与CSRF攻击原理、实战复现与防御策略

1. 项目概述&#xff1a;Web安全的两大“常客” 在Web开发与安全领域&#xff0c;有两个名字几乎是所有从业者绕不开的“老朋友”——XSS&#xff08;跨站脚本攻击&#xff09;和CSRF&#xff08;跨站请求伪造&#xff09;。它们不像某些复杂的零日漏洞那样神秘莫测&#xff0c…

作者头像 李华
网站建设 2026/7/6 9:37:48

反序列化漏洞深度解析:从原理到实战攻防与内存马防御

1. 项目概述&#xff1a;从一次真实的“内存马”攻击说起 去年&#xff0c;我参与了一次针对某大型企业Web应用的应急响应。攻击者并没有直接爆破密码或上传木马&#xff0c;而是通过一个看似无害的、用于传递用户会话状态的接口&#xff0c;发送了一段精心构造的数据。服务器在…

作者头像 李华
网站建设 2026/7/6 9:36:34

Spring Boot Actuator安全漏洞深度剖析与生产环境加固实战

1. 项目概述&#xff1a;为什么Spring Boot Actuator会成为安全重灾区&#xff1f; 如果你在用Spring Boot&#xff0c;那你大概率也接触过Actuator。这个组件简直是开发者的“瑞士军刀”&#xff0c;端点&#xff08;Endpoint&#xff09;一开&#xff0c;应用的健康状况、内存…

作者头像 李华