每年毕业设计季,我总能碰到好几个选体育新闻网站的同学。题目通常是“基于SpringBoot的WEB体育新闻网站”,或者长一点变成“基于SpringBoot+Vue的在线体育赛事与新闻发布系统”,听起来覆盖面很广,但很多人做着做着就变成了一个“新闻CRUD”——登录、加个富文本编辑器、发文章、列表展示,完事交差。这其实挺可惜的,因为体育网站真正难的从来不是发新闻,而是赛事数据组织、用户互动和实时看球体验这三件事。这篇文章我就把做这类全栈项目的完整思路写下来,从需求拆解、技术选型、数据库建模、核心功能实现,到联调部署和踩坑记录,全流程复盘一遍。内容既适合正在做这个毕设课题的同学,也适合想系统学习SpringBoot+Vue全栈开发的朋友。
1. 看懂需求再动手:体育资讯平台到底在解决什么问题
题目里“体育新闻网站”“全端体育资讯互动平台”“在线体育赛事与新闻发布系统”这几个说法,本质上是同一套系统的三个侧面。你首先要明白它想干什么:它不只是承载文章的CMS,还要覆盖赛事信息的结构化展示和用户互动,甚至要兼顾管理后台的发布效率。把这个搞清楚了,后面的设计才不会跑偏。
1.1 体育资讯场景的三个特性
体育新闻内容有很强的“时效性”“结构化”和“互动性”。时效性意味着比赛结束几分钟内要有快讯发布,这就要求后台发布流程足够简洁,不能为了发一篇战报还要填十个表单字段。结构化意味着比分、排名、射手榜、赛程这些数据不能往富文本正文里一塞完事,必须拆成独立的数据库表和字段。互动性则更直接,球迷看完一场比赛,情绪需要一个出口,评论、点赞、收藏这些功能不是锦上添花,而是这类站点的刚需。
这三点决定了很多通用博客系统或新闻网站的代码不能直接拿过来改改就用。比如你做一个普通的个人博客,文章表可能只需要标题、正文、发布时间就够了;但体育网站不行,单是一个赛事详情页,就需要关联两支球队、一个联赛、一个比分、一个比赛时间,还得算小组排名。数据模型一开始设计得不对,后面写接口和前端页面的时候会非常痛苦。
1.2 从使用者角度拆解需求
拿真实场景来看,这个平台至少有三种使用者,需求完全不一样:
| 使用者 | 核心诉求 | 对应的功能模块 |
|---|---|---|
| 游客 | 浏览资讯、查看比分和赛程、搜索感兴趣的内容 | 首页新闻流、赛事列表、赛事详情、搜索 |
| 注册用户 | 参与讨论、收藏内容、关注关注度高的球队或赛事、管理个人资料 | 登录注册、评论、点赞、收藏、个人中心 |
| 管理员/编辑 | 快速发布新闻、维护赛事数据、管理分类和评论、查看访问数据 | 后台管理、新闻管理、赛事管理、评论审核、统计面板 |
你做完这套分析就会发现,光是一个“登录”就能带出一串问题:游客可以看什么?登录之后能做什么?管理员和普通用户看到的接口是不是同一套?评论是否需要审核?这些都得在写代码前有一个明确结论。我的建议是:游客拥有所有查询接口的权限,注册用户可以执行评论、点赞、收藏等写操作,后台接口则统一走独立的前缀并做角色校验。
1.3 功能清单按优先级切一刀
毕设项目最忌讳的就是需求无限膨胀。我给一个合理的优先级划分:
基础闭环(必须做)
- 用户注册、登录、注销,统一JWT鉴权
- 新闻分类、列表、详情,后台新闻发布与修改
- 赛事列表、赛事详情、比分展示
- 评论、点赞、收藏
进阶加分(有余力做)
- 热门新闻排行榜、标签推荐
- 资讯内容中的图片上传与富文本编辑
- 后台数据统计(新闻量、用户量、访问量)
亮点功能(答辩加分项)
- 基于WebSocket的比分实时推送
- 视频集锦播放(M3U8)
- 关键词搜索与搜索高亮
先保证基础闭环真的能用,再考虑亮点。见过太多同学一上来就想做数据大屏,结果核心功能都没跑通,答辩现场演示到一半页面报错,得不偿失。
2. 技术选型的底气:为什么SpringBoot+Vue是当前最稳的组合
“SpringBoot + Vue”这个组合在Web开发里已经是近乎标准答案的存在,但你要能在答辩时说得清楚它到底好在哪,而不是只会说“因为大家都用”。
2.1 SpringBoot到底帮你省了哪些事
SpringBoot的核心价值是自动配置和生态整合。传统SSM项目要写一堆XML配置文件,SpringBoot用starter机制把这些都收敛成了依赖和约定。内嵌Tomcat解决了外置容器的部署问题,一个java -jar就能跑起来。对毕设来说,这意味着你可以把精力集中在业务代码而不是配置反复横跳上。
同时SpringBoot的周边生态非常成熟:MyBatis-Plus操作数据库、Spring Security做安全控制、Validation做参数校验、Mail发邮件通知,几乎每个功能都能找到对应starter。出了任何问题,搜索引擎上都有海量现成案例,这对独立开发的学习者来说价值极高。
2.2 Vue为什么适合做这类互动型站点
体育资讯互动平台的前端有典型的“多状态、多交互”特点:页面组件多、新闻列表要滚动加载、赛事状态要动态刷新、用户登录信息要全局共享。Vue的组件化开发把这些都管理得很舒服——每个页面拆成若干组件,组件内部只管自己的状态,跨页面的共享数据交给Vuex或Pinia。
另一个好处是Vue对中文开发者非常友好。Element Plus这类组件库提供现成的表格、表单、弹窗组件,后台管理系统几十分钟就能搭出基础界面。相比React庞大的生态链,Vue的学习曲线要平缓很多,特别适合毕设周期内边学边做。
2.3 技术方案对比:有多大必要非得前后端分离
很多同学会纠结:“我是不是用JSP+SpringBoot更简单?”我直接给结论:对这个题目来说,前后端分离明显更合适。
| 方案 | 优点 | 缺点 | 适合场景 |
|---|---|---|---|
| JSP + SpringBoot | 开发链路短,不用考虑跨域 | 前后端耦合严重,页面逻辑和Java代码混在一起,维护困难 | 老项目维护 |
| 若依/RuoYi脚手架 | 功能齐全,直接生成大量代码 | 代码复杂,答辩提问容易被难住,很难说清是自己的设计 | 快速原型 |
| SpringBoot + Vue 前后端分离 | 职责清晰,前端交互体验好,接口可复用,答辩有讲头 | 需要处理跨域、联调、部署等额外问题 | 本项目的正解 |
我个人不推荐零基础同学直接拿若依这类脚手架改。毕业设计答辩的核心是“你设计了什么、你解决了什么问题”,如果用脚手架改了改界面,老师追问RBAC设计、权限拦截、接口鉴权的时候,你很难讲明白。自己从零搭一套,哪怕简单一点,也完全经得起细问。
2.4 工程目录怎么组织才不混乱
我习惯把项目拆成两个独立目录,各自拥有独立的Git仓库:
后端结构:
backend/ ├── src/main/java/com/example/sports/ │ ├── controller/ # 接口层 │ ├── service/ # 业务逻辑层 │ ├── mapper/ # 数据访问层 │ ├── entity/ # 数据库实体 │ ├── dto/ # 接口传输对象 │ ├── config/ # 全局配置 │ └── common/ # 统一返回、异常处理、工具类前端结构:
frontend/ ├── src/ │ ├── views/ # 页面组件 │ ├── components/ # 公共组件 │ ├── api/ # 接口请求封装 │ ├── router/ # 路由配置 │ ├── store/ # 全局状态 │ └── utils/ # 工具函数前后端分离开发的第一步就是定好这个结构,不然随着代码变多,找文件都会变成一种折磨。
3. 数据库设计是关键:别把赛事数据当普通新闻存
数据库设计是整个项目的地基。我发现很多同学做这类系统最大的问题是“所有内容都往一张新闻表里放”,球队、比分、赛季、联赛全部用文本字段拼,最后写查询的时候一团乱麻。
3.1 用户与权限模型
用户模块我建议按轻量RBAC来设计,至少四张表:
- sys_user:用户表,字段包括id、username、password、nickname、avatar、status、create_time
- sys_role:角色表,id、role_name、role_code(比如ADMIN、EDITOR、USER)
- sys_user_role:用户角色关联表
- sys_menu:菜单/权限表(如果不想做太复杂,可以改成permission表,记录权限标识)
如果你觉得五张表太重,也可以用最简单方案:user表加一个role字段,存USER或ADMIN字符串。这个方案在答辩时也能说清楚,但“为什么不做标准RBAC”这个问题你得很合理,不然容易被动。我建议正统一点,用标准的RBAC模型,后面如果做后台菜单权限,直接复用。
3.2 新闻模块的数据表
新闻不能只做一张表,至少要拆出分类表和新闻表:
news_category:id、name、sort、status news:id、category_id、title、summary、content、cover_image、author_id、status、view_count、comment_count、like_count、is_top、publish_time、create_time、update_time
几个容易被忽略的字段说明:
- status:0草稿、1已发布、2已下线。发布和下线是后台最基本操作,必须有独立状态管理。
- summary:列表页展示用的摘要,不能把整个正文拿过去截取,性能和排版都难看。
- is_top:置顶标识,体育网站头部需要展示重要赛事快讯。
- view_count和like_count:冗余计数,读多写少的场景直接查冗余字段,不需要实时count。
content字段类型用LONGTEXT,富文本编辑出的HTML体积不小,TEXT类型可能不够用。图片不要存相对零碎的路径,建议统一存/uploads/2025/06/xxx.jpg这样带日期目录的格式,方便后续迁移和静态资源映射。
3.3 赛事模块的建模思路
赛事模块是这类网站和普通新闻网站最大的区别。比赛信息本身就具备结构化特征,必须拆表:
league(联赛表):id、name、logo、country、season team(球队表):id、name、logo、league_id、founded_year、home_court match_info(赛事表):id、league_id、home_team_id、away_team_id、home_score、away_score、match_time、match_round、status、venue
拆表的核心原因是射手榜、积分榜、球队详情页都需要从多个维度查询数据。比如一个球队详情页要展示“该队所有比赛”和“该队所在联赛的积分排名”,如果这些信息全塞在一张新闻表里,查询逻辑根本无法写。这里还可以衍生一张standings积分榜表,每轮比赛更新后重新计算排名,或者比赛更新时实时计算。毕设建议独立一个standings表,因为计算逻辑清晰,答辩好讲:
standings:id、league_id、team_id、matches_played、wins、draws、losses、goals_for、goals_against、points、rank
3.4 互动数据:评论、点赞、收藏怎么设计
互动数据有一个常见选择:点赞和收藏是分两张表,还是用一张表加type区分。我建议一张表加type字段,理由是点赞和收藏的行为结构高度一致,都是“用户user_id”对“内容business_id”做一次操作,无非是业务类型不同:
user_interaction:id、user_id、business_id、business_type(1新闻、2赛事)、type(1点赞、2收藏)、create_time
这样查询一个人的点赞列表和收藏列表都只需要一条SQL加条件筛选。评论表单独设计:
comment:id、user_id、business_id、business_type、content、parent_id、status、create_time
parent_id用于楼中楼回复,为0表示一级评论。status字段在体育社区非常重要,因为评论体量大、内容参差不齐,设置待审核/通过/删除三个状态,后台才能做内容管理。
3.5 核心建表SQL参考
我贴一段赛事模块的核心建表语句,照着这个风格扩展其他表即可:
CREATE TABLE `league` ( `id` bigint NOT NULL AUTO_INCREMENT, `name` varchar(64) NOT NULL COMMENT '联赛名称', `logo` varchar(255) DEFAULT NULL COMMENT '联赛logo', `country` varchar(64) DEFAULT NULL COMMENT '国家/地区', `season` varchar(32) DEFAULT NULL COMMENT '赛季,如2024-2025', `status` tinyint DEFAULT '1' COMMENT '1启用 0停用', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='联赛表'; CREATE TABLE `match_info` ( `id` bigint NOT NULL AUTO_INCREMENT, `league_id` bigint NOT NULL COMMENT '所属联赛', `home_team_id` bigint NOT NULL COMMENT '主队ID', `away_team_id` bigint NOT NULL COMMENT '客队ID', `home_score` int DEFAULT '0' COMMENT '主队比分', `away_score` int DEFAULT '0' COMMENT '客队比分', `match_time` datetime DEFAULT NULL COMMENT '比赛时间', `match_round` varchar(32) DEFAULT NULL COMMENT '轮次', `venue` varchar(128) DEFAULT NULL COMMENT '比赛场地', `status` tinyint DEFAULT '0' COMMENT '0未开始 1进行中 2已结束', PRIMARY KEY (`id`), KEY `idx_league_time` (`league_id`, `match_time`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='赛事表';我看到很多初学者会纠结到底加不加外键约束。演示项目加外键会自动建立索引、保证关联数据完整性,但对后期的数据更新和删除会造成麻烦,比如删除一个联赛,所有赛事都要级联处理。我的建议是表结构设计保留逻辑关联关系,不强制加物理外键,但一定要在常用查询字段上建索引,比如match_info的league_id和match_time。
4. 核心功能落地:从登录认证到视频播放的实现思路
需求想清楚了,表结构定好了,接下来就是最耗时的编码阶段。这一节我把几个关键功能的实现方案和代码骨架全部写出来,你可以直接照着推演。
4.1 JWT认证:简单方案还是Spring Security
安全认证是答辩一定会被问到的问题。对于这个项目,我建议你用手写拦截器 + JWT的方案,而不是直接上Spring Security。理由有三点:代码量小、逻辑可控、答辩时你可以清晰地从头讲到尾。Spring Security虽然功能强大,但概念太多,一旦配置不当,排查问题的成本可能比写整个业务还要高。
流程是这样的:
- 用户注册,密码使用BCrypt加密存储。
- 用户登录成功后,后端生成JWT令牌返回给前端。
- 前端把Token存放在本地存储,每次请求放在请求头:
Authorization: Bearer <token>。 - 后端写一个拦截器,拦截除登录注册外的所有请求,解析Token并校验有效期。
JWT生成的核心代码:
@Component public class JwtUtil { @Value("${jwt.secret}") private String secret; @Value("${jwt.expire}") private Long expire; public String createToken(Long userId, String username, String role) { return Jwts.builder() .setSubject(username) .claim("userId", userId) .claim("role", role) .setExpiration(new Date(System.currentTimeMillis() + expire)) .signWith(SignatureAlgorithm.HS512, secret) .compact(); } public Claims parseToken(String token) { return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody(); } }拦截器里把解析出的userId和role存入ThreadLocal,这样在Controller中就能随时获取当前登录用户的信息,而不需要每个接口都手动从Token里拆字段。记住在请求结束后要清理ThreadLocal,避免线程池复用时数据串号。
4.2 权限控制怎么做
权限控制在SpringBoot中有几种常见方式:一是上面说的拦截器判断请求路径前缀,二是使用Spring Security的@PreAuthorize注解,三是自定义注解+AOP。毕设推荐方案一,简单直观。
比如后台管理接口统一以/api/admin/**开头,拦截器里判断登录用户角色是否为ADMIN或EDITOR,不是就直接返回403:
if (request.getRequestURI().startsWith("/api/admin/")) { String role = (String) claims.get("role"); if (!"ADMIN".equals(role) && !"EDITOR".equals(role)) { response.setStatus(403); return false; } }普通用户只能访问自己id下的资源这种细粒度校验,尽量放在Service层做,而不是全部丢给拦截器,因为拦截器拿不到业务上下文,硬搞只会让代码越来越绕。
4.3 富文本编辑与图片上传
新闻发布功能绕不开富文本编辑器。国内比较省心的选择是wangEditor,中文文档完整,UI简洁,直接用npm引入即可,不需要额外的复杂配置。Quill也很优秀,但是分页、上传图片这些扩展需要自己调,对新手来说有门槛。
富文本编辑器里的图片上传是个关键细节。编辑器只会把图片以base64形式嵌入,如果你没有配置自定义上传,一篇带大图的文章可能直接把后端接口打爆。正确的做法是:前端配置编辑器使用自定义上传接口,把图片传到后端,后端返回图片的访问URL,编辑器再把URL插入内容里。
后端上传接口需要注意三点:
- 文件类型白名单:只允许jpg、png、gif、webp
- 文件大小限制:单张图片建议不超过5M
- 随机文件名:避免用户传一个中文名或特殊字符
图片存储路径建议配置一个upload目录,然后通过配置静态资源映射,让SpringBoot直接暴露这个目录:
@Configuration public class WebMvcConfig implements WebMvcConfigurer { @Value("${file.upload-dir}") private String uploadDir; @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/uploads/**") .addResourceLocations("file:" + uploadDir + "/"); } }这样上传成功后返回的URL就是http://localhost:8080/uploads/2025/06/xxx.jpg,图片既不需要打进前端代码包,也不需要额外引入OSS。
4.4 赛事信息展示与比分更新
赛事列表页建议直接按时间倒序查询“今天和未来一周”的比赛,前端用卡片展示主队、比分、客队、联赛logo和比赛状态。比分更新入口放在管理后台,编辑比赛结束后保存比分,同时把状态改为“已结束”。
这里要做一个判断:比分更新之后,前端怎么拿到最新数据?两种方案:
**方案A:前端定时轮询。**前端每隔30秒请求一次赛事列表接口,看数据有没有变化。实现简单,缺点是不够实时。
**方案B:WebSocket广播。**后端在比分更新时,向所有连接的客户端推送一条消息。前端收到消息后重新拉取数据或者直接更新本地状态。
我推荐你优先实现方案A,把系统跑通,然后花半天时间额外加一个WebSocket比分推送作为答辩亮点。这个功能的实现思路是:前端连上/ws/live,后端在比分更新接口中通过广播对象推送新比分,前端页面监听消息后刷新对应卡片。WebSocket和轮询结合的方式也是大多数体育网站的真实做法。
4.5 M3U8视频播放:防晕指南
体育资讯网站经常要挂视频集锦,而目前视频流最常见的格式是M3U8。这个和热词里的“vue播放m3u8免安装”“web端实时视频”直接相关。前端播放M3U8流,我推荐使用video.js结合videojs-contrib-hls插件,或者单独使用hls.js。
以hls.js为例,在Vue组件里播放一个M3U8地址:
<template> <video ref="videoEl" controls class="video-player"></video> </template> <script> import Hls from 'hls.js' export default { name: 'M3U8Player', props: { src: { type: String, required: true } }, mounted() { if (Hls.isSupported()) { const hls = new Hls() hls.loadSource(this.src) hls.attachMedia(this.$refs.videoEl) hls.on(Hls.Events.MANIFEST_PARSED, () => { this.$refs.videoEl.play() }) } else if (this.$refs.videoEl.canPlayType('application/vnd.apple.mpegurl')) { // Safari原生支持 this.$refs.videoEl.src = this.src } } } </script>播放M3U8最常见的问题是跨域导致请求失败。M3U8的本质是HTTP请求一个多段的m3u8索引文件,然后在播放过程中不断请求ts分片文件。服务器要正确响应Range请求头,代理配置也要允许Range头透传。如果你后端用Nginx做代理,需要确认proxy_set_header Range $http_range;这类配置没有丢掉。
4.6 搜索与热门推荐
搜索功能毕设阶段没必要上Elasticsearch,MySQL本身就够用。最简单的方案是:
SELECT * FROM news WHERE status = 1 AND (title LIKE CONCAT('%', #{keyword}, '%') OR summary LIKE CONCAT('%', #{keyword}, '%')) ORDER BY publish_time DESC这个写法能跑,但只有一两万数据没问题;如果数据量大,建议加个MySQL全文索引。推荐热门内容,可以设计一个加权排序:
SELECT id, title, view_count, like_count, comment_count, publish_time, (view_count + 2 * like_count + 3 * comment_count) / (TIMESTAMPDIFF(HOUR, publish_time, NOW()) + 2) AS hot_score FROM news WHERE status = 1 ORDER BY hot_score DESC LIMIT 10;这个公式的含义是:浏览数、点赞数、评论数越多越热,同时距离发布时间越近,时效权重越高,防止旧闻霸榜。答辩时你把这个公式推导讲清楚,比任何花里胡哨的算法都更有说服力。
5. 前后端联调与部署中的硬核细节
前后端分离开发,前期各自跑得很欢,一到联调阶段就各种“诡异问题”。这一节我把最容易卡住人的几个点全部过一遍。
5.1 解决跨域:代理优先,CORS兜底
开发环境最省心的方式是前端代理。在Vite项目中配置:
// vite.config.js export default defineConfig({ server: { port: 3000, proxy: { '/api': { target: 'http://localhost:8080', changeOrigin: true }, '/uploads': { target: 'http://localhost:8080', changeOrigin: true } } } })这样前端页面里请求/api/news/list,会被Vite开发服务器转发到http://localhost:8080/api/news/list,浏览器里看不到任何跨域报错。
如果某些大型网络环境不好用代理,必须后端开CORS,那么可以配置一个全局CORS。注意:开了跨域之后,处理预检请求OPTIONS时必须放行,否则AJAX请求会一直失败。曾经帮人排查过一个诡异问题:前端Post请求一直报405,最后发现是Controller没处理OPTIONS请求,踩了一天坑。
5.2 Vue打包后如何放进SpringBoot
这个操作被问过无数遍。前端项目执行npm run build后会生成一个dist目录,里面包含index.html、js、css等静态资源。把dist目录下的内容直接复制到SpringBoot项目的src/main/resources/static目录,SpringBoot会自动作为静态资源托管。
但有一个关键配置,不改就会出大问题。Vue默认的构建配置会生成绝对路径的静态资源引用,比如/assets/index-xxx.js,而你的项目可能部署在服务器根路径下,这样没问题;一旦你不想用根路径,或者端口后面还要加前缀,就必须改:
// vite.config.js export default defineConfig({ base: './' })base: './'让打包后的index.html里的资源引用变成相对路径,不管部署在哪一层路径下都能找得到静态文件。
5.3 路由刷新404:这个坑几乎人人踩
Vue采用history路由模式时,地址是http://xxx.com/news/123这种形式。本地开发没问题,因为Vite开发服务器会做fallback处理。但打包放进SpringBoot之后,你直接访问http://xxx.com/news/123,SpringBoot会拿着/news/123去找静态资源,找不到就返回404。这就是热词里“vue路由”“vue打包放进springboot中”最常见的问题。
解决办法是让SpringBoot把所有不是/api、不是静态资源的路径,都转发到index.html。前端路由再由Vue接管。在SpringBoot中写一个转发Controller:
@Controller public class ForwardController { @GetMapping(value = {"/", "/news/**", "/match/**", "/user/**"}) public String forward() { return "forward:/index.html"; } }更好的做法是不在代码里枚举路径,而是通过重写资源处理器实现:先尝试找静态资源,找不到就转发到/index.html。不管哪种思路,核心都是把“未知路径”交给前端路由,而不是交给404页面。
5.4 生产环境配置三件套
本地跑通只是第一步,部署到服务器还经常有人翻车。最关键的三件事:
第一,配置文件要区分环境。application.yml放公共配置,application-dev.yml放本地数据库,application-prod.yml放服务器配置,启动时用--spring.profiles.active=prod选择环境,避免本地密码不小心提交到仓库。
第二,数据库时区问题。MySQL连接串一定要加serverTimezone=Asia/Shanghai,否则插入时间和真实时间会差8个小时。useSSL=false也要显式声明,避免开发环境因为没有SSL证书狂打警告日志。
第三,文件上传大小限制。SpringBoot默认上传限制是1MB,富文本里传几张高清图就报错了,在配置里放开:
spring: servlet: multipart: max-file-size: 10MB max-request-size: 50MB启动脚本我习惯写成这样:
#!/bin/bash nohup java -jar sports-news-1.0.0.jar \ --spring.profiles.active=prod \ --server.port=8080 \ >> logs/app.log 2>&1 &日志重定向到文件,前端页面导致的问题至少能通过日志定位,而不是跑到服务器上抓瞎。
6. 实测踩坑记录:这几个问题几乎见一个踩一个
开发过程中踩坑是常态。这里直接列出我在类似项目中实际遇到过、也最有代表性的几个问题,每一个都是花过时间查资料、翻源码总结出来的。
6.1 SpringBoot版本与Java版本不匹配
这个真的非常经典。现在很多人新建项目习惯直接选最新的SpringBoot版本,而SpringBoot 3.x要求Java 17以上。如果你本机装的是Java 8,创建完项目之后就会一直报编译错误。出现“SpringBoot版本太高”类似问题的,绝大多数都是版本对不上。
我的建议是看本机JDK版本定SpringBoot版本:
| 本机JDK | SpringBoot版本 | MyBatis-Plus版本推荐 |
|---|---|---|
| JDK 8 | 2.7.x | 3.5.3及以上兼容即可 |
| JDK 11 | 2.7.x 或 3.x | 3.5.5+ |
| JDK 17 | 3.x | 3.5.5+(注意需要适配项) |
启动类报红,第一反应不是怀疑代码,而是先看项目SDK版本和Maven依赖的版本对不对。
6.2 hamcrest与JUnit版本冲突
如果你引入MyBatis-Plus或者做单元测试时,碰到“No tests found”或者测试类报java.lang.NoSuchMethodError,八成是JUnit 4和JUnit 5的hamcrest版本冲突。SpringBoot用JUnit 5做单元测试,默认依赖的hamcrest是2.2版本。如果你手动引了旧版hamcrest,就会在运行测试时各种诡异报错。解决方式是注释掉自己额外引入的hamcrest依赖,让SpringBoot统一管理版本。
6.3 MyBatis-Plus分页不生效:没配置分页插件
MyBatis-Plus的selectPage方法如果没有配置分页拦截器,表面上不报错,但传入的Page对象根本不会执行分页,会把全表数据都查出来。这个坑非常隐蔽,尤其是列表数据量小的时候根本看不出问题。
正确配置分页插件:
@Configuration public class MybatisPlusConfig { @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; } }6.4 M3U8视频流跨域与Range请求
前端能正常访问接口,但M3U8视频就是播不出来,控制台报NETWORK_ERROR或MEDIA_ERR_SRC_NOT_SUPPORTED。这种情况八成是服务器没有正确处理视频文件的Range请求,或者后端代理没有把Range头传递给视频存储服务器。排查步骤很简单:用curl请求视频地址,看返回头里有没有Accept-Ranges: bytes和Content-Length。没有这个头,浏览器就不会按分片方式播放。如果你用Nginx托管的视频文件,检查Nginx配置里是不是被CDN缓存或者压缩模块改掉了相关头。
6.5 给接口写一份文档,省下大量返工时间
这个是我个人最想强调的一点。做完整套项目后,给自己写一份接口文档,把每个接口的请求方式、参数、返回值字段列清楚。这不是额外工作,而是联调和答辩的保命符。前后端分离开发时,你经常为一两个字段来回沟通;没有文档就只能一次次翻代码。答辩时老师如果让你说明某个接口怎么设计,你直接掏出文档,马上就显得专业。
最后再分享一个我自己做体育类项目的小技巧:新闻系统和赛事系统不要做成两套独立的代码,让赛事模块直接复用新闻模块的评论、点赞、收藏逻辑,互动数据用business_type字段区分业务。这样核心代码集中在几个Controller里,后期加新的互动对象(比如直播、球员评分)就像加一个枚举值一样简单。代码量最少,结构也最干净,答辩时也很容易讲清楚设计思路。