带过不少课程设计项目也帮人排查过无数次明明代码没问题但就是跑不起来的现场。语言在线考试与学习交流网页平台这类题目在Java课程设计里出现频率极高但真正能让人眼前一亮、逻辑完整、还能当场演示的系统并不多。这篇就专门聊聊我梳理过的一套完整源码方案SpringBoot后端 Vue前端 MySQL数据库前后端分离拿到手导入就能启动。如果你正准备做类似的在线考试系统或者正在为课程设计发愁这篇文章会把技术选型、数据库设计、后端核心逻辑、前端交互踩坑全部拆开讲照着做就能撑起一个结构完整的项目。1. 选型逻辑SpringBoot Vue MySQL 在课程设计里的真实价值1.1 SpringBoot 为什么是后端首选先说句实在话课程设计项目最怕的不是功能太少而是架子太重、跑不起来。SpringBoot 的价值在于它把传统 SSM 那套繁琐的 XML 配置全部内化了JavaConfig 自动装配让一个 Web 项目从零到能启动只需要一个SpringBootApplication注解。这个项目选 SpringBoot 还有一个实际考量在线考试系统天然就适合按请求-响应的方式实现。考试创建、题目查询、答案提交、成绩查询每一个动作都是典型的 RPC 式业务接口无状态服务端 JWT 认证就能覆盖全部场景完全不需要自己造状态同步的轮子。SpringBootApplication MapperScan(com.platform.exam.mapper) public class ExamApplication { public static void main(String[] args) { SpringApplication.run(ExamApplication.class, args); } }有人会问为什么不用 Spring Cloud 或者微服务架构对一个考试系统来说微服务的服务发现、配置中心、网关这些组件只会加重部署负担。一个可运行、可演示、逻辑闭环的单体 SpringBoot 应用才是课程设计和中小型项目的正确打开方式。1.2 Vue 前端的分层价值前端用 Vue 2/Vue 3 Element UI/Element Plus 这套组合最大的好处是组件化开发让页面复用率极高。考试管理后台里的表格、表单、对话框几乎都能用现成组件拼出来。Vue 的核心思路是数据驱动视图页面上的倒计时数字、答题卡状态、实时得分这些本质上是状态的变化Vue 的响应式机制会自动更新 DOM不用像 jQuery 时代那样手动操作节点。// main.js 中注册核心依赖 import Vue from vue import ElementUI from element-ui import element-ui/lib/theme-chalk/index.css import App from ./App.vue import router from ./router import store from ./store Vue.use(ElementUI) new Vue({ router, store, render: h h(App) }).$mount(#app)在考试场景里Vue 的响应式优势尤其明显答题卡上每题的状态未答/已答/标记就是一组状态数据点击选项时更新状态界面自动刷新。如果用手写 DOM 操作这种联动逻辑会写成一片混乱的 querySelector 和 innerHTML调试起来非常痛苦。1.3 MySQL 在考试数据场景下的定位MySQL 在这套系统里的定位是唯一数据源承担用户数据、题库数据、答题记录、学习交流内容等所有持久化存储。选择 MySQL 而不是更重的 Oracle 或 PostgreSQL主要考虑到学习成本和部署便利MySQL Workbench、Navicat 都有图形化操作界面课程设计的场景下完全够用。1.3.1 为什么不需要 Redis考试系统的数据特征是写完基本不热改题库上传后、试卷发布后数据相对稳定。用户答题的阶段虽然有一定并发但课程设计级别的演示场景一般只有几个到几十个并发用户MySQL 配合索引优化完全能扛住。引入 Redis 反而多了一层缓存一致性维护成本得不偿失。我见过不少同学简历上写Redis 缓存热点数据但问细节时说不清楚缓存穿透、缓存雪崩怎么处理。如果技术深度一般老老实实把 MySQL 的表设计和 SQL 优化讲透彻比硬生生堆技术名词有用得多。1.3.2 三种技术栈方案对比方案后端前端数据库优点缺点方案一本系统SpringBootVueMySQL前后端分离、就业方向对口、生态完善部署环境需要 Node 和 JDK方案二SSMJSPMySQL单体应用、概念直观前后端耦合、页面老旧方案三DjangoVueMySQLPython 上手快、自带 AdminJava 课程设计不符、岗位方向偏课程设计场景下我推荐方案一原因非常直接这套技术栈是目前国内 Java 开发岗位的主流配置做完一个完整项目后简历上写的每一个技术名词你都能有实际代码去印证。2. 系统功能拆解从普通考试系统到考试学习交流的差异化设计这套系统叫语言在线考试与学习交流网页平台功能上比单纯的语言类课程考试系统多了学习交流属性。这个定位很好理解考试不是目的以考促学才是核心。系统服务三类角色——教师教务人员、学生考生/学习者、管理员三者的功能边界需要清晰划分。2.1 角色体系与权限矩阵2.1.1 三种角色的核心痛点教师角色最痛的是出题、判分、统计成绩的重复劳动。系统需要提供题库批量导入Excel/Word、自动判分客观题、成绩导出等功能。学生角色最痛的是考试时的紧张感和考后的学习闭环。系统需要提供模拟考试、即时查分、错题回顾、以及考后还能交流讨论的社区感觉。管理员角色最痛的是用户管理和数据监控。系统需要提供用户启用禁用、系统数据概览、异常考试行为日志等。2.1.2 权限控制方案权限控制采用 SpringBoot 拦截器 Vue 路由守卫双重校验。后端拦截器控制 API 访问权限前端路由守卫控制页面跳转权限两层叠加防止跳过登录直接访问页面的情况。功能模块管理员教师学生用户管理支持只读无权限题库管理全部操作操作自己的题库无权限创建考试全部创建无权限参加考试可以可以支持成绩统计全校自己班级查看本人学习交流管理帖子发帖回帖发帖回帖2.2 在线考试闭环五个状态流转一次完整的考试流程分为五个阶段准备阶段教师录入题目手动/批量标记题目类型、难度、知识点标签。组卷阶段教师创建考试配置考试时间、限时时长、题型结构选择手动选题或按规则随机组卷。进行阶段学生在规定时间内答题。前端展示倒计时系统定时保存答案防意外关闭到点自动交卷。判分阶段客观题系统自动判分主观题教师手动打分。成绩自动汇总。复盘阶段学生查看成绩、错题解析教师查看正确率统计和知识点薄弱环节分析。这五个阶段的闭环是系统区别于demo 级项目的重要标志。我见过很多课设项目只做到第四步出成绩就结束了但如果没有复盘阶段学习交流这个定位就落空了。考试成绩出来之后让学生回到学习交流模块去讨论错题、讲解知识点才算把以考促学做完整。2.3 学习交流模块把论坛功能做成轻量级社区学习交流模块本质上是一个轻量级论坛核心数据模型是帖子 Post → 评论 Comment → 回复 Reply的三级结构。对比常见的复杂论坛这里刻意不做积分体系、不做帖子审核流、不做关注关系圈。原因很简单课程设计项目的核心评分标准是功能完整闭环 技术实现合理把交流功能做到能发帖、能回帖、能删帖、能标精华就已经完善了堆太多功能反而容易暴露出 bug。2.3.1 关键交互设计帖子列表页需要支持按标签筛选如语法词汇听力和按热度/时间排序。详情页要支持 Markdown 渲染或富文本因为语言学习类帖子经常需要展示代码、例句、时态表格纯文本展示体验很差。我建议在帖子列表上用 Element UI 的el-tabs组件实现最新热门未回复三个 Tab配合el-pagination分页组件这样页面视觉上不会显得空而且每个组件都有明确的数据来源答辩时讲起来有逻辑。el-tabs v-modelactiveTab tab-clickhandleTabChange el-tab-pane label最新 namelatest/el-tab-pane el-tab-pane label热门 namehottest/el-tab-pane el-tab-pane label待回复 nameunanswered/el-tab-pane /el-tabs3. 数据库设计几张关键表的关系与字段取舍数据库设计是一套系统的地基。很多项目跑着跑着出现数据混乱、统计不对根源往往就是表结构没设计好。这套系统的核心表可以拆成四大块用户体系、考试题库体系、答题记录体系、交流社区体系。3.1 用户表设计用户表不必区分三张表给三种角色用一张表加role字段区分即可。设计一张sys_user表CREATE TABLE sys_user ( id BIGINT NOT NULL AUTO_INCREMENT COMMENT 主键, username VARCHAR(50) NOT NULL COMMENT 登录名, password VARCHAR(100) NOT NULL COMMENT BCrypt加密后的密码, real_name VARCHAR(50) DEFAULT NULL COMMENT 真实姓名, role TINYINT NOT NULL DEFAULT 0 COMMENT 角色 0-学生 1-教师 2-管理员, status TINYINT NOT NULL DEFAULT 1 COMMENT 状态 1-正常 0-禁用, avatar VARCHAR(200) DEFAULT NULL COMMENT 头像地址, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, UNIQUE KEY uk_username (username) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT系统用户表;为什么统一用一张表因为三种角色在登录认证、基本信息维护上的逻辑完全一致拆三张表反而会造成大量重复的 JOIN 操作。角色不同只是功能权限不同数据模型层面用字段区分就够了。3.2 题库与试卷的关联设计题库表question的字段需要覆盖题目的通用属性和特定属性CREATE TABLE question ( id BIGINT AUTO_INCREMENT PRIMARY KEY, subject VARCHAR(50) DEFAULT NULL COMMENT 学科/科目, type TINYINT DEFAULT 0 COMMENT 题目类型 0-单选 1-多选 2-判断 3-简答, difficulty TINYINT DEFAULT 1 COMMENT 难度 1-简单 2-中等 3-困难, knowledge_point VARCHAR(100) DEFAULT NULL COMMENT 知识点标签, content TEXT COMMENT 题干, options TEXT COMMENT 选项JSON如[{key:A,content:...}], answer TEXT COMMENT 答案单选/判断存KEY多选存JSON数组简答存参考答案, analysis TEXT COMMENT 解析, creator_id BIGINT DEFAULT NULL COMMENT 出题教师ID, create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT题目表;为什么选项不用六列option_a, option_b...存这是很多初学者的常见误区。固定列设计在后续扩展时极其痛苦如果某天要支持不定项选项或者图片选项还得改表结构。用 JSON 字符串存选项配合前端解析渲染灵活性和可维护性都高得多。题目和考试的关系是多对多典型设计是建立关联表exam_questionCREATE TABLE exam_question ( id BIGINT AUTO_INCREMENT PRIMARY KEY, exam_id BIGINT NOT NULL COMMENT 考试ID, question_id BIGINT NOT NULL COMMENT 题目ID, score INT DEFAULT 5 COMMENT 该题分值, sort_order INT DEFAULT 0 COMMENT 题目排序 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT考试题目关联表;通过这张关联表可以实现一套题库多次组卷一个考试灵活调整每题分值的能力。随机组卷的实现思路是先根据规则筛选出符合条件的题目 ID再按比例和分数算出每道题应放几个进试卷最后批量写入关联表。3.3 答题记录表考试的账本考试过程中最怕的三个问题学生断电断网、意外刷新、交卷后数据丢失。所以答题记录表设计上要支持多次保存、最终以最后一次为准。核心设计是一题一条记录表结构如下CREATE TABLE exam_record ( id BIGINT AUTO_INCREMENT PRIMARY KEY, user_id BIGINT NOT NULL COMMENT 考生ID, exam_id BIGINT NOT NULL COMMENT 考试ID, question_id BIGINT NOT NULL COMMENT 题目ID, user_answer TEXT COMMENT 学生答案JSON, is_correct TINYINT DEFAULT 0 COMMENT 0-未批改 1-正确 2-错误 3-部分正确, score DECIMAL(5,1) DEFAULT 0 COMMENT 本题得分, submit_time DATETIME DEFAULT NULL COMMENT 保存时间, UNIQUE KEY uk_user_question (user_id, exam_id, question_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT答题记录表;这里有个容易踩坑的点为什么不用每次交卷把所有答案塞进一个大 JSON 字段的设计因为业务上需要按题目统计正确率如果用大 JSON 存统计时需要把 JSON 全量拉出来解析数据量大时性能会很差。拆分到行后一条 SQL 就能算出来SELECT question_id, COUNT(*) AS total_count, SUM(CASE WHEN is_correct 1 THEN 1 ELSE 0 END) AS correct_count FROM exam_record WHERE exam_id ? GROUP BY question_id;3.4 学习交流模块的表设计交流模块需要三张表post帖子、post_comment评论、post_reply回复。字段设计上我直接把点赞数浏览数作为冗余字段放进了post表。这违反了严格的三范式但非常实用CREATE TABLE post ( id BIGINT AUTO_INCREMENT PRIMARY KEY, user_id BIGINT NOT NULL, title VARCHAR(100) NOT NULL, content TEXT, tag VARCHAR(30) DEFAULT NULL, view_count INT DEFAULT 0, like_count INT DEFAULT 0, comment_count INT DEFAULT 0, is_essence TINYINT DEFAULT 0, status TINYINT DEFAULT 1, create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT交流帖子表;为什么不实时 COUNT因为在列表页需要展示每个帖子的评论数、点赞数、浏览数如果每次都去三张表里精确统计SQL 会写得非常复杂而且索引覆盖不到。用一个冗余字段每次发生评论、点赞时同步1即可保证最终一致性。课程设计阶段这种用空间换时间的取舍在答辩时是加分项说明你考虑过性能问题。4. 后端核心实现登录鉴权、随机组卷、自动判分与防作弊4.1 JWT 登录鉴权和密码加密用户密码存储绝对不能用明文。SpringSecurity 的BCryptPasswordEncoder是业界标准做法每次加密都会混入随机盐即便两个用户密码相同加密后的字符串也不同。// 注册用户时加密 String encode new BCryptPasswordEncoder().encode(123456);登录成功后签发 JWT Token将用户 ID、角色信息放入 token 声明中。后续请求通过拦截器解析 token并将用户信息放入ThreadLocal中方便业务代码随时取用当前用户。Component public class JwtInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String token request.getHeader(Authorization); if (token null || !token.startsWith(Bearer )) { throw new RuntimeException(未登录); } token token.substring(7); Claims claims JwtUtil.parseToken(token); request.setAttribute(userId, claims.get(userId)); request.setAttribute(role, claims.get(role)); return true; } }4.1.1 为什么选 JWT 而不是 Session课程设计答辩时老师一定会追问这个问题。核心原因是项目采用前后端分离架构前端可能部署在 8080 端口后端在 9090 端口存在跨域问题。Session 机制依赖 Cookie 自动携带跨域情况下 Cookie 策略会带来很多麻烦JWT 则把状态完全放在客户端后端不保存会话天然适合这种架构。4.2 随机组卷的两种实现方案组卷功能是考试系统的核心亮点也是答辩时最值得展开讲的部分。实现方式主要分人工组卷和自动组卷两种。4.2.1 自动组卷规则设计自动组卷需要教师先配置试卷结构比如单选题 10 道每道 5 分多选题 5 道每道 6 分判断题 10 道每道 2 分简答题 2 道每道 10 分同时可指定难度分布和知识点范围。配置完成后后端按规则抽题填充。// 按题型抽题的简化逻辑 ListLong selectQuestionIds(String type, Integer count, Integer difficulty) { QueryWrapperQuestion wrapper new QueryWrapper(); wrapper.eq(type, type) .eq(status, 1) .eq(difficulty ! null, difficulty, difficulty) .orderByAsc(RAND()) // MySQL随机排序 .last(LIMIT count); ListQuestion questions questionMapper.selectList(wrapper); return questions.stream().map(Question::getId).collect(Collectors.toList()); }提示.last(LIMIT count)拼接的是后端数字参数不会产生 SQL 注入风险。但在写代码时仍然要小心orderByAsc(RAND())在小数据量下没问题题库超过十万条时性能会明显下降课程设计场景不需要过度优化但这话答辩时最好主动说体现你懂边界。4.2.2 人工组卷人工组卷的实现就简单一些从题库列表中勾选题目加入试卷后端接收exam_id和question_id[]数组循环插入exam_question表同时保存每道题的分值。4.3 客观题自动判分的实现细节自动判分的核心是答案对比。客观题答案存储统一约定单选题存B判断题存1或0多选题存[A,C,D]的 JSON 数组。// 单选题、判断题对比 boolean isCorrect question.getAnswer().equalsIgnoreCase(userAnswer); // 多选题对比顺序无关 ListString correctKeys JSON.parseArray(question.getAnswer(), String.class); ListString userKeys JSON.parseArray(userAnswer, String.class); Collections.sort(correctKeys); Collections.sort(userKeys); boolean isCorrect correctKeys.equals(userKeys);这里最需要注意的是多选题的判分策略全对才得分还是少选给半分我在代码里默认实现的是严格判分完全匹配才得分但在exam表中留了一个objective_rule字段方便扩展为少选按比例得分。自动判分时对应的exam_record.is_correct和score字段被更新。所有客观题判断完成后再汇总出考生总分Transactional public void autoMarkExam(Long examId, Long userId) { ListExamRecord records examRecordMapper.selectList(...); for (ExamRecord record : records) { Question q questionMapper.selectById(record.getQuestionId()); if (q.getType() 2) { // 0-单选 1-多选 2-判断 // 执行判分逻辑 } } // 更新考试记录总表 }4.4 防作弊逻辑的三种设计在线考试无法完全杜绝作弊但关键在于增加作弊的技术成本。这套系统实现的三层防作弊机制都是课程设计里容易实现又效果明显的手段4.4.1 切屏检测前端实现在 Vue 项目里通过document.addEventListener(visibilitychange, ...)监听页面可见性状态。当用户切出页面比如打开其他应用时记录一次切屏日志超过设定次数如 3 次后自动交卷。document.addEventListener(visibilitychange, () { if (document.visibilityState hidden this.isExamStarted) { this.switchCount // 超过3次自动交卷 if (this.switchCount 3) { this.submitExam(true) } } })4.4.2 考试时长限制前后端双校验前端倒计时只是 UI 层提示真正的交卷校验必须在后端做。后端在保存答案和交卷接口中都要校验当前时间是否超过考试截止时间// 交卷时校验 if (exam.getEndTime().before(new Date())) { throw new RuntimeException(考试已结束系统自动收卷); }4.4.3 答题中途自动保存考生每完成一题或每 30 秒前端自动向后端发送保存答案请求。这样即使考生意外关掉浏览器重新登录后仍能恢复已答题目。这个功能对用户体验提升非常明显也是很多商业考试系统的标配。// 定时自动保存 setInterval(() { this.saveAnswer() }, 30000)5. 前端实现考试倒计时、答题卡交互与页面路由权限5.1 考试页面三栏布局与答题卡状态联动考试页面是整个前端最复杂的交互场景我的参考布局是左侧题目序号区答题卡、中间题目内容区、右侧信息栏倒计时交卷按钮。答题卡是典型的状态驱动场景每一题有三种状态未作答灰色空心已作答蓝色实心标记待检查黄色感叹号data() { return { answerMap: {}, // 题目ID - 答案 markedList: [], // 标记的题目ID数组 orderedQuestions: [] // 考试中的所有题目 } }答题卡点击事件处理函数handleCardClick(index) { this.currentIndex index this.$refs.questionScroll.scrollToIndex(index) }这里用scrollToIndex跳转到对应题目同时高亮当前题。整体交互逻辑比较直观用户点击答题卡序号区任意题号右侧题目内容区自动滚动到对应题号用户在内容区选择选项后又反过来更新答题卡状态。5.2 Axios 封装与 Token 注入前端所有 HTTP 请求统一走 Axios 实例拦截器负责两件事请求时附加 token、响应时统一处理错误码。// axios.js import axios from axios const service axios.create({ baseURL: /api, // 开发环境通过代理转发 timeout: 15000 }) // 请求拦截 service.interceptors.request.use(config { const token localStorage.getItem(token) if (token) { config.headers[Authorization] Bearer token } return config }) // 响应拦截 service.interceptors.response.use( response { return response.data }, error { if (error.response error.response.status 401) { // 登录过期跳回登录页 router.push(/login) } return Promise.reject(error) } )将 axios 挂到 Vue 原型上后业务组件里可以这样调用this.$http.get(/exam/list, { params: { page: 1, limit: 10 } }).then(...)5.3 路由守卫与角色权限控制前端路由配置时给每个路由声明meta.roles然后在全局前置守卫里做角色校验router.beforeEach((to, from, next) { const token localStorage.getItem(token) const userRole localStorage.getItem(role) if (!token) { // 去登录页 next(/login) } else if (to.meta.roles !to.meta.roles.includes(Number(userRole))) { // 无权限去无权限提示页 next(/403) } else { next() } })5.3.1 为什么需要路由守卫 后端拦截器路由守卫只是提升用户体验不是安全手段。真正决定用户能不能操作某个接口的是后端拦截器。前端的 403 跳转只是让用户第一时间看到提示后端仍然要兜底校验。答辩时建议主动强调这一点说明你理解前端控制是体验后端控制才是安全。5.4 学习交流模块页面实现学习交流页面的核心诉求是让发帖和回帖的门槛足够低。发帖组件用富文本编辑器我选的是wangeditor这类轻量编辑器学习成本低、体积小数据提交时走 HTML 格式存储。回帖交互参考了常见论坛的一楼一贴方式追加回复以时间倒序展示每层楼高亮当前用户名。帖子列表页要处理一个细节如果帖子正文特别长列表页要用 CSS 截断只保留前三行加全文展开按钮。用 Element UI 的el-collapse组件也能做但我建议直接在列表中用纯 CSS 的-webkit-line-clamp实现代码更少效果更利落。6. 把它跑起来环境准备、跨域处理与踩坑记录6.1 本地启动需要准备什么启动这套系统本地需要准备以下环境依赖版本建议说明JDK1.8 或 11SpringBoot 2.x 对应 JDK8 足够Maven3.6项目管理工具Node.js14.x-16.xVue 2 项目建议用 14/16避免太高版本npm/yarn随 Node依赖安装MySQL5.7 或 8.0建库后要设置时区参数Navicat/Workbench任意数据库图形化工具可选具体启动步骤创建数据库CREATE DATABASE exam_platform DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;导入sql/exam_platform.sql脚本。在application.yml中修改数据库账号密码。启动后端mvn spring-boot:run或在 IDEA 中运行ExamApplication。前端安装依赖npm install耐心等待然后npm run serve。浏览器访问http://localhost:8080。6.2 最常见的四个环境坑6.2.1 MySQL 8.0 驱动和时区问题如果项目在 MySQL 8.0 环境下运行报Public Key Retrieval is not allowed或时区错误在 JDBC URL 上加上参数url: jdbc:mysql://localhost:3306/exam_platform?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/ShanghaiallowPublicKeyRetrievaltrue注意allowPublicKeyRetrievaltrue是 MySQL 8.0 连接时常见的坑不加上会报错。但生产环境要谨慎开启课程设计本地启动加上没毛病。6.2.2 Node 版本过高导致依赖安装失败Vue 2 项目如果 Node 版本超过 17npm install经常会报OpenSSL Error这是因为 Node 17 改了 OpenSSL 策略。解决方案是安装 Node 14/16或者临时方案在项目根目录加.npmrc文件legacy-peer-depstrue但最稳妥的还是切换 Node 版本。建议提前装好nvmWindows 上可以用nvm-windows快速切换版本。6.2.3 前端跨域请求被拦截开发环境下前后端分别跑在 8080 和 9090 端口直接请求必然跨域。处理方案是在vue.config.js中配置代理module.exports { devServer: { port: 8080, proxy: { /api: { target: http://localhost:9090, changeOrigin: true, pathRewrite: { ^/api: } } } } }这样前端代码里统一以/api开头请求接口开发时由 Webpack 代理转发到后端浏览器只看到同源请求不会报跨域错误。6.2.4 Maven 依赖下载过慢或失败国内网络环境从中央仓库拉取 SpringBoot 依赖经常非常慢建议在~/.m2/settings.xml中配置阿里云镜像mirror idaliyunmaven/id mirrorOf*/mirrorOf name阿里云公共仓库/name urlhttps://maven.aliyun.com/repository/public/url /mirror6.3 如果运行遇到问题如何快速定位不少同学拿到源码后跑来问我运行报错怎么办但连报错日志都不发。实际上排查问题是有固定套路的看后端控制台SpringBoot 启动时打印的日志是定位问题的第一线索。如果是Error creating bean with name...通常是 Service/Mapper 注入问题或配置问题如果是Table xxx doesnt exist说明数据库脚本没导入成功。看浏览器 F12 Console前端所有请求都会在 Network 面板展示。404 说明后端接口路径和前端请求路径不一致500 说明后端代码或 SQL 有问题去后端日志看栈信息。看数据库连接确认 MySQL 服务已启动、账号密码正确、exam_platform库已建立且有表数据。我最常遇到的问题其实是同学直接把application.yml里的密码复制过去没有修改或者本机 MySQL 密码是空的但配置里写了密码。这些都是小问题但第一次跑项目的人最容易在这里卡住。7. 项目管理目录结构、答辩亮点和后续扩展方向7.1 推荐的项目目录结构前后端分离项目的目录组织方式直接影响代码可读性也是答辩时老师关注的重点。后端推荐按模块分包而不是按层分包com.platform.exam ├── controller/ # 接收HTTP请求 ├── service/ # 业务逻辑层 │ └── impl/ # 业务实现 ├── mapper/ # MyBatis-Plus数据访问层 ├── entity/ # 数据库实体对象 ├── dto/ # 请求/响应数据传输对象 ├── config/ # 配置类跨域、拦截器、MyBatisPlus分页 ├── common/ # 全局异常、统一返回结果、工具类 └── ExamApplication.java # 启动类用dto而不是直接把entity暴露给前端这一点说出来就是加分项。比如保存答案接口的请求体应该是一个AnswerSaveDTO包含 examId、questionId、userAnswer 三个字段。如果直接把实体类暴露出去前端可以传任何字段安全性和规范性都很差。7.2 这套项目在答辩时怎么讲出亮点我评审过不少课程设计项目能拿高分的大多不是堆资源最多的而是能讲清楚为什么这么做的。这套系统可以从以下角度讲7.2.1 讲业务闭环从题库录入、随机组卷、考试答题、自动判分到错题数据分析、交流社区讨论完整闭环。这是课程设计里比较稀缺的完整故事线。7.2.2 讲合理的取舍比如为什么不用 Redis为什么用 JSON 存选项而不是拆字段为什么答题记录一行一题这些取舍背后都有数据一致性和统计性能的考量比背概念强得多。7.2.3 讲安全的纵深防御JWT 无状态认证、BCrypt 加密、前端路由守卫与后端拦截器双层控制、切屏检测、交卷时间后端二次校验每一条都能展开讲出具体代码实现。7.3 后续可以扩展的五个方向如果你学有余力以下几个扩展方向性价比很高也能显著提升项目的完整度成绩数据分析大屏用 ECharts 展示考试成绩分布、知识点正确率雷达图、班级排名趋势视觉冲击力极强。Excel 导入导出一键化接入 EasyExcel 库实现题库批量导入、成绩导出这是真实企业里的高频场景。错题本功能把答错过的题目自动聚合生成个人薄弱知识点清单并推荐学习交流社区里的相关高质量帖子。在线监考前端接入摄像头 API考试过程中定时抓拍、后端保存照片。这个在前端和运维层面有挑战性但做出来很亮眼。WebSocket 实时通知教师发布新考试后在线学生即时收到通知。WebSocket 的引入能在技术深度上明显拉开差距。我个人实际带项目的体会是这类课程设计系统的核心难点从来不是某个单个技术点而是把多个模块串成一个完整闭环。很多人卡在题库有了、考试有了、成绩也有了但题目答完以后数据对不上——这通常不是写代码的问题而是数据库表设计时没有考虑好关联关系和判分流程。所以这篇文章我花了大量篇幅讲表结构设计、答案存储约定、判分策略这些看似不起眼但实际最容易出错的环节。按这套思路搭建启动后跑一遍完整考试流程你会发现很多问题在代码编写阶段就被设计规避掉了。最后再分享一个小技巧拿到任何同类源码不要急着改功能先把项目原样跑起来用不同角色各走一遍完整流程再动手做二次开发你会事半功倍。
