做毕业设计选“在线教育平台”这类题目的人特别多,原因很简单:业务贴近日常、功能边界清晰、前后端分离的技术栈正好是主流,不管是展示代码还是写论文,都有足够的素材可挖。但我也见过不少同学把这类项目做成“半成品”——视频传不上去、权限一改就崩、论文内容和代码对不上。这篇东西不打算再贴一遍环境搭建步骤,而是把我实际开发一个基于 Spring Boot + Vue 的在线视频教育平台时,从立项、数据库设计、核心功能实现,到前后端联调、部署、写论文的完整思路和踩坑记录整理出来。适合正在做毕业设计,或者想用一套规范化流程做前后端分离项目的开发者参考。
1. 项目定位与需求拆解
很多同学拿到“在线视频教育平台”这个题,第一反应是去抄一个电商系统的结构,再把商品换成课程。这个思路能跑通,但很容易被答辩老师追问:“你的用户角色到底有几种?课程发布流程里,审核状态是怎么流转的?”所以动手写代码之前,先把业务边界划清楚,比急着建表重要得多。
1.1 三类核心用户与业务闭环
这个平台至少要覆盖三类角色:学生(普通用户)、讲师(内容提供方)、管理员(运营方)。
学生侧重消费端:注册登录、浏览课程、查看课程详情、试看视频、下单购买、观看完整视频、发表评论、查看学习进度。讲师侧重生产端:创建课程、维护章节、上传视频、设置试看章节、提交课程审核、查看自己课程的数据。管理员侧重治理端:审核讲师提交的课程、管理用户状态(禁用/启用)、处理评论、看平台维度的统计。
这三类角色不是孤立存在的,它们通过业务闭环串在一起。我在做需求分析时画过一条最核心的链路:讲师创建课程,提交审核;管理员审核通过后课程上架;学生浏览并购买课程;学生观看视频、写评论;讲师根据反馈迭代课程内容。答辩时如果你能把这条链路讲清楚,并指出系统设计如何支撑它,就已经比大多数只做了“增删改查”的毕业设计高一个档次。
这里特别提醒一点:购买闭环往往被忽略。有的项目做了课程列表和视频播放,却没有订单表,学生页面直接显示“播放”按钮。这在逻辑上是漏洞——任何人都能绕过付费直接看视频。在线教育平台的核心是“交易 + 内容”,订单模块必须存在,哪怕只是模拟支付流程,也能体现业务完整性。
1.2 技术选型背后的真实考量
选择 Spring Boot + Vue,不是因为它“流行”或者“网上资料多”,而是这四个维度恰好适合毕业设计:
第一,开发效率。Spring Boot 的自动配置、约定优于配置,让后端不用在 XML 配置里耗费大量时间;Vue 的组件化开发让前端页面可以按功能拆分子组件,复用程度高。相比传统的 SSM + JSP,这套组合在同样的开发周期内能多完成至少两个模块。
第二,前后端分离带来的清晰边界。后端只提供 RESTful API,前端只负责页面渲染和交互。这个边界在写论文时可以明确拆成“后端接口设计”和“前端页面实现”两章,结构非常工整,不会出现代码和文档互相纠缠的情况。
第三,生态成熟度。无论是用户认证(JWT)、文件上传、分页查询,还是前端 UI 组件库(Element UI / Element Plus)、HTTP 库(Axios)、状态管理(Vuex / Pinia),都有成熟的解决方案。项目中的多数痛点都能找到现成方案,这种确定感对于毕业设计这种有时间节点的项目非常重要。
第四,团队协作(如果需要两人或多人一起做)。前后端分离意味着一个人专职做接口,另一个人专职做页面,互相只需要对齐接口文档。这在实际开发中也更接近企业的真实工作模式。
为什么不建议用更“重”的技术方案,比如 Spring Cloud 微服务、Redis 集群、高可用架构?因为毕业设计考察的核心是“你能否独立完成一个完整系统并理解其中的设计逻辑”,把单体应用做扎实、把核心业务吃透,远比引入一堆中间件然后讲不清楚原理更安全。过度设计在答辩现场很容易翻车,一套“你答不上来为什么这么用”的方案只会给自己挖坑。
2. 系统架构设计与数据库建模
需求理清之后,下一步是架构设计。这个项目的架构不复杂,但有几个关键设计点能直接影响开发效率和系统健壮性。
2.1 前后端模块划分
后端按功能域拆包,而不是按技术层拆包。我采用的是常见的 Controller / Service / Mapper 三层结构,但包名按业务域划分:controller 包下放 AuthController、CourseController、OrderController、CommentController、AdminController;service 包下放对应的业务逻辑;mapper 包放数据访问接口。这样找代码的时候非常直观,整个项目的结构在论文里也好画成模块图。
前端按“页面 + 组件”组织:views 目录下放页面级组件(首页、课程列表、课程详情、课程播放、购物车/订单、个人中心、后台管理),components 目录下放可复用组件(课程卡片、视频播放器、评论列表、分页条)。路由用 Vue Router 配置,通过路由守卫实现前端部分的权限控制。
之所以强调包和目录划分,是因为我在实际开发中发现,很多同学的项目代码全堆在几个文件里——后端一个 Controller 几千行,前端一个 vue 文件包含所有逻辑。这种代码不是不能运行,但调试和扩展时会非常痛苦,而且论文里“模块化设计”这一小节根本没法写。
2.2 数据库核心表设计与字段解析
数据库是这个项目的命根子,表结构设计好坏直接决定功能实现难度。下面是我梳理出的核心表清单,每张表我都标注了关键设计理由。
用户表 user:
字段包括 id、username、password、nickname、avatar、role(STUDENT / TEACHER / ADMIN)、status(0 禁用 / 1 正常)、created_time。password 存的是 BCrypt 加密后的密文,绝不存明文;role 虽然用简单字符串字段就能满足当前项目,但如果你后续想扩展更细的权限,可以考虑拆成 RBAC 三张表,不过对于毕设,一个 role 字段已经够用。
课程表 course:
字段包括 id、teacher_id、title、subtitle、cover、category_id、price(decimal(10,2))、original_price、description、status(0 草稿 / 1 待审核 / 2 上架 / 3 下架)、purchase_count、created_time。price 必须用 decimal,绝不用 float / double,否则涉及金额计算时会有精度问题。status 用 int 而不是字符串,是为了后端好做枚举判断和状态流转控制。
章节表 course_chapter:
字段包括 id、course_id、title、sort、created_time。章节和课程的关系是多对一,sort 字段控制章节排序。
视频表 course_video:
字段包括 id、chapter_id、title、video_url、duration、is_trial(0 非试看 / 1 试看)、sort。is_trial 是很多初学者容易漏掉的字段——在线教育平台通常允许用户免费试看部分视频,这个字段就是支撑“试看逻辑”的关键。后端接口返回视频列表时,根据当前用户是否购买课程,决定是返回全部可播放地址还是只标记试看视频。
订单表 orders:
字段包括 id、order_no、user_id、course_id、amount、status(0 待支付 / 1 已支付 / 2 已取消)、pay_type、pay_time、created_time。这里必须强调 order_no:对外暴露的自增 id 很容易被爬虫或恶意用户遍历,所以需要单独生成一个业务订单号,比如时间戳加随机数,或者用 UUID 的简化版本。订单表与课程表、用户表形成交易闭环,是支撑“购买后观看”权限校验的数据基础。
评论表 course_comment:
字段包括 id、course_id、user_id、content、parent_id、status、created_time。parent_id 支持楼中楼回复;status 字段用于管理员审核或删除违规评论后的标记。
除了这些主表,可能还需要表单表和保存表单。如果需要记录学习进度,建议建一张 study_record 表,字段包括 id、user_id、course_id、video_id、watch_duration、last_watch_time。这个表不是必须的,但如果你的项目想增加“继续学习”这个功能,它就是核心支撑。
建表时注意三点:所有表都加 created_time 和 update_time;字符集统一用 utf8mb4;关联字段(如 course 表的 teacher_id)建议定义外键约束——注意,这里说的是逻辑外键,即字段存在但数据库层面不强制建物理外键,这样查询性能更好,也避免删除数据时被外键拦截。
3. 核心功能实现与细节解析
数据库设计好之后,开发就按模块推进。这个项目我不打算罗列全部代码,而是挑几个“做了能加分、不做会踩坑”的关键点详细讲。
3.1 基于 JWT 的用户认证方案
用户认证我采用的是 JWT + 后端拦截器方案,而不是引入 Spring Security。原因有三:Spring Security 对毕业设计来说学习曲线陡峭,配置 Filter 链、UserDetailsService、加密策略等一套下来很容易迷失;JWT 方案逻辑直观,论文里好解释,答辩时你说得清楚“token 如何签发、如何校验、如何续期”;项目规模不大,自研拦截器完全够用,而且更能体现你对认证机制的理解。
具体流程是这样:用户登录成功后,后端用用户 id 和角色生成 JWT,返回给前端;前端把 token 存到 localStorage;之后每次请求,前端在 Axios 请求拦截器中把 token 放到 Authorization 头里;后端写一个拦截器,在 Handler 执行前解析 token,把用户信息放到 ThreadLocal 或请求属性中,供 Controller 和 Service 使用。
密码存 MySQL 前必须用 BCrypt 加密。为什么不用 MD5?因为 MD5 是哈希算法,虽然不可逆,但可以通过彩虹表碰撞。BCrypt 内部加了随机盐,每次加密同一个密码得到的密文都不同,这是目前比较推荐的密码存储方案。Spring Security 的 crypto 包里就有 BCryptPasswordEncoder,可以单独引进来用,不需要引入整个 Spring Security。
JWT 也有坑要提醒:token 有效期不要设太长,我一般是 7 天;token 里不要放敏感信息(比如手机号、真实姓名),因为 JWT 默认只是 Base64 编码,并没有加密;注销功能需要前端主动删掉本地 token,因为 JWT 是无状态的,后端无法“主动让一个 token 失效”,除非你引入黑名单机制,毕业设计一般不需要。
3.2 视频上传与播放的三种方案
在线教育平台最核心的资源是视频,视频怎么存的决策直接影响开发量和成本。我把方案分成三档:
本地存储方案:视频文件传到服务器的指定磁盘目录,数据库里只存访问路径。开发环境最方便,localhost 映射即可。缺点是磁盘空间有限,多用户并发上传时容易把服务器撑爆,且视频没有转码能力,不兼容的格式播放时会很难受。
云对象存储方案(对象存储 OSS 的通用方案):把视频文件上传到云对象存储服务,拿到一个公网 URL,数据库存这个 URL。优点是接入简单、成本可控、不占用自己服务器带宽,是毕业设计最推荐的方式。很多云厂商都有 SDK,封装一个 FileService 接口,上传逻辑可以统一管理。
云视频点播服务:直接上传源视频,云服务自动转码、切片、分发,而且自带加密和播放器 SDK。体验最好,但价格相对高,配置也复杂,适合商业项目。毕业设计如果预算充足也可以选,但要注意文档阅读成本。
我实际做的时候选择的是“本地 + 云对象存储”双实现:封装一个 FileService 接口,本地环境走磁盘存储,部署上线切换成云对象存储。这里的关键不是选哪个,而是封装好接口,让上层业务(课程视频模块)不感知底层文件存储细节。
视频播放还有一个绕不开的问题:权限校验。视频不是静态资源直接暴露给人看的,未购买的用户即使拿到视频 URL 也不能让他看完整版。我的实现办法是:后端在返回视频播放地址的接口中做权限判断,根据当前登录用户的 order 记录和 course_video 的 is_trial 字段,决定返回“可播放地址”还是“试看标记”。前端拿到数据后再控制播放器的行为。这样虽然不能做到绝对安全(比如通过浏览器开发者工具查看网络请求),但对毕业设计来说已经在业务逻辑层面实现了“购买后才能观看”的核心规则。
3.3 统一返回结果与全局异常处理
前后端分离项目最忌讳的是每个接口返回结构都不一样——有的返回裸数据、有的返回带 code 的包装、出错时有的返回 null、有的抛 500。前端 Axios 拦截器没法统一处理,维护起来极其痛苦。
我的做法是定义一个 Result 类,统一格式为 code、message、data 三部分。正常返回时 code 为 200,业务错误时 code 为对应的错误码(比如 1001 表示未登录、1002 表示无权限),系统异常时 code 为 500。Controller 层所有接口都返回 Result,成功时调用 Result.success(data),失败时抛出 BusinessException,由全局异常处理器捕获并转成 Result 返回。
全局异常处理用 @RestControllerAdvice 注解,配合 @ExceptionHandler 分别处理 BusinessException(业务异常)、MethodArgumentNotValidException(参数校验失败)、Exception(兜底)。这样带来的直接好处是:Controller 里不会有大量的 try-catch,业务逻辑只关心正常流程,异常统一交给切面处理。写论文时,“统一响应与全局异常处理”这个设计点也非常适合作为项目亮点展开。
3.4 课程搜索与列表分页
课程列表页是用户进入平台后最先接触的页面,接口设计直接影响使用体验。我提供一个课程分页查询接口:入参包含 keyword、categoryId、sortType、pageNum、pageSize;sortType 支持默认(按上架时间)、销量最高、价格从低到高、价格从高到低。实现上用 MyBatis-Plus 的 LambdaQueryWrapper 构造条件,再用 Page 对象分页。
这里有一个容易被忽略的字段:purchase_count(购买人数)。它不仅是列表页的展示字段,还参与了排序逻辑。所以每次订单支付成功,事务里要同时更新课程表的 purchase_count 加 1,并记录学习进度,这一条链路要保证一致性。
前端列表页用 Element UI 的 Pagination 组件展示分页,Axios 请求携带 pageNum 和 pageSize。新增筛选条件时只需要给接口加参数,前端传参即可,不需要新增接口,这也是设计接口时留好扩展余地的经验。
4. 前后端联调与高发问题排查
这个项目的前后端联调环节,我踩过的坑比你想象的多。下面这几个问题几乎每个做 Spring Boot + Vue 项目的人都会遇到,提前知道能省很多时间。
4.1 开发环境的跨域问题
开发时前端跑在 5173(Vite)端口,后端跑在 8080,两个端口不同,浏览器默认会拦截跨域请求。解决方案有两种:后端开启 CORS,或者前端用 proxy 代理。
推荐前端代理方案。以 Vite 为例,在 vite.config.js 里配置:
server: { proxy: { '/api': { target: 'http://localhost:8080', changeOrigin: true } } }这样前端请求 /api/xxx 会转发到后端 8080,浏览器看到的是同源请求,不会触发跨域。
为什么不推荐后端直接开 CORS?因为开发环境虽然可以靠开启 CORS 解决,但生产环境你肯定希望所有请求走 Nginx 反向代理(后面部署章节会讲),届时后端开 CORS 反而多此一举,还可能引入安全隐患。统一在代理层解决更干净。
4.2 Long 类型精度丢失
MySQL 表的 id 我用了 bigint,Java 实体类对应 Long 类型。问题来了:Long 可以表示的整数范围超过 JavaScript 的 Number 安全范围(2^53 - 1),当后端返回 id 为 19 位数字时,前端 JS 解析 JSON 会丢失精度,导致 id 最后几位变成 0。
这个问题在“获取课程详情”这种用 id 查询的场景中会引发诡异 bug:用户点击课程卡片,前端拿到错误的 id,请求详情接口得到 404。
解决方案是在后端统一配置 Jackson,将 Long 类型序列化为字符串:
@Bean public Jackson2ObjectMapperBuilderCustomizer longToStringCustomizer() { return builder -> builder.serializerByType(Long.class, ToStringSerializer.instance); }前端拿到的 id 是字符串,处理时不会丢精度,传给后端接口时用字符串也不会报错,因为 Spring MVC 能自动把字符串转回 Long。
4.3 视频上传大小限制
Spring Boot 对上传文件大小默认限制是 1MB,上传视频肯定会超。在 application.yml 中配置:
spring: servlet: multipart: max-file-size: 200MB max-request-size: 500MB这只是后端限制。如果你用 Nginx 部署,Nginx 默认限制也是 1MB,客户端会看到 413 错误。需要在 Nginx 配置中加 client_max_body_size 200m。
这是一个典型的“后端改完前端还报错”的情况,排查时记得链路同步检查。
4.4 时间字段的时区问题
MySQL 和 Java 时间因为时区不一致,经常出现“查询结果比实际时间早 8 小时”的怪象。JDBC 连接串加 serverTimezone=Asia/Shanghai 解决:
spring: datasource: url: jdbc:mysql://localhost:3306/edu_platform?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai同时在 Jackson 配置中统一日期格式,避免前端拿到不友好的时间字符串。我一般配置成:
spring.jackson.date-format=yyyy-MM-dd HH:mm:ss spring.jackson.time-zone=GMT+8一个经验:把时间问题当成编码规范来对待,所有时间字段统一由后端格式化,前端只负责展示,不要在前端做二次时区转换。
5. 部署上线与论文撰写的配合
很多同学开发完项目,到部署和写论文这一步反而最慌。其实这两件事可以合并推进,部署过程本身就是论文“系统实现与测试”章节的素材。
5.1 前后端打包与 Nginx 配置
打包部署不复杂,按顺序做就行。
后端打包:
mvn clean package -DskipTests生成的可执行 Jar 包用 java -jar 启动。如果想更稳定,用 systemd 把后端注册成服务,开机自启,日志重定向到文件。
前端构建:
npm run build构建产物在 dist 目录,把整个 dist 目录上传到服务器。用 Nginx 托管静态资源,并配置反向代理把 /api 转发到后端:
server { listen 80; server_name your_domain; root /opt/edu-platform/dist; index index.html; client_max_body_size 200m; location /api { proxy_pass http://127.0.0.1:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } location / { try_files $uri $uri/ /index.html; } location /files { alias /opt/edu-platform/upload/; } }location /files 是给视频文件访问用的。如果视频是本地存储,上传路径在 /opt/edu-platform/upload 下,通过 /files 前缀映射出去,不要直接把 upload 目录暴露到根路径。
5.2 论文结构与项目实现对照
论文结构建议这样安排:
第一章绪论:写研究背景与意义、国内外研究现状。背景可以从在线教育需求侧展开,不要写大而空的话,落脚到“为什么需要一个平台”就好。
第二章相关技术介绍:逐个介绍 Spring Boot、Vue、MySQL、MyBatis-Plus、JWT,每项技术写清楚在本项目中承担的角色。
第三章系统分析:可行性分析、需求分析、功能性需求与非功能性需求、用例图。需求分析要和第一章的“三类用户、三条流程”严格对应。
第四章系统设计:总体架构图、功能模块划分、数据库设计。数据库设计部分把表结构图和字段说明放上去,这部分要和实际建表 SQL 完全一致。
第五章系统实现:按模块贴关键代码并配上界面截图。代码不用全贴,贴最能体现代码质量的片段,比如全局异常处理、JWT 拦截器、统一返回结果、购买课程的幂等处理。
第六章系统测试:写测试用例表(功能测试、接口测试),记录测试环境、测试结果。至少要有登录、课程浏览、购买流程、视频播放、后台审核这几个核心用例的通过记录。
总结部分:说明已完成的工作、遇到的困难与解决过程、不足与展望。
写论文最大的坑是“设计文档与代码不一致”。比如论文里说用 Redis 做缓存,代码里根本没有 Redis;数据库设计表有 8 张,实际建表只建了 5 张。答辩老师随机翻页看代码和文档,发现对不上就直接扣分。我的建议是:先完成所有功能并跑通,再根据真实代码写文档,文档中的表结构直接从数据库导出,接口说明从后端注释导出,不要凭记忆写。
6. 常见问题速查与避坑心得
开发这套平台的过程中,我积累了不少容易踩坑的点,整理成一份速查表,对各阶段都有用。
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| 前端请求接口 404 | 后端路由不存在或路径写错 | 检查前端请求 URL 与后端 @RequestMapping 是否对齐 |
| 前端请求接口 500 | 后端代码异常或被全局异常处理器捕获 | 查看后端日志,定位堆栈信息 |
| 登录后页面刷新退出登录 | 用户信息只存在内存或组件内 | 把 token 和用户信息存到 localStorage,页面初始化时读取 |
| 视频上传一直失败 | 超过 Spring Boot 或 Nginx 上传大小限制 | 同步检查 multipart 配置与 Nginx client_max_body_size |
| 视频地址能访问但页面 404 | 前端路由没有配置该地址 | Nginx location / 加 try_files 规则,或确认路由配置 |
| 列表页数据正常但详情页报错 | Long 精度丢失导致 id 不对 | 全局将 Long 序列化为字符串 |
| 时间显示差 8 小时 | MySQL 时区与 Java 不一致 | 连接串加 serverTimezone=Asia/Shanghai |
| 购买课程后仍显示未购买 | 订单状态没有更新或视频权限判断没读取订单 | 检查支付回调后的订单状态,及播放接口的权限查询逻辑 |
| 后台管理页面无法进入 | 前端路由守卫或后端拦截器未配置 | 检查路由 meta 的 role 字段与后端 token 中 role 是否一致 |
还有一些开发阶段的通用心得。第一,先跑通核心闭环再完善细节。我一开始就急着做了很多展示页面,结果核心的“课程购买-视频播放”链路还很脆弱,这是本末倒置。控制好 Main 路径,第一遍迭代只保证注册登录、课程列表、课程详情、购买、视频播放、后台审核这一条链路能跑通,之后再加附属功能。第二,接口设计好后先写接口文档(哪怕只是 Markdown 表格),前后端各做各的,不要边写边改。第三,Git 版本控制一定要用,每次完成一个功能模块就 commit,出现问题能快速回滚,论文里也可以提到项目管理工具的使用。
最后再分享一个关于调试的小技巧。前后端联调时开启浏览器开发者工具的 Network 面板,重点关注请求的请求头(Authorization 是否带上)、响应状态码、响应体中的 code 字段。对接不上的问题九成能从这三个地方直接看出来。我见过太多同学盯着页面报错信息猜半天,其实网络请求里已经把答案写得很清楚了。