1. 项目概述与整体设计思路
1.1 这个项目解决的是什么问题
古典舞在线交流平台,说直白点,就是把线下的舞蹈圈子搬到网上去。以前大家学古典舞、聊古典舞,要么靠线下培训班,要么靠微信群传视频,信息散、体验碎、找人请教还要看缘分。这个项目想做的事情,是给古典舞爱好者一个相对完整的线上据点——能看作品、能发视频、能评论交流、能关注喜欢的舞者,还能按舞种和难度找内容。
从毕设角度讲,这个选题踩中的点比较巧。一方面古典舞是一个细分领域,天然带文化属性,评委愿意认可选题的差异化;另一方面它本质上还是一个社区型内容平台,用户、视频、帖子、评论、点赞、收藏这一整套数据模型非常典型,能把Java后端开发的核心知识点都串起来。对准备找工作的同学来说,这类项目写在简历上比纯粹的学生管理系统、图书管理系统有辨识度得多。
适合什么人参考也很明确:正在做Java毕业设计、需要技术方案参考的同学;想自己动手做一个完整前后端项目的初学者;以及想了解Spring Boot社区类项目如何从零搭建的开发者。
1.2 核心功能拆解与需求分析
一个在线交流平台,说透了就是两条线:内容生产和内容消费。你站在用户角度想,打开这个平台最想做的是什么事?逛一逛,看看别人跳得怎么样,有感觉就点个赞;自己录了一段练习视频,想发出来让同好指点一下;看见一个作品想评论两句,或者私信请教动作细节;遇到感兴趣的人,想关注他的后续更新。这些动作对应到后端功能模块上,就是非常标准的用户体系、视频模块、交流模块、社交关系模块。
用户模块涉及注册、登录、个人信息管理。这里要注意,毕设项目不需要一上来就搞OAuth2.0、第三方登录那一套,手机号/邮箱加密码注册就够了。但密码不能明文存,至少用MD5加盐或者BCrypt做哈希处理,很多同学在这个细节上栽过跟头,答辩时被问密码安全策略一问三不知,很尴尬。
视频模块是平台的核心,包含视频上传、转码处理、播放列表、封面截图等。这里有一个现实问题:视频文件和普通文本不一样,涉及存储、访问、防盗链等多个层面。我在项目里采用的是本地磁盘存储方案,也就是把视频文件保存到服务器指定目录,数据库里存访问路径。这个方案对毕设来说是最合理的,因为不需要额外购买云存储,也不涉及复杂的OSS接口对接,把所有逻辑控制在自己手里,出现问题也好排查。
交流模块包括发帖、评论、回复、点赞、收藏。设计时要把帖子和评论分开建表,点赞和收藏建议设计成关联表而不是在帖子表里加一个数字字段,因为用户需要知道“我是否点过赞”,必须有独立的记录才能支撑这个查询。
社交模块就是关注/粉丝关系。技术上就是一张关注关系表,加一个关注时间字段,查询“我关注的人发布了什么视频”时做一次关联查询。这个功能看起来简单,但涉及分页查询性能优化,是很好的面试谈资。
2. 技术方案选型与架构设计
2.1 为什么选Spring Boot而不是SSH或SSM
现在讨论Java后端技术栈,Spring Boot基本已经是事实标准。它和传统SSM框架相比,最大的变化是“约定大于配置”,不需要写一堆XML配置文件,内嵌Tomcat容器,打成一个Jar包就能直接跑。对毕业设计来说,好处非常实际:省下大量配置时间,把精力花在业务逻辑上;自带Spring MVC和自动配置,开发效率高一个档次;社区资料丰富,遇到问题搜一下就有一堆解决方案。
当然,用Spring Boot不等于不做分层。我实际项目里的架构是经典的三层结构:Controller层负责接口接收和参数校验,Service层写业务逻辑,Mapper层(基于MyBatis-Plus)直接和数据库打交道。中间再加一个entity包放数据库实体类,dto包放接口传输对象。这样分层的好处是,答辩的时候老师问你某个功能怎么实现的,你可以很清晰地说出请求从Controller进来之后到Mapper的调用链路,逻辑非常清楚。
2.2 数据存储与数据库表结构设计
数据库是这类项目的重头戏,因为所有功能最终都落到表的关联查询上。我建表时遵循了一个原则:宁可表多一点,也不要在一个表里塞一大堆冗余字段。
用户表是最基础的,字段包含用户ID、用户名、密码哈希、昵称、头像路径、个人简介、用户角色(普通用户/管理员)、注册时间。这里我额外加了一个状态字段,用来标记账号是否被封禁,虽然毕设阶段不一定用得上,但设计上考虑到了。
视频表包含视频ID、发布者ID(外键关联用户表)、标题、视频描述、视频存储路径、封面图路径、播放量、点赞数、收藏数、分类ID(对应古典舞的不同风格,比如扇舞、剑舞、水袖舞等)、难度等级(初级、中级、高级)、发布时间。注意播放量是一个高频更新的字段,不要每次播放都去UPDATE,我在项目里是攒着批量更新,或者直接用Redis做计数,定期同步到MySQL。
帖子表对应交流区的文字内容,关键字段包括帖子ID、作者ID、标题、内容、板块分类(这里有朋友问要不要分板块,我建议分,比如“练功打卡”、“舞剧资讯”、“动作技巧讨论”,既能丰富页面展示层次,也方便做条件查询)、置顶状态、创建时间。
评论表要想清楚层级关系。我采用的是父评论ID递归方案:一级评论父ID为0,回复某条评论时带上父ID。这样接口返回时可以根据父ID做嵌套组装,实现楼中楼效果,比每次都关联查询省事得多。
这里我整理了一张核心表结构清单,供参考:
| 表名 | 核心字段 | 关联关系 |
|---|---|---|
| t_user | id, username, password, nickname, avatar, role, status | 被视频表、帖子表关联 |
| t_video | id, user_id, title, cover_url, video_url, category, level, like_count | 关联用户表、点赞表 |
| t_post | id, user_id, title, content, section, top_flag | 关联用户表、评论表 |
| t_comment | id, user_id, post_id, video_id, parent_id, content | 自关联 + 关联内容表 |
| t_like_record | id, user_id, target_type, target_id, create_time | 多态关联设计 |
| t_follow | id, user_id, follow_user_id, create_time | 用户关联用户 |
点赞记录为什么要设计target_type和target_id?因为用户既可能给视频点赞,也可能给帖子点赞,甚至给评论点赞。如果每种点赞建一个表,后面加新功能就要加表,很麻烦。用一个多态字段,不管给什么内容点赞都往这一张表插记录,查的时候带类型条件和ID条件就行。这是我在实际项目里特别满意的设计之一,代码上还省了一大截。
2.3 视频资源如何处理:本地存储方案
视频上传是这个项目里最容易出问题的环节,没有之一。网上很多教程都会告诉你接云存储,比如阿里云OSS或者七牛云,但这些服务要么要实名认证,要么涉及付费,对毕设来说不是最优解。
我的方案是:本地磁盘存储 + Nginx反向代理访问。具体来说,后端接收MultipartFile文件后,按照日期创建目录结构,比如/data/classical_dance/videos/2025/11/21/,文件名用UUID拼接原始后缀名,避免重名。视频保存后,数据库里记录的是相对路径,比如/upload/videos/2025/11/21/uuid.mp4,前端通过这个相对路径拼接服务器地址来访问。
这里有一个关键细节:Spring Boot默认上传文件大小限制是1MB,如果不改配置,一个30秒的舞蹈视频直接报错。我当时的做法是在application.yml里加上两个关键配置:
spring: servlet: multipart: max-file-size: 200MB max-request-size: 200MB同时还需要配置静态资源映射,告诉Spring Boot哪些URL前缀要映射到哪个本地目录:
@Configuration public class WebConfig implements WebMvcConfigurer { @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/upload/**") .addResourceHandler("/data/classical_dance/**") .setCachePeriod(3600); } }为什么要用UUID重命名?因为原文件名可能是中文名、包含非法字符、或者两个用户传了相同文件名的视频,直接存原文件名会带来各种边界问题。UUID方案虽然可读性差一点,但保证唯一且稳定。
3. 核心模块实现与关键细节
3.1 用户注册登录与权限控制
用户模块看起来简单,但权限控制做不好,后面所有模块都要返工。我用的是Spring Security + JWT(JSON Web Token)这套组合方案。
先说JWT。传统Session方案在单体应用里完全够用,但JWT的好处是无状态,服务端不用存Session记录,用户登录后拿到一个Token,后续每次请求在请求头里带上这个Token,服务端验签通过了就认为是合法请求。对讲求前后端分离的毕设项目来说,JWT更贴合实际的开发模式。
用户注册时的密码处理,我推荐使用Spring Security自带的BCryptPasswordEncoder,而不是自己写MD5。原因很简单:BCrypt自动生成随机盐,相同密码加密后的结果每次都不一样,能有效防止彩虹表攻击。代码就一行:
String encodedPassword = passwordEncoder.encode(user.getPassword());登录接口校验密码时使用matches方法,再把生成的Token返回给前端。前端拿到Token存在本地存储里,之后每次请求在Axios拦截器里统一加上Authorization: Bearer ${token}请求头。
权限控制上,我没有精细到按钮级别,只区分了普通用户和管理员两种角色。管理员可以删除违规视频、帖子、封禁用户,普通用户只能操作自己的内容。实现方式是写一个自定义拦截器,读取Token中的角色信息,判断当前请求的接口是否允许该角色访问。这个逻辑放在一个切面里统一处理,比在每个Controller方法里手写判断优雅得多。
3.2 视频上传播放模块的坑与解决方案
视频上传整个流程可以拆成:前端选文件 -> 上传到后端 -> 后端校验并保存 -> 返回视频访问URL -> 前端预览播放。这一条链路里,我认为有三个坑必须提前规避。
第一个坑是后端接收MultipartFile时没有做类型校验。用户随便传一个.exe文件上来,虽然不影响运行,但会污染数据。我在Service层增加了文件扩展名白名单过滤,只允许mp4、mov、flv、webm这几种格式,其他的一律拒绝。同时还要检查Content-Type,不能只看扩展名,防止伪造类型的情况。
第二个坑是视频封面处理。设计里我允许用户自己上传封面,但没有强制要求。如果用户不传,系统需要自动截取视频某一帧作为默认封面。Java实现视频截图可以用JAVE或者FFmpeg的Java封装库,但毕设阶段不建议引入额外工具链。我在项目里做了一个妥协方案:用户上传视频时如果不传封面,就使用默认封面图片,图片上写着“古典舞在线交流平台”Logo。这虽然不算完美,但视觉上不突兀,而且节省了大量处理时间。
第三个坑是播放兼容性。视频编码格式五花八门,有些手机拍摄的视频是H.265编码,在浏览器上直接通过HTML5的video标签播放不出画面。这个问题目前最稳妥的解决方案是让前端用支持转码的播放器(如ckplayer)或者接受转码工具(如FFmpeg)统一转成H.264编码的MP4格式。但毕设阶段,我的建议是:在视频上传页面明确提示用户推荐上传MP4格式、H.264编码的视频,从源头规避兼容性问题。
3.3 在线交流模块:发帖、评论与私信
交流模块是这个平台区别于普通视频网站的灵魂,所以我花了不少心思在细节设计上。
发帖功能本身不复杂,就是一个标题加内容的表单提交。真正要处理好的是列表展示效率。帖子表里要关联用户头像昵称,如果每条帖子都去查一次用户表,数据量一上来就慢。我的处理办法是在发帖时把用户名和头像冗余存储到帖子表的冗余字段里,查询时不用再做连表,展示速度快很多。虽然这不符合第三范式的严格要求,但属于典型的以空间换时间,技术上完全站得住脚。
评论功能的难点在于楼中楼。直接展示所有评论然后前端递归渲染,数据量小的时候没问题,但评论一多界面就乱了。我实现时的方案是分两次查询:第一次查出所有一级评论,按时间倒序分页;第二次根据一级评论的ID查出二级评论。然后组装成嵌套结构返回前端。这里要注意评论总数也要返回,前端用来显示“xx条评论”的统计数字。
私信功能我放在了相对靠后的版本里。技术上就是一张消息表,包含发送者ID、接收者ID、消息内容、发送时间、是否已读。查询时分成两个会话列表:我发出的和发给我的。未读消息的红点计数功能,是用一个查询统计接收者ID等于我、已读状态为false的记录数。这里有一个体验优化点:私信列表展示最后一条消息和发送时间,我通过子查询按会话分组取最新记录,SQL写得稍微复杂一点,但效果很接近微信的聊天列表观感。
4. 实操过程与核心环节实现
4.1 本地环境搭建:从零构建开发环境
先说说我在项目开发过程中实际使用的环境配置,给想要复现的同学一个参考。JDK用的1.8,虽然现在JDK 17甚至JDK 21已经是主流,但考虑到很多学校教学环境还在用1.8,而且Spring Boot 2.x系列最高支持到JDK 8,兼容性最稳妥。Maven用的3.6.3,MySQL用的5.7,这算是经过大量验证的老搭档组合。
数据库初始化方面,我没有使用像Flyway这类自动化迁移工具,因为毕设场景下数据库结构相对固定,写一个初始化SQL脚本就够了。项目启动时通过Spring Boot的初始化机制自动执行脚本,或者手动在Navicat里执行一次。我的建议是脚本执行一次,后续改动数据库结构再用增量脚本,避免每次重启都把数据清掉。
项目结构上,我使用的包名是com.dance.platform,下面分别有controller、service、service.impl、mapper、entity、dto、config、common等子包。配置类放在config包,统一放Swagger配置、WebMVC配置、CORS配置等公共内容。
启动整个过程只需要三步:
mvn clean install -DskipTestsmvn spring-boot:run启动成功后打开浏览器访问http://localhost:8080,能看到Swagger接口文档页,说明后端环境就绪。前端开发环境建议使用Vue CLI,执行npm install后npm run serve,开发服务器默认占8080端口,我让前端开发服务器改用3000端口,后端在CORS配置里放行这个来源。
4.2 项目启动与初始化流程
第一次启动这个项目,有几个细节容易被忽视。第一件是数据库连接配置。我的application.yml里的数据库连接信息如下:
spring: datasource: url: jdbc:mysql://localhost:3306/dance_platform?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver注意characterEncoding=utf8这个配置,以及serverTimezone=Asia/Shanghai。前者确保中文不乱码,后者解决MySQL 8.x版本连接时差八小时问题。如果连接的是MySQL 5.7,driver-class-name可以不写,但如果是MySQL 8,必须加这个驱动配置。
初始化一个管理员账号,我在ApplicationRunner里做了判断,如果用户表里没有管理员角色,就自动创建一个默认账号:用户名为admin,密码为admin2025。这个做法方便开发,但正式部署前必须注释掉,否则任何人都能启动项目后拿到管理员权限。
4.3 核心接口设计与前端联调记录
接口设计这块,我遵循RESTful风格。这里列出几个典型接口,方便理解整个系统的交互模式:
| 方法 | 接口路径 | 功能含义 |
|---|---|---|
| POST | /api/user/register | 用户注册 |
| POST | /api/user/login | 登录,返回JWT |
| GET | /api/video/list | 分页获取视频列表 |
| POST | /api/video/upload | 上传视频 |
| GET | /api/video/{id} | 视频详情 |
| POST | /api/video/like | 点赞视频 |
| POST | /api/post/create | 发布帖子 |
| GET | /api/post/list | 分页获取帖子 |
| POST | /api/comment/add | 发表评论 |
| GET | /api/follow/list | 获取关注列表 |
前端和后端联调时最常遇到的问题就是跨域(CORS)。我用Spring Boot的跨域配置类统一处理:
@Configuration public class CorsConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOrigins("http://localhost:3000") .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") .allowedHeaders("*") .allowCredentials(true) .maxAge(3600); } }这里要注意allowCredentials(true)和allowedOrigins不能同时使用通配符*,不然浏览器会拒绝携带Cookie的跨域请求。我当时在这个问题上卡了很久,最后定位到是这两行配置的冲突,把allowedOrigins改成精确地址就解决了。
5. 常见问题与排查技巧实录
5.1 端口占用与启动失败
遇到最多的就是8080端口被占用。排查方式很简单,Windows下用netstat -ano | findstr 8080查PID,接着在任务管理器里结束对应进程,或者直接改配置把端口换成8081。
还有一种情况是数据库密码配置错误。Spring Boot启动时检查DataSource连接不上,会报一个很长的异常栈。很多人第一次遇到会慌,其实核心错误信息就一句话:Access denied for user 'root'@'localhost' (using password: YES)。这基本就是密码不对或者权限没开。用Navicat单独测试一下数据库连接,如果连不上,问题就不在项目代码里。
还有一次启动失败是因为本地JDK版本过高。我同学的电脑上装的JDK 17,跑Spring Boot 2.3项目直接报IllegalArgumentException,因为新版本JDK模块系统对反射访问控制更严格。解决的方案很干脆:装一个JDK 8,IDE里切换Project SDK搞定。
5.2 视频上传失败与路径配置异常
视频上传失败有很多种原因。最常见的一种是只改了Spring的文件大小限制,但前端Nginx或网关也有上传大小限制。如果在浏览器里上传大视频返回413状态码,说明是Nginx侧的限制,需要在Nginx配置里加上client_max_body_size 200m;。
还有一种是本地路径不存在导致上传报错。我需要保存视频到/data/classical_dance/videos目录,但新服务器上根本没有这个目录,代码里又没有自动创建目录的逻辑。后来我在方法开头加了:
File directory = new File(uploadPath); if (!directory.exists()) { directory.mkdirs(); }这个小改动让程序具备了自愈能力。
5.3 数据库中文乱码与连接超时问题
中文乱码问题几乎每个做中文项目的Java开发者都遇到过。现象是存进去是中文,查出来变成“???”。造成这个问题的原因很多,我在项目里是彻底把三处都改了才解决。第一处是数据库连接URL加上characterEncoding=utf8,第二处是MySQL建表时设置DEFAULT CHARSET=utf8mb4,第三处是JDBC驱动选型——用com.mysql.cj.jdbc.Driver时需要注意它默认的字符集行为。
连接超时问题更多出现在长时间空闲后首次请求。MySQL默认的wait_timeout是8小时,如果项目跑着但一直没有数据库操作,超过了这个时间连接就断了。Spring Boot的HikariCP连接池有对应的保活配置:
spring: datasource: hikari: connection-test-query: SELECT 1 idle-timeout: 30000 maximum-pool-size: 20加了这个之后,连接池会定期检测空闲连接是否有效,无效就重建,不会再出现“半夜还能用,第二天一早就报错”的尴尬。
我把在项目开发过程中遇到的问题整理成了速查表,方便遇到同类问题快速定位:
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
| 启动报数据库连接失败 | 密码错误 / 驱动缺失 | 检查yml配置、检查依赖 |
| 上传视频显示413 | Nginx上传大小限制 | 配置client_max_body_size |
| 中文存进数据库变问号 | 连接URL未指定UTF-8 | 加characterEncoding=utf8 |
| 跨域请求被拦截 | allowedOrigins与allowCredentials冲突 | 指定精确域名,不用通配符 |
| Token过期但接口未拦截 | 拦截器未放行预检请求 | 放行OPTIONS方法请求 |
| 评论列表查出来顺序乱 | 缺少排序条件 | 按ID或时间加order by |
6. 项目经验总结与进阶扩展思考
6.1 答辩时最值得讲的技术亮点
做这个项目的过程中,有几个技术细节是我觉得答辩时很有底气讲出来的点。
第一个是JWT无状态认证的方案设计。当老师问到“你们怎么处理用户登录状态”时,能完整讲清楚加密Token的生成过程、前端存储方式、拦截器验签逻辑,以及为什么不用Session,这些细节比单纯说“用了Spring Security”更有说服力。
第二个是文件存储的路径策略。UUID文件名、按日期分目录、相对路径入库、Nginx映射访问,这一整套逻辑成本低但效果明显,而且能现场演示——上传一个视频,打开数据库看存储路径,再请求视频URL拿到播放链接。作为一个完整闭环,老师很难挑出毛病。
第三个是数据库设计里的多态关联思路。点赞记录一张表覆盖视频、帖子、评论三种内容类型,这个设计有过实际系统开发经验的老师会非常认可。面试中也可以作为亮点来展示自己对表设计的理解。
6.2 从毕设到真实项目的差距在哪里
做完这个项目,我最大的体会是:毕设和真实项目之间有很大一段距离,而这正是需要刻意去思考的地方。
毕设阶段的代码,增删改查为主,单机部署为主,数据量在千级别以下,这些都是合理的简化。但真实上线项目要考虑的问题远不止这些。比如视频压缩转码,光靠提示用户上传指定格式不是长久之计,生产环境一定需要接入转码服务;比如搜索功能,我没做分词和索引优化,量大了全文检索要走Elasticsearch;再比如内容审核,平台内容全靠用户自觉,肯定不行,真实项目必须有敏感词过滤和人工审核机制。
把这些差距理清楚,对自己的认知提升帮助很大。哪怕答辩老师不会问你这些问题,写进“项目展望”章节里也是加分项。
我个人在实际操作中的体会是:做这种类型的毕设项目,不要贪大图全,把一个核心功能做到真正好用,比做一堆半吊子功能更有价值。把这个古典舞交流平台的视频上传、播放、评论互动这条主链路打磨顺畅,辅以清晰的数据库设计和规范的接口文档,已经能达到一份优秀毕设的标准了。
最后再分享一个小技巧:项目文档里的系统测试部分,不要只写“功能正常”四个字,要带上具体的用例、前置条件、操作步骤、预期结果和实际结果。哪怕测试用例只有十几个,也比空口无凭的结论有力得多。这条经验不仅适用于这个项目,对任何技术分享、作品展示都适用——能让人信服的,永远是可复现的细节,而不是模糊的描述。