每年到三四月份,就会有一批计算机专业的同学开始四处找毕业设计。要么是实验室项目做不下去,要么是工作实习两头烧,实在没精力从零写一套系统。我接触过好几套市面上的毕设源码,其中SpringBoot+Vue+MySQL这套组合出现的频率最高,比如这套“光影平台”,本质上就是一个视频点播管理系统。今天不吹不黑,就从实际跑通这套项目的角度,把整个技术拆解、部署流程、常见坑位一次说清楚,给拿到同款源码的同学趟趟路。

这套项目典型的做法是:SpringBoot做后端接口,Vue做前端页面,MySQL存业务数据,再配上Maven、Node.js、Nginx这些周边工具,组成一个完整的前后端分离应用。它能做的功能不外乎用户注册登录、视频分类浏览、视频播放、后台管理、轮播图管理、评论管理这些。你说它多高大上?不见得。但作为一个毕业设计,它的模块完整度、技术覆盖面、工作量展示,恰好卡在答辩老师最满意的位置上。

我用几天时间完整跑通了这套源码,包括前端依赖安装、后端环境配置、数据库初始化、本地联调、服务器部署。这篇文章把每一步的关键细节都写出来,重点说那些文档里不会写、但实际操作中一定会遇到的事。如果你想拿这套项目做毕设,或者想搞清楚SpringBoot+Vue项目到底怎么从源码变成能跑的系统,这篇可以帮你省掉大部分试错时间。

1. 项目底细:这套“光影平台”到底是什么

1.1 一套标准的视频点播系统包括哪些模块

先把项目边界画清楚。所谓光影平台,说白了就是一个带后台管理的视频点播网站,和B站、腾讯视频那种大型平台肯定没法比,但功能骨架是一致的:前台用户能看视频、搜视频、发评论;后台管理员能传视频、管分类、管用户、配轮播图。

从前端视角看,用户端页面一般包含:首页(轮播图+推荐视频)、视频列表页(按分类筛选)、搜索页(按关键词匹配标题)、视频详情页(播放器+视频信息+评论列表+点赞收藏)、个人中心(我的收藏、我的评论、个人信息修改)、登录注册页。管理端页面则包含:数据概览(总用户数、总视频数、总播放量)、视频管理(增删改查、上架下架)、分类管理、用户管理、轮播图管理、系统设置。

从后端视角看,接口按业务域划分就很清晰:认证模块负责注册、登录、Token签发与校验;用户模块负责个人资料查询与修改;视频模块负责视频列表、详情、上传、上下架;分类模块负责分类树的维护;评论模块负责评论的增删查;收藏模块负责收藏与取消收藏;还有轮播图模块和统计模块。每个模块对应一张到几张数据表,接口风格一般走RESTful,用JSON传数据。

这套项目的典型之处在于:它不是一个只有一个CRUD的“空壳系统”,而是真的有视频上传、有播放、有权限区分、有数据统计,每个模块都能在答辩时被问到,每个模块也都能扩展出很多可以聊的技术点。这就是它作为毕设项目最值钱的地方——工作量既能被看见,讲解时也有内容可以展开。

1.2 为什么是SpringBoot+Vue+MySQL,而不是别的

很多同学会纠结:别人用Python+Django写的,或者用Go写的,我非要用Java吗?这里面的道理很简单:SpringBoot是目前国内企业级Java开发的事实标准,社区资料最多,遇到问题随便一搜就有答案;Vue在国内前端的普及率同样很高,Element Plus、Ant Design Vue这些组件库直接把后台管理页面的开发效率拉满;MySQL则是开源数据库里最通用、面试问得最多的那一款。

这三个技术栈叠在一起,意味着答辩现场不管老师从哪个方向追问,你都能找到大量现成的学习资料和技术博客。而且这套组合在招聘市场上也最对口,简历上写“熟悉SpringBoot+Vue+MySQL全栈开发”,比写某些小众框架更有说服力。

