1. 项目概述与选题思路
1.1 为什么选“校园生活服务平台”这个题目
每年到毕设季,后台咨询最多的题目类型之一就是“Spring Boot + Vue”。原因很简单:这个组合是目前国内中小型系统开发最主流的技术栈,企业里用得多,网上教程也多,遇到问题容易找到解决方案,对于需要独立完成一个完整项目的毕业生来说,容错率最高。
但“Spring Boot + Vue”能做的题目太多了,图书管理、在线考试、宿舍管理……我特别推荐校园生活服务平台,是因为它的业务场景足够丰富,能覆盖到一个毕设项目应展示的所有核心技术点:用户体系、信息发布与审核、文件上传、站内搜索、消息通知,甚至地图定位。整个系统的复杂度适中——太简单了体现不出工作量,太复杂了毕设时间又撑不住。
最关键的一点是,校园生活服务平台本身就是解决真实痛点的系统。大学校园里信息极度碎片化:二手物品交易散布在几十个QQ群里,社团活动通知靠朋友圈转发,失物招领可能还停留在贴公告栏的阶段。一个集中式的服务平台,是学生真的会去用的产品,这样的项目无论是写论文、做答辩展示,还是后续扩展,都有足够的 “故事”可讲。
1.2 技术选型:Spring Boot + Vue 为何是黄金组合
先说后端。Spring Boot 选它的理由不用多讲,简化配置、内嵌Tomcat、生态完善,一个main方法就能把服务跑起来。Spring Boot 3.x 搭配 Java 17 是目前推荐的选择,如果你的电脑还在用 Java 8,那安稳选 Spring Boot 2.7.x 就好,没必要为难自己。
前端选 Vue 的原因也很直接。Vue 的学习曲线比 React 平缓,模板语法接近原生 HTML,一个后端出身的学生从零上手到能写页面,大概只需两周。Vue 的响应式机制和组件化开发模式,在维护一个小型中后台系统时极其舒服。
这里强调一个概念——前后端分离。这在毕设答辩时基本上是必问的知识点。简单说,前端和后端是两个独立部署的应用,通过 RESTful API 进行数据交互。前端只管页面渲染和交互,后端只管业务逻辑和数据存取。这带来的实际好处我后面会结合项目细讲。
2. 系统架构与功能模块设计
2.1 系统整体架构分层
在做任何编码之前,先把架构图画出来,这是我从一开始就强调的。校园生活服务平台采用经典的三层架构,整体结构如下:
- 前端展示层:Vue 3 + Vue Router + Pinia,配合 Element Plus 组件库构建后台管理界面,Vant 构建移动端界面。
- 后端服务层:Spring Boot 提供 RESTful API,Spring Security 负责认证与授权。
- 数据存储层:MySQL 存储结构化业务数据,Redis 处理缓存和验证码,MinIO 做文件对象存储,Elasticsearch 如果有余力可以引入做全文检索,没有也不影响主体功能。
这种分层的核心价值只有一个:当某一层需要变化时,尽量不影响其他层。比如数据库从 MySQL 切换到 PostgreSQL,业务代码不需要动;前端把首页改版,后端接口也不需要动。听起来有点像教科书知识点,但实际开发中你就知道它有多重要了。
对于毕设项目,我建议 Controller 层做参数接收和数据校验,Service 层写业务逻辑,Mapper 层(或 Repository 层)负责数据库操作,三层职责分明,答辩时老师问起来也回答得清楚。
2.2 核心业务功能拆解
校园生活服务平台的功能模块,我建议做成五个核心模块加一个后台管理模块。毕设不是越大越好,而是每个模块都能讲出业务逻辑和技术亮点。
第一个模块是用户系统。校园平台必须面对的一个问题是身份真实性问题。这里的方案是学生注册时填写学号,通过邮件验证码或管理员审核的方式确认身份。用户角色分为普通学生、管理员、超级管理员三种,对应不同的操作权限。Spring Security + JWT 做无状态认证,用户登录后拿到 Token,后续请求带着 Token 访问受保护资源。
第二个模块是二手市场。这是校园平台活跃度最高的功能,用户可以发布闲置物品、浏览分类、搜索商品、发起留言或直接联系卖家。这里最值得做的功能点是图片上传,多张商品图、压缩处理的逻辑,能让你在论文里写出一小节的干货。技术上我用 MinIO 做对象存储,关于为什么要引入 MinIO,我后面单独展开。
第三个模块是失物招领。发布丢失物品和捡到物品的信息,支持按类型、时间、地点筛选,寻回后可以标记状态。这个模块看似简单,但它是体现平台社会价值的点睛之处,建议保留轻量级的评论或留言功能,增加系统的互动性和论文内容量。
第四个模块是活动发布与报名。社团或学生会可以发布活动,学生浏览活动详情并在线报名。这里需要处理的一个核心逻辑是报名人数限制和截止时间判断。这个模块适合展示事务处理和定时任务的应用场景——活动结束后系统自动发送提醒通知。
第五个模块是校园互助问答(或者叫校园论坛)。学生可以发帖提问,其他人可以回复。这个模块我建议做基础版即可,发帖、回帖、浏览量统计,不用搞得太复杂——因为如果时间预算有限,这个模块是最容易被砍掉也不影响系统完整性的。
后台管理模块对应管理员端的功能,核心功能包括用户管理(审核注册、封禁用户)、内容管理(审核帖子、处理举报)、数据看板(用户数、发帖数、成交量统计图表)。
2.3 为什么把MinIO加入技术栈
关于文件存储,很多毕设项目直接用本地路径存储上传图片。在你单人开发、并发量极低的情况下这当然没问题,但项目部署到服务器时问题就来了:图片存储路径写死在代码里,迁移服务器就得改代码重新打包发布,多台服务器时每台机器上文件不同步,所有上传的图片在重启时丢失——这些坑我都踩过。
用 MinIO 解决这些问题是非常轻量的方案。MinIO 是一个开源的对象存储服务,兼容 S3 协议的 API,Docker 一行命令就能在本机跑起来,代码层面的接入只需引入依赖。将图片、视频等静态资源放到对象存储里,业务数据和静态资源分离管理,部署时不用再担心文件路径问题。
Spring Boot 整合 MinIO 的代码非常简单:
@Configuration public class MinioConfig { @Value("${minio.endpoint}") private String endpoint; @Value("${minio.access-key}") private String accessKey; @Value("${minio.secret-key}") private String secretKey; @Bean public MinioClient minioClient() { return MinioClient.builder() .endpoint(endpoint) .credentials(accessKey, secretKey) .build(); } }文件上传的 Service 代码核心也就十几行:
public String uploadFile(MultipartFile file, String bucketName) { // 生成唯一文件名,这里用 UUID 避免重名 String objectName = UUID.randomUUID().toString().replace("-", "") + getFileExtension(file.getOriginalFilename()); // 检查bucket是否存在,不存在则创建 boolean found = minioClient.bucketExists(BucketExistsArgs.builder() .bucket(bucketName).build()); if (!found) { minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build()); } // 上传文件 minioClient.putObject(PutObjectArgs.builder() .bucket(bucketName) .object(objectName) .stream(file.getInputStream(), file.getSize(), -1) .contentType(file.getContentType()) .build()); // 返回可访问的URL return endpoint + "/" + bucketName + "/" + objectName; }注意:如果 MinIO 和前端应用不在同一个域名下,上传后返回的 URL 会存在跨域问题。建议在 MinIO 的 Bucket 设置中配置 Access Policy 为 public,或者通过 Nginx 对
/minio路径做反向代理转发,让图片 URL 和你的应用同源。
3. 后端开发:Spring Boot 核心实现
3.1 项目初始化与基础配置
后端项目可以从 start.spring.io 生成,也可以直接用 IDEA 内置的 Spring Initializr。这里注意一下Spring Boot 版本问题,很多人在这一步就卡住了:某个依赖找不到对应的 starter 版本,或者某段配置在新版本里被标记为废弃。建议不要无脑选择最新版本,而是选择一个稳定版本。我实测下来 Spring Boot 3.2.x 配 Java 17 是当前比较稳的组合。
项目的包结构我建议这样规划,一目了然:
com.campus.life ├── controller // 接口层,接收请求、返回响应 ├── service // 业务逻辑层,接口+实现 │ └── impl ├── mapper // 数据访问层(MyBatis-Plus) ├── entity // 数据库实体类 ├── dto // 数据传输对象 ├── vo // 视图对象 ├── config // 配置类(安全配置、跨域配置、MinIO等) ├── common // 通用类(统一返回结果、异常处理、工具类) ├── utils // 工具类 └── CampusLifeApplication.java // 启动类application.yml的基础配置建议这样写:
server: port: 8080 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/campus_life?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai username: root password: xxxxxx redis: host: localhost port: 6379 servlet: multipart: max-file-size: 10MB max-request-size: 50MB mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl global-config: db-config: id-type: auto minio: endpoint: http://localhost:9000 access-key: minioadmin secret-key: minioadmin bucket-name: campus-images统一返回结果这个设计很老套,但极其好用。定义一个Result<T>类,包含 code、message、data 三个字段,所有接口都返回这个结构,前端拦截器统一处理,减少了大量沟通成本。
3.2 用户认证与权限控制实战
校园平台的权限控制,我分三层来讲:认证、授权、接口安全。
认证采用 JWT 方案。用户输入学号和密码登录成功后,后端生成一个 Token,Token 里面包含用户 ID、角色等关键信息,设置合理的过期时间(我建议 24 小时,过期后需要重新登录)。前端拿到 Token 存入 localStorage,之后在每个请求头里加Authorization: Bearer <token>。
Spring Security 的过滤链配置是这个部分的核心,我贴一段核心配置:
@Configuration @EnableWebSecurity public class SecurityConfig { @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http.csrf().disable() .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() .authorizeHttpRequests(auth -> auth .requestMatchers("/api/auth/**", "/api/public/**", "/minio/**").permitAll() .requestMatchers("/api/admin/**").hasRole("ADMIN") .anyRequest().authenticated() ) .exceptionHandling().authenticationEntryPoint(unauthorizedHandler()); // 添加JWT过滤器 http.addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class); return http.build(); } }这段配置里的三个关键设计要多注意:关闭 CSRF,因为我们是无状态 API 服务;配置放行路径,登录注册接口和公开资源不拦截;管理员接口必须 ADMIN 角色才能访问。
JJWT 库生成 Token 的工具类封装大概二十行代码,核心方法如下:
public String generateToken(Long userId, String role) { Date now = new Date(); Date expire = new Date(now.getTime() + 86400000); // 24小时 return Jwts.builder() .setSubject(String.valueOf(userId)) .claim("role", role) .setIssuedAt(now) .setExpiration(expire) .signWith(SignatureAlgorithm.HS256, secretKey) .compact(); }3.3 业务接口设计与事务处理
关于接口设计,我踩过一次很深的坑,想把教训分享出来:一开始把后端接口设计得太“细”,前端每查一次数据就调两三个接口。比如获取商品详情,要调商品接口拿基本信息,再调图片接口拿图片列表,再调用户接口拿卖家信息。后来意识到这样做的坏处很明显——前端页面渲染慢,而且接口之间还有依赖关系,任何一个接口出了问题,页面就渲染不出来。
接口设计的原则是按页面需求聚合数据。一个页面需要什么数据,后端就提供一个接口返回完整的数据结构。比如详情页接口就返回一个复合对象:商品基本信息 + 图片列表 + 卖家信息 + 收藏状态,一次请求全部搞定。这对前端开发效率的提升非常明显。
事务处理的典型场景是“发布商品”这个功能。发布信息时要同时做两件事:将商品记录插入商品表,扣减用户的发布额度或者记录发布日志。这两个操作必须同时成功或同时失败。用@Transactional注解解决得非常优雅:
@Transactional(rollbackFor = Exception.class) public Long publishItem(ItemPublishDTO dto) { Item item = new Item(); BeanUtils.copyProperties(dto, item); item.setUserId(CurrentUser.get().getId()); item.setStatus(0); // 待审核状态 itemMapper.insert(item); // 如果启用了发布审核日志,一并写入 auditLogMapper.insert(new AuditLog(item.getId(), "发布商品", "待审核")); return item.getId(); }我在这里强烈建议:所有涉及多表写入的 Service 方法都标注@Transactional。这不是一个可有可无的优化,而是数据一致性的基本保障。答辩时老师如果问“发布商品时如果数据库写入插入商品表成功但日志表失败,会怎样”,你就能从容回答。
4. 前端开发:Vue 3 核心实现
4.1 Vue 环境搭建与项目结构规划
前端环境搭建是很多人卡住的第一个地方。我梳理一个标准流程:先安装 Node.js(建议 18 以上版本),然后配置 npm 镜像源,接着用 Vite 创建项目。
# 安装 Node.js 18+,官网下载安装包即可 # 检查安装 node -v npm -v # 配置镜像源(国内网络环境必须做) npm config set registry https://registry.npmmirror.com # 创建 Vue 3 项目 npm create vite@latest campus-life-ui -- --template vue # 进入项目目录并安装依赖 cd campus-life-ui npm install # 安装路由和状态管理 npm install vue-router@4 pinia # 安装 UI 组件库(Element Plus 或 Vant,看你用端还是移动端) npm install element-plus npm install axios # 启动开发服务器 npm run dev这个流程跑完,你的前端骨架就搭好了。vite.config.js里一定要配置开发服务器代理,否则请求后端接口会产生跨域问题:
export default defineConfig({ plugins: [vue()], server: { port: 5173, proxy: { '/api': { target: 'http://localhost:8080', changeOrigin: true } } } })4.2 前端路由设计与权限控制
Vue Router 是前端的核心。校园服务平台的页面可以分成三类:公开页面(登录注册、首页、商品详情、失物招领列表)、用户页面(个人中心、发布信息、报名记录)、管理员页面(用户管理、内容审核、数据统计)。这就涉及到一个毕设高频题:动态路由。
动态路由为什么用?因为不同角色的用户看到的菜单和能访问的页面是不同的。实现思路是:用户登录后,后端返回该用户可访问的路由表(菜单权限数据),前端通过router.addRoute动态添加路由规则。代码核心如下:
// 在用户登录拿到权限列表之后 const menuRoutes = buildRoutes(userInfo.permissions); // 构建路由规则 menuRoutes.forEach(route => router.addRoute(route));如果你做的是精简版,也可以只用静态路由配合beforeEach路由守卫做判断:
router.beforeEach((to, from, next) => { const token = localStorage.getItem('token') if (to.path === '/login') { next() } else if (!token) { next('/login') } else if (to.meta.role && to.meta.role !== userInfo.role) { next('/403') // 无权限访问 } else { next() } })Vue Router 的路由配置中还有个实用技巧——懒加载。采用懒加载后,页面代码会被按需分包加载,首次加载速度显著提升。代码写法如下:
const routes = [ { path: '/item-detail/:id', name: 'ItemDetail', component: () => import('../views/ItemDetail.vue') } ]4.3 前端交互核心:Axios 封装与状态管理
Axios 封装是前端开发中值得认真做的事情。我推荐封装一个统一请求模块,把 baseURL、超时时间、请求拦截器(自动携带 Token)、响应拦截器(统一处理业务错误码和无权限状态)都内置进去。
响应拦截器里有一个经验值得分享:后端返回的 code 为 200 时正常放行,code 为 401 时自动跳转登录页并清除本地 Token,其他 code 则统一弹出错误提示。这样前端业务代码里就不需要每个请求都写错误处理逻辑了,代码量减少一大半。
状态管理我用的 Pinia,它是 Vue 3 官方推荐的状态管理库。核心使用场景是保存用户信息、购物车数据、站点全局配置。一个典型的用户 Store 代码如下:
export const useUserStore = defineStore('user', { state: () => ({ token: localStorage.getItem('token') || '', userInfo: {}, permissions: [] }), actions: { async login(loginForm) { const { data } = await api.post('/api/auth/login', loginForm) this.token = data.token this.userInfo = data.userInfo this.permissions = data.permissions localStorage.setItem('token', data.token) }, logout() { this.token = '' this.userInfo = {} this.permissions = [] localStorage.removeItem('token') } } })测试时我发现一个很容易忽略的细节:刷新页面时 localStorage 中有 Token,但 Pinia 的 store 里数据已经丢了。解决方案是在 main.js 初始化时重新从 localStorage 恢复用户状态,或者在路由守卫里检测用户信息为空时再调用一次获取用户信息的接口。
5. 论文写作:结构框架与核心章节详解
5.1 毕业论文的标准结构拆解
论文和代码同样重要,甚至可以说论文决定了毕设成绩的上限。很多代码写得不错的人,论文结构混乱,一样拿不到高分。我建议的论文结构是八章:
- 第一章 绪论:研究背景与意义、国内外研究现状、研究内容与论文结构。
- 第二章 相关技术介绍:Vue、Spring Boot、MySQL、Redis、MinIO 的核心概念和选型理由。
- 第三章 系统分析:可行性分析、需求分析、功能模块划分、用例图、非功能性需求。
- 第四章 系统设计:总体架构设计、数据库设计(ER图+数据字典)、模块详细设计、接口设计。
- 第五章 系统实现:每个功能模块的实现思路、核心代码展示、界面截图。
- 第六章 系统测试:测试环境、功能测试用例、测试结果分析。
- 第七章 总结与展望:项目完成情况、遇到的问题和解决方案、不足之处和未来改进方向。
从实际指导经验来看,论文的核心只在第三章、第四章和第五章。这三章写充实了,论文质量就有保障了。
5.2 需求分析和数据库设计怎么写
需求分析不要写“系统需要实现用户管理功能”这种大而空的表述。要写具体到角色的场景描述,举一个示范:
学生用户登录系统后,可以在二手市场模块中查看商品分类列表,点击某个分类查看该分类下的所有商品。商品以卡片形式展示,包括商品缩略图、标题、价格和发布者信息。用户点击商品卡片可以进入商品详情页,浏览商品图片、详细描述、成色信息及卖家的校园认证信息。用户可以通过“联系卖家”按钮与卖家取得联系,也可以点击“收藏”按钮将商品加入个人收藏夹。
这种写法的好处是既给数据库设计提供了依据,又给系统实现的测试用例提供了基础。
数据库设计是整个论文里最好凑字数也最显功力的部分。建议使用数据库建模工具(如 Navicat、PDManer)先画出 ER 图,再列出核心表结构。我列出几张关键表的设计,你参考一下:
用户表(user):
| 字段名 | 类型 | 说明 |
|---|---|---|
| id | bigint | 主键,自增 |
| student_no | varchar(20) | 学号,唯一索引 |
| password | varchar(100) | BCrypt加密后的密码 |
| nickname | varchar(50) | 昵称 |
| avatar | varchar(255) | 头像URL |
| role | tinyint | 0-普通用户 1-管理员 |
| status | tinyint | 0-待审核 1-正常 2-封禁 |
| create_time | datetime | 注册时间 |
商品表(item):
| 字段名 | 类型 | 说明 |
|---|---|---|
| id | bigint | 主键 |
| user_id | bigint | 发布者ID,关联user表 |
| title | varchar(100) | 商品标题 |
| description | text | 商品描述 |
| price | decimal(10,2) | 价格 |
| category | varchar(50) | 分类 |
| images | varchar(2000) | 图片URL列表,JSON格式或逗号分隔 |
| status | tinyint | 0-待审核 1-在售 2-已售 3-下架 |
| view_count | int | 浏览数 |
| create_time | datetime | 发布时间 |
设计表时要特别注意外键逻辑和索引。比如查询某分类的商品列表是高频操作,就一定要在category字段上建索引。联合查询时,优先使用关联表的主键。
5.3 论文格式规范与查重降重经验
格式问题是论文最容易丢分的地方。字体字号、行距、页边距,每个学校都有自己的模板,务必下载官方模板,逐个修改样式,不要自己凭空设置。图表编号、参考文献格式要统一。
这里说下论文查重降重的经验。技术背景介绍的章节是查重的重灾区,因为描述 Spring Boot、Vue 特性的句子,网上资料几乎是雷同的。实际操作方法:用自己的理解改写技术介绍,打乱表述顺序,多结合项目中的具体场景来讲。比如介绍 Vue 时不要写“Vue 是一套用于构建用户界面的渐进式框架”,而是写“在校园生活服务平台的开发中,Vue 负责处理页面渲染和用户交互,其中商品列表页的响应式布局和组件复用均由 Vue 的组件化机制实现”。
系统实现的代码部分不会有查重问题,但注意代码排版规范。核心代码建议用代码块形式放入附录或正文,设置合适的字体样式,不要贴大段无注释的代码。
6. 验收与答辩:常见问题应对
6.1 系统部署与演示环境准备
毕设答辩前最重要的,是确保系统能在演示时稳定运行。部署环境建议租一台轻量级云服务器(内存最好不低于 2GB),安装 Docker 和 Docker Compose,将 MySQL、Redis、MinIO、后端、前端分别做成容器。
使用 Docker 部署 Spring Boot 项目的 Dockerfile 非常简单:
FROM openjdk:17-jdk-slim WORKDIR /app COPY target/campus-life-0.0.1-SNAPSHOT.jar app.jar EXPOSE 8080 ENTRYPOINT ["java", "-jar", "app.jar"]前端构建后的 Nginx 配置需要注意一个重要细节:将前端静态文件与服务端 API 放在同一个域下,通过 Nginx 的 location 反向代理,这样可以彻底解决跨域问题。配置如下:
location / { root /usr/share/nginx/html; index index.html; try_files $uri $uri/ /index.html; # vue-router history模式必须加 } location /api { proxy_pass http://后端容器名:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; }演示时的准备工作也很有讲究:提前在系统里准备好 10 条以上数据的展示;准备两类用户账号,一个是普通学生(学生端),一个是管理员(管理员端),切换展示不同的功能边界;网络环境如果教室不稳定,提前准备一台笔记本做热点,或者直接本地演示(后端在前端本机跑)。
6.2 答辩高频问题与回答思路
总结我参与评审时看到的答辩问题,高频的集中在这几个方向:
第一个方向是技术选型问题,是答辩老师的必问项:“为什么用 MinIO 而不是把图片存数据库?”“为什么用 Redis 而不是只存 MySQL?”“JWT 相比 Session 的优势是什么?”这类问题回答的核心逻辑是:从云原生思想出发,结合项目的实际场景分析——对象存储的静态文件与业务数据分离优势、Redis 基于内存的IO性能和缓存穿透风险的规避方案、JWT 的无状态特性和横向扩展能力。
第二个方向是安全类问题:“密码是如何存储的?如果数据库泄露了会不会直接泄露所有用户的密码?”“如何防止 SQL 注入和 XSS 攻击?”“如果用户越权访问管理接口怎么办?”回答时我就直接点出实际的应对方案:数据库中的密码使用 BCrypt 进行不可逆加密,每个用户的盐都是独立的;MyBatis 本身的预编译机制能有效防御 SQL 注入,前端 Vue 模板默认转义可以阻止大部分 XSS 攻击;越权防护主要用后端的 JWT 权限校验逻辑确保每个接口都做身份认证和角色鉴权。
第三个方向是扩展性问题:“这个系统哪里还有改进空间?”这个问题是送分题,也是最容易答砸的题。高分思路是先承认系统的局限性,再提出具体可落地的改进方案:基于 Elasticsearch 的商品搜索优化、消息通知改为 WebSocket 实时推送、分布式部署时的缓存一致性、引入消息队列处理高并发场景。
这个问题交给你的时间可以安排为:“系统目前的搜索方式是 MySQL 的 LIKE 模糊查询,在数据量达到万级以后响应时间会明显上升。如果未来用户规模扩大,我计划引入 Elasticsearch 建立商品索引,利用倒排索引实现全文检索,预计在十万级数据下查询响应能保持在毫秒级别。”
6.3 我踩过的坑:给后来者的实战提醒
最后分享一些我在做类似项目过程中实际踩过的坑。
第一个是 Spring Boot 版本兼容性问题。项目初期我选择了 Spring Boot 3.3 最新版,结果引入某个第三方依赖时发现它只兼容 2.x,不得不降级重来。经验是:不是最新版本就一定好,稳定优先于新潮。
第二个是前端 M3U8 视频播放的跨域问题。如果你在平台上做了视频功能,视频管理需要播放 m3u8 格式的视频切片文件。本地开发时我遇到了视频文件加载失败的问题,排查后发现核心在于后端响应头里缺少 CORS 头,并且 m3u8 文件里的相对路径如果不正确,视频同样加载不出。这个排查经历了整整两天的抓包比对才最终定位到根源。
第三个是反复出现的时区问题。Spring Boot 默认的 Jackson 序列化对 LocalDateTime 类型的数据有时区处理差异,导致前端看到的创建时间比实际时间早了 8 和 12 个小时不等。解决方法是统一在application.yml里配置:
spring: jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT+8写到这里,我想再最后说一个感受。做毕设这一年,最宝贵的不是那几万行代码,而是每天反复调试的耐心,遇到问题时排查的思路,以及把一个模糊想法逐步变成一个可用系统的那种踏实感。这些能力,在真正进入职场后,也会是你最有用的底气。校园生活服务平台这个题目,它够具体、够真实,技术幅度也刚刚好;只要你按着这个路线一步步走,从环境搭建到代码实现再到论文和答辩,你一定可以完成得很好。