毕业设计选什么题,大概是每个大四学生都绕不开的一道坎。很多人纠结了快两周,最后在我反复权衡之下,敲定了“基于 Spring Boot 的音乐播放器小程序”这个方向。为什么选它?业务逻辑清晰但不过度复杂,能完整覆盖后端、小程序前端、数据库、部署四条链路,刚好落在毕业设计最舒服的复杂度区间里。今天这篇就把这个项目的完整开发链路掰开揉碎讲一遍——从技术选型、源码结构、数据库设计、接口实现,到小程序音频播放、服务器部署、高频踩坑点,再到答辩怎么演示,全程干货,弯路尽量帮你提前踩掉。
这篇内容适合几类人:正在为毕业设计选题发愁的学生;手上已经拿到“音乐播放器小程序91909”这类项目包,但不知道怎么跑通、怎么改、怎么在答辩上讲清楚的人;还有想在简历上放一个能现场演示的完整项目的初级开发者。不管你属于哪一类,我建议你先别急着运行代码,把这篇文章当一份“项目说明书”看,心里有了全局地图,后面每一步都会快很多。
1. 选题逻辑与整体技术栈:这个组合赢在哪
1.1 一份毕设项目,到底覆盖了多少能力点
先别急着写代码,把账算清楚。老师评一份毕业设计,看的不是代码有多炫,而是这套东西展示了哪些能力、工程完整度有多高。“Spring Boot + 音乐播放器小程序”恰好踩中了工科毕业生最该展示的四个能力面:
- 后端工程能力:Spring Boot 的工程结构、Maven 依赖管理、三层架构(Controller、Service、Mapper)、统一异常处理、JWT 鉴权,这些是企业级 Java 开发的基本盘,面试时也常被问到。
- 数据库设计能力:用户表、歌手表、歌曲表、歌单表、收藏表之间的关系,至少涉及一对多、多对多两种建模模型,足够体现你的数据库基本功。
- 小程序前端能力:页面生命周期、请求封装、组件交互、本地存储、音频播放控制,这层技能点在就业市场上相当吃香,很多团队都在招小程序开发。
- 部署运维能力:打 jar 包、扔到服务器、用进程守护把服务拉起来、配置反向代理、挂上 HTTPS,这一套虽然只是入门级,但放到毕设答辩里是实打实的加分项。
更重要的是,这个项目没有“过度设计”。我见过不少学生一上来就 Spring Cloud + Redis + 消息队列,配置写了几百行,业务逻辑反而没几行。答辩时老师一问“你用了什么?为什么用?”,答不上来,最后评价也不高。音乐播放器这个体量,单机 Spring Boot 加 MySQL 就能撑住,每个技术选型都能给出明确理由,这才是毕设该有的分寸感。
1.2 为什么是原生小程序,而不是其他前端方案
前端这边原本有几个候选:原生小程序、Vue 做移动端网页、uni-app 跨端方案。我最终推荐原生小程序,理由有三条。
第一,毕设的演示场景通常就是“老师在电脑前看你在模拟器里跑”,原生小程序配合官方开发者工具,启动最快、调试最直观,不需要再起一套 Node 服务、不需要处理移动端浏览器兼容,成本最低。第二,小程序自带音频播放组件(就是后面要说的 InnerAudioContext),做音乐类应用天然顺手,不用自己封装一套播放器。第三,原生小程序的页面结构是 wxml、wxss、js、json 四件套,理解成本低,哪怕之前没写过前端,对照官方示例几天就能上手。
那 uni-app 值不值得考虑?如果你打算同一套代码以后同时发布 App 和 H5,可以;但如果只是为了毕业设计,跨端能力属于冗余。至于项目编号里那个“91909”,我理解是这套项目在分发平台上的内部编号,用来区分不同版本和配套资料,实际工程里不参与任何代码逻辑,你拿到工程后不用纠结这个数字。
1.3 技术栈清单与版本建议
说下我实际推荐验证过的版本组合,都是相对稳定、资料全、不容易出幺蛾子的:
| 组件 | 推荐版本 | 说明 |
|---|---|---|
| JDK | 8 或 11 | Spring Boot 2.7 系列用这两个都行,不要贪新 |
| Spring Boot | 2.7.x | 文档全、资料多,大多数项目包都是这个系列 |
| MyBatis-Plus | 3.5.x | 省去大量手写 SQL,分页、逻辑删除都内置 |
| MySQL | 8.0 | 注意驱动要配 mysql-connector-j,别用老版本 |
| Maven | 3.6+ | 打包和依赖管理 |
| 小程序端 | 原生框架 | 基础库 2.x 即可 |
这里特别提醒一下:拿到项目包后,第一件事就是看 pom.xml 里的 Spring Boot 版本。如果 Boot 是 2.x,JDK 用 8 或 11;如果是 3.x,JDK 必须 17 以上,而且很多配置写法变了。版本不匹配是毕设项目跑不起来的头号原因,比代码逻辑出错还常见。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 源码结构拆解:拿到项目后先看这几个地方
2.1 后端工程的目录与关键配置文件
不管项目包是谁写的、从哪来的,Spring Boot 的工程结构万变不离其宗。拿到源码先不要急着点运行按钮,花十分钟把目录过一遍,后面能省下大把排错时间。
一个典型的后端结构长这样:
code复制src/main/java
└─ com.xxx.music
├─ controller # 接口层,接收前端请求
├─ service # 业务逻辑层
├─ mapper # 数据访问层,对应数据库操作
├─ entity # 实体类,对应数据库表
├─ config # 配置类(跨域、拦截器、文件上传等)
└─ common # 通用类(统一返回结果、异常处理)
src/main/resources
├─ application.yml # 核心配置
└─ mapper # XML 文件目录(如果用 XML 写 SQL)
三个文件建议优先打开看。application.yml 里的数据源配置,确认数据库地址、账号密码——很多同学不先改密码就启动,数据库连不上报一堆错,还以为项目有问题,其实只是你自己的 MySQL 密码和配置里不一致。pom.xml 里的依赖清单,重点确认 Spring Boot 版本、MyBatis-Plus 版本,以及有没有引入 JWT、文件上传这些工具包。还有主启动类(带 @SpringBootApplication 注解的类),确认包扫描路径,能扫到 controller 和 mapper;如果包名被改过又没改全,启动时会报“找不到 Bean”。
2.2 小程序端目录与前后端连接点
小程序端的目录通常按页面划分,常见的有首页、歌单页、搜索页、播放页、个人中心页。别被页面数量吓到,大部分页面结构完全一样,会一个就会全部。
看小程序代码,重点抓两个文件。app.js 里定义了全局数据,比如用户信息、播放器状态,还有请求的 baseURL;utils/request.js 里封装了 wx.request,所有接口调用都走这一个入口,登录后拿到的 token 也是在这里统一塞进请求头的。
前后端怎么连上的?在小程序代码里找 baseURL,它指向后端地址。本地调试填 http://localhost:8080,真机预览填电脑的局域网 IP,上线再换成服务器域名。这个地址是所有“前端调不通接口”问题的最常见来源,看到接口报错先回去看一眼 baseURL。
2.3 数据库脚本与初始数据
数据库脚本一般是单个 .sql 文件,建表语句、初始数据都在里面。这里有个小技巧:先看插入的数据,再看建表语句。因为初始数据决定了你演示时能展示什么——里面有没有预置歌手、歌曲、歌单,如果没有,你到时候就得自己准备音频文件,不然播放器根本没有内容可播。
另外留意脚本里的字符集设置,推荐统一用 utf8mb4,不然遇到表情符号或特殊字符会存不进去。导入时用命令行 source 命令或图形化工具运行整份脚本都可以,前提是新建的数据库名要和 application.yml 里配置的一致。
3. 数据库设计:音乐业务的表结构怎么建才不返工
3.1 核心表及字段规划
一套音乐播放器,表面功能看着不多,拆一下业务,至少需要六张核心表。我把字段和设计理由一起列出来,你拿项目包时可以对照检查有没有漏。
先看用户表(user):
id:主键,BIGINT 自增。别用 int,数据量再小也别省nickname:昵称avatar:头像路径phone:手机号,一般做唯一索引password:密码的哈希值,绝不允许明文created_at、updated_at:创建与更新时间
然后是歌手表(singer)和歌曲表(song)。歌手表字段简单:id、name、avatar、intro。歌曲表是整个系统的核心,字段相对多:
id:主键title:歌曲名singer_id:外键指向歌手表,一首歌属于一个歌手,一对多album_id:可选项,如果做专辑功能则指向专辑表audio_url:音频文件的访问路径或完整 URL,这是播放器实际要加载的资源cover_url:封面图地址duration:时长,单位秒,前端进度条要用play_count:播放次数,首页热门榜单直接按它排序lyric:歌词文本或歌词文件路径,扩展功能备用
歌单是典型的多对多结构。playlist 表存歌单本身:id、user_id、name、cover、description。另建一张关联表 playlist_song,字段就三个:id、playlist_id、song_id。为什么要单独拆关联表?因为一个歌单可以包含多首歌,一首歌也可以出现在多个歌单里,这种多对多关系只有通过中间表才能干净表达。如果图省事在歌单表里存逗号分隔的歌曲 id 字符串,后面做增删改查时每一处都要解析字符串,代码又丑又容易出 bug。
收藏和评论建议也做上。favorite 表存 user_id、song_id、created_at,查询时按用户和歌曲组合去重。comment 表存 user_id、song_id、content、reply_to(回复哪条评论,可空)、created_at。评论功能看着小,但能撑起“功能完整度”的观感,答辩时很加分。
3.2 字段设计里的三个常见错误
我帮人改这类项目时,见过不少表设计翻车的案例,挑三个最典型的讲。
一是用文件路径还是用 URL 不分。音频和封面这种二进制内容,千万别直接存 BLOB 字段进数据库。正确做法是把文件丢到服务器的某个目录,数据库里只存相对路径或完整 URL。数据库是给查询用的,不是给存储大文件用的,混在一起会让备份、迁移、查询性能全面变差。毕设阶段本地磁盘加路径存库完全够用。
二是时间字段不用数据库默认值。很多人建表时不给 created_at 设默认值,插入数据时又忘了赋值,最后列表页全是空时间。建议后端配合 MyBatis-Plus 的自动填充功能,在插入和更新时统一填时间,比每条 SQL 手写函数省心得多。
三是删除用物理删除。用户取消了收藏、评论被删掉,这些场景如果真把行删掉,数据不可追溯,关联关系也可能产生脏数据。建议每张业务表加一个 deleted 字段,配合 MyBatis-Plus 的 @TableLogic 注解实现逻辑删除,查询时自动过滤,维护成本几乎为零,而且答辩被问“数据怎么恢复”时你能答得上来。
3.3 初始数据与演示效果的关系
很多同学忽略初始数据的价值,觉得“反正能跑,数据我手动加不就行了”。等你真开始插数据,会发现一首歌要准备音频文件、封面图、时长、歌手关联,一套下来比写代码还烦。所以项目包里带的初始数据是宝贝,别乱删。
理想情况下,预置数据应该覆盖这些场景:三类左右的歌单(热门、清新、怀旧),十首左右真实可播的音频链接,两三个歌手及其简介,再加几个测试账号。这样你打开首页就是“满”的,截图、录屏都很体面。如果项目包里的初始音频链接失效了,记得换成有效的公共音频地址或本地文件资源,这属于上线前必查项。
4. 后端接口实现:登录鉴权与核心业务的完整链路
4.1 统一返回结构和全局异常处理
接口设计的第一件事不是写业务,而是定一套大家都能认的“语言”。毕设项目里我强烈建议用统一返回包装类:
java复制public class Result<T> {
private Integer code; // 200 成功,500 失败,401 未登录
private String message; // 提示信息
private T data; // 实际数据
}
所有接口都返回这个结构,前端 request.js 里只需要解析一次,判断 code 就能知道这次请求是否成功。很多同学直接返回裸数据或 Map,后端改一个字段名,前端就要跟着改一个字符串,接口一多就乱套。
异常处理同样要统一。写一个全局异常处理器,捕获业务异常、参数校验异常、未知异常,分别转成对应的 Result 返回。好处是接口永远返回结构化的 JSON,而不是让 Spring Boot 默认的错误页直接怼到小程序里,前端拿到错误信息才能弹出像样的提示。
4.2 注册、登录与 JWT 鉴权
音乐播放器的登录逻辑,基本是每个 Java 项目都会用到的标准套路。注册接口接收手机号和密码,密码一律用 BCrypt 加密后入库,绝不能明文存储。登录时查出用户,用 BCrypt 比对密码,比对通过后签发一个 JWT 令牌返回给前端。
为什么用 JWT 而不是传统 Session?因为小程序和后端是分离的,后端可能部署在任何地方,Session 依赖服务器内存,扩容、重启都会失效。JWT 把用户信息加密放进令牌本身,后端拿到令牌验签即有效,天然无状态,适合前后端分离架构。这个理由写进论文、答辩时说出来都站得住。
签发令牌的核心代码大致这样:
java复制String token = Jwts.builder()
.setSubject(userId.toString())
.setExpiration(new Date(System.currentTimeMillis() + 7 * 24 * 3600 * 1000L))
.signWith(SignatureAlgorithm.HS256, secretKey)
.compact();
有效期我习惯设 7 天,对毕设演示足够。注意把 secretKey 放到配置文件里,不要写死在代码中。
有了令牌还要有校验机制。在 config 包下写一个拦截器,拦截所有 /api/** 请求,登录注册接口除外,从请求头 Authorization 里取令牌,解析失败就返回 401。前端收到 401 后自动清掉本地登录态、跳回登录页,这条链路后面踩坑部分还会细说。
4.3 歌曲、歌单和收藏接口的设计与实现
核心业务接口按资源划分,遵循 RESTful 风格,前端也容易记忆:
| 接口 | 方法 | 说明 |
|---|---|---|
| /api/user/register | POST | 注册 |
| /api/user/login | POST | 登录,返回 token 和用户信息 |
| /api/song/list | GET | 歌曲列表,支持关键词分页搜索 |
| /api/song/hot | GET | 热门歌曲,按播放次数排序 |
| /api/song/detail/ | GET | 歌曲详情 |
| /api/playlist/list | GET | 歌单列表 |
| /api/playlist/detail/ | GET | 歌单详情,带歌曲列表 |
| /api/favorite/add | POST | 收藏歌曲 |
| /api/favorite/list | GET | 我的收藏 |
| /api/file/upload | POST | 音频或图片上传 |
写歌曲列表时用 MyBatis-Plus 的 LambdaQueryWrapper 做条件查询,比如标题模糊匹配、歌手过滤、按播放量排序,一套组合拳下来十几行代码就能搞定。分页用 MyBatis-Plus 内置分页插件,前端传 page 和 limit,后端返回总条数和当前页数据,接口响应层次清晰。
歌单详情接口稍微特殊,因为它跨三张表:歌单、歌单歌曲关联表、歌曲表。实现方式是 Service 层先查歌单基础信息,再查关联表拿到歌曲 id 列表,最后查歌曲详情组装成返回对象。如果追求性能可以写一条多表 join 的 SQL,但毕设阶段三次查询可读性更好,也方便答辩时对着代码一行行讲逻辑。
文件上传接口用 MultipartFile 接收,存到配置好的本地目录,返回可访问的 URL。细节是:上传目录必须配置成绝对路径,并且要给目录写权限,否则 Linux 服务器上经常出现“保存成功但访问 404”的情况。
4.4 联调阶段最容易忽略的三个细节
第一是跨域。小程序由于平台限制不触发浏览器跨域,但你如果直接用浏览器调试后端接口,跨域问题立刻暴露。建议在后端统一加 CORS 配置,一劳永逸。
第二是时间格式。后端默认序列化 LocalDateTime 可能输出一串数组,前端解析非常痛苦,建议在配置里统一指定 yyyy-MM-dd HH:mm:ss 格式。
第三是接口返回的音频 URL 必须能被外部直接访问。很多同学把音频放在项目内部资源目录,通过接口才能读到,结果播放器拿不到一个可请求的 URL,导致播放失败。音频文件应该放在静态资源目录或独立的上传目录,配置好静态资源映射,确保直接访问 URL 能返回文件内容,这个点必须在联调前验证。
5. 小程序端实现:页面怎么串、音频怎么播
5.1 页面规划与 tabBar 导航
一个小程序端的音乐应用,页面数量控制在六到七个比较合适,太多做不完,太少显得单薄。我推荐这样的结构:
- 首页:顶部搜索入口、热门歌曲榜、推荐歌单入口
- 歌单列表页:展示所有歌单,分页加载
- 歌单详情页:歌单封面、描述、歌曲列表,点击歌曲进入播放页
- 播放页:封面大图、歌曲名、歌手名、进度条、上一首/播放暂停/下一首
- 搜索页:搜索歌曲、歌手,结果列表
- 个人中心页:登录入口、我的收藏、我创建的歌单、关于
底部 tabBar 放三个:首页、歌单、我的。播放页和搜索页通过导航接口跳转,不进 tabBar。整个导航结构是“三主一辅”,用户心智清晰,开发量也合理。
5.2 音频播放:InnerAudioContext 的正确用法
小程序播放音频,官方提供 wx.createInnerAudioContext() 能力,它能播放远程或本地音频,支持进度、倍速、循环等控制。核心播放逻辑建议封装成一个工具模块甚至全局对象,避免每个页面各写一套。
播放流程是这样:用户点击某首歌,把歌曲信息存到全局数据,跳到播放页。播放页创建 InnerAudioContext 实例,设置 src 为歌曲的 audio_url,然后调用 play()。播放中用 onTimeUpdate 回调更新当前进度和剩余时间,拖动进度条时调用 seek(),播放结束触发 onEnded 则自动切到下一首。
最容易出 bug 的地方是切歌时没有销毁上一个播放实例。InnerAudioContext 相当于一个常驻播放器对象,如果每次切歌都新建而不销毁旧的,系统里会同时存在多个播放实例,旧的可能还在后台出声,新歌旧歌叠在一起。正确写法是每次切歌先 stop() 再 destroy(),然后重新创建。
js复制// 切歌前的清理
if (this.audioContext) {
this.audioContext.stop();
this.audioContext.destroy();
}
this.audioContext = wx.createInnerAudioContext();
this.audioContext.src = song.audioUrl;
this.audioContext.play();
另一个细节是播放状态同步。用户从首页点了歌曲 A,切到播放页开始播;返回首页再点歌曲 B,播放页也要更新成 B 并继续播。所以当前播放歌曲的信息要放到全局数据里,播放页监听全局数据变化来刷新 UI,而不是自己在页面的 data 里存一份。
5.3 后台播放与类目资质问题
这里提前打个预防针:小程序如果要“退出页面后继续播放”,也就是后台播放、息屏播放,是需要音乐类目资质审核的,个人主体通常无法开通。如果你在毕设里做了这个功能,答辩时“一切正常”,但真上线会被卡在资质环节。
毕设阶段怎么处理?两个思路。要么老老实实做“前台播放”,播放页存活时音频一直响,返回首页后停止播放,不需要额外资质,完全合规;要么做一个“悬浮播放条”,返回首页后页面顶部常驻一个小播放条,通过它快速切回播放页,既解决体验问题,又避开资质门槛。我推荐第二种,实现成本不高,演示观感提升非常明显。
5.4 请求封装与登录态处理
小程序端所有接口都走 utils/request.js 里封装的一个函数,把 wx.request 包一层。逻辑很简单:请求发起前从本地存储取 token,有就塞进 header;收到响应后先看返回码,200 正常返回 data,401 表示登录过期,清掉本地登录态并跳转到登录页,其他错误码弹出提示。
这个统一封装的作用是,以后任何页面调用接口都只需要关心业务数据,不用在每处写 token 和错误处理。代码长这样(示意):
js复制function request(url, method, data) {
return new Promise((resolve, reject) => {
const token = wx.getStorageSync('token');
wx.request({
url: BASE_URL + url,
method,
data,
header: token ? { Authorization: token } : {},
success(res) {
if (res.data.code === 200) {
resolve(res.data.data);
} else if (res.data.code === 401) {
wx.clearStorageSync();
wx.navigateTo({ url: '/pages/login/login' });
reject(new Error('未登录'));
} else {
wx.showToast({ title: res.data.message, icon: 'none' });
reject(new Error(res.data.message));
}
},
fail(err) { reject(err); }
});
});
}
baseURL 建议集中在一个配置文件里,本地调试、真机预览、上线部署三个环境分别写好注释,谁接手都能立刻改对。
5.5 域名合法性与开发者工具的小技巧
小程序真机访问后端接口,有个绕不开的规则:request 的域名必须在小程序管理后台配置成合法域名,还要 HTTPS。这条规则在开发阶段是最大的拦路虎,很多同学真机一测发现接口全挂了,以为代码写错了,其实只是域名没配。
开发阶段怎么绕过?开发者工具里有个选项叫“不校验合法域名、web-view(业务域名)、TLS 版本以及 HTTPS 证书”,调试时勾上它,接口随便填 IP 或 localhost 都能访问。真机预览需要手机和电脑连同一局域网,baseURL 改成电脑的局域网 IP。等真要上线了,再去后台配置服务器域名。这个技巧虽然不是什么高深知识点,但缺了它项目根本没法演示,必须单独强调。
6. 部署全流程:本地跑通、服务器上线,一步不落
6.1 第一步:本地把整套系统跑起来
部署这件事,先本地后服务器,顺序不能反。本地跑通的目标有三个:数据库脚本导入且后端启动无报错、后端接口自测通过、小程序能连上后端正常播放。
按顺序操作:新建数据库并导入 SQL;改 application.yml 里的数据库账号密码为本机实际值;在 IDE 里运行主启动类,观察控制台日志,出现“Started”字样说明启动成功;打开接口文档页面或用命令行工具测一个简单接口;最后在小程序开发者工具里导入前端工程,勾选“不校验合法域名”,baseURL 指向 http://localhost:8080,编译运行,走一遍注册、登录、浏览、播放流程。
如果哪一步卡住,别急着怀疑代码,按“数据库账号密码 → 端口是否占用 → baseURL 是否写对 → 后端是否真的启动成功”这个顺序排查,十有八九就在这四个原因里。
6.2 第二步:打包与服务器部署
本地确认无误后开始打包。在项目根目录执行:
bash复制mvn clean package -DskipTests
target 目录下会生成可执行的 jar 包。这一步常见报错是依赖下载失败,给 Maven 配一个国内镜像源基本能解决。
服务器这里不展开具体买哪家,常规操作是买一台轻量云服务器,装好 JDK 和 MySQL,把 jar 包上传上去。启动命令用:
bash复制nohup java -jar music-player.jar > app.log 2>&1 &
注意几个细节。MySQL 8 的密码认证方式有不少坑,如果后端连不上,看错误日志,多半是驱动版本或者 useSSL、时区配置问题,建议连接串加上 serverTimezone=Asia/Shanghai&useSSL=false。服务器防火墙和安全组记得放行后端端口,否则外部访问不通。日志输出到文件后,出问题直接 tail -f app.log 看报错,比黑盒猜原因高效得多。
6.3 第三步:反向代理、HTTPS 与小程序上线配置
后端 jar 跑起来后,默认是某个端口提供服务。小程序正式版要求 HTTPS,所以需要再加一层 Nginx 做反向代理和静态资源服务,同时把证书挂上去。证书可以申请主流免费渠道的证书,拿到后配置到 Nginx 即可。
Nginx 的关键配置片段示意:
nginx复制server {
listen 443 ssl;
server_name your-domain.com;
ssl_certificate /etc/nginx/cert/fullchain.pem;
ssl_certificate_key /etc/nginx/cert/privkey.pem;
location /api/ {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
}
}
然后把音频、封面等静态文件放在 Nginx 的静态资源目录,配好 location,保证小程序能直接拿到文件 URL。最后到小程序管理后台把域名添加到 request 合法域名,前端 baseURL 改成 https://your-domain.com,重新提交审核上线。
这里要提醒一句:本地和小程序开发者工具里一切正常,和“真机正式环境正常”是两码事。上线的坑主要集中在域名备案、证书链完整、合法域名配置这三点,建议至少提前一周把域名和证书搞定,别等答辩前夜才发现审核还没通过。
7. 踩坑实录:三类高频问题的完整排查链路
7.1 音频不播放或切歌后杂音
现象:点了播放按钮没有声音,或者切歌之后出现两个声音叠在一起。
排查链路:先看控制台有没有报错,比如 url not found 或 invalid src。如果 src 报错,多半是音频 URL 拼错了,或者后端返回的 audio_url 是相对路径,小程序端需要补上完整域名。如果 src 没问题但没声音,检查音频文件格式,部分手机系统对某些编码格式支持很差,建议统一转成 mp3 再放上去。如果是切歌后叠音,基本可以确认是没销毁上一个 InnerAudioContext,按前面说的 stop + destroy 处理即可。
我用一个表格总结过这类问题,排查时直接对照:
| 现象 | 可能原因 | 处理方式 |
|---|---|---|
| 播放没反应 | URL 不合法或格式不支持 | 检查 URL,统一用 mp3 |
| 切歌叠音 | 旧播放实例未销毁 | stop + destroy 后重建 |
| 真机播放失败 | 域名未配置或证书问题 | 勾选调试选项或配置合法域名 |
| 进度条不动 | onTimeUpdate 未绑定 | 创建音频实例后立即绑定回调 |
7.2 Token 过期后页面假死
现象:用户登录状态明明还在,但所有请求都返回 401,页面一直转圈不跳转登录页。
问题出在请求封装上。很多同学只在某个页面处理了 401,其他页面没处理,结果 token 一过期,收藏页、歌单页、个人中心页全部静默失败。正确做法是在统一的 request 函数里拦截 401,然后做两件事:清除本地存储的登录态,导航到登录页。只要所有请求都走这一个入口,修复就自动覆盖全应用。另外可以给 token 续期做个简单策略,比如某些接口返回新 token 时更新本地存储,体验会更好。
7.3 后端启动失败,排除代码后的三个隐蔽原因
现象:后端启动报错,日志一大片,看起来像代码问题,但代码其实没动过。
第一个隐蔽原因是 MySQL 时区问题。报错一般是“无法连接数据库”或提示时区无效,处理方式就是在连接串加 serverTimezone=Asia/Shanghai,一劳永逸。
第二个是端口被占用。服务器上可能跑了其他 Java 进程,8080 被占,报“Port already in use”。解决方法是改 server.port 或先停掉占用进程。启动前用命令看一眼端口监听情况,比等报错再猜快得多。
第三个是 JDK 版本不匹配。Spring Boot 2.7 系列用高版本 JDK 启动,可能表面没问题,但某些依赖在运行时会抛版本异常。建议严格按照项目要求的 JDK 版本安装,别贪新。
7.4 文件上传“假成功”:保存了但访问不到
还有一个高频坑是文件上传,现象是接口返回上传成功,数据库也存了路径,但浏览器访问返回 404。排查后发现是上传目录配成了相对路径,实际存到了 jar 包所在目录下的某个相对位置,和 Nginx 的静态资源根目录对不上。
解法就两条:上传目录一律配绝对路径,比如 /data/music/upload;Nginx 里单独配一个目录映射,路径对不上就 404。另外记得调大上传文件大小限制,小程序上传音频动辄几兆,Spring Boot 默认 1MB 限制会直接报错,在配置里放开:
yaml复制spring:
servlet:
multipart:
max-file-size: 50MB
max-request-size: 50MB
8. 答辩演示怎么讲,以及这个项目还能怎么长
8.1 五分钟演示脚本
答辩演示的核心原则:把最稳、最完整的链路走一遍,千万别临时演示冷门功能。我建议准备一套五分钟的脚本:
第一步,开场三十秒讲选题背景和整体架构,一页架构图讲清数据流向后端、小程序、数据库、服务器。第二步,打开小程序,演示注册或登录,顺手把 JWT 鉴权原理一句话带过。第三步,浏览首页热门歌曲和推荐歌单,进入歌单详情。第四步,点击一首歌进入播放页,展示进度、暂停、切歌,这是全场唯一“动”的部分,一定要提前用本地稳定音频反复测十遍。第五步,回到个人中心展示收藏列表,说明收藏的数据流转。最后用一分钟提一句部署:jar 包在云服务器上运行,Nginx 反代,HTTPS 访问。到这里老师通常已经对工程完整性有充分好感了。
8.2 可扩展的方向
如果答辩还剩时间,或者你想让项目在简历上更有竞争力,这几个方向都可以接:歌词滚动展示(拿到歌词文件后按时间戳切行);评论功能完整化(点赞、回复);歌单分享与二维码;基于播放次数和标签的简易推荐;后台统计页面。每个方向单独拿出来都能凑一小节论文内容,但对主系统零侵入,属于典型的增量扩展。
8.3 一点个人体会
做这种全套项目,我最大的体会是:真正让学生卡住的从来不是某一个难点,而是环境与集成。数据库连不上、域名没配、依赖版本错乱,这些问题让人觉得“代码有问题”,实际上问题全在工程外围。所以这篇文章里我刻意把本地跑通放在部署最前面,把常见坑单独列了一章,所有建议都围绕一件事——让一个没有生产环境经验的人,也能独立把一个全栈项目从零跑起来。
说到底,毕设不是让你炫技,是让老师相信你能独立完成一个完整工程。Spring Boot 加小程序的音乐播放器,恰好是一个既不会让你陷进复杂中间件泥潭、又能完整覆盖工程链路的平衡点。把代码跑通、把链路讲清、把坑避开,这个项目就能成为你简历上最拿得出手的一段实践经历。希望这篇拆解能让你少踩几个坑,祝答辩顺利。
