尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

基于SpringBoot+Vue3的在线考试系统设计与实现

基于SpringBoot+Vue3的在线考试系统设计与实现 做个在线考试系统听起来不算新鲜但真要把考试流程、题库管理、学习交流这些模块全部打通还能扛住并发、方便二次开发其实有不少门道。我这段时间基于 Java 技术栈完整实现了一套前后端分离的在线考试与学习交流平台技术选型用的是 SpringBoot Vue3 MyBatis MySQL正好把实际开发过程中踩过的坑、做过的取舍都记录下来给需要做类似系统或者正在准备相关技术面试的朋友一些参考。这套系统的定位很明确不只是一个能提交试卷的答题器而是把题库建设—试卷组织—在线考试—自动阅卷—成绩分析—学习交流串成一条完整闭环的教学辅助平台。学生可以参加考试、查看错题、在交流区提问教师可以管理题库、组卷、查看成绩统计系统管理员负责用户和资源配置。对想练手前后端分离项目的人或者正在做毕业设计、公司内部培训系统的同学这篇内容可以直接对照落地。1. 项目全貌与技术选型思路1.1 在线考试系统要解决的核心问题在线考试系统表面上是把纸质试卷搬到网页上但实际落地时会发现核心难点远不止出题和答题这么简单。一是试卷生成策略。一场考试需要支持手工组卷和随机组卷两种方式。手工组卷要求教师从题库中逐题挑选随机组卷则要按题型、知识点、难度比例自动抽题。这里涉及复杂的查询条件和权重匹配逻辑不是简单 SELECT 就能搞定的。二是考试过程中的状态管理。考试开始后学生需要计时答题、保存答案、切换题目、最后提交。前端要维护每道题的作答状态后端要处理异常退出、断线重连、超时自动交卷等问题。三是阅卷与成绩统计。客观题单选、多选、判断系统自动判分主观题需要教师人工评分成绩出来后还要做班级维度、知识点维度的统计分析这又涉及到数据聚合和可视化展示。四是学习交流模块。考试不是终点考完之后的错题回顾、知识点讨论、同学互助才是提升学习效果的关键。所以系统里还得有类似轻量论坛的交流区支持发帖、回复、浏览。我最初收到这个需求时第一反应是直接找一个现成的开源在线考试系统改改但很快放弃了这个念头。现有开源项目要么技术栈太老很多还是 JSP Servlet要么业务耦合太深、二次开发成本高。与其在一个不熟悉的老代码库里翻来覆去不如基于成熟的技术栈从零搭建把每个模块都掌控在自己手里。1.2 为什么选择 SpringBoot Vue3 MyBatis 这套组合技术选型不是越新越好而是要看团队熟悉度、社区生态、学习资料丰富程度和维护成本。这套组合属于当前 Java 后端开发最主流的技术栈之一几乎每个招聘 JD 上都会出现选它本身就具备学习价值。SpringBoot 解决的是后端基础设施的复杂度。自动配置机制让我不用像传统 SSM 项目那样写一堆 XML 配置文件内嵌 Tomcat 让应用可以一键启动Spring Security 或拦截器可以轻松实现登录认证与权限控制。更重要的是SpringBoot 的生态极其丰富不管后续要接入 Redis、RabbitMQ、ElasticSearch 都有现成的 Starter。Vue3 是当前前端框架的绝对主流。Composition API 让组件代码逻辑更加聚合配合 Vite 构建工具开发时热更新速度非常快。对在线考试这种交互复杂、状态多的业务场景Vue3 的响应式系统可以让考试页面做到极致的交互流畅度。MyBatis 选它的理由是灵活和可控。考试系统的查询逻辑非常复杂比如随机组卷、多条件筛选题目、成绩聚合统计这些 SQL 手写比 ORM 自动生成更高效、更精准。MyBatis 的 XML SQL 映射机制特别适合这类场景而且它对 JDBC 的封装程度合适通过标签拼接 SQL性能调优的空间更大。MySQL 则是数据库层面的稳妥选择。它足够稳定、资料丰富、部署简单考试系统这类业务量级完全没有上分布式数据库的必要单机 MySQL 合理的索引设计已经能支撑数千人同时在线考试。1.3 前后端分离的架构辨析这套系统采用前后端完全分离的架构前端和后端分别独立开发、独立部署通过 RESTful API 接口通信。前端工程跑在 Nginx 上负责页面渲染和用户交互后端工程跑在独立的 Tomcat 端口上只提供 JSON 数据接口。二者的通信通过 HTTP 请求完成用户身份认证采用 JWT 令牌机制登录后前端拿到 token之后每次请求都在 Header 中携带。前后端分离的好处非常明显开发效率高前后端可以并行推进只需要提前约定好接口文档部署灵活前端静态资源走 CDN 加速后端接口做横向扩容面试时这是一个非常标准的加分项能体现对工程化的理解难点在于接口设计规范、跨域处理、联调效率。我在开发过程中吃过接口定义不清晰的亏后来通过编写 OpenAPI 文档 Mock 数据服务把联调时间压缩了不少。2. 数据库设计与核心表结构2.1 核心业务实体梳理在线考试系统的业务实体可以从人—题—卷—考—学五个维度来梳理人用户学生、教师、管理员三种角色题题库选择题、判断题、简答题等卷试卷包含多道试题有总分、考试时长、组卷策略等属性考考试记录某学生某次考试的答题明细、得分、状态学学习交流内容帖子、回复、收藏围绕这五个维度数据库表的设计大致如下表名用途核心字段sys_user用户信息表id、username、password、role、nickname、avatarexam_subject课程科目表id、name、descriptionquestion_bank试题表id、subject_id、type、difficulty、content、analysis、answerexam_paper试卷表id、subject_id、title、duration、total_score、statusexam_paper_question试卷-试题关联表id、paper_id、question_id、score、sortexam_record考试记录表id、paper_id、user_id、score、status、start_time、end_timeexam_answer_detail作答明细表id、record_id、question_id、user_answer、is_correct、scorestudy_post交流帖子表id、user_id、title、content、view_count、like_countstudy_reply回复表id、post_id、user_id、contentsys_notice系统公告表id、title、content、create_time每个业务模块的核心表基本都在这里了表数量不算多但关系不简单设计时要注意字段冗余和查询效率的平衡。2.2 表结构设计与关系建模这里重点说几处关键的建模细节。**用户表设计。**关于用户表需要注意角色设计。我采用的是单一字段 role 区分角色1 表示管理员、2 表示教师、3 表示学生。做权限控制时只需要解析 JWT 里的 role 字段再配合后端的拦截器做判断即可。密码字段不能明文存储通过 BCrypt 算法加密。多数人会忽略 BCrypt 的一个重要特性自动加盐同一密码两次加密后的密文不同这比普通 MD5 安全得多。**题库表设计。**试题表我设计了 type 字段1 单选题、2 多选题、3 判断题、4 简答题。content 存放题干analysis 存放题目解析answer 存放标准答案。这里有个坑要注意单选题和多选题的答案存储格式不一样单选、判断题直接存 A/B/C 这种选项编号多选题我用了逗号分隔的字符串比如 A,C,D 这样方便后续比对。简答题的 answer 字段存的是参考答案文本和得分要点能不能得分还是得靠教师人工批改。难度用 1-5 表示5 为最难组卷时按难度拆分抽取比例。**试卷与试题关联表设计。**一张试卷对应多道题目如果直接在 exam_paper 表里拼题目 JSON 字符串虽然看起来省事但后续做成绩统计、逐题分析时会非常痛苦。我的方案是用 exam_paper_question 这个中间表来维护关联关系每条记录存试卷 ID、题目 ID、该题分值、排序号。这样既能方便查询某张试卷包含哪些题目也能反过来查某道题被哪些试卷引用过对导出统计报表非常友好。**考试记录设计。**exam_record 表要记录考试的状态status 字段 0 未开始、1 进行中、2 已完成。start_time 和 end_time 用来做超时判断后端定时检查状态为进行中但已经超过结束时间的记录强制提交。2.3 索引设计与性能考虑线上考试系统最怕的是两类问题一是并发提交试卷时数据库连接被占满二是按条件筛选题目时查询超时。索引设计上我优先保证了高频查询的路径ALTER TABLE exam_record ADD INDEX idx_record_user (user_id); ALTER TABLE exam_record ADD INDEX idx_record_paper (paper_id); ALTER TABLE exam_paper_question ADD INDEX idx_pq_paper (paper_id); ALTER TABLE exam_paper_question ADD INDEX idx_pq_question (question_id); ALTER TABLE question_bank ADD INDEX idx_q_subject_type (subject_id, type); ALTER TABLE study_post ADD INDEX idx_post_user (user_id);考试提交环节的优化特别重要。学生点击提交按钮时前端会把整张试卷的所有答案一次性组装成 JSON 数组后端接收到数据后需要批量插入答题明细。MyBatis 提供了批量插入的 foreach 语法但要注意 SQL 参数数量限制的问题MySQL 单条 SQL 能处理的参数个数默认是 16KB 包大小内如果题目太多容易超限。我实测单次提交超过 100 道题时用 foreach 批量插入非常稳定但必须保证一次性插入的数据量不要太大否则需要分批插入。我从后端做了拆包逻辑每 50 道题拆一次完美规避了这个问题。3. 后端核心模块实现详解3.1 项目初始化与目录规划后端工程我基于 SpringBoot 2.7.x 版本搭建JDK 采用 1.8。有朋友问为什么不直接上 SpringBoot 3.x我的回答是SpringBoot 3 强制要求 JDK 17 以上部分企业内部环境还没有升级到这个版本2.7 是目前生产环境兼容性最稳妥的选择。如果你的目标企业技术栈已经切到 JDK 17也可以用 3.x对应功能差异并不大。工程的核心依赖如下dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-validation/artifactId /dependency dependency groupIdorg.mybatis.spring.boot/groupId artifactIdmybatis-spring-boot-starter/artifactId version2.3.1/version /dependency dependency groupIdcom.mysql/groupId artifactIdmysql-connector-j/artifactId scoperuntime/scope /dependency dependency groupIdio.jsonwebtoken/groupId artifactIdjjwt/artifactId version0.9.1/version /dependency dependency groupIdcom.alibaba/groupId artifactIdfastjson2/artifactId version2.0.46/version /dependency目录结构我采用了标准的分层架构com.example.exam ├── common # 统一返回结果、全局异常处理 ├── config # 配置类跨域、拦截器、WebMvc ├── controller # 接口控制层 ├── service # 业务逻辑层 ├── mapper # MyBatis Mapper 接口 ├── entity # 数据库实体类 ├── dto # 数据传输对象 ├── vo # 视图对象 └── utils # 工具类JWT、加密 └── interceptor # JWT 拦截器这种分包方式非常通用后续无论接支付系统还是消息队列只需要在对应目录下新增类即可项目结构天然就是可扩展的。3.2 用户认证与权限控制认证这块我用了 JWT 拦截器方案。JWT 令牌由三部分组成头部、载荷、签名。登录成功后后端为每个用户生成一个有效期为 24 小时的 token载荷中存放用户 id 和角色信息签名用密钥确保无法伪造。登录接口的核心逻辑是这样PostMapping(/login) public Result login(RequestBody LoginDTO loginDTO) { String username loginDTO.getUsername(); String password loginDTO.getPassword(); // 1. 根据用户名查询用户 SysUser user userMapper.selectByUsername(username); if (user null) { return Result.error(用户不存在); } // 2. BCrypt 校验密码 if (!BCrypt.checkpw(password, user.getPassword())) { return Result.error(密码错误); } // 3. 生成 JWT String token JwtUtil.generateToken(user.getId(), user.getRole(), user.getNickname()); return Result.success(token); }权限控制这块我来重点聊一下。后端不能只依赖拦截器就行拦截器只负责拦截那些需要登录的接口请求但是具体到学生只能操作自己的数据、教师只能管理自己科目的题库这种细粒度权限就得在业务层做二次校验。我的做法是写了一个RequireRole注解配合 Spring AOP 实现基于角色的方法级权限控制比直接在每个 Controller 方法里手写 if 判断干净得多。Target(ElementType.METHOD) Retention(RetentionPolicy.RUNTIME) public interface RequireRole { int value(); // 1管理员 2教师 3学生 }举一个实际例子删除试题的接口必须要求角色是教师或管理员RequireRole(2) DeleteMapping(/question/{id}) public Result deleteQuestion(PathVariable Long id) { questionService.deleteById(id); return Result.success(); }需要说明的是前端菜单的权限控制同样重要。Vue3 前端在路由守卫中读取用户角色根据角色动态生成可访问的路由表。这种前后端双重权限校验是必须的不能只防后端不防前端否则普通用户可以绕过页面跳转直接请求受限接口。3.3 考试模块核心流程实现考试模块是整个系统的核心业务逻辑最为复杂。我把完整的考试流程拆成了五个阶段组卷、获取试卷、答题、提交、阅卷。**组卷阶段。**后台接收组卷请求请求中携带有试卷标题、考试时长、题目数量、难度分布等参数。组卷逻辑分为固定组卷和随机组卷。随机组卷的核心 SQL 是 MyBatis 的动态查询在 Mapper XML 中写动态 SQL按知识点和难度分组随机抽取select idselectRandomQuestions resultTypeQuestion SELECT * FROM question_bank WHERE subject_id #{subjectId} AND type #{type} AND difficulty #{difficulty} ORDER BY RAND() LIMIT #{limit} /select这里有个性能地方需要注意如果题库数据量特别大ORDER BY RAND()的性能会退化得非常明显。题库达到几万道题以上时RAND 排序会导致全表扫描再随机排序非常慢。我的优化方案是先查出符合条件的题目 id 集合在内存中用 Java 的 Random 类做随机选择和洗牌再根据 id 批量查出完整题目信息实测下来查询耗时降低了 80% 以上。**获取试卷阶段。**学生点击开始考试按钮时后端要做两件事创建一条考试记录状态为进行中返回试卷的完整题目信息。这里要注意试卷的答案绝对不能随题目一起返回给前端否则前端开发者工具一打开就能看到正确答案。我从接口设计上就把答案字段做成了 null 返回只有提交后才能看到对错。**答题阶段。**为防止频繁请求数据库前端把学生的作答过程保存在内存中每切换一道题就把当前题的答案写入本地状态管理考试提交时统一发送后端。**提交阶段。**用户在交卷前前端需要做二次确认防止误点。后端接收提交请求后先校验考试记录状态避免重复提交再解析每个题目的答案进行判分。**自动阅卷阶段。**客观题判分逻辑相对简单// 单选题和判断题 boolean isCorrect userAnswer.equalsIgnoreCase(question.getAnswer()); // 多选题——需要对答案排序后再比较 boolean isCorrect Arrays.equals( userAnswerArray, // 前端传来的 [A,C,D] correctAnswerArray // 数据库存的 A,C,D 拆分后的数组 ); // 简答题跳过自动判分需要注意多选题判分有个细节答案顺序。如果数据库答案是 A,C,D用户答 C,A,D不应该判断为错。所以我对多选题的答案做了先排序再比较的处理。主观题阅卷则展示给教师逐题评分系统记录最终得分后回写到 exam_answer_detail 表。3.4 MyBatis 使用要点与缓存优化MyBatis 在这套系统里承担了所有 SQL 操作实际编码中的几个细节很值得展开。**Mapper XML 与注解的取舍。**简单增删改查我用注解直接写比如Select、Update。复杂动态查询一定用 XML比如组卷、成绩统计、帖子分页搜索。XML 的好处是 SQL 可以集中管理也更方便做 SQL 性能优化。**一二级缓存的理解。**很多人在面试中会被问到 MyBatis 缓存机制这里实际使用后的经验是一级缓存默认开启作用域是 SqlSession在同一个会话中多次查询同一语句会命中缓存。但在 Spring 管理事务的环境下SqlSession 的生命周期被框架管理一级缓存的优势并不明显。二级缓存默认不开启需要手动配置。Echoing 我在项目中的实践我不建议在考试系统中随便开二级缓存因为缓存刷新时机难以精确控制如果学生提交试卷后缓存没有及时刷新成绩统计可能出现脏数据。与其依赖缓存不如把 SQL 写精准、数据库索引建到位SQL 优化的收益比缓存更可控。**分页插件推荐直接配置 PageHelper。**PageHelper 是 MyBatis 最常用的分页插件只需要引入依赖后在查询前调用PageHelper.startPage(pageNum, pageSize)插件会在 SQL 执行前自动拼接 LIMIT 语句查询结果封装成 PageInfo 对象之后返回给前端。PageHelper.startPage(pageNum, pageSize); ListStudyPost postList studyPostMapper.selectPostWithUser(); PageInfoStudyPost pageInfo new PageInfo(postList);数据库配置上连接池我用的是默认的 HikariCP配置了最大连接数 20最小空闲连接数 5。一个需要留意的地方是如果考试场景并发量大建议调大连接数同时给 MySQL 配置 max_connections 参数否则高峰期连接池耗尽会导致页面打不开。4. 前端核心页面与交互实现4.1 Vue3 项目搭建与工程化配置前端技术栈选择了 Vue3 Vite Pinia Vue Router Element Plus Axios。Vite 创建项目非常快几秒钟就能跑起来。npm create vitelatest exam-web -- --template vue cd exam-web npm install npm install vue-router pinia axios element-plus工程目录结构我按页面维度来分src ├── api # 后端接口封装 ├── assets # 静态资源 ├── components # 公共组件 ├── router # 路由配置含动态路由 ├── stores # Pinia 状态管理 ├── views # 页面组件 │ ├── login # 登录 │ ├── home # 首页 │ ├── exam # 考试相关 │ ├── question # 题库管理 │ ├── paper # 试卷管理 │ ├── study # 学习交流区 │ └── user # 个人中心Vue3 相比 Vue2 最大的区别是 Composition API。在线考试页面中题目切换、答案存储、计时器、考试状态等逻辑非常多。我使用了setup语法糖将全部考试页面的业务逻辑拆分到独立的 Composition Function 中。4.2 核心页面拆解考试、学习、交流**考试页面设计。**考试页面的布局分为三栏左侧题目导航区、中间题目内容区、右侧答题卡。题目导航区实时显示题目序号已作答的标记为绿色未作答的标记为灰色。中间区域展示题目内容和选项单选、多选、判断都封装成独立组件。右侧答题卡可以快速跳转到任意题目。考试倒计时功能我单独封装了一个useCountdown函数每秒更新时间时间归零时自动触发提交。这里有个坑浏览器标签页如果处于后台定时器会被浏览器节流导致倒计时不准。我的解决方案是前端同时监听visibilitychange事件页面重新可见时从后端拉取剩余时间校正一次保证考试时长准确。**题库管理页面。**题库管理是教师的核心工作台。数据结构上我用了一个表格 筛选表单的组合。教师可以按科目、题型、难度筛选题目支持题目新增、批量导入、修改、删除。批量导入我实现了 Excel 模板功能后端用 EasyExcel 解析上传的 Excel 文件这是一条效率提升很大的路径。学习交流区。交流区页面参照主流论坛做了精简版。列表页展示帖子标题、作者头像、回复数、点赞数、浏览数支持分页加载。帖子详情页包含主帖内容和楼中楼回复结构用户可以直接在底部输入框回复。交流区的接口设计有个细节列表接口不要带着帖子内容一起返回只返回标题、摘要和前 100 个字符的正文内容这样列表查询的响应速度能快很多。帖子详情再做二次查询才返回完整正文。4.3 前后端联调与数据交互前后端分离后接口联调效率直接关系到开发进度。我的实际经验是可以按照下面的分工来组织后端把接口文档写到 OpenAPI 格式我是用knife4j自动生成接口文档前端根据 OpenAPI 文档自动生成 axios 接口请求函数后端直接跑起来提供真实接口前端在 Vite 配置 proxy 代理解决跨域问题Vite 的跨域代理配置如下export default defineConfig({ server: { proxy: { /api: { target: http://localhost:8080, changeOrigin: true } } } })开发环境下前端请求/api/login会被自动转发到http://localhost:8080/api/login完美避开浏览器跨域限制。生产环境则通过 Nginx 做反向代理配置前端静态资源和后端接口可以共用同一个域名避免跨域。接口响应格式我做了统一封装所有的后端返回结果都是{ code: 200, message: 操作成功, data: { } }code 为 200 表示成功400 参数错误401 未登录或 token 过期403 没有权限500 服务端异常。前端 axios 响应拦截器里统一处理这些 code遇到 401 时自动跳转登录页不用在每个页面重复写错误处理逻辑。5. 部署、测试与常见问题排查5.1 本地环境搭建与一键部署方案开发环境我统一推荐如下配置工具版本说明JDK1.8后端运行环境Maven3.8后端依赖管理MySQL8.0数据库需要提前创建库Node.js16.20前端构建环境Nginx1.24生产环境静态资源服务后端首次启动流程# 1. 创建数据库 mysql -uroot -p CREATE DATABASE exam_system DEFAULT CHARACTER SET utf8mb4; # 2. 修改 application.yml 数据源配置 spring: datasource: url: jdbc:mysql://localhost:3306/exam_system?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: your_password # 3. 启动后端 mvn spring-boot:run前端首次启动流程npm install npm run dev # 访问 http://localhost:5173生产环境部署方案# 后端打包 mvn clean package -DskipTests nohup java -jar exam-admin.jar --spring.profiles.activeprod # 前端打包 npm run build # 把 dist 目录中的文件拷贝到 Nginx 的 html 目录Nginx 的配置要点是把所有/api开头的请求转发给后端服务server { listen 80; server_name exam.example.com; location / { root /usr/share/nginx/html; index index.html; try_files $uri $uri/ /index.html; } location /api/ { proxy_pass http://localhost:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }try_files配置很关键Vue3 的 History 路由模式下页面刷新时请求的是真实的路径Nginx 找不到对应文件会返回 404加上这行配置后所有路由都会回退到 index.html由前端路由接管页面跳转。5.2 高频踩坑记录开发这套系统的过程中遇到的问题真不少。挑几个典型记录下来大家少走弯路。**跨域请求问题。**后端配置了跨域过滤器前端也配置了代理本地联调没问题。但部署到服务器后发现前端页面能打开接口请求全部失败。排查后发现是因为 Nginx 只做了静态资源服务没有配置/api代理后端接口根本没有暴露出来。把 Nginx 的/apilocation 配置好后正常。**Vue3 组件通信问题。**考试页面中答题卡组件需要实时显示每道题的作答状态这个状态存在 Pinia 中题目标题组件通过storeToRefs取出来用。最初我没有用storeToRefs直接对 store 解构导致响应式失效题目切换后答题卡状态不更新。原因在于 Pinia 的 state 是用 reactive 包裹的直接解构会丢失响应式代理需要用storeToRefs取属性。**MyBatis 动态 SQL 拼接出错。**多条件筛选题目时使用where标签包裹条件因为 if 判断内部的参数名写错导致 where 条件没有生效查询返回了全表数据。解决办法是给所有 Mapper 方法的参数加上Param注解明确指定参数名增强可读性。**Excel 批量导入乱码问题。**前端上传 Excel 文件后端用 EasyExcel 解析中文全部变成乱码。排查后发现是初始模板文件的编码格式用了 GBK改成 UTF-8 编码后解决。这里尤其要注意读取 xlsx 文件时日期和数字格式在底层有特殊的处理逻辑如果不对应好很容易报格式转换异常。**前端路由跳转后页面空白。**部署后刷新某个二级页面出现 404 或白屏控制台报错 cant match any routes。这就是前面提到的 History 路由问题需要 Nginx 配置try_files指向 index.html。如果用了 Hash 路由则不会有这个问题但是 URL 会带#号不够美观。**MySQL 8.0 密码认证插件问题。**系统部署到新服务器后后端一直报连接失败。查看日志发现是 MyBatis 连接 MySQL 时使用了 caching_sha2_password 认证插件旧版 mysql-connector-java 不支持。解决方案是使用mysql-connector-j最新版本驱动或者把数据库用户改为 mysql_native_password 加密方式。5.3 经典问题排查清单最后整理一份排查清单不管是你自己写代码还是面试中被问到系统问题如何处理都可以直接参考问题现象可能原因排查方法后端启动失败提示数据库连接失败MySQL 未启动或配置错误确认 MySQL 服务状态检查 application.yml 中 url、账号、密码接口返回 401token 过期或未携带检查请求头是否带 Authorization检查 token 有效期接口返回 403角色权限不足确认当前用户角色和接口要求的角色是否一致前端页面打不开端口被占用或打包资源路径不对检查 8080/5173 端口占用Vue 打包后的 base 配置考试提交后成绩为 0答案提交格式错误或判分逻辑有误打印前端提交的 JSON 载荷检查多选题答案格式考试成绩统计报表错乱时间字段时区问题MySQL 连接串加 serverTimezoneAsia/Shanghai确认服务器时区答题超时无法交卷定时器被节流使用后端时间字段对比校验剩余时间不依赖前端计时上传图片后无法访问静态资源映射路径配置错误配置 WebMvc 资源映射指定上传目录为可访问目录高并发下页面卡顿数据库连接池满或接口响应慢检查数据库慢查询日志调大连接池参数给高频 SQL 加索引系统重启后帖子访问记录丢失缓存未持久化检查是否把访问量存在了 Redis 而没有同步到数据库考虑到很多朋友拿这个项目练手或者用作毕业设计我再补充一点面试官如果问到你的系统如何防止学生作弊你可以从限制切换页面次数、考试中途离开检测、答案随机排序这几个维度回答我在这套系统里就做了这两项答题期间监听visibilitychange事件页面失焦超过三次就记录异常标记选择题的选项顺序按用户 ID 进行确定性随机打乱。这些功能不需要太复杂的实现但是很能体现你对业务场景的思考深度。在实际开发这套系统的过程中我最大的体会是前后端分离的架构方式非常看重接口定义的准确性和业务边界的清晰度。如果你自己一个人同时负责前后端一定要严格要求自己先写接口文档再写代码否则后面返回的数据结构一变前端的改动成本很高。另外建议考试系统这类强状态业务前后端都要做重试机制和状态兜底比如用户中途关掉浏览器下次登录后应该能继续未完成的考试而不是直接重新开始。这套系统从第一行代码跑通到全流程稳定运行踩过的坑基本都记录在上面了。接下来你可以在此基础上继续扩展比如接入 WebSocket 做实时在线情况监测、引入 Redis 缓存题库热点数据、增加更细粒度的成绩分析报表每一步都值得单独再写一篇实战总结。
返回列表