毕业设计选题会上“疫情防控下的云课堂在线教育系统”这个题目几乎每届都会出现。之前我带的学生里有把它做成一个普通CRUD管理后台的也有把它做成直播平台的前者答辩时被老师说“看不出云课堂在哪”后者开发到一半就崩了。这个题目真正的难处不在增删改查而在于要按照在线教育的真实业务链路把教学内容组织、学习行为记录、课堂互动这三个点串起来。这篇文章我就把这个项目从选题、技术选型、数据库设计、后端实现、前端打包到答辩准备的完整过程拆开讲给准备做Spring Boot毕业设计的同学一个能直接照着走的参考。整套方案以Spring Boot MyBatis-Plus Vue为主体结合JWT做认证前端最终打包进Spring Boot统一部署这也是目前毕设里最稳的组合。1. 从选题到落地这个毕设题目到底在考察什么1.1 题目拆解场景背景、系统形态与核心价值先说题目本身。“疫情防控下的云课堂在线教育系统”可以拆成三个层次场景背景、系统形态、业务核心。场景背景是“疫情防控”反映的是线下教学受限制时学校需要一套能支撑远程教学的平台说白了就是解决“学生不在教室课怎么继续上”的问题。毕业设计里不需要你去讨论公共卫生政策而是要把这个背景转化成具体的系统需求录播课程点播、直播课堂安排、学习进度跟踪、作业线上化。系统形态是“云课堂在线教育系统”不是单纯的CMS内容管理也不是视频网站。它至少要覆盖三个端学生端专注学习和交作业教师端专注建课、开课、批改管理端专注用户和内容管理。理解了这个分层你就知道数据库怎么设计、权限怎么控制、菜单怎么划分。业务核心是“教与学的数据闭环”。老师在系统里创建课程添加课时发起课堂布置作业学生选课后观看课程视频、参与课堂、提交作业系统把学生的每一次学习行为记录下来。做这个项目如果只是把课程做成了新闻列表就丢掉了“在线教育”的本质也失去了答辩时最有力的差异化亮点。1.2 角色全景管理员、教师、学生三条业务线我建议把系统角色明确分为三个权限边界从表设计就开始控制管理员登录后台负责用户管理、课程审核、公告发布、数据概览。演示时先展示这部分能快速体现系统的完整性。教师创建课程维护课时发起直播课堂布置和批改作业查看自己课程下的学生列表。学生注册登录浏览课程选课加入观看课时视频参与课堂签到与聊天提交作业查看成绩和公告。三条业务线互有交集教师创建的课程被学生选取学生提交的作业被教师批改管理员监管整个流程。用这种方式向老师解释系统结构比直接报一堆接口名要清晰得多。实际建表时也应当把“角色”作为用户表的一个关键字段而不要一上来就设计五张权限表那是企业级项目的做法放在毕设里容易把自己绕进去。1.3 工作量规划一个月到三个月的开发节奏毕设最怕的是前期规划太松散后期熬夜赶工。我给学生的建议是按三周出主体、两周打磨、一周准备答辩来分配第一周数据库设计定稿Spring Boot项目骨架搭好完成用户注册登录和角色权限拦截。第二周完成课程管理、课时管理、选课功能接口文档同步整理。第三周完成学习进度记录、作业发布与提交前端核心页面联调。第四周Vue打包进Spring Boot做一体化部署写项目文档和演示脚本。第五周开始打磨细节、补充异常处理、准备答辩问题。如果时间充裕可以加入通知公告、课程评论、学习数据可视化如果时间紧张先保证主链路跑通再考虑锦上添花。记住一个原则宁可功能少而完整不要功能多而残缺。答辩时演示一个能跑通的闭环永远比展示十个报错的入口更有说服力。2. 技术选型为什么是Spring Boot MyBatis-Plus Vue这套组合2.1 后端与持久层先替你做决定再说理由很多学生纠结技术选型我直接给结论后端用Spring Boot 2.7.x持久层用MyBatis-Plus前端用Vue 2 Element UI数据库用MySQL 8.0。这套组合我指导过多届学生是容错率最高的方案。先说Spring Boot而不是SSM。Spring Boot本质上是Spring的自动化封装它通过自动装配机制把配置这件事做到了极致。原来SSM项目里要手写web.xml、spring-mvc.xml、mybatis-config.xml那一堆配置文件在Spring Boot里只需要一个application.yml。这一点对毕设非常关键因为大部分时间应该留给业务代码而不是消耗在环境配置上。面试或答辩被问“为什么选Spring Boot”时你可以直接回答它简化了项目配置实现了自动装配和约定优于配置让我能把开发重心放在业务逻辑上。持久层选MyBatis-Plus的理由更实际。原生MyBatis需要手写大量XML映射文件一张表的增删改查就得几十行而MyBatis-Plus内置了通用Mapper单表操作几乎不用写SQLBaseMapper提供了insert、updateById、selectPage这些现成方法。项目里的课程表、用户表、作业表都属于单表操作直接用LambdaQueryWrapper就能完成条件查询。它还有一个对毕设特别友好的特性分页插件。课程列表页、作业列表页都需要分页MyBatis-Plus的MybatisPlusInterceptor配置好之后调用page方法就能拿到分页数据配合前端的分页组件一天能搞定平时需要两天的活。2.2 前端选型与认证方案为什么用JWT而不是Session前端用Vue理由很直接组件化开发效率高Element UI的组件库能让页面在短时间内达到不错的视觉效果而且Vue对中文社区的学习资料非常友好出问题随时能查到解决方案。Vue 2虽然官方进入了维护期但生态最成熟Element UI、vue-router 3、vuex 3这些配套完全够用毕业设计根本不需要为了新而新。认证方案我推荐JWT而不是传统Session。原因有三点第一前后端分离的开发模式下前端打包后放在Spring Boot的静态资源目录里请求都是同域但接口本身要适配不同端JWT的无状态特性更灵活第二Session依赖Servlet容器保存状态分布式环境会出现共享问题而JWT把用户信息加密放在Token里服务端只负责校验签名天然支持水平扩展第三答辩时关于JWT的讨论是一个稳定且能体现知识深度的加分话题。实现上登录接口校验密码后生成Token返回给前端前端将其存储在localStorage每次请求在Axios拦截器中放入Authorization请求头。后端用拦截器统一校验白名单放行登录、注册、课程列表等接口。2.3 开发环境与版本匹配清单这一节非常容易踩坑。Spring Boot版本过高、JDK版本不匹配、前端Node版本过低都会造成启动失败或打包报错。列出我实际验证过的组合JDK1.8对应Spring Boot 2.7.x稳定且兼容性最好。Spring Boot 3.x要求JDK 17对于毕设不是必须反而可能因为环境问题浪费时间。Maven3.6及以上配置阿里云镜像加速依赖下载。MySQL 8.0驱动用mysql-connector-java 8.0.33。Node.js 16.xnpm 8.xVue CLI 4.x或5.x。IDEA 2022及以上版本社区版即可没必要追求最新版本。数据库连接池不需要额外引入Druid直接用Spring Boot默认的HikariCP性能足够配置也简单。只是为了演示方便我在application.yml里把数据库连接参数单独提取出来并配置了日志级别的SQL打印方便调试时查看MyBatis-Plus生成的SQL语句。这个小习惯对排查关联查询问题非常管用。3. 数据库设计在线教育系统的核心表结构3.1 用户表与角色设计数据库设计是决定项目上限的环节。我见过太多学生把用户表只做成“账号密码”两个字后面做权限、做统计时全部推倒重来。这里给出一个经过验证的用户表结构CREATE TABLE user ( id BIGINT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(100) NOT NULL, real_name VARCHAR(50), role TINYINT NOT NULL COMMENT 1管理员 2教师 3学生, avatar VARCHAR(255), email VARCHAR(100), phone VARCHAR(20), status TINYINT DEFAULT 1, create_time DATETIME, update_time DATETIME );密码字段至少要能存BCrypt加密后的密文60位左右VARCHAR(100)是安全起见。角色用TINYINT存数字而不是直接存字符串一是节省空间二是后端的角色判断代码更简洁。用1、2、3这种数字枚举配合常量类或枚举类比在代码里到处写teacher字符串要规范得多。有一点容易被忽略status字段。用户被封禁或停用时不需要删除记录而是把status置为0。同理课程状态也可以设计为状态值。演示时想展示管理员功能一个封禁操作就够了。3.2 课程内容组织课程、课时、课堂三级结构课程和课时是云课堂系统的内容主干。一个教师创建一门课程课程下面挂多个课时课时可能是录播视频也可能是直播课堂。所以数据上要用“课程表 课时表”的二级结构而不是把视频直接挂在课程表上。课程表核心字段包括课程名称、封面图、简介、所属教师ID、分类ID、课程状态0草稿 1已发布 2已下架、选课人数。课时表字段包括所属课程ID、课时标题、课时类型0录播 1直播、视频地址、直播开始时间、课时时长、排序号。这种设计的好处是课程详情页天然就是一个“课时列表”学生学习进度可以精确到每一个课时教师添加课时和排序都非常灵活。直播课和录播课虽然形态不同但在课时表里共用一个type字段区分业务逻辑统一处理不会因为两个模块而增加表数量。课程分类可以做一张简单的分类表字段就三个id、分类名称、排序。如果希望分类支持两级再加一个parent_id字段。不做无限级分类那是电商项目的复杂度。3.3 学习过程落表选课、进度、作业在线教育系统的“在线”体现在学习过程的数字化。至少需要这几张表选课关系表、学习记录表、作业表、作业提交表。选课关系表字段id、用户ID、课程ID、选课时间。学生点击“加入课程”就是在这张表插入一条记录同时累加课程表的选课人数。课程是否已选用user_id和course_id做联合查询即可。学习记录表是整个系统的核心字段包括用户ID、课时ID、课程ID、观看进度0到100、最近观看位置视频秒数、最后学习时间。这里有一个经验用“课时ID 用户ID”做唯一索引保证一个学生在一个课时下只有一条记录。以后无论查“我的学习进度”还是“教师查看学生学情”都基于这张表聚合。作业表属于教师侧字段包括所属课程ID、所属课时ID、作业标题、作业内容、截止时间。作业提交表是学生侧的字段包括作业ID、学生ID、提交内容、附件地址、得分、教师评语、提交时间。两张表通过作业ID关联构成完整的作业闭环。附件存储建议用本地磁盘加静态资源映射云存储配置复杂不是必选项。4. 后端核心功能实战从接口设计到代码落地4.1 项目骨架与分层结构Spring Boot项目结构我采用经典的四层分包controller、service、mapper、entity实体类外加config、common、util三个公共包。四层分包虽不新颖但对毕设来说清晰、易答辩、代码不迷路。com.demo.cloudclass ├── controller # 接口层 ├── service # 业务逻辑层 ├── mapper # MyBatis-Plus持久层接口 ├── entity # 数据库实体类 ├── config # 配置类拦截器、分页插件、静态资源映射 ├── common # 统一返回结果封装、异常处理 └── util # JWT工具类等统一返回结果类一定要有。定义一个Result类包含code、message、data三个字段所有接口都返回这个结构。别小看这步它能统一前后端的数据交互格式前端axios封装也会轻松很多更重要的是给答辩老师留下“代码规范”的好印象。实体类上直接使用MyBatis-Plus的注解如TableName、TableId(type IdType.AUTO)字段用驼峰命名数据库字段用下划线命名MyBatis-Plus默认开启驼峰映射不用过多配置。4.2 注册登录与JWT拦截器实现注册登录是每个系统必备的入口但实现质量能拉开差距。注册接口要做三件事校验用户名是否重复、密码用BCrypt加密、设置初始角色为“学生”并入库。这里我用的工具是Spring Security中的BCryptPasswordEncoder类单独引入spring-security-crypto依赖即可不需要引入整个Spring Security。登录接口的逻辑是根据用户名查用户用BCrypt校验密码校验通过后调用JWT工具类生成Token返回给前端。Token中放入userId、username、role三个字段过期时间设置为24小时。PostMapping(/login) public Result login(RequestBody LoginDTO dto) { User user userService.getOne(new LambdaQueryWrapperUser() .eq(User::getUsername, dto.getUsername())); if (user null || !passwordEncoder.matches(dto.getPassword(), user.getPassword())) { return Result.error(用户名或密码错误); } if (user.getStatus() ! 1) { return Result.error(账号已被禁用); } String token jwtUtil.generateToken(user.getId(), user.getUsername(), user.getRole()); return Result.success(token); }在config包中写一个拦截器类实现HandlerInterceptor接口在preHandle中解析请求头里的Authorization校验Token有效性把用户信息存入ThreadLocal或HttpServletRequest的attribute中。然后在WebMvcConfigurer里注册拦截器排除登录、注册、课程浏览等放行路径。这里要特别注意放行的路径和拦截的路径要写清楚不然会出现前端拿不到数据的诡异问题。4.3 课程模块最典型的业务闭环课程模块包含课程CRUD、课时管理、选课、课程列表分页几个核心接口。课程创建必须校验当前登录用户的角色是教师否则拒绝。这个校验逻辑封装成一个RequireRole注解或者直接在service里判断两者皆可但建议做一个简单的角色判断工具因为后面作业、课堂等多个接口都要用。课程列表接口要支持分页、关键字搜索、分类筛选和状态过滤。使用MyBatis-Plus的Page分页对象配合LambdaQueryWrapper实现GetMapping(/list) public Result list(RequestParam(defaultValue 1) int page, RequestParam(defaultValue 10) int size, RequestParam(required false) String keyword, RequestParam(required false) Long categoryId) { PageCourse p new Page(page, size); LambdaQueryWrapperCourse wrapper new LambdaQueryWrapper(); if (StringUtils.hasText(keyword)) { wrapper.like(Course::getTitle, keyword); } if (categoryId ! null) { wrapper.eq(Course::getCategoryId, categoryId); } wrapper.eq(Course::getStatus, 1).orderByDesc(Course::getId); return Result.success(courseMapper.selectPage(p, wrapper)); }选课接口要防止重复选课。查询选课关系表中是否已存在记录存在则提示不存在则插入同时课程表的studentCount自增。这个操作在演示时最容易出问题因为测试者会反复点击务必做幂等处理。课程详情接口除了返回课程基本信息还要返回课程下的课时列表和当前用户的学习进度前端详情页才能一次性渲染完整。4.4 学习进度接口的设计思路学习进度这块很多学生做成一次性提交演示时学了一下退出再进数据还是老样子非常尴尬。正确思路是“定时上报 后端幂等更新”。前端在视频播放时通过video标签的timeupdate事件获取当前播放位置每30秒调用一次进度上报接口。上报参数包括课时ID、观看位置秒、已经观看的秒数。后端拿到数据后先根据“用户ID 课时ID”查询记录存在则更新播放位置并重新计算进度百分比不存在则插入。进度的计算方式可以简单用“本次上报的观看秒数 / 课时总时长”得到百分比但更合理的做法是记录最远观看位置取最远位置与时长的比值这样学生快进时进度会立即体现演示效果更直观。PostMapping(/record) public Result record(RequestBody LearningRecordDTO dto, HttpServletRequest request) { Long userId (Long) request.getAttribute(userId); Section section sectionService.getById(dto.getSectionId()); if (section null) { return Result.error(课时不存在); } LearningRecord record recordMapper.selectOne(new LambdaQueryWrapperLearningRecord() .eq(LearningRecord::getUserId, userId) .eq(LearningRecord::getSectionId, dto.getSectionId())); int progress dto.getLastPosition() * 100 / section.getDuration(); progress Math.min(progress, 100); if (record null) { record new LearningRecord(); record.setUserId(userId); record.setSectionId(dto.getSectionId()); record.setCourseId(section.getCourseId()); record.setLastPosition(dto.getLastPosition()); record.setProgress(progress); recordMapper.insert(record); } else { record.setLastPosition(dto.getLastPosition()); record.setProgress(progress); recordMapper.updateById(record); } return Result.success(); }这里再补充一个关键功能课程整体进度的计算。对学生端首页展示的“我的学习进度”应该显示整门课程的完成百分比计算方式是该课程下已完成即单课时进度为100的课时数除以总课时数。统计在学生端做还是后端做都行但建议后端做一个聚合接口直接在SQL里用AVG或COUNT计算避免前端处理复杂逻辑。5. 前端Vue实现与SpringBoot打包部署全过程5.1 前端页面结构与路由划分前端项目基于Vue CLI创建使用Vue Router和Vuex或Pinia。页面结构划分为四个区域门户页面、学生端、教师端、管理端。不要把所有页面平铺在同一个层级里而是通过权限路由做划分。最简单的做法是登录后根据角色跳转到不同的首页路由然后在路由元信息中标记所需角色在全局前置守卫里做判断。学生端核心页面包括门户首页课程列表、课程详情页课时列表 视频播放、我的学习进度列表、作业中心。教师端包括课程管理课程列表 创建课程 课时管理、作业管理布置作业 批改列表。管理端包括用户管理、课程管理审核下架、数据概览。课程详情页是前端最复杂的页面它同时包含课程信息、课时的Tab列表、视频播放器。视频播放用原生video标签配合进度上报逻辑即可不必引入Video.js或vue-video-player这类大依赖。直播课堂页面则建议使用WebSocket或轮询方式实现聊天室为了降低复杂度课堂聊天可以采用每3秒轮询一次最新消息的简化实现演示时效果和实时性差异不大。5.2 axios封装与路由模式选择axios必须做统一封装这是前端代码规范的关键。在src/utils/request.js中创建axios实例设置baseURL为空字符串或/api前缀配置请求拦截器和响应拦截器。请求拦截器负责在每次请求头中加入JWT Token响应拦截器统一处理业务错误码遇到401则清除本地Token并跳转登录页。前端路由模式建议使用createWebHashHistory哈希模式不要用createWebHistoryhistory模式。原因在后端部署时非常关键history模式下刷新某个子页面如/student/course/1时Spring Boot的静态资源映射找不到对应的路径直接返回404。哈希模式下路由通过#号后路径传递不会触发服务端路由匹配刷新无压力。这个选择能避免一个极其常见且排查起来费时的部署问题。5.3 把Vue打包放进Spring Boot具体步骤与配置这是毕设部署中最容易出错也最体现经验的部分。网上说法很多我直接给出验证过的完整流程第一步修改vue.config.js设置publicPath。const { defineConfig } require(vue/cli-service) module.exports defineConfig({ publicPath: ./, outputDir: dist, assetsDir: static, productionSourceMap: false })publicPath设置为./能让静态资源全部走相对路径避免部署到非根路径或打成jar包后资源路径错误。assetsDir设置为static是为了将打包出的JS和CSS统一放入static目录与Spring Boot的静态目录结构保持一致。第二步执行npm run build项目根目录会生成dist文件夹包含index.html和static目录。第三步把dist目录下的index.html和static目录整体复制到Spring Boot项目的src/main/resources/static目录中。注意是static目录下直接放index.html和static子目录不是把dist文件夹整个放进去。第四步在Spring Boot中做一个简单的页面转发Controller将访问根路径时转发到index.html或者在application.yml中配置静态资源路径。默认情况下Spring Boot会扫描classpath下的static目录所以不做额外配置也能访问但做一个转发Controller可以让根路径直接进入首页。Controller public class IndexController { RequestMapping(/) public String index() { return forward:/index.html; } }第五步执行mvn clean package打包获得xxx.jar文件。使用java -jar xxx.jar启动后浏览器访问http://localhost:8080即是完整的云课堂系统。这个方案的好处极其明显整个系统是一个Java进程没有Nginx、没有独立前端服务跨域问题直接消失演示时只需要保证Java环境和一个正在运行的jar包几分钟就能上线。很多学生问为什么不分开部署分开部署意味着要处理Nginx跨域与代理配置对毕设来说是给自己增加不必要的风险。5.4 部署验证与常见故障排查打包部署完成后建议按清单走一遍验证流程访问根路径能打开首页而不是出现404或报错日志。登录接口能正常返回Token携带Token访问个人中心能拿到数据。播放录播视频时视频地址能正确加载。视频文件放在Spring Boot项目的static/uploads目录下或通过自定义静态资源映射指定本地磁盘路径。刷新课程详情页不出现404确认使用了hash路由。部署阶段最容易遇到的三个问题我这里直接给出排查路径第一个是静态资源404。打开页面后按F12查看控制台如果JS和CSS请求路径中出现绝对路径导致找不到资源基本是publicPath没有设置成./。第二个是接口404或405。确认后端Controller路径是/api开头还是根路径前端request.js的baseURL要与之对应。Vue CLI自带的devServer跨域代理在开发环境有效打包后不再生效统一使用相对路径最省心。第三个是端口占用。默认8080被占用时在application.yml中修改server.port或在启动命令中指定java -jar xxx.jar --server.port8081。6. 答辩亮点包装与常见问题应对6.1 项目亮点的合理深化答辩时忌讳把项目说得像新闻联播也忌讳只说“我用了Spring Boot”。项目亮点要落在具体的技术点上我会引导学生准备三个层面的亮点第一业务闭环。从课程创建、学生选课、视频学习、进度跟踪到作业提交批改整个教学链路的数据是可以串起来的。这句话的价值在于让评委知道你不是在写页面而是在做真实业务系统。第二认证安全。JWT无状态认证替代Session配合拦截器统一权限校验接口层面做了角色管理。可以顺带说明BCrypt加密存储密码防止明文泄露这比你背十条Spring Boot面试题都管用。第三部署架构。前后端分离开发最终通过将Vue打包产物放入Spring Boot静态资源目录实现单Jar部署项目在任何一台带JDK的机器上都能一键启动。这句话直接点明你理解整套系统的运行方式。如果代码中确实用到了WebSocket做课堂聊天、MyBatis-Plus分页、自定义异常处理全局拦截这些都可以作为亮点。但切记不要虚构没用过的技术答辩时老师追问几句细节就会露馅这是我的学生用教训换来的经验。6.2 演示环境的容错准备演示翻车率最高的不是写代码而是现场环境。我建议准备三套保险第一笔记本本地装好JDK和MySQL确保项目在完全离线的环境下也能跑起来因为演示现场的网络状况不可控第二数据库提前准备好数据至少包含三个角色的测试账号、5门课程、每个课程下3到5个课时、若干作业和提交记录特别是学习进度要有已有数据演示时直接展示效果第三录屏备份。如果现场无论如何都启动不了一个操作顺畅的演示视频比冷场强一百倍。演示脚本要有设计感。建议开场用推荐位课程列表和课程详情页先抓眼球然后登录学生账号看课程、播放课时、看学习进度变化再切换到教师账号展示课程管理和作业批改最后用管理员账号展示用户管理和数据概览。整个演示大约十分钟覆盖全部核心功能节奏感比随意点击好得多。6.3 高频答辩问题预案列举几个问概率极高、且回答好就是加分项的问题为什么选择Spring Boot答简化配置、自动装配、约定优于配置通过效率提升把精力放在业务实现上。JWT和Session的区别是什么答Session服务端存储有状态分布式时需要共享存储JWT客户端持有无状态服务端只验证签名天然支持分布式缺点是无法主动失效所以设置了24小时过期时间。直播功能怎么实现的答说明课堂状态管理机制和聊天室实现方式如果是用WebSocket就讲清广播推送逻辑如果用的轮询也大方说明是为了降低复杂度本质与业务效果等价。学习时长如何记录答前端定时上报播放位置后端持久化最远位置和进度百分比通过唯一索引保证记录唯一性。最后还有一个容易被忽略的问题老师可能会问“你在这个项目里遇到的最大的坑是什么”。这个问题一定要提前准备一个有技术含量的答案。我常用的是Vue打包到Spring Boot后的资源404问题以及它背后的history路由与hash路由差异然后补充说明自己是如何定位、修复并验证的。这种问题回答好了比回答十个“我学会了什么”都更能打动评委。结尾给正在做这个题目的同学一点补充这个项目做完后如果还想继续扩展有两条思路。一是往学习数据的智能化方向走把学习记录表的数据做统计分析做出学生学情画像和课程热度排行二是往课堂互动深挖引入WebSocket做实时聊天和通知推送甚至可以做一个简单的在线答疑模块。两条路都能让项目在“云课堂”这个背景里立得住脚而不是停在一个课程管理系统。最后再分享一个小技巧写项目文档时把数据库设计里的ER图、核心功能的时序图、部署架构图配上再附一页接口清单。这一页东西在开题、中期、答辩三个环节都能复用省掉很多临时整理材料的功夫。希望这套从选题到落地的完整方案能让你这个Spring Boot毕业设计少走点弯路。