每年毕设季,我都会收到大量类似的咨询:能不能带一个SpringBoot + Vue + MySQL的课程管理系统?说实话,这三个词组合在一起,几乎是目前Java Web毕设的“标配组合”。选题经典、技术栈主流、答辩好讲、扩展空间也大,更重要的是网上资料齐全,哪怕你基础一般,只要按照正确路线走,完全能独立做出来。
这篇内容我打算直接围绕“在线课程管理系统”这个毕设项目,从需求拆解、数据库设计、后端接口实现、前端页面搭建,一直到论文撰写和服务器部署,把整条链路完整过一遍。无论你现在是刚打开IDEA的新手,还是已经写完基础功能正在调试的老哥,这篇文章都值得看完,里面有不少是我实际带项目时踩过的坑,属于网上教程不会细讲的细节。
1. 在线课程管理系统到底在做什么:需求分析与模块拆解
1.1 从用户角色反推系统功能
很多同学拿到毕设题目就直接打开代码工具开始写,写到最后发现功能堆了一大堆,但逻辑是乱的。正确做法是先想清楚:这个系统有哪几类人在用,他们各自要做什么。
在线课程管理系统常规而言有三类角色:学生、教师、管理员。所有功能模块都是围绕这三类人的需求来设计的。
学生端最核心的需求是“找课、看课、学课”——浏览课程列表、查看课程详情、选课、在线观看视频资源、记录学习进度。再细致一点,可能还有课程评论、收藏课程、查看已选课程列表。
教师端核心需求是“开课、管课”——发布新课程、维护课程分类、上传课程视频和资料、查看自己课程的选课人数、管理选课学生列表。有的系统还会加一个简单的数据统计,比如每门课程的选课趋势。
管理员端做的是平台层面的管理——用户管理(封禁、重置密码)、课程审核(教师发布的课程是否需要审核)、分类管理、系统数据概览。
这三类角色的功能边界一定要在编码前就划好,否则后面做权限控制的时候很容易失控。我在实际项目里见过不少同学把所有代码写在一个类里,结果Controller几万个行,答辩时老师问模块划分直接卡壳。
1.2 为什么是SpringBoot + Vue + MySQL这套组合
选这套技术栈有它的现实逻辑,不是为了炫技,而是因为它确实是当前JavaWeb领域综合成本最低、出活最快、覆盖面最广的组合。
SpringBoot解决了传统SSM时代大量XML配置的问题。我记得以前用SpringMVC搭一个项目要折腾一周的配置文件,SpringBoot用自动配置和内嵌Tomcat把这些全部简化了。对毕设来说,这意味着你可以把有限的时间从“配置地狱”里解放出来,投入到业务逻辑和功能实现上。加上SpringBoot本身在企业里的使用率极高,这个选型本身就是答辩时的加分项。
Vue是前端框架里上手曲线最友好的一个。它的响应式数据绑定和组件化开发模式,让没有系统学过前端的人也能快速做出像样的页面。配合Element UI这类组件库,表格、表单、弹窗、分页这些后台系统的高频组件都是现成的,不会前端也能拼出来。
MySQL就更不用说了,开源、免费、资料多,而且它的SQL语法是行业基础,几乎所有后端岗面试都会问。你在毕设里用MySQL,既稳妥又有代表性。
1.3 前后端分离架构,这个架构为什么是主流
这个项目的架构是典型的前后端分离:前端是一个独立的Vue工程,通过axios发HTTP请求调用后端接口;后端是SpringBoot项目,对外提供JSON格式的RESTful API;MySQL只通过后端访问,前端不直连数据库。
前后端分离最大的好处是职责边界清晰。前端只负责页面渲染和交互逻辑,后端只负责业务逻辑和数据持久化。两边可以并行开发——只要你提前约定好接口文档。对于小组合作做毕设的同学来说,这个模式天然适合分工,一个人管前端一个人管后端,互不阻塞。
另一个实际好处是部署灵活。前端打包成静态文件扔给Nginx,后端打成jar包独立运行。前端挂了不影响后端,后端升级也不需要动前端。这个架构在答辩时也是标准的聊点,老师一定会问“为什么用前后端分离”,你要能说出这些理由。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 数据库设计:表结构才是一个课程系统的灵魂
2.1 核心表结构设计与字段规划
数据库设计是很多同学的短板,但恰恰又是最不该出问题的地方。表结构设计得好,后面写代码是顺水推舟;设计得烂,代码就要不停补丁打补丁。
一个标准的在线课程管理系统,核心表大概有这么几张:
用户表、课程分类表、课程信息表、选课记录表、课程资源表(主要存视频信息)、学习记录表。如果你的功能范围更大,还可以加上评论表、收藏表、公告表,但基础六张表已经能覆盖主干功能。
以课程信息表为例,字段设计要有这个意识:
- course_id:主键,课程唯一标识
- course_name:课程名称,VARCHAR(100)不可为空
- category_id:关联分类表的外键
- teacher_id:关联用户表的外键,指向教师用户
- course_desc:课程简介,TEXT类型
- cover_url:封面图地址
- course_price:价格字段,DECIMAL(10,2),免费课就存0
- status:上下架状态,用0和1表示
- create_time / update_time:创建时间和更新时间
这种字段设计思路必须养成习惯:每个字段的命名、类型、长度、是否可空,都要在动手建表前想明白。我见过太多人把课程价格设计成VARCHAR,后面做价格排序直接傻眼。
2.2 外键、索引与唯一约束
表之间的关系要明确,但这里有个容易被忽视的点:在互联网企业实际开发中,外键约束用得越来越少,更推荐在应用层维护关联关系。原因是外键会影响插入和删除性能,而且分库分表后外键直接失效。
毕设项目不会遇到那么极端的性能场景,所以我的建议是:可以不建物理外键,但逻辑外键关系必须清晰。比如课程表的teacher_id关联用户表的user_id,你不在数据库层面强约束,但写代码时一定要保证这个关联的正确性。这样做的好处是删除用户时不会因为外键约束报一堆错,坏处是需要写代码时多留个心眼保证数据一致性。
索引的设计要结合查询场景。最常见的查询场景是“按分类查课程列表”“按教师查课程列表”,所以category_id和teacher_id这两个字段建议建索引。选课记录表必须给(user_id, course_id)建联合唯一索引,这是防止学生重复选课的最底层保障,比在代码里做判断要可靠得多。
特殊提醒一句:字符串类型的字段如果要做模糊查询,比如按课程名称关键字搜索,那种场景下普通索引也不生效,需要了解like的匹配规则,或者用全文索引。毕设一般用like '%关键词%'就够了,但答辩老师可能问性能问题,你要知道这个查询是无法命中索引的,能解释清楚原因就行。
2.3 时间字段的三个隐形规范
时间字段是新手最容易翻车的地方。建议所有核心表都加create_time和update_time这两个字段。Data类型用datetime就好。SpringBoot的MyBatis-Plus有自动填充功能,配置一下插入和更新时自动填值,省心且不会漏。
还有一个很多人踩过的坑:MySQL的datetime和Java的LocalDateTime对应时,默认情况下从数据库查出来是“2025-06-01T12:00:00”这种带T的格式,前端显示很丑。解决方案是在后端的JSON序列化配置里统一修改LocalDateTime序列化格式,或者直接在实体类时间字段上加上@JsonFormat注解。
3. 后端从0到1:SpringBoot项目的核心实现路径
3.1 项目初始化与目录结构规范
用IDEA创建SpringBoot项目是常规操作,但有几个细节要提前确定。一个是SpringBoot版本和JDK版本的匹配问题。我推荐用SpringBoot 2.7.x搭配JDK 8或11,这是最稳妥的组合,网上教程资源也最丰富。千万不要一上来就选最新的SpringBoot 3.x,因为它基于Jakarta EE,很多老教程的javax包全部要替换,对新手是额外负担。
还有一个小技巧是跳过官网的Spring Initializr下载或者网络慢时,直接在IDEA里用阿里云的镜像地址创建项目,速度快很多。具体做法是在创建项目的页面把Server URL替换为阿里云的Spring Boot脚手架地址。
项目结构建议按这样分包:
- config:配置类(跨域、拦截器、MyBatis-Plus分页)
- controller:接口层
- service / service.impl:业务逻辑层
- mapper:数据访问层
- entity:数据库实体映射
- dto / vo:入参对象和视图返回对象
- common:统一返回结果、异常处理、工具类
分包逻辑要分到具体,哪怕类少也没关系,但层次感出来之后,代码维护性和答辩观感都会好很多。
3.2 登录鉴权:用JWT而不是Session
在线课程系统必然涉及不同角色的权限区分,Session的方式在前后端分离架构里会遇到跨域cookie携带的种种问题,建议直接用JWT。
JWT的核心逻辑是这样的:用户登录成功后,后端生成一个加密的token返回给前端,前端存储起来,后续每次请求都在请求头里带上这个token。后端写一个拦截器,拦截需要登录才能访问的接口,校验token是否有效,从token里解析出用户信息。
SpringBoot里实现JWT方案,需要引入两个依赖:jjwt或者hutool的jwt工具。jjwt是Java社区最流行的JWT库,但用起来稍繁琐。Hutool工具包的JWTUtil封装得更友好,几行代码就能生成和校验token。我用Hutool比较多,代码量确实少。
在校验环节想清楚一个问题:拦截到没有token的请求返回401,前端路由守卫检测到401后跳转登录页。这个闭环逻辑通了,整个权限体系就顺了。
还有一点要从项目一开始就设置好:跨域配置。前后端分离项目,前端跑在localhost:5173(Vite默认),后端跑在localhost:8080,端口都不一样,必须配置跨域。SpringBoot里用CorsFilter就能解决,把允许的域名、请求方法、请求头都放行,然后允许携带凭证。
3.3 最容易写崩的选课功能:事务与并发
在线课程系统的业务核心是选课和退课,这个逻辑一定要写对。
先说退课,逻辑很简单:删除选课记录,同时把课程表的已选人数减一。这里的关键是两条数据操作必须放在同一个事务里,要么都成功,要么都失败。在Service层方法上加上@Transactional注解就能搞定。不加注解的后果是删了记录但是选课人数没减,数据不一致。
选课逻辑要处理的点更多:
- 判断课程是否存在并且已上架
- 判断学生是否已经选过这门课
- 判断课程人数是否已满
- 插入选课记录
- 课程已选人数加一
这里的高并发问题值得准备一下。如果两个学生同时选最后一门课的最后一个名额,会导致超选。解决方式有很多,悲观锁、乐观锁、Redis分布式锁,但对毕设来说,最简单可靠的做法是数据库层面控制——利用user_id和course_id的联合唯一索引兜底,插入时捕获DuplicateKeyException异常,然后提示“您已选过这门课”。另一个关键是更新人数的SQL语句要这样写:UPDATE course SET selected_count = selected_count + 1 WHERE course_id = #{courseId} AND selected_count < max_count,利用条件更新保证不会超过总名额。这个点如果在答辩时能讲出来,老师会觉得你有并发意识。
3.4 视频资源处理:m3u8播放与文件存储
在线课程系统通常要支撑视频在线播放,这就涉及到视频文件的存储和播放方案。
教学类的在线课程视频,很多时候会在服务端转码成HLS格式(m3u8)。之前有同学被这个卡了两三周,其实本身就是常见多媒体协议转换。m3u8是一个索引文件,里面记录了若干个.ts视频分片文件的播放顺序。这种格式最大的优势是支持流媒体播放,可以拖动进度条,不需要等整个视频下载完,而且天然支持多码率自适应。这也解释了为什么现在主流视频平台都走HLS方案。
毕设项目的视频文件存储有两种常见方式:
- 本地上传并存储到服务器指定目录下,通过Nginx开一个静态访问路径来映射
- 接入云存储(如阿里云OSS或腾讯云COS),上传后返回CDN加速的访问URL
本地方案省事但服务器带宽压力大,且视频文件占空间。云存储方案需要先开通服务、创建Bucket、配置读写权限、引入SDK,但上传成功后拿到的URL可以直接用于播放,部署到真实服务器时稳定很多。具体选哪种,看你的预算和时间安排。如果只是交个毕设,本地存储加Nginx静态映射已经足够了。
前端播放m3u8文件,建议用video.js框架,配置好后它支持自动识别m3u8格式并且调用hls.js进行解码播放。也可以直接用hls.js这个轻量库,自己封装一个播放器组件。核心代码逻辑是创建Hls实例、传入视频地址、绑定到video元素上,兼容通过MediaSource实现播放。
4. 前端工程化实践:Vue项目搭建与核心模块落地
4.1 开发环境准备与项目初始化
前端这块很多后端方向的同学发怵,但好消息是Vue的上手门槛真的不高。
第一步是安装Node.js。这里要提醒:不要装太新的版本。Vue项目对Node版本有一定要求,太高或太低都会出现奇怪的问题。目前推荐Node 16到Node 18之间,稳定且兼容性好。安装完成后在终端里执行node -v确认版本号。
创建Vue项目有两种常用方式,Vue CLI(基于webpack)和Vite。Vite现在更推荐——启动速度快、构建快、配置简单。用下面这条命令就能创建项目:
bash复制npm create vite@latest course-frontend -- --template vue
安装依赖并启动开发服务器:
bash复制cd course-frontend
npm install
npm run dev
启动之后浏览器访问终端提示的地址,看到Vue的默认页面说明环境已经通了。
4.2 开发时依赖的安装配置
项目创建好后,还需要根据需求安装几个核心依赖。用npm命令安装,一条条来,避免网络问题导致中途失败。
bash复制npm install vue-router@4
npm install pinia
npm install axios
npm install element-plus
npm install @element-plus/icons-vue
vue-router是前端路由,这是单页应用的基础设施。Pinia是状态管理工具,可以管理用户登录信息、购物车等全局状态。Axios是HTTP请求库,用来对接后端接口。Element Plus是UI组件库,后台管理系统直接用它的菜单、表格、表单、弹窗,省去大量自写样式的时间。
环境配置的问题是“代码还没开始写,环境先崩了”的高发区。最常见的报错是版本冲突,比如vue-router版本和vue版本不兼容。解决思路很简单:安装时注意大版本号,Vue 3项目用vue-router 4、pinia 2,Element Plus必须2.x以上版本,这样组合才兼容。
4.3 路由与权限控制的前端实现
在Vue项目里,路由贯穿整个前端框架,必须先理清楚。
路由配置的核心结构是一个数组,每个路由对象包含path、name、component和meta。比如登录页路由、首页路由、课程列表页路由、课程详情页路由、个人中心路由。然后把路由配置挂到router实例上,在main.js里通过app.use(router)注册。
课程的详情页通常需要带上课程ID,可以用动态路由,路径写法是/course/:id,页面里通过this.$route.params.id或者useRoute().params.id获取这个参数,然后调用后端接口查询课程详情。
路由守卫是权限控制的关键。在router.beforeEach里检查当前访问的路径是否需要登录,如果需要且本地没有token,就跳转到登录页。这是一个标准的全局前置守卫写法:
javascript复制router.beforeEach((to, from, next) => {
const token = localStorage.getItem('token')
if (to.meta.requiresAuth && !token) {
next('/login')
} else {
next()
}
})
这里有一个细节要补充——管理员和普通学生、教师的权限需要按角色区分。可以在meta里放一个roles数组,比如/admin路由的meta为{ roles: ['ADMIN'] },在守卫里判断当前用户角色是否包含在内,不匹配就跳转首页并提示无权限。
4.4 axios封装:统一处理请求和响应
项目里请求后端的代码如果不做统一封装,每个页面都写一遍完整的axios调用,代码会非常冗余。建议创建src/utils/request.js,把axios实例的创建、请求拦截器、响应拦截器统一放进去。
请求拦截器做两件事:在请求头里自动加上token,统一设置Content-Type为JSON。响应拦截器做三件事:后端的token过期时统一跳转登录页;后端返回的业务码,非200的情况统一弹出错误提示;成功时直接返回data部分,页面里拿到的就是干净数据。
javascript复制import axios from 'axios'
import { ElMessage } from 'element-plus'
import router from '@/router'
const request = axios.create({
baseURL: '/api',
timeout: 10000
})
request.interceptors.request.use(config => {
const token = localStorage.getItem('token')
if (token) {
config.headers.Authorization = `Bearer ${token}`
}
return config
})
request.interceptors.response.use(
response => {
const res = response.data
if (res.code !== 200) {
ElMessage.error(res.msg || '请求失败')
if (res.code === 401) {
router.push('/login')
}
return Promise.reject(new Error(res.msg))
}
return res.data
},
error => {
ElMessage.error('网络异常,请稍后重试')
return Promise.reject(error)
}
)
跨域问题是前后端联调时绝对绕不开的坑。前端开发环境解决跨域最简单的方式是在Vite配置里加代理:devServer.proxy把所有/api开头的请求转发到后端的8080端口。这样前端的请求地址直接写/api即可,后端也无需开启烦人的跨域配置。
4.5 课程视频播放与m3u8处理
如果课程详情里包含视频,Vue里播放m3u8格式的视频,标准做法是用hls.js库。这个库会利用浏览器的Media Source Extensions能力,把m3u8里记录的ts文件逐段拉下来并拼接播放。
封装一个video-player组件是这个环节的关键,它应该做到传入视频地址就能自动判断格式并选择播放方式。对于mp4这种浏览器原生支持的格式,直接设置video的src就能播放。对于m3u8格式,则需要先动态引入hls.js,判断当前环境是否支持。
一个值得注意的点是:m3u8和ts分片文件也需要支持跨域访问。如果你用的是本地Nginx静态文件访问,需要在Nginx配置里加上Access-Control-Allow-Origin的头,否则视频会播放不出来。这个问题排查起来很隐蔽,页面不报错但画面一直黑屏加载,多半是跨域或CORS没处理好。
4.6 打包构建与部署前的前端调整
前后端分离项目的部署,前端最终会变成一个dist目录,里面全是静态文件,由Nginx对外提供服务。
执行打包命令前有两个地方必须调整,否则打出来的包会有问题。
第一是Vite的base配置。默认base是‘/’,如果你的项目部署在服务器根路径,保持不变即可。如果部署在子路径,比如http://域名/course/,就必须改成base: ‘/course/’。否则页面找不到JS和CSS文件。
第二是路由模式。如果用了vue-router的history模式,打包部署后直接刷新页面会出现404,因为Nginx找不到对应的真实文件。解决方式是在Nginx配置里加try_files配置,让请求先去找静态文件,找不到的就重新路由到index.html:
nginx复制location / {
root /usr/share/nginx/html;
index index.html;
try_files $uri $uri/ /index.html;
}
这里涉及的Nginx配置在实际部署中非常常见,尤其是跨域配置方面。如果需要给静态视频文件开启跨域,可以在静态资源相关的location块里加上相应的响应头配置。但要注意Access-Control-Allow-Origin不能简单地设成*,因为前端代码里如果配置了允许携带凭证,就要求明确指定来源,否则请求会被浏览器拦截。我建议先把视频存储和前端页面放在同一个域下,用相对路径访问,这样最稳妥。
打包后的经典问题就是样式错乱、布局异常。最常见的原因是:打开的页面直接访问的HTML文件路径不对,导致CSS和JS加载失败;还有全局样式没有按预期生效,Element Plus组件样式没引入。排查思路是先看浏览器控制台有没有静态资源404,有的话优先检查base配置。
5. 从空项目到线上系统:论文撰写与服务器部署
5.1 毕设论文的章节结构与每一章该写什么
这个系统做完只是完成了一半工作量,论文同样重要。论文的评审老师看你系统演示的时间很少,但看你论文的时间很多,论文没写好,系统做出来也是减分。
标准结构是:
第一章绪论。写选题背景与研究意义,这部分可以展开在线教育的发展现状,描述传统线下教学和在线课程管理中的痛点,再写国内外的研究现状——国外的Coursera、edX,国内的慕课网、中国大学MOOC等平台,最后用一小节概括本文的主要工作。
第二章相关技术介绍。逐项介绍SpringBoot、Vue、MySQL,以及MyBatis/MyBatis-Plus、JWT、HLS流媒体协议等。注意不能只写百度百科式的定义,要写“在本项目中用来做什么”。这部分是凑篇幅重灾区,也是答辩老师扫一眼就过的章节,但技术选型理由要写清楚,这是毕设评审的常见关注点。
第三章系统分析。做可行性分析(技术可行性、经济可行性、操作可行性),然后系统需求分析——功能需求用用例图描述,非功能需求写性能、安全性、易用性要求。
第四章系统设计。先画系统总体架构图,然后分模块设计,数据库设计放在这一章。数据库设计至少有概念结构设计(ER图)和逻辑结构设计(数据表结构),每个表列出字段、类型、约束,并附上说明。
第五章系统实现。按模块展示效果截图和核心代码片段,每一小节先描述功能,再放截图,再贴关键代码并解释思路。
第六章系统测试。写测试环境、测试用例设计、测试结果。至少要有登录、选课、退课、课程管理这些核心流程的测试用例,以表格形式呈现,包含测试编号、测试步骤、预期结果、实际结果。
最后是总结与展望。写本项目完成了什么、存在什么不足、未来可以从哪个方向改进。
写论文的通用经验是:截图要清晰、统一,不要用和开发环境明显不同的图片;代码要挑关键逻辑贴,不要贴一堆没解释的配置;所有图和表的编号、标题要对齐,格式错误在答辩中非常显眼。
5.2 服务器部署的完整流程
部署环节如果不在论文里体现,答辩也会被问,“你的系统能不能跑在服务器上”是高频追问。这里给出一个标准部署链路。
第一步是准备一台云服务器,阿里云或者腾讯云的入门套餐即可,推荐规格是2核4G,操作系统选CentOS 7或Ubuntu 22.04。2核2G跑这个项目也够,但4G更从容。
第二步安装基础环境:JDK 8或11,MySQL 8.x,Nginx,如果前端项目需要Node环境打包的话,本地打包好上传dist也可以,服务器上不一定要装Node。
第三步安装配置MySQL。安装完成后要执行安全初始化脚本设置root密码,创建项目的数据库,导入SQL文件。MySQL 8默认的认证插件是caching_sha2_password,旧版驱动可能连不上,要注意mysql-connector-java版本,使用8.0以上即可解决。
第四步上传后端jar包。在IDEA里执行Maven的package命令打好jar包,上传到服务器,用systemd配置一个服务让jar开机自启、崩溃自动重启。核心systemd配置:
ini复制[Unit]
Description=course-server
After=network.target
[Service]
Type=simple
User=root
ExecStart=/usr/bin/java -jar /opt/course/course-server.jar
Restart=on-failure
RestartSec=10
[Install]
WantedBy=multi-user.target
第五步配置Nginx反向代理。前端dist目录上传到服务器后,修改Nginx默认配置,把前端静态文件指向dist目录,同时把所有/api开头的请求反向代理到后端的8080端口:
nginx复制server {
listen 80;
server_name your_domain;
root /opt/course/dist;
index index.html;
location /api {
proxy_pass http://127.0.0.1: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;
}
location / {
try_files $uri $uri/ /index.html;
}
}
这里要提醒的是环境问题排查方式。mysql安装完成后的连接、用户名密码、远程访问权限、项目里的application.yml数据库配置,数据分析要逐项对照。很多新手的通病是数据库连接不上就怀疑代码,实际上是MysqL服务没启动、密码错误或者数据库没创建成功。
6. 常见问题与排查技巧实录
6.1 搭建阶段的高频报错
我在辅导学生的过程中,总结出一份高频问题速查表。这些问题不算复杂,但几乎天天有人踩,而且新人一旦被卡住就很容易卡一整晚。
前端npm install失败的问题。大概率是网络源问题,可以通过切换npm镜像源解决。执行npm config set registry https://registry.npmmirror.com,然后重试。如果还不行,检查Node版本是否过高,有些依赖在Node 20以上存在兼容问题。
后端启动时提示端口占用。SpringBoot默认用8080端口,如果本机装了其他服务占用该端口,启动就会报Web server failed to start。查看占用情况:Windows用netstat -ano | findstr 8080,然后taskkill /PID 对应进程号 /F;Linux用lsof -i:8080。
Maven依赖下载慢或者下载失败。检查IDEA的Maven设置,确认使用的是阿里云镜像仓库。这个配置是常规操作,网上有非常成熟的镜像地址,配好之后下载速度能提升几十倍。
6.2 前后端联调阶段的问题
接口联调是整个项目里消耗时间最长的阶段。前端调用后端接口报404,先确认后端接口路径是不是真的存在。很多人代码里写的是‘/course/list’,后端Controller映射写的是‘/course/all’,实际请求自然对不上。
报CORS错误,也就是浏览器控制台明确提示跨域请求被拦截。解决方案是优先在前端配置Vite代理,打包后则在Nginx里配置反向代理。后端配置跨域也可以解决,但Nginx代理的方式生产环境更规范。
前端能拿到数据但页面渲染不出来。打开Vue的调试工具,检查数据结构。最常见的问题是后端返回的是统一结果对象,前端直接拿到整个响应体去遍历,自然拿不到数组数据。
6.3 视频播放与文件上传的专项问题
m3u8视频无法播放的排查步骤建议按顺序走一遍:检查视频地址是否能在浏览器直接访问,检查服务端是否允许跨域访问,检查前端hls.js是否成功加载,检查浏览器是否支持MSE。这四个检查点基本能定位90%以上问题。
视频上传一半就失败。检查服务器临时目录的磁盘空间是否充足,检查Nginx的client_max_body_size是否设置了足够大的值,检查SpringBoot的spring.servlet.multipart.max-file-size和max-request-size配置。这个配置项是很多视频播放失败问题的根源——文件压根没上传成功,后面自然播放不了。
6.4 答辩时的高频问题准备
答辩除了演示功能,老师一定会从技术层面提几个问题。提前准备几个必答题的答案,现场就不会懵。
“为什么选择前后端分离?”要有技术深度地答,重点讲开发效率、部署灵活性、分工协作的优势。
“SpringBoot相比SpringMVC有什么优势?”从自动配置、内嵌服务器、起步依赖、监控运维几个方面展开。
“JWT的Token过期了怎么办?”答前端拦截器检测到401统一跳转登录,后端可以结合Redis实现刷新机制,但毕设阶段做到前端跳转登录已经够了。
“项目里的数据如何保证一致性?”提事务和数据库约束。这个问题的加分回答是选课场景下的防超卖方案——联合唯一索引、条件更新、事务三管齐下。
“MySQL的索引如何优化?”结合自己的表设计来说,比如哪个表建了什么索引、为什么建。如果完全不建索引还答不上来,这里容易被扣分。
7. 针对该项目实际工作量的评估建议
7.1 按时间倒排期,毕设怎么规划
做毕设最典型的失败方式是前面松懈后面崩溃。这套系统如果不拖延,严格按节奏来,一个人大概需要一个半月每天三到五个小时。
第一周做需求分析、数据库设计、搭建前后端工程骨架。第二周实现用户登录注册和课程分类管理。第三周实现课程CRUD和课程列表、详情展示。第四周实现选课退课和角色权限。第五周实现视频上传、播放和学习记录。第六周做前端页面美化、统一交互细节。第七周写论文、做测试、剪演示视频、部署上线。
每周末用固定版本的代码做一次提交和备份。这个习惯能在你改崩代码的时候帮你快速回到稳定版本,我见过太多人改了一下午代码改崩了,最后只能全部推翻重来,非常浪费时间。
7.2 功能扩展方向与进阶亮点
如果基础功能做完还有余力,可以往这几个方向扩展,这些方向也是答辩时的加分项。
引入Redis做缓存,把课程分类列表、热门课程列表等热点数据缓存到Redis,减少数据库压力。第二个方案是在选课场景里结合Redis实现分布式锁,防止并发超选问题,这个并发方案在答辩时拿出来讲会拉开和其他人的差距。
接入Elasticsearch做课程搜索,支撑关键词搜索、分类筛选、结果高亮展示。这个功能能讲的深度很大,但工程量也不小,适合基础好且时间充裕的同学。
接入阿里云短信或邮件做验证码登录,在用户表加手机号字段,登录时先发验证码。这种企业级开发里的常用交互,在毕业设计里属于亮点型功能。
上面这些方向选一两个加进去就够了,不要贪多。很多同学一开始想做一个很庞大的系统,结果到最后核心功能都没做完,反而得不偿失。
7.3 部署文档与README的组织方式
标题里提到的“部署文档”,在实际交付时建议写成一个独立的DOCX或MD文件,内容包括:运行环境要求(JDK版本、Node版本、MySQL版本)、数据库初始化步骤、后端启动步骤、前端开发模式启动步骤、打包部署步骤、常见问题说明。
如果想让老师快速跑起来,可以提供一键部署脚本——一个bat或shell脚本,自动检查环境、初始化数据库、启动后端、启动前端,把人工干预降到最低。虽然这个步骤文档稍麻烦,但老师在验收演示时体验会好很多,这种“用户思维”在毕设评审里很有价值。
8. 一个更稳妥的收尾:关于这部系统的通用经验之谈
带了很多届学生做类似的系统之后,最大的体会是这个项目虽然技术栈主流、架构清晰,但真正拉开分数差距的往往是那些不起眼的细节:数据库字段设计规不规范、日志和异常处理有没有做、接口返回格式统不统一、前端按钮有没有loading状态、打包部署能不能一条命令跑通。
这些细节加起来,在答辩时的体感就是“这个学生真的完整做过项目”和“这个学生是照着教程敲的”之间的差别。真心建议在功能全部跑通之后,留出至少3天专门做细节打磨和写文档。一个极简但重要的优化:把后端的统一异常处理写完整,确保任何情况下接口返回的都是统一的JSON结构,而不是默认的500错误页面。
如果你正在做类似的系统,不用追求功能极其复杂,把最核心的链路——登录、课程管理、选课、学习、统计——做到扎实可用,把架构的合理性讲清楚,把常见的高频问题都踩过一遍并且能给出解决方案,这个毕设就已经稳稳过关了。
