ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

SpringBoot+Vue+MyBatis+MySQL音乐网站管理系统全栈项目实战

SpringBoot+Vue+MyBatis+MySQL音乐网站管理系统全栈项目实战 做过几个前后端分离的音乐类项目之后我最大的感受是音乐网站看起来简单真正动手做才发现它并不是一个“能放歌的网页”那么简单。尤其当标题里加上“企业级”“管理系统”这两个词意味着你要处理的就远不止播放暂停还有用户体系、版权素材管理、歌单收藏、评论互动、后台数据统计这些完整闭环。这篇文章就以一套 SpringBoot Vue MyBatis MySQL 实现的 web 音乐网站管理系统为例从业务功能拆解、数据库设计、后端接口实现到前端播放器交互、部署上线和常见故障排查把整个项目怎么落地讲清楚。适合正在做课程设计、毕业设计或者想从零搭建一个完整全栈项目的开发者参考。内容会偏实战代码上不会整段贴完但关键的实现思路、表结构、接口设计、踩坑点都会交代清楚。1. 做项目之前先想清楚音乐网站的核心业务闭环1.1 音乐站不是只有播放器还要有“管理”二字很多人一上来就写播放器做得再炫酷本质上还只是一个前端 Demo。企业级 web 音乐系统重点落在“管理”上管理员要能维护歌曲、歌手、专辑、歌单用户要能注册、登录、搜歌、收藏、评论系统要能记录播放行为并做数据统计。这样一套业务闭环才是“管理系统”四个字的真正含义。所以动手编码前我习惯先把业务模块画成一张脑图。以我这个项目为例整体拆成了两个端C 端用户端注册登录、首页推荐、歌曲搜索、歌手专辑浏览、歌单详情、播放器、收藏、评论、个人中心、播放历史。B 端管理端仪表盘统计、用户管理、歌手管理、专辑管理、歌曲管理、歌单管理、评论审核、系统配置。两端共用一套 SpringBoot 后端接口前端分别用 Vue 构建两个独立应用部署时同域共端口通过路径前缀区分。这么做的好处是权限模型清晰管理员接口统一走/admin/**前缀普通用户接口走/api/**前缀后端通过拦截器区分角色。1.2 什么样的功能清单才算“企业级”我见过不少音乐系统功能写得很满但一细看就露馅没有权限校验、没有统一的返回格式、没有全局异常处理、SQL 全是SELECT *。所谓企业级我认为最少要满足以下标准一是统一响应协议。所有接口返回{ code, message, data }结构方便前端统一处理错误码。二是完善的登录鉴权。用户和管理员两套身份体系接口必须做权限控制不能前端隐藏按钮就算安全。三是合理的数据表设计和索引。业务数据随便查都是几十毫秒内响应不能一个报表接口搞到数据库 CPU 飙红。四是可配置、可维护。比如文件上传路径、CDN 地址、歌曲转码参数都应该写在配置文件中。这套标准的背后对应的是扎实的 SpringBoot 工程结构。我习惯分包为controller / service / mapper / entity / dto / config / common把拦截器、异常处理器、工具类都归到 common 包下避免 controller 里写一大堆业务逻辑。2. 技术选型的账这样算SpringBootVueMyBatisMySQL为什么能打2.1 放弃SSH、放弃JPA的原因早几年做 Java Web绕不开 Spring MVC Spring MyBatis 这套 SSM 组合配置繁琐到让人怀疑人生。SpringBoot 出现后通过自动配置把大量 XML 配置干掉内嵌 Tomcat一个mvn spring-boot:run就能起服务这也让它成为目前 Java 后端的绝对主流。说到 MyBatis 和 JPA很多新手纠结。我的实际体验是音乐系统里大量涉及多表关联查询、动态条件筛选、统计报表这类 SQL 用 MyBatis 的 XML 文件维护起来非常灵活你可以精确控制每一条 SQL而 JPA 在简单 CRUD 上确实更爽但一旦遇到复杂查询要么写 JPQL要么走原生 SQL反而绕远路。MyBatis 还有个优势是 SQL 可以直接拿出去在 Navicat 里执行验证排查数据问题时效率高得不是一点半点。2.2 Vue 在前端选型中的主导地位Vue 在国内开发者中的普及度不言而喻。对于音乐站这种交互密集的应用Vue 的响应式数据绑定和组件化开发能极大提升开发效率。尤其播放器这类的核心模块我把播放状态、播放列表、当前歌曲封装成一个全局组件通过 Vuex 管理状态这样在页面跳转后播放不会中断。Vue 生态里的 Vue Router 和 Pinia/Vuex 基本是标配。老项目喜欢 Vuex新项目建议直接用 PiniaAPI 更简洁还天然支持 TypeScript 类型推导。我这个项目用的是 Vuex主要是为了兼容团队既有代码如果你从零开始直接上 Pinia 就行。2.3 版本选型的血泪教训版本是坑最多的地方。SpringBoot 3.x 要求 JDK 17如果你本地还是 JDK 8强行升级会碰到一堆依赖不兼容问题。我的建议是做企业项目优先选 SpringBoot 2.7.x JDK 8 这套组合稳定、教程多、遇到问题网上随便一搜都有答案。MyBatis 用 mybatis-spring-boot-starter 2.3.x 版本MySQL 用 8.0 以上连接驱动记得加com.mysql.cj.jdbc.Driver而不是老的com.mysql.jdbc.Driver。前端的话Vue 3 Vite 是主流方向Vue 2 已进入维护末期新项目别再用了。Node 版本至少要 16装依赖时如果报ERESOLVE unable to resolve dependency tree多半是 npm 版本和依赖冲突换个淘宝镜像源或者用 pnpm 可以省很多事。3. 数据库建模把“音乐”拆成表是最费脑力的一步3.1 核心表设计与ER关系数据库设计决定了项目能走多远。我把音乐系统拆成以下核心表user用户表字段包括 id、username、passwordBCrypt加密、nickname、avatar、gender、phone、email、status、create_time。singer歌手表字段包括 id、name、avatar、intro、area华语/欧美/日韩、style流行/摇滚/民谣。album专辑表字段包括 id、singer_id、name、cover、publish_time、description。song歌曲表字段包括 id、album_id、singer_id、name、duration、url、lyric、play_count、status。song_list歌单表字段包括 id、user_id、name、cover、description、play_count、type分类标签。song_list_song歌单歌曲关联表字段包括 id、song_list_id、song_id。favorite用户收藏表字段包括 id、user_id、type0歌曲/1歌单/2专辑、target_id、create_time。comment评论表字段包括 id、user_id、song_id、content、parent_id、like_count、create_time。play_history播放历史表字段包括 id、user_id、song_id、play_time、duration_played。admin管理员表字段包括 id、username、password、role、last_login_time。表之间的核心关系很简单歌手一对多专辑专辑一对多歌曲用户多对多歌单用户多对多歌曲通过收藏。但在实际的 ER 图中你会发现问题集中在关联表的设计上比如一个歌单里歌曲顺序是否要记录我的做法是加一个sort_order字段方便歌单自定义排序。3.2 索引、唯一约束与外键的最佳实践表结构设计完索引设计直接决定线上性能。我在这些位置加了索引登录场景user.username、admin.username建唯一索引。搜索场景song.name、singer.name建普通索引量大了以后可以升级为全文索引。列表展示song.singer_id、song.album_id、song_list.user_id建普通索引避免联合查询全表扫描。播放历史play_history.user_id play_history.play_time建联合索引个人中心查询最近播放走这个索引非常快。外键这块我项目里没有使用物理外键全部通过逻辑关联维护。原因很简单互联网业务模块拆分后外键约束会严重拖累写入性能而且后续如果要分库分表物理外键会变成巨大的阻碍。你只需要在应用层保证删除歌手时同时删除其专辑歌曲或者标记删除状态即可。3.3 MySQL版本与字符集踩坑MySQL 8.0 推荐使用utf8mb4字符集不是utf8因为utf8在 MySQL 里最多 3 字节存不了 emoji 表情。用户昵称里一旦出现 emoji写入就报错这种问题排查起来特别隐蔽。另外MySQL 8.0 默认认证插件是caching_sha2_password老版本的驱动连不上会报Unable to load authentication plugin caching_sha2_password要么换驱动版本要么创建用户时指定mysql_native_password。这里我踩过好几次坑建议直接写在建库脚本里CREATE DATABASE music_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; CREATE USER music_app% IDENTIFIED WITH mysql_native_password BY your_password; GRANT ALL PRIVILEGES ON music_db.* TO music_app%;这样后端配置连接串时指定characterEncodingutf8serverTimezoneAsia/Shanghai基本就不会再遇到乱码和时区问题了。4. 后端核心模块落地认证、文件、播放和搜索4.1 JWT实现登录态Session为何退场传统单体应用喜欢用 Session Cookie 保存登录态但前后端分离后前端可能部署在独立域名下接口服务器是另一个地址Cookie 跨域处理非常麻烦。我的选择是用 JWT后端把用户 id、角色、过期时间签发进 token前端存到 localStorage每次请求在Authorization头里带上。JWT 实现认证的流程并不复杂登录接口校验用户名密码成功则用jjwt库生成 token 返回。自定义拦截器拦截需要认证的路径从 header 取 token 并解析。校验通过后把用户信息放入ThreadLocal业务代码直接获取当前用户。token 过期返回 401前端收到后跳转登录页。至于 token 过期时间我的设置是普通用户 24 小时管理员 2 小时。没有做 refresh token 机制因为音乐网站对登录连续性的要求没那么高。4.2 歌曲/专辑封面的上传与静态资源映射管理端需要上传歌曲文件和封面图片这里涉及两个问题文件存哪里、前端怎么访问。本地开发时我建议直接把文件存到磁盘某个固定目录比如/data/music/cover和/data/music/audio不要存数据库数据库只放 URL 路径。SpringBoot 需要配置静态资源映射把 URL 路径映射到磁盘目录spring: servlet: multipart: max-file-size: 50MB max-request-size: 100MB resources: static-locations: classpath:/META-INF/resources/,classpath:/resources/,classpath:/static/,classpath:/public/,file:/data/music/再把 WebMvcConfigurer 里的 addResourceHandlers 加一个映射这样上传后的文件路径存/cover/xxx.jpg前端就能直接通过域名访问到。注意生产环境千万别把文件和应用放在同一块小磁盘上日志和音频并发写入会把磁盘 IO 拖垮。4.3 m3u8播放链路的接入与兼容方案音乐网站对接音频流时经常会遇到 m3u8 格式。m3u8 本质是一个索引文件里面记录了一串 ts 分片地址播放器按顺序拉取分片实现流式播放。这种格式的优势在于支持多码率、拖动进度方便、天然适合 CDN 分发。我做这个项目时客户给的素材有一部分就是 m3u8 直播录制文件。Vue 前端播放 m3u8原生audio标签是不支持的我用了hls.js这个库用法非常简单import Hls from hls.js; function playM3u8(url, videoElement) { if (Hls.isSupported()) { const hls new Hls(); hls.loadSource(url); hls.attachMedia(videoElement); hls.on(Hls.Events.MANIFEST_PARSED, () { videoElement.play(); }); } else if (videoElement.canPlayType(application/vnd.apple.mpegurl)) { videoElement.src url; videoElement.play(); } }但这里有个后端必须配合的点浏览器跨域请求 m3u8 文件时如果响应头里没有Access-Control-Allow-Origin分片 ts 文件会被拦截。我在 Nginx 代理音频路径时统一加了 CORS 头这问题才算彻底解决。4.4 模糊搜索与热门歌单推荐的小技巧搜索是音乐站的高频操作我做了歌曲、歌手、专辑的全局模糊搜索对应 SQL 大概是这样SELECT s.id, s.name, s.duration, s.url, si.name AS singer_name FROM song s LEFT JOIN singer si ON s.singer_id si.id WHERE s.name LIKE CONCAT(%, #{keyword}, %) OR si.name LIKE CONCAT(%, #{keyword}, %) ORDER BY s.play_count DESC LIMIT 30数据量不大时 LIKE 完全够用。但如果歌曲表到了百万级建议接入 Elasticsearch 或 MySQL 全文索引。推荐模块我偷了个懒用了最简单的“热门歌单”按播放量倒序取前 10 个歌单再随机打乱返回给用户首页成本和效果达到了一个不错的平衡。5. 前端Vue工程把播放体验做成“自来水”5.1 项目初始化和路由设计Vue 前端我拆成了两个工程music-admin和music-web这样管理端和用户端独立开发、独立部署。初始化用 Vite 脚手架npm create vitelatest music-web -- --template vue cd music-web npm install路由设计上用户端包含首页、歌单、歌手、排行榜、搜索页、歌单详情、歌手详情、个人中心这些页面。前端路由需要按需加载用const routes [{ path: /, component: () import(/views/Home.vue) }]这种写法避免首屏包体过大。管理端路由要设置一个前置守卫每次跳转时检查 localStorage 里有没有 token没有就重定向到登录页。后端接口也要校验不能只靠前端拦这点必须反复强调。5.2 播放器组件与状态管理的集成玩法播放器是整个前端最复杂的组件。它的状态太多当前播放歌曲、播放列表、播放状态、当前时间、总时长、音量、播放模式顺序/随机/单曲循环。我把它封装成全局组件PlayerBar.vue用 Vuex 管理播放状态。播放器设计有一个重要细节歌曲切换时如果直接替换audio标签的 src浏览器会重新加载中间存在明显卡顿。我的做法是保留一个隐藏的audio元素切换歌曲时只更新 src 并调用load() play()进度条用timeupdate事件更新拖动进度条时用currentTime赋值。收藏功能与播放器的联动也需要设计好。播放列表里每首歌显示红心图标用户点击后调收藏接口成功后更新 Vuex 中的收藏列表。这里的关键是接口返回后要有一个 loading 状态避免用户连续点击导致重复调用。5.3 你大概率会遇到的m3u8和跨域问题前端联调时最常遇到两个问题。一个是本地开发跨域Vite 的 dev server 配置 proxy 解决server: { proxy: { /api: { target: http://localhost:8080, changeOrigin: true } } }另一个是上面提到的 m3u8 播放。我一开始用video.js播放 m3u8发现移动端兼容性不好后来换了hls.js桌面浏览器兼容性直接拉满。还有一个坑是音频文件如果代理配置不对浏览器拿到的是 HTML 而不是音频流加载直接失败这种情况下先开 DevTools Network 面板看响应内容比瞎猜效率高得多。6. Nginx部署与线上故障排查记录6.1 前后端分离部署的目录与代理配置前端打包后生成dist目录里面有静态的 html、css、js 文件。Nginx 里我按两个 server 块配置一个负责用户端一个负责管理端也可以合并成同一个 server 用不同 location。我是独立域名分开的结构更清晰server { listen 80; server_name www.music-example.com; root /data/www/music-web/dist; index index.html; location / { try_files $uri $uri/ /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; } location /audio/ { alias /data/music/audio/; add_header Access-Control-Allow-Origin *; } }try_files这行是解决 Vue Router history 模式下刷新页面 404 的关键。如果没有它用户在/song/123页面刷新Nginx 找不到对应文件直接返回 404。加上这行之后刷新请求会全部回退到 index.html由前端路由接管。6.2 线上音乐加载慢定位与优化部署后用户反馈点一首歌要转好几秒第一反应是接口慢但打开 Network 面板发现接口 50ms 就返回了卡在音频文件加载上。查了下发现音频文件存在普通磁盘上单个文件 8MB用户带宽一般自然要等很久。我的优化方案有几步第一音频文件改用对象存储 OSS开启 CDN 加速静态资源访问速度快很多第二接口返回歌曲列表时数据里带一个preload字段前端在当前歌曲即将播放完时预加载下一首第三封面图片都走 WebP 格式压缩体积降低 60% 以上。经过这三步优化实际体感基本是秒开。6.3 会话失效、静态资源404、数据库连接池爆掉线上遇到过几个很典型的故障这里做一个记录。会话失效问题用户反馈用着用着突然跳登录排查发现是因为 JWT token 没有续期策略。登录状态保持 24 小时对音乐网站来说够用但用户长时间停留页面token 到期后任何请求都 401前端统一弹登录框。后来我在前端加了一层拦截401 时静默刷新一次 token用本地存的 refreshToken刷新失败才跳登录页体验提升很多。静态资源 404部署管理端后发现登录页的 JS/CSS 加载不出来看 Nginx error log 发现路径被拼错了。管理端打包时默认资源路径是/assets/xxx.js而管理端部署在子路径/admin下导致请求到了/assets/找不到文件。解决方式是 Vite 配置base: /admin/重新构建一次就正常了。数据库连接池爆掉上线初期连接池配置用的是 HikariCP 默认值maximum-pool-size: 10每天高峰时段都会有几十个请求报Connection is not available, request timed out。调整配置后解决spring: datasource: hikari: maximum-pool-size: 50 minimum-idle: 10 connection-timeout: 30000 max-lifetime: 1800000核心逻辑是连接池大小不是越大越好一般CPU核心数*2 磁盘数量是一个参考值。但业务高峰期查询量大适当调大连接数并增加 wait 超时时间能有效缓解瞬时并发压力。同时给play_history这类高频写入表加批量插入逻辑减少连接占用。配置调整之后还是要治本检查慢 SQL。我用show processlist抓到几条全表扫描的统计 SQL给相关字段补了索引连接池占用率马上降下来了。这算是线上调优最实在的一课。7. 这套源码还能怎么改项目做完后我常被问的一个问题是“接下来还能加点什么”。我说几个我认为性价比很高的方向一是做推荐算法。目前首页的热门歌单是纯按播放量排序你可以把用户的历史播放数据做一个协同过滤推荐算出的用户相似度存入一张user_recommend表定时任务每天更新一次。虽然算法本身不复杂但带来的体验提升非常明显。二是评论系统增强。当前评论只支持一级评论你可以扩展为楼中楼增加点赞、举报、审核状态。这块后端改动不大主要是前端组件结构要支持嵌套。三是接入第三方登录。目前只有账号密码登录加上微信扫码或 GitHub OAuth用户门槛会低很多。OAuth 的核心逻辑就是引导用户跳第三方授权页拿到 code 后后端调接口换 token再解析用户信息整体不难。四是在线播放数据统计。目前只有播放总量没有每首歌的 UV、PV、播放完成率你可以基于play_history表做一个定时分析任务输出运营日报。如果你手头正好要做一个音乐站的课程项目或者想基于这套架构接一个真实的商业需求建议先把用户管理、权限控制、文件存储这几条主链路吃透剩下的一切都是锦上添花。我在实际开发中体会最深的一点是项目跑通只是开始把数据库索引和连接池这些地基打牢才是后面不被线上事故追着跑的前提。
返回列表