2026/10/11 5:14:06

基于Spring Boot的古典舞在线交流平台设计与实现

基于Spring Boot的古典舞在线交流平台设计与实现 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_userid, username, password, nickname, avatar, role, status被视频表、帖子表关联t_videoid, user_id, title, cover_url, video_url, category, level, like_count关联用户表、点赞表t_postid, user_id, title, content, section, top_flag关联用户表、评论表t_commentid, user_id, post_id, video_id, parent_id, content自关联 关联内容表t_like_recordid, user_id, target_type, target_id, create_time多态关联设计t_followid, 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 JWTJSON 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.3MySQL用的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?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/Shanghai username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver注意characterEncodingutf8这个配置以及serverTimezoneAsia/Shanghai。前者确保中文不乱码后者解决MySQL 8.x版本连接时差八小时问题。如果连接的是MySQL 5.7driver-class-name可以不写但如果是MySQL 8必须加这个驱动配置。初始化一个管理员账号我在ApplicationRunner里做了判断如果用户表里没有管理员角色就自动创建一个默认账号用户名为admin密码为admin2025。这个做法方便开发但正式部署前必须注释掉否则任何人都能启动项目后拿到管理员权限。4.3 核心接口设计与前端联调记录接口设计这块我遵循RESTful风格。这里列出几个典型接口方便理解整个系统的交互模式方法接口路径功能含义POST/api/user/register用户注册POST/api/user/login登录返回JWTGET/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 rootlocalhost (using password: YES)。这基本就是密码不对或者权限没开。用Navicat单独测试一下数据库连接如果连不上问题就不在项目代码里。还有一次启动失败是因为本地JDK版本过高。我同学的电脑上装的JDK 17跑Spring Boot 2.3项目直接报IllegalArgumentException因为新版本JDK模块系统对反射访问控制更严格。解决的方案很干脆装一个JDK 8IDE里切换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加上characterEncodingutf8第二处是MySQL建表时设置DEFAULT CHARSETutf8mb4第三处是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配置、检查依赖上传视频显示413Nginx上传大小限制配置client_max_body_size中文存进数据库变问号连接URL未指定UTF-8加characterEncodingutf8跨域请求被拦截allowedOrigins与allowCredentials冲突指定精确域名不用通配符Token过期但接口未拦截拦截器未放行预检请求放行OPTIONS方法请求评论列表查出来顺序乱缺少排序条件按ID或时间加order by6. 项目经验总结与进阶扩展思考6.1 答辩时最值得讲的技术亮点做这个项目的过程中有几个技术细节是我觉得答辩时很有底气讲出来的点。第一个是JWT无状态认证的方案设计。当老师问到“你们怎么处理用户登录状态”时能完整讲清楚加密Token的生成过程、前端存储方式、拦截器验签逻辑以及为什么不用Session这些细节比单纯说“用了Spring Security”更有说服力。第二个是文件存储的路径策略。UUID文件名、按日期分目录、相对路径入库、Nginx映射访问这一整套逻辑成本低但效果明显而且能现场演示——上传一个视频打开数据库看存储路径再请求视频URL拿到播放链接。作为一个完整闭环老师很难挑出毛病。第三个是数据库设计里的多态关联思路。点赞记录一张表覆盖视频、帖子、评论三种内容类型这个设计有过实际系统开发经验的老师会非常认可。面试中也可以作为亮点来展示自己对表设计的理解。6.2 从毕设到真实项目的差距在哪里做完这个项目我最大的体会是毕设和真实项目之间有很大一段距离而这正是需要刻意去思考的地方。毕设阶段的代码增删改查为主单机部署为主数据量在千级别以下这些都是合理的简化。但真实上线项目要考虑的问题远不止这些。比如视频压缩转码光靠提示用户上传指定格式不是长久之计生产环境一定需要接入转码服务比如搜索功能我没做分词和索引优化量大了全文检索要走Elasticsearch再比如内容审核平台内容全靠用户自觉肯定不行真实项目必须有敏感词过滤和人工审核机制。把这些差距理清楚对自己的认知提升帮助很大。哪怕答辩老师不会问你这些问题写进“项目展望”章节里也是加分项。我个人在实际操作中的体会是做这种类型的毕设项目不要贪大图全把一个核心功能做到真正好用比做一堆半吊子功能更有价值。把这个古典舞交流平台的视频上传、播放、评论互动这条主链路打磨顺畅辅以清晰的数据库设计和规范的接口文档已经能达到一份优秀毕设的标准了。最后再分享一个小技巧项目文档里的系统测试部分不要只写“功能正常”四个字要带上具体的用例、前置条件、操作步骤、预期结果和实际结果。哪怕测试用例只有十几个也比空口无凭的结论有力得多。这条经验不仅适用于这个项目对任何技术分享、作品展示都适用——能让人信服的永远是可复现的细节而不是模糊的描述。