当然,每个技术选型都有取舍。SpringBoot的优点是好上手、生态丰富、自动配置省心,缺点是内存占用偏高,打出来的jar包动不动几十上百MB;Vue的特点是响应式开发快、组件化程度高,但前端依赖安装和构建过程对新手不太友好;MySQL胜在稳定通用,但在大规模高并发场景下不如PostgreSQL或分布式数据库。对于毕设这种规模的项目,这些缺点完全够不上威胁,反而是优点更突出。

1.3 资料包里到底有什么,先盘清楚

拿到这套项目,第一步不是急着运行代码,而是先拆开压缩包,看清楚里面有几样东西。一套完整的“光影平台”毕设资料,通常包含四类内容:源码(分前端和后端两个目录)、数据库脚本(一个SQL文件或一组SQL脚本)、论文文档(Word或PDF格式,一般是开题报告、任务书、毕业论文三件套)、部署文档(说明如何配置环境、初始化数据库、启动前后端)。

先花半个小时把目录结构过一遍:前端目录里找package.json和src目录,确认是不是Vue项目;后端目录里找pom.xml(Maven项目标志)或build.gradle(Gradle项目标志),确认Spring Boot版本和Java版本要求;数据库脚本要看SQL文件里的建库语句,确认数据库名是什么;部署文档先看它要求的环境版本,比如JDK要求1.8还是17,Node要求14还是18,MySQL要求5.7还是8.0。

这一步特别重要,因为不同版本之间兼容性问题非常多。我见过最典型的例子是:人家用Spring Boot 2.7 + JDK 8写的代码,你本地装了JDK 17,一编译就报错;或者代码里用的是MySQL 5.7的方言写法,你装的是MySQL 8.0,跑起来各种报错。先把版本对应关系摸清楚,后面能省掉一大半折腾时间。

2. 系统架构与数据库设计拆解

2.1 前后端分离架构下的请求流转

“前后端分离”这个词几乎每个毕设论文里都会写,但很多同学并不真正理解它意味着什么。简单说,就是把原本“服务器渲染页面”的模式,拆成两个独立应用:后端只负责提供数据接口,前端只负责渲染页面和发送请求。两者之间通过HTTP协议的JSON数据交互,前端开发时用Vite或Webpack做本地代理转发请求,部署时用Nginx做反向代理把两个应用串起来。

拿“用户登录”这个最简单功能举例,整个请求链路是这样的:用户在Vue页面上输入用户名密码,点击登录按钮,Axios发送POST请求到 /api/user/login ,后端Controller接收到请求,调用Service层校验用户名密码,校验通过后用JWT生成一个Token字符串返回给前端,前端把Token存到localStorage或Pinia状态管理里。之后前端发任何需要登录的请求,都会在请求拦截器里自动带上这个Token,后端通过拦截器验证Token有效后,才放行请求到对应接口。

这里有个很容易被忽略的细节:跨域问题。前端页面跑在 localhost:5173 ,后端接口跑在 localhost:8080 ,当浏览器直接从前端页面发起请求到后端接口时,会因为同源策略被浏览器拦截。解决办法常见有两种:一种是在后端CORS配置类里加允许跨域的设置,另一种是开发时用Vite的proxy代理,把 /api 开头的请求转发到后端地址上。两种方案各有优劣:后端CORS配置简单粗暴,适合快速联调;Vite proxy更贴近生产环境,适合练习真实部署,二者的实现方式我在后面的章节展开。

2.2 核心数据表设计与关系梳理

数据库设计是一套系统的地基。光影平台的核心表我梳理了一下,至少有五到六张:用户表、视频表、分类表、评论表、收藏表,再加一张可选的管理员表。

用户表(user)的核心字段通常有:id(主键)、username(用户名,唯一索引)、password(加密后的密码)、nickname(昵称)、avatar(头像URL)、phone(手机号)、email(邮箱)、role(角色标识,区分普通用户和管理员)、status(账号状态,启用/禁用)、create_time(创建时间)、update_time(更新时间)。

