1. 校园视频平台,毕设选它到底值不值
每年毕业设计选题季,我都会被问到同一个问题:“学长,我想做个视频平台,用Spring Boot加Vue行不行?”我的回答通常很直接:行,而且这个组合非常稳。你看到的这个标题“基于SpringBoot + Vue校园系视频平台系统”几乎就是这类题目的标准配置,它之所以能成为计算机毕业设计里的常青树,原因其实不复杂:一方面,前后端分离的架构是目前企业开发的主流形态,面试官看着顺眼;另一方面,Spring Boot加Vue的技术栈覆盖了后端、前端、数据库、权限、文件存储、视频流处理等多个维度,足够撑起一篇有分量的毕业论文。
但我要先给你泼一盆冷水:这类项目最大的坑不在技术本身,而在于很多同学图省事直接去下载一个“完美源码”,结果答辩的时候连项目结构都讲不明白,被老师追问几个细节就直接卡壳。我见过太多这种翻车现场了。所以这篇博文我不会只给你贴一段代码让你抄,我会把整个校园视频平台从零搭建的思路、数据库设计、核心功能实现到答辩准备,一条龙讲清楚。不管你最终是打算自己敲一遍,还是在现成源码基础上改造,你都需要理解这个系统每一层到底在干什么、为什么这么做。
这个项目适合谁?三种人最爱:第一,Java方向的学生,想通过一个项目把Spring Boot、MyBatis-Plus、JWT这些知识点串联起来;第二,前端方向的学生,想练手Vue全家桶、Element UI组件库、视频播放器集成这些实战技能;第三,就是那些已经工作但想巩固全栈能力的开发新人。无论你是哪一类,读完这篇文章,你应该能回答这几个问题:数据库为什么这么建表?视频为什么走m3u8切片而不是直接传MP4?跨域问题怎么处理?部署的时候需要注意什么?
2. 整体设计思路与技术选型拆解
2.1 前后端分离,到底“分”的是什么
校园视频平台这类系统,核心用户是学生和教师两类角色,功能无外乎浏览视频、上传视频、搜索、评论、收藏、个人中心。功能看着简单,但你如果真做成一个单体项目——也就是前端页面由Thymeleaf模板直接渲染,后端一把梭——那这个毕设的技术含量就大打折扣了。前后端分离不是说把代码分成两个文件夹就叫分离,它的本质是两个独立的工程,通过HTTP接口通信。前端工程跑在Node环境下,负责渲染页面、路由跳转、状态管理;后端工程跑在Tomcat内嵌容器里,只负责提供RESTful API和业务逻辑处理。
我为什么建议你一定要做前后端分离?第一个理由是就业导向。你去翻一下现在的Java岗位JD,十个有八个要求“熟悉前后端分离开发模式”,你毕设里体现了这个,面试的时候就有话可讲。第二个理由是调试体验。前端用Vite或者Vue CLI启动后实时热更新,改一行代码浏览器立刻刷新,后端用Spring Boot DevTools也能做到改代码自动重启,开发效率比传统JSP高了一个量级。第三个理由是部署灵活,前端构建出静态资源后丢到Nginx,后端打成Jar包跑在服务器上,两边互不干扰。
当然,前后端分离也有代价。最典型的就是跨域问题。浏览器为了安全,默认不允许一个端口上的页面去请求另一个端口上的接口。你在Vue开发服务器(默认8080端口)去访问Spring Boot(默认8081端口)的时候,就会遇到CORS报错。解决思路一般有两种:要么在后端配置CORS允许跨域,要么通过Nginx做反向代理,把 /api 前缀的请求转发到后端服务器。我个人的习惯是后端开发阶段用@CrossOrigin注解或者一个CorsConfig配置类搞定,上线阶段再交给Nginx处理。这样分工明确,也符合企业实践。
2.2 Spring Boot版本怎么选,高了真的会出问题
热搜词里有一条是“springboot版本太高”,这个坑我还真踩过。很多同学在毕设开始前搜教程,看到最新的Spring Boot 3.x版本就跃跃欲试,结果项目搭到一半发现这个依赖不支持、那个配置变了,心态直接崩了。我的建议很简单:做毕设,用Spring Boot 2.7.x这个稳定版本就好。为什么?因为2.x版本生态成熟,网上能找到的教程、遇到过的坑、开源社区的回答数量都是最多的,你遇到问题基本都能搜到解决方案。3.x版本虽然性能更好,但它要求Java 17起步,而且很多老版本的第三方库(比如某些MyBatis-Plus版本、某些OSS SDK)没有及时适配,会带来一系列兼容性麻烦。
这里给你一个非常具体的参考配置:JDK建议用1.8或者11,如果你用2.7.18这个终极版,用JDK 8完全没问题。Maven仓库坐标大概是这样的:
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.7.18</version> <relativePath/> </parent>依赖方面,视频平台系统常用到的起步依赖有spring-boot-starter-web(Web开发)、spring-boot-starter-data-jpa或者MyBatis(数据访问)、spring-boot-starter-security(安全控制,可选)、spring-boot-starter-validation(参数校验)、spring-boot-starter-data-redis(缓存,可选)。这里我要多说一句:Spring Boot版本高低的本质不是“新就好用”,而是“生态追踪”。你做毕设的目的是在有限时间内把项目完整跑通并讲明白,没必要为了追新版本给自己增加排障碍的时间成本。等到你工作了,在真实项目中再按团队的技术规范来选择版本也不迟。
2.3 Vue怎么选:Vue 2还是Vue 3,环境怎么配
热词里关于Vue的搜索量很大,特别是“vue安装及环境配置”和“idea开发vue项目”。如果你Spring Boot选了2.7.x,那前端这边我建议直接用Vue 3加Vite构建工具。别犹豫,Vue 3从2020年发布到现在已经非常稳定了,而且组合式API(Composition API)会让你写代码的逻辑更清晰,答辩的时候也更有内容可以讲。Vite比Vue CLI(Webpack)启动速度快得多,大型项目冷启动从几十秒压缩到一两秒,开发体验完全不同。
Vue环境配置这块,我把步骤给你理一下。第一步,去Node官网下载Node.js 16或者18的长期支持版本,安装的时候一路默认就行,注意安装完成后要重新开一个终端窗口,敲node -v和npm -v验证一下。第二步,用npm install -g @vue/cli或者直接创建Vite项目。我更推荐Vite方式:
npm create vite@latest campus-video -- --template vue cd campus-video npm install npm run dev第三步就是装UI组件库。做后台管理类的页面,Element Plus几乎是首选。执行npm install element-plus,然后在main.js里引入:
import { createApp } from 'vue' import ElementPlus from 'element-plus' import 'element-plus/dist/index.css' import App from './App.vue' const app = createApp(App) app.use(ElementPlus) app.mount('#app')这里注意一点:Element Plus的按需导入虽然能减少打包体积,但配置起来稍麻烦,毕设项目我建议直接全量引入,代码好写,维护也没压力。Vue装完之后,再配一下Vue Router(路由)和Pinia(状态管理),这两个是Vue生态里的标配,视频平台的多页面跳转和用户登录状态管理都得靠它们。
2.4 视频播放方案:m3u8切片才是正解
做视频平台,绕不开一个问题:视频怎么播?最朴素的做法是后端存一个MP4文件的URL,前端用
具体怎么操作?后端用FFmpeg工具把用户上传的视频转码成m3u8格式。我用一个真实的命令行给你演示:
ffmpeg -i input.mp4 -profile:v baseline -level 3.0 -start_number 0 -hls_time 10 -hls_list_size 0 -f hls output.m3u8这个命令的意思是:读取input.mp4,把视频切成长度约10秒一片的ts分片,并生成output.m3u8索引文件。参数-hls_time 10控制每个切片时长,-hls_list_size 0表示不限制分片数量。如果是1080p高清视频,还可以加上-c:v libx264 -crf 23 -preset medium压一下码率,否则转出来的文件体积会很夸张。
前端怎么播放m3u8?如果你用的Vue,我推荐video.js加videojs-contrib-hls插件。注意一点,如果你用Vue 3,原生video.js的HLS支持在部分浏览器上不够完整,需要额外处理。一个比较省事的做法是安装hls.js这个库,它兼容性好,而且社区活跃。前端页面里引入hls.js后,判断当前浏览器是否原生支持HLS(Safari),不支持就走hls.js加载:
if (video.canPlayType('application/vnd.apple.mpegurl')) { video.src = videoUrl } else if (Hls.isSupported()) { const hls = new Hls() hls.loadSource(videoUrl) hls.attachMedia(video) }这样用户看视频就非常顺滑了。锅里的肉始终有,视频永远不会等你。
3. 数据库设计与核心表结构
3.1 从需求倒推建表:五张核心表不能少
数据库设计是答辩时老师必问的环节,一问一个准。你要能说清楚每个表的字段为什么这么设计。校园视频平台我建议至少包含这几张核心表:用户表(user)、视频表(video)、分类表(category)、评论表(comment)、收藏表(favorite)。如果还想做得更完整,还可以加一张视频点赞表(video_like)或者播放记录表(play_history)。
用户表字段不复杂:id主键、username用户名、password密码、nickname昵称、avatar头像地址、role角色(0学生、1教师)、create_time创建时间。密码不能明文存,一定用BCrypt加密后存入数据库。Spring Security或者Spring Boot自带的BCryptPasswordEncoder就能干这个事。这里要提一句:很多人图省事建表时不加create_time、update_time,等到后面做“最近发布”和排序功能时又回来补,开学季这种返工我见得太多了,建议你一上来就把这两个字段加上。
视频表是核心中的核心,设计的时候要想清楚:一个视频不仅要有基本元数据,更要有存储地址、封面图、转码状态、播放量这些业务字段。基本结构如下:
- id:视频唯一标识
- user_id:上传用户ID,外键关联用户表
- category_id:所属分类ID
- title:视频标题
- description:简介
- cover_url:封面图地址
- video_url:原始视频地址(或m3u8地址)
- duration:视频时长(秒)
- play_count:播放次数
- status:状态(0转码中、1已发布、2下架)
视频表里有两个设计细节值得留意。第一,play_count这种字段我建议直接冗余在视频表里,而不是通过统计播放记录表来count,因为count查询在数据量大时非常慢,而且视频平台的播放次数本身就是个展示型数据,实时性要求没那么高,加一个字段更新即可。第二,video_url我在生产环境中存的其实是m3u8文件所在的目录路径,前端拼URL时动态拼前缀,这样换域名或者换OSS的时候不用改数据库。这个思路是从实际项目里总结出来的,虽然毕设阶段可能感觉不到好处,但答辩时可以主动讲出来,会显得你考虑问题很周全。
3.2 评论表和收藏表,别小看它们的设计
评论表设计最重要的点是自关联。因为评论要支持楼中楼回复,最简单的方式是加一个parent_id字段,默认为0表示顶层评论,不为0则表示回复的是哪条评论。再搭配reply_user_id记录被回复人的ID,前端做@功能的时候就非常轻松。表结构核心字段:id、video_id、user_id、parent_id、reply_user_id、content、create_time。
收藏表是典型的多对多关系拆解。用户和视频是多对多,一个用户可以收藏多个视频,一个视频也可以被多个用户收藏。为了描述这个关系,必须拆一张关联表favorite:id、user_id、video_id、create_time。查询某个用户是否已收藏某视频,只需要查这张表有没有对应记录。删除收藏就是删记录。这里的唯一性约束要加上,联合唯一索引(user_id, video_id)确保同一用户对同一个视频不会产生重复收藏记录。
分类表就很简单了:id、name、sort。毕设阶段可以在启动时手动插入几条数据,比如编程语言、软件教程、考研考公、校园活动、其他等。这一块没什么技术含量,但注意要在文档里写清楚分类数据是如何初始化的,答辩时有细节可讲。
3.3 数据库增删改查的规范写法
热词里有“数据库增删改查”,这个基础功夫还是要练扎实。你用JPA还是MyBatis-Plus?我推荐后者。MyBatis-Plus在国内使用率极高,它把单表的增删改查简化到几乎不需要写SQL。引入依赖后用BaseMapper 接口继承就能获得insert、deleteById、selectById、updateById这些方法,再配合lambdaQueryWrapper做条件查询,开发效率高到飞起。
举个例子,写一个分页查询视频列表的接口:
public PageResult<Video> getVideoList(int page, int size, Long categoryId) { Page<Video> pageInfo = new Page<>(page, size); LambdaQueryWrapper<Video> wrapper = new LambdaQueryWrapper<>(); if (categoryId != null) { wrapper.eq(Video::getCategoryId, categoryId); } wrapper.orderByDesc(Video::getCreateTime); videoMapper.selectPage(pageInfo, wrapper); return new PageResult<>(pageInfo.getRecords(), pageInfo.getTotal()); }注意几个细节:字段使用Lambda表达式,既安全又省得写字符串导致拼写错误;排序用orderByDesc按创建时间倒序;status要加过滤器,只查出已发布状态,转码中的视频还没生成完就不能放出来。这套写法在整个项目里可以复用,几乎所有的列表查询都是同一个套路,写完一个功能其他的都是“复制改”。
4. 核心功能实操与代码讲解
4.1 用户登录注册与JWT权限控制
校园视频平台的登录注册功能,我强烈建议用JWT(JSON Web Token)方案,而不是传统的Session。因为前后端分离后,后端服务不再保存用户登录状态,而是签发一个令牌给前端。前端每次请求时把这个令牌放在HTTP请求头里,后端校验通过就放行。这样做的好处是服务无状态,方便以后做多实例部署。
具体流程:前端用户在登录页输入用户名密码,后端接收后先用BCryptPasswordEncoder去比对密码是否匹配。匹配成功就用userId、用户名、角色等信息生成一个JWT令牌,有效期设个24小时就行,设置太长不安全,太短则用户频繁需要重新登录,体验不好。后端取值校验可以写一个拦截器或者用一个简单的OncePerRequestFilter:
public class JwtAuthenticationFilter extends OncePerRequestFilter { @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException { String token = request.getHeader("Authorization"); if (StringUtils.hasText(token) && token.startsWith("Bearer ")) { token = token.substring(7); // 解析token,获得userId,存入request属性 Long userId = JwtUtil.parseToken(token); request.setAttribute("userId", userId); } chain.doFilter(request, response); } }注册的流程就更直接了:前端提交username、password、nickname,后端先查一下username是否已存在,存在就返回“用户名已被注册”;不存在就进行密码加密,然后插入用户表。注意这一步的参数校验不能省略,用户名和密码都不能为空,密码长度至少6位,使用@Valid注解加DTO校验就能做到。
4.2 视频上传、转码与访问路径设计
视频上传是这个项目的重头戏。用户选中本地视频文件后,前端先把文件对象通过axios传到后端,后端接收MultipartFile,将其写入磁盘的指定目录。目录结构建议按日期分层,比如 /data/video/20240401/ 下面存放当天的视频文件,这样以后清理旧数据或者做存储分析都很方便。核心代码:
@PostMapping("/api/video/upload") public Result upload(@RequestParam("file") MultipartFile file, @RequestParam Long categoryId, HttpServletRequest request) { if (file.isEmpty()) { return Result.error("文件不能为空"); } // 校验文件类型,只允许mp4等常见格式 String originalFilename = file.getOriginalFilename(); String suffix = originalFilename.substring(originalFilename.lastIndexOf(".")); if (!suffix.equals(".mp4")) { return Result.error("仅支持MP4格式"); } // 生成唯一文件名,避免冲突 String fileName = UUID.randomUUID().toString().replace("-", "") + suffix; // 保存原始文件到临时目录 file.transferTo(new File(uploadDir + fileName)); // 存入数据库,status设为0(转码中) // 异步调用FFmpeg转码脚本 transcodeService.transcodeToHls(fileName); return Result.success("上传成功"); }上传后的转码动作一定要用异步线程池去执行,不能堵在主请求里。因为转码可能要几十秒甚至几分钟,用户不可能在页面上干等那么久。用@Async注解标记一个转码方法,或者手动丢进线程池执行都可以。转码完成后要更新数据库中的video_url字段,并把status改成1。
访问路径设计上有个容易踩坑的点:如果把视频文件放在Spring Boot的static目录下,虽然开发和测试省事,但生产环境极不推荐。更好的方案是单独配置一个资源映射,让 /videos/** 前缀映射到磁盘上的存储目录。这样视频文件和应用程序完全解耦,将来换服务器、扩展存储都有余地。配置方式也简单:
spring: mvc: static-path-pattern: /videos/** resources: static-locations: file:D:/data/video/4.3 前端视频播放器页面封装
前端视频播放页需要做两件事:视频详情展示加播放器集成。页面布局一般是页面左上方是video播放器,下面是标题、作者、播放量、简介、评论区域。播放器我建议封装成一个独立组件,这样视频详情页、用户个人页、后台预览都能复用。
封装VideoPlayer.vue组件时,props接收videoUrl,watch监听url变化后重新加载播放器。这里有一个非常关键的cleanup操作:组件卸载时一定要调用hls.destroy()方法,否则组件来回切换会内存泄漏,播放几次之后页面明显变卡。很多人的Vue项目写着写着就卡成PPT,十有八九就是没有销毁播放器实例。
评论区域也别做得太简单。发评论的时候需要把当前视频ID、用户ID、内容一起提交,提交成功后刷新评论列表。回复评论时,除了评论内容还要传parent_id和reply_user_id。前端为了方便,回复输入框就放在每条评论下方,点击“回复”按钮时把这个评论的信息填入表单,输入完成后POST到同一接口。这样评论楼层的数据结构在后端统一处理,前端代码也会清爽很多。
4.4 搜索功能怎么做才不low
搜索功能如果只是查出标题里包含关键字的视频,那太初级了。至少要做到两点:第一,模糊匹配标题和简介两个字段;第二,搜索后能按相关度或者时间排序。用一个更专业的词来描述,这就是“简单站内搜索”。当然,如果只是用MySQL的LIKE来做,数据量在几千条时也没什么问题:
wrapper.like(Video::getTitle, keyword) .or(wrapper.like(Video::getDescription, keyword));想要更进一步,给视频表加一个“热度”字段,用播放量的对数、点赞数、评论数组合成一个热度值,搜索时把热度作为排序条件之一。毕设阶段如果不想引入Elasticsearch(说实话,引入ES对毕设有点过度设计,而且答辩时候容易被追问集群、分片这些问题),那就用MySQL的LIKE加上一个排序字段来撑着,已经够用了。
5. 常见问题排查与避坑手册
5.1 Spring Boot版本过高引发的连锁地震
前面我提了“springboot版本太高”,这里把具体惨状展开一下。有同学用Spring Boot 3.2去整合MyBatis-Plus,结果发现默认的mybatis-plus-boot-starter版本下无法启动,报错多是“NoSuchMethodError”或者“Failed to instantiate”。为什么?因为Spring Boot 3.x是基于Jakarta EE规范的,javax.servlet命名空间变成了jakarta.servlet。第三方库如果不更新到适配版本,就会因为包名对不上而启动失败。
排错思路也不难:看一眼控制台的错误日志,找第一行Caused by的位置,绝大多数时候就是某个依赖版本不对。别再全网搜“为什么报错NoSuchMethodError”了,先确认你Spring Boot版本是多少,再回去看对应库的兼容矩阵。方法总比问题多,但前提是你得知道问题出在哪一层。
5.2 前后端联调时的CORS跨域与接口对接
跨域报错长这样:CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. 此时我用过的比较省心的解决方案是后端写一个配置类:
@Configuration public class CorsConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOriginPatterns("*") .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") .allowedHeaders("*") .allowCredentials(true) .maxAge(3600); } }这段配置允许所有来源跨域访问。毕设阶段这样做没问题,但我要提醒一句,生产环境allowedOriginPatterns不应该写“*”,而应该只写前端的域名。接口联调时还有另一个高频问题:前端传参格式和后端接收不一致,要么用qs库将对象转成URLSearchParams格式,要么后端统一用@RequestBody接收JSON,选择一套方案然后全项目统一,不要一会儿用表单格式一会儿用JSON。保持代码风格的一致性,是让项目不“廉价”的关键。
5.3 视频播放黑屏或转码卡住的排查清单
视频上传后页面黑屏、播放不出画面,这个问题的排查路径很固定。第一步看FFmpeg执行的日志,看转码有没有完成、m3u8文件是否生成。第二步看m3u8文件里的ts分片引用路径是相对路径还是绝对路径,如果引用路径里写死了本机磁盘路径,前端自然访问不到。第三步看前端播放时的Network面板,点击那个ts分片请求看返回状态码,如果404就说明路径拼接有问题;如果200却还黑屏,就要检查CORS了,因为m3u8和ts文件序列默认是跨域的,服务器需要响应正确的CORS头。
再一个常见问题是FFmpeg命令执行权限不足导致转码失败。Windows下如果装的是Linux子系统或者使用了某些压缩版FFmpeg,路径配置错就会静默失败。建议转码方法里加异常日志的记录,把FFmpeg的完整输出写到日志文件里,排查时直接打开看具体是哪一步失败,能省下一大堆时间。
5.4 数据库连接不上和中文乱码
数据库连不上,翻来覆去就那几个原因:密码写错了、端口写错了、IP写了localhost但MySQL绑定的地址不是、MySQL服务没启动。你在application.yml里配置数据源时,一定要加上useUnicode=true&characterEncoding=utf8,防止中文乱码。连接MySQL 8.x时,驱动类要写 com.mysql.cj.jdbc.Driver,写旧的com.mysql.jdbc.Driver虽然有时候不报错,但会有警告,答辩时被看到不专业。完整的配置参考:
spring: datasource: url: jdbc:mysql://localhost:3306/campus_video?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai username: root password: yourpassword driver-class-name: com.mysql.cj.jdbc.Driver建表时统一utf8mb4字符集,不要用默认的latin1。utf8mb4比utf8多支持了emoji字符集,评论功能大家发个表情,也不至于乱码。
6. 交付三件套:源码、数据库、文档怎么准备
做毕业设计,老师最看重的不只是功能跑通,更是你交上来的东西“像不像一个完整的工程”。标题里强调“源码+数据库+文档”这三个交付物,我按照经验帮你把每个部分应该长什么样理清楚。
源码这块,前端和后端要分两个独立文件夹,每个下面都要有清晰的README.md文件。README里至少要包含:项目简介、技术栈列表、启动步骤(怎么装依赖、怎么初始化数据库、怎么启动后端、怎么启动前端)、默认账号密码。不要小看这个README,答辩时老师很可能直接打开你的项目,按你的文档一步步操作,你的文档写得越顺畅,老师心里的分就给得越高。后端项目的代码结构以常见的controller、service、mapper、entity、config、utils分层为佳,每层的职责要单一,不要在Controller里写一大段业务逻辑,不要在Service里拼接HTML(这个坏毛病我在很多下载的源码里见过,看见就想骂人)。
数据库交付物有两种形式。第一种是创建一个database.sql文件,包含建表语句和初始化的测试数据。但有些老师会直接要求你提供数据库脚本,所以我建议你把建库、建表、插入初始数据分成三段SQL,用注释分隔,方便老师按顺序执行。第二种是使用数据库管理工具导出一个备份文件。我个人推荐前者,因为.sql文件用文本编辑器就能查看,而且兼容性最好,老师不管是MySQL 5.7还是8.0都能顺利导入。
文档是重头戏,一般包括开题报告、需求分析、系统设计、数据库设计、系统实现、总结致谢这些章节。系统设计中的功能模块图、流程图,不要画那种又花哨又空洞的图,要跟你的代码实际实现一一对应。数据库设计部分把ER图和表结构说明贴全,每个字段的中文含义写清楚。系统实现部分不要期望口头描述能糊弄过去,配关键功能截图,把核心代码段贴上去并逐段解释。别忘了准备一个“系统演示说明”,讲清楚演示时先展示什么、再展示什么、如果用测试账号登录后能做什么操作。
另外提醒一个很多人会忽略的细节:项目里的配置文件不要出现你自己的绝对路径,比如D盘某处文件夹。所有路径尽量用相对路径或者是可从application.yml里配置的可变量,这体现的是工程素养。前端所有请求地址可以单独抽出一个api.js文件,统一管理baseURL,不要在每个组件里硬编码localhost加端口。如果交作业时老师换个机器运行,硬编码的地址就会让人跑不起来,印象分直接腰斩。
7. 答辩准备与演示技巧,别让好项目毁在嘴上
其实代码写完了,项目能跑起来,你的毕设已经完成80%了。剩下那20%是答辩。很多技术很强的同学最后栽在答辩环节,不是因为他没做出来,而是他没法把自己的工作清晰地传达给老师。我建议你答辩前准备一个“演示脚本”,严格按顺序过一遍:先登录系统(展示正常登录、错误密码登录),接着浏览首页视频列表(展示分类筛选、搜索功能),然后点开一个视频播放(展示播放器加载、评论展示),接着发一条评论(展示评论功能、权限校验),再切换用户或者进入后台(展示管理员和普通用户的不同权限),最后讲数据库的几个关键表和字段设计。
演示过程中最容易被提问的知识点,十有八九集中在这几个位置:事务管理是怎么做的——比如用户点赞时,点赞表插入记录和视频表点赞数加一,这两步是不是在一个事务里;缓存用没用、为什么用;分页是怎么实现的;上传视频过程中如果网络断开了怎么处理(这里可以坦诚讲清楚,毕设阶段有考虑但不完善,然后说出你的改进思路,老师反而会觉得你思路清晰)。当年我见过一个学生,被问到缓存时说“还没做,但这块可以优化,计划在查询视频列表时用Redis缓存热数据”,老师笑了笑直接放过。关键不在于你要把所有功能做到完美,而在于你要能指明系统的边界和后续扩展方向。这比空吹牛强一万倍。
8. 我踩过的几个坑,提前告诉你
最后分享几条实操中积累下来的经验,都是常规文档不会写的。第一,前端依赖安装时,npm经常因为网络问题卡住或者慢到怀疑人生。可以先执行npm config set registry https://registry.npmmirror.com 换成国内镜像,这会让你省下大半天的安装等待时间。第二,Maven同样有镜像问题,在Maven的settings.xml里配置阿里云镜像,下载依赖的速度差别巨大。第三,校园视频平台如果视频量不大(几百个视频),没必要单独用OSS存储,放到本地磁盘就够,但如果视频量真的上来了,记得把存储策略调整成对象存储加CDN加速,这些答辩时都可以当成“系统演进方向”讲出来。
还有一点是关于部署的。你应该至少学会把整个项目在本地以“生产模式”跑一遍。也就是前端执行npm run build生成dist静态文件,然后用Nginx托管,再单独启动后端Jar包。这个过程走通后,你对整个前后端交互的理解会产生质的变化。很多人平时开发依赖Vite的代理转发,从来没有意识到真实环境里静态资源和API的分离是什么状态。我确信你走完这一步再写总结时,会写出很多别人写不出的东西。
如果你正在做这个题目,做完之后你会发现,这个项目表面上是一套视频平台系统,实际上它把Java后端开发、前端工程化、数据库设计、多媒体处理、文档编写的能力全都串了一遍。这就是它作为毕业设计的最大价值——它逼着你把大学四年学的东西从碎片变成体系。哪怕你以后不从事Web开发,这套从需求分析到设计到实现的完整闭环思维,走到哪里都用得上。祝你在做毕设的这段时间里,少熬夜,多产出,答辩顺利。