视频表(video)是整个系统最核心的表,字段一般包括:id、title(视频标题)、cover(封面图URL)、url(视频播放地址)、category_id(所属分类ID,外键关联分类表)、description(视频简介)、play_count(播放次数)、like_count(点赞数)、status(发布状态:草稿/已发布/已下架)、user_id(上传者ID,外键关联用户表)、create_time、update_time。

分类表(category)比较简单:id、name(分类名)、parent_id(父分类ID,用于支持两级分类)、sort_order(排序权重)、create_time。评论表(comment)有:id、video_id(评论所属视频)、user_id(评论者)、content(评论内容)、create_time。收藏表(favorite)记录用户和视频的收藏关系:id、user_id、video_id、create_time,一般还会给 user_id 和 video_id 建联合唯一索引,防止重复收藏。

这些表之间的关系画出来就是一个标准的关系型数据库模型:一个分类下有多个视频,一个用户上传多个视频也能收藏多个视频,一个视频下有多条评论。在MyBatis-Plus中体现为实体类之间的关联查询:比如查视频详情时需要把分类信息和上传者昵称查出来,就用表连接或者MyBatis-Plus的关联查询来实现。

2.3 建表与索引的几个心得

数据库设计这块,很多毕设是直接把SQL文件里的建表语句复制过来就完事了,但答辩时老师一定会问“你这个表为什么这么设计”“索引加在哪几个字段上”。所以光会导入SQL不够,你得理解背后的设计逻辑。

第一,主键统一用自增id,不要花里胡哨搞雪花算法。毕设这种量级用自增id完全够用,代码简洁,性能也最好。“选主键策略”时有一个通行的判断逻辑:单机并发量不高就用自增id,分布式高并发才会引入雪花ID或UUID,这是面试和答辩时很加分的表述方式。

第二,外键关系怎么处理。常见做法是逻辑外键而不是物理外键——也就是在代码层面维护关联关系,不真正在数据库里建FOREIGN KEY约束。理由很直接:物理外键在数据量大时会拖慢插入和更新速度,而且不方便分库分表,互联网公司普遍废弃物理外键。如果老师问“为什么删除分类时视频表里的category_id不会报错”,你就用这套说法回答。

第三,索引不是越多越好。视频表的title字段经常被搜索,建议加普通索引;用户表的username字段用于登录,要加唯一索引;收藏表的user_id和video_id组合要加联合唯一索引。但那些几乎不会出现在WHERE条件里的字段,比如description、content,就别加索引了,纯属浪费磁盘空间和写入时间。

3. 后端实操:SpringBoot项目的关键落地细节

3.1 版本选择与项目初始化,先避掉SpringBoot 3的坑

第一步先把后端环境对齐。以这套光影平台为例,强烈建议用下面这组版本组合:JDK 1.8、Spring Boot 2.7.x、MySQL 8.0、MyBatis-Plus 3.5.x、Maven 3.6+。为什么特意避开Spring Boot 3?因为Spring Boot 3强制要求JDK 17,而且把原本的 javax 包全部换成了 jakarta ,很多老代码和网上搜到的大量教程完全不兼容。

如果你打开pom.xml发现spring-boot-starter-parent的version是2.x,就老老实实配JDK 8。如果项目用到了Spring Boot 3,那就要确认JDK 17已经装好。判断代码里的包路径最准:代码里import的是 javax.servlet 就是Spring Boot 2,是 jakarta.servlet 就是Spring Boot 3。

后端项目的分层结构是固定的套路:controller(接收请求、返回结果)、service(业务逻辑、事务控制)、mapper(数据持久化,一般继承MyBatis-Plus的BaseMapper接口)、entity(数据库实体类)、config(各类配置类,比如CORS配置、拦截器配置、Knife4j接口文档配置)、common或utils(统一返回结果类、异常处理类、JWT工具类)。第一次接触的同学,建议先按这个目录结构把每个文件扫一遍,理解每个类大概干什么,再动手启动项目,不然连报错都不知道去哪里看。

3.2 application.yml配置里的门道

后端跑起来之前,配置文件要先搞定。核心配置文件是 src/main/resources/application.yml ,如果拆成多环境,还会有 application-dev.yml 和 application-prod.yml 。一个典型的yml配置大概长这样:

server:
  port: 8080

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/guangying?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai
    username: root
    password: 123456
  servlet:
    multipart:
      max-file-size: 200MB
      max-request-size: 200MB
  jackson:
    date-format: yyyy-MM-dd HH:mm:ss
    time-zone: GMT+8

mybatis-plus:
  configuration:
    map-underscore-to-camel-case: true
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  global-config:
    db-config:
      logic-delete-field: deleted
      logic-delete-value: 1
      logic-not-delete-value: 0

这里面有几个容易被忽略的坑。第一, serverTimezone=Asia/Shanghai 不能省,否则连接MySQL 8.0时会报时区错误。第二,MySQL 8.0的驱动类是 com.mysql.cj.jdbc.Driver ,不是老版本的 com.mysql.jdbc.Driver 。第三,视频系统的上传上限一定要调大,Spring Boot默认只有1MB,不改成200MB以上,传个视频直接报 FileSizeLimitExceededException 。第四,如果代码里做了逻辑删除,MyBatis-Plus的全局逻辑删除配置必须和实体字段对应上,不然删除数据会直接变成物理删除。

数据库密码这一块,有些做得好一点的毕设会用Jasypt做yml密文加密,配置里的密码是一串密文,启动时用密钥解密。这个可以作为论文里的一个安全亮点来写,哪怕是用了jasypt-spring-boot-starter写两行配置,也体现出你对配置安全的考虑。

3.3 JWT登录鉴权的完整思路

光影平台的登录模块一般用的是JWT(JSON Web Token)方案。JWT的三个部分——Header(头部)、Payload(载荷)、Signature(签名)——分别存放加密算法、用户信息、签名结果。服务端签发Token时把用户id、用户名、过期时间写进Payload,用密钥做签名,客户端拿到Token后每次请求放在请求头 Authorization 字段里带着,后端拦截器验证签名和过期时间通过后,解析出用户信息,放行请求。

核心实现分成几块。第一块是JWT工具类,负责生成和解析Token,常用库是 io.jsonwebtoken 的jjwt。第二块是拦截器,实现Spring的 HandlerInterceptor 接口,在 preHandle 方法里从请求头取Token,解析异常就返回401状态码。第三块是配置类,把拦截器注册到WebMvcConfigurer里,并设置哪些路径需要拦截:登录、注册、验证码、视频列表这些接口要放行, /admin/** 、 /user/** 这类接口要拦截。

还有一个细节:前后端分离下,后端拿到Token后怎么知道当前登录用户是谁?常见做法是在拦截器里把Token解析出的userId放到 request.setAttribute("userId", userId) ,Controller里再加一个注解或工具类取出来。用ThreadLocal存当前用户信息是更优雅的方案,答辩时可以主动提,体现出你理解线程隔离和变量传递的机制。

3.4 视频上传与文件存储策略

视频上传是光影平台里最带“业务感”的功能。前端用element-plus的上传组件 el-upload ,设置 action 为后端接口地址,或者用 http-request 自定义上传逻辑。后端收到MultipartFile后,用UUID重命名文件,防止文件名冲突,然后保存到本地磁盘指定目录,最后把访问路径存入数据库。

本地存储的代码框架大概是:

String originalFilename = file.getOriginalFilename();  // 原文件名
String ext = originalFilename.substring(originalFilename.lastIndexOf(".")); // 扩展名
String newFileName = UUID.randomUUID().toString().replace("-", "") + ext;  // 新文件名
String datePath = new SimpleDateFormat("yyyy/MM/dd").format(new Date());  // 按日期分目录
String rootPath = fileUploadProperties.getPath();  // 自定义上传根路径
File dir = new File(rootPath + "/" + datePath);
if (!dir.exists()) dir.mkdirs();
file.transferTo(new File(dir, newFileName));  // 保存

两个关键点需要特别说明。第一,文件不能直接丢到 项目根目录/static 下面,打包成jar之后这个目录不在可写范围里,最好配置一个独立的绝对路径,比如 D:/upload/ 或服务器上的 /data/upload/ ,用@ConfigurationProperties注解定义一个上传路径配置类。第二,要解决“上传后怎么访问”的问题。本地开发时前端要用 http://localhost:8080/文件路径 访问,但Spring Boot默认访问不到静态资源目录之外的磁盘文件,所以要写一个静态资源映射配置,把本地的上传目录映射到 /files/** 路径上。

如果以后想把项目部署到云服务器,还可以换成对象存储(OSS/COS/S3)。把上传策略抽象成一个接口,本地实现和OSS实现切换调用,论文里就能写“基于策略模式的文件上传方案,支持扩展分布式存储”,这一个设计模式就能让系统设计评分往上走一档。

4. 前端实操:Vue项目的搭建与视频播放

4.1 环境准备与依赖安装,npm报错全实录

前端技术栈一般是Vue 3 + Vite + Pinia + Vue Router + Element Plus(或Ant Design Vue)+ Axios。开始之前,本机必须装好Node.js,并且注意版本要求:Vite 4以上要求Node 14.18+或16+;如果用的Vite 5,Node版本至少要16.14,推荐直接用Node 18长期支持版。

进入前端目录,第一件事是:

npm install

如果你用的是淘宝镜像源,有些旧的包版本会报错,建议把镜像源切换成官方源再装。网上很多同学在这个环节卡很久,我建议直接把源配死:

npm config set registry https://registry.npmmirror.com
npm install

安装完成后启动开发服务器:

npm run dev

默认地址一般是 http://localhost:5173 。开发时会有一个很常见的需求:前端请求 /api/xxx ,后端接口在 localhost:8080 ,如何不写死完整URL?在 vite.config.js 里配置proxy代理:

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'

export default defineConfig({
  plugins: [vue()],
  server: {
    port: 5173,
    proxy: {
      '/api': {
        target: 'http://localhost:8080',
        changeOrigin: true
      }
    }
  }
})

这样配置之后,前端请求 /api/user/login 就会被自动转发到 http://localhost:8080/api/user/login ,浏览器里看不到跨域报错。这种方式比后端CORS设置更接近生产环境,Nginx部署时也是同样的反向代理逻辑。

4.2 路由设计与权限控制

Vue Router是单页应用的核心。一个典型的用户端路由配置会区分“公开路由”和“需要登录的路由”:

const routes = [
  { path: '/', component: Home },
  { path: '/video/:id', component: VideoDetail },
  { path: '/category/:id', component: VideoList },
  { path: '/login', component: Login },
  { path: '/register', component: Register },
  { path: '/user', component: UserCenter, meta: { requiresAuth: true } },
  { path: '/admin', component: AdminLayout, meta: { requiresAuth: true, role: 'admin' } }
]

路由传参的两种方式在毕设里都常用:一种是路由路径参数,比如 /video/1001 ,用 route.params.id 获取;另一种是查询参数 /video?id=1001 ,用 route.query.id 获取。详情页推荐用路径参数,因为URL更直观,刷新后能保留,而且搜索引擎优化也更友好。

登录鉴权一般配合路由守卫来做。在 router.beforeEach 钩子里检查目标路由的 meta.requiresAuth ,如果是true,就取本地存储的Token判断是否登录,没登录就跳转到 /login 页面并带上 redirect 参数。管理员路由还要额外验证当前用户的role字段是不是admin。这套逻辑说给答辩老师听,就是一个标准的“前端路由守卫 + 后端接口拦截”双层权限控制体系。

4.3 m3u8视频播放的完整方案

光影平台播视频,玩法比普通毕设多了一层技术含量——如果视频文件是m3u8格式的,就不能直接用HTML5的video标签播放,因为浏览器原生不支持HLS协议。网上很多同类型毕设所谓的“在线播放”只是把MP4地址塞进video标签里,而用m3u8流媒体播放,是可以在答辩时多讲几句的亮点。

m3u8播放的常见方案有两个:video.js配合videojs-contrib-hls插件,或者hls.js。我推荐hls.js,它更轻量、出错信息更清晰。安装:

npm install hls.js

在VideoDetail.vue里的核心逻辑:

<template>
  <video ref="videoRef" controls autoplay class="video-player"></video>
</template>

<script setup>
import { ref, onMounted, nextTick } from 'vue'
import Hls from 'hls.js'

const videoRef = ref(null)
const videoUrl = ref('http://localhost:8080/files/video/2025/03/01/xxx.m3u8')

onMounted(() => {
  nextTick(() => {
    const video = videoRef.value
    if (Hls.isSupported()) {
      const hls = new Hls()
      hls.loadSource(videoUrl.value)
      hls.attachMedia(video)
      hls.on(Hls.Events.MANIFEST_PARSED, () => {
        video.play()
      })
    } else if (video.canPlayType('application/vnd.apple.mpegurl')) {
      // Safari直接支持HLS
      video.src = videoUrl.value
    }
  })
})
</script>

这里还有一个后端的配套操作值得写进论文:如果视频以m3u8文件存放,一般会配套一个ts分片目录和一个m3u8索引文件。把m3u8文件放在静态资源映射目录下,前端可以直接通过URL请求到。如果某些环境对m3u8请求会报跨域,或者内容乱码,注意检查后端静态资源映射的Content-Type是否设置正确,必要的时候写一个接口将m3u8文件内容转码成正确的编码格式返回。这整个HLS切片、索引文件、分片加载流程,比单纯塞一个MP4地址进去能聊的内容多得多。

4.4 Axios封装与请求/响应拦截

前端所有接口调用,建议统一封装在一个 request.js 里,而不是每个页面散着写 axios.get 。一个比较标准的封装包含:基础URL(或走代理时直接写 /api 前缀)、请求超时时间(如10秒)、请求拦截器(自动加token)、响应拦截器(统一处理业务码和HTTP错误)。

import axios from 'axios'
import { ElMessage } from 'element-plus'
import router from '@/router'

const request = axios.create({
  baseURL: '/api',
  timeout: 10000
})

request.interceptors.request.use(config => {
  const token = localStorage.getItem('token')
  if (token) config.headers.Authorization = token
  return config
})

request.interceptors.response.use(
  response => {
    const res = response.data
    if (res.code !== 200) {
      ElMessage.error(res.message || '请求失败')
      if (res.code === 401) {
        localStorage.removeItem('token')
        router.push('/login')
      }
      return Promise.reject(new Error(res.message))
    }
    return res
  },
  error => {
    ElMessage.error(error.message || '网络错误')
    return Promise.reject(error)
  }
)

export default request

这个封装的细节答辩时非常加分:为什么统一拦截401?因为Token过期后所有接口都可能返回401,如果每个接口都单独加判断,代码重复到爆炸。统一在拦截器里处理,一次登录失效,所有接口自动跳回登录页,这就是“面向切面”思想在业务代码里的体现。同理,上传进度、参数错误统一提示、loading统一管理,都可以在拦截器层面解决。

5. 部署上线:从本机跑通到服务器发布

5.1 MySQL 8.0安装与数据库初始化

本机跑通了,下一步就是完整走一遍部署流程。先装MySQL。这里只强调几个毕设部署时最容易踩的坑。

MySQL 8.0安装过程中,一定记住你设置的root密码,且要留意安装类型。Windows上如果用的MSI安装包,一路Next时会要求设置密码,注意选择加密方式:强烈建议选 Use Legacy Authentication (兼容旧版)那一项,或者后续改配置文件,否则用老版本的Navicat、代码驱动连接时,会因为 caching_sha2_password 认证插件连不上。如果装完已经选了默认的强加密方式,解决办法是登录MySQL执行:

ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '你的密码';

装好后导入数据库脚本。打开Navicat或命令行,创建一个数据库(名字一般和SQL脚本里的库名一致,比如 guangying ),然后选择运行SQL文件,把源码包里那个SQL文件跑一遍。导入成功后,看下表结构能不能对上,user表里有没有初始管理员账号,视频表里有没有测试数据。这一步别偷懒,提前确认数据完整性,后面部署出问题时能快速定位是数据问题还是代码问题。

5.2 后端打包与部署

后端部署很简单,就是打成jar包,丢到服务器上跑。在项目根目录执行:

mvn clean package -DskipTests

打包完成后, target 目录下会生成一个 xxx.jar 。本地测试可以先执行:

java -jar target/xxx.jar

启动后测试一下 http://localhost:8080/api/video/list 这类接口,能返回数据就说明后端没问题。注意启动时连的是 application.yml 里配置的数据库地址,部署到服务器时要改成服务器本地或云数据库的连接地址。

生产环境跑后端,一般用 nohup 挂到后台:

nohup java -jar guangying.jar --spring.profiles.active=prod > server.log 2>&1 &

--spring.profiles.active=prod 意思是加载 application-prod.yml 这个环境的配置,里面放生产环境的数据库地址、上传路径、端口号等。如果你只配置了一个 application.yml ,这条命令可以不写,但多环境配置这招可以在论文里写上,证明你考虑到了开发环境和生产环境的配置隔离。

5.3 前端构建与Nginx托管

前端部署要两步:构建加托管。先执行:

npm run build

构建完成后, dist 目录里就是纯静态文件。然后把它扔给Nginx托管。在 nginx.conf 里配置一个server块:

server {
    listen 80;
    server_name your-domain.com;

    root /usr/share/nginx/html;
    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;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }

    location /files/ {
        proxy_pass http://127.0.0.1:8080;
    }
}

这里有三处关键的配置。第一, try_files $uri $uri/ /index.html; 是Vue Router history模式必须配的——否则刷新页面时Nginx会去磁盘上找 /video/1001 对应的真实文件,找不到就返回404,而实际上这个路径是前端路由路径,所以要全部回退到 index.html 。第二, location /api/ 做反向代理,把前端API请求转发到后端8080端口,和本机开发时的Vite proxy作用一模一样。第三,如果后端文件存储是本地磁盘, /files/ 路径也要代理给后端,否则视频和封面图加载不出来。

5.4 部署自检清单

部署最怕的就是“看起来都配好了,但就是不行”。我把自己排错时常用的一套自检顺序整理成清单,每次上线都按这个查一遍:

检查项 检查方法 常见问题
数据库连接 服务器上执行 mysql -u root -p 并用代码里的密码登录 密码不一致、端口未开放
后端进程状态 执行 ps -ef | grep java 看进程是否存在 jar包启动报错后进程退出
后端日志 查看 server.log 里的报错信息 端口被占用、数据库连不上
前端静态文件 浏览器访问域名根路径能否打开首页 dist目录路径配置错误
API反向代理 浏览器访问 域名/api/video/list 是否返回JSON proxy_pass路径没配对
视频地址访问 在浏览器打开视频完整URL 静态资源映射缺失、路径拼接错误

6. 高频问题排查与答辩准备

6.1 常见的五个部署/运行问题

问题一:前端 npm install 卡住不动或报ELIFECYCLE错误。多数是网络问题,改镜像源,或者删除 node_modules 和 package-lock.json 后重装。还有可能是Node版本过高过低,用 nvm 切换Node版本到18试一遍。

问题二:后端启动报 Failed to configure a DataSource 。这是最常见的数据库配置错误,原因多半是 application.yml 里的url、用户名、密码写错,或者数据库服务没启动。也有可能是pom.xml引入了数据库驱动的依赖但yml没配置数据源,SpringBoot启动时会自动探测。

问题三:前端请求接口报跨域。分两种情况:本地开发就用Vite proxy,别在后端加CORS;如果你改用了后端CORS方案,注意 allowedOrigins 不能用 * 还要带上credentials,否则带Token的请求依然会被拦截。

问题四:视频上传报文件大小超限。Spring Boot默认上传大小只有1MB,必须改 spring.servlet.multipart.max-file-size 和 max-request-size ,同时Nginx的 client_max_body_size 也要调大,比如设置成 200m ,否则Nginx这层就先拦截了。

问题五:前端刷新页面404。原因只有一个:Nginx没有配置 try_files 回退到 index.html 。把location /里的try_files加上去就好了。

6.2 问题排查速查表

实际写这套项目和部署的过程里,我整理了一个速查表,按“先看哪层”来排查,效率非常高:

现象 优先排查方向 排查命令/工具
前端页面白屏 浏览器Console报错、Network请求 F12开发者工具
登录提示用户名或密码错误 数据库初始账号是否正确 直接查 user 表
上传视频失败 后端日志、文件目录权限 ls -l /data/upload
视频播放转圈 视频文件路径是否真实存在、静态资源映射 浏览器直接访问视频URL
接口返回401 Token是否过期、请求头是否带上Token Network请求头查看Authorization
接口返回500 后端日志堆栈 tail -100 server.log

6.3 论文与答辩的准备思路

源码跑通了,部署也验证了,但毕设拿高分并不是“能跑就行”,论文和答辩同样关键。大部分同学拿到论文初稿之后喜欢直接交,我的建议是:把论文中“系统实现”部分和你的代码逐条对照,确保每个截图都是你自己运行出来的界面,每条代码思路和你代码里写的一致,因为答辩老师真的会抽查——他让你现场演示某个功能,结果你连按钮在哪都找不到,场面就很尴尬了。

论文里值得重点展开的地方有:需求分析章节把用例图补全,每个角色能做什么要列清楚;系统设计章节画好架构图、功能结构图、E-R图(注意不能用mermaid,要用绘图工具导出图片插到论文里),数据库表要至少列核心的五张表;系统实现章节按模块写,配关键代码片段和界面截图;测试章节至少写功能测试用例表、接口测试结果(Postman截图)、兼容性测试结论,性能测试这块哪怕是JMeter一行代码没写过,也要把测试报告的基本格式学会,填几组并发数、响应时间的测试数据。

答辩的常见问题也要提前准备。比如“为什么用JWT而不用Session”,回答思路是:前后端分离架构下,Session依赖Cookie,跨域和移动端都不友好,JWT无状态扩展性好。“你的系统怎么防止SQL注入”,回答思路是:MyBatis的 #{} 预编译机制能防止拼接注入,尽量避免用 ${} ,管理员输入做了白名单校验。“视频上传慢怎么解决”,回答思路是:前端可以分片上传断点续传,后端可以走异步任务转码,这些虽然我没全部实现,但我知道实现方案——把扩展点准备好,既显诚实又显深度。

还有一个细节:部署文档是给谁看的?一般是给答辩老师、查重老师和后续接手的同学看。所以部署文档要写成“小白也能跟着做”的程度:JDK怎么装、MySQL怎么装、SQL文件怎么导入、前端依赖怎么装、后端怎么打包、Nginx怎么配置,每一步都配截图。这套部署文档本身就是你的实践笔记,做完这一步,你对整套系统的理解会比光看代码深一个层次。

写在最后

从拿到源码到完整部署上线,这套光影平台我前后折腾了三四天时间,中间踩得最多的坑就是版本兼容和路径映射。如果你也正在跑类似的SpringBoot+Vue+MySQL项目,我的建议是:先别急着看代码逻辑,先把环境版本对齐,把数据库导进去,让系统先跑起来再说。系统能跑起来之后,你再对着代码一行一行读,每读一个模块,就对应看论文里的那一节,很快就能把整套东西串起来。

最后再分享一个答辩时的实用技巧:把后端启动、前端启动、数据库导入、Nginx部署这四步操作练到闭着眼也能完成,然后用手机录一份三分半钟的完整演示视频,存到U盘里带着。万一现场电脑出问题、投影仪连不上、网络断掉,视频一放,从容不迫。这招我当年就用过,关键时刻真的能救场。

Logo

邀请您加入社区

更多推荐