简介:基于Java SSM与Vue框架的在线视频点播系统,面向高校毕业设计、课程设计及期末大作业场景,提供一套可直接运行的前后端完整项目。代码采用IDEA开发,配合MySQL 8.0数据库脚本与Tomcat部署环境,导入后无需修改即可使用,经过严格调试并获导师指导,适合计算机专业学生用于学习或二次开发。压缩包共1414个文件,以JavaScript、CSS、JSP、Java等前后端源码为主,同时包含SQL脚本、Word论文文档及开发工具说明,整体约48MB,目录结构清晰,便于按需检索。项目实现视频分类、检索、播放和后台管理等功能,界面简洁、操作便捷,有助于理解SSM整合、Vue交互与数据库设计;当前已有44人学习,可满足毕业设计答辩和课程作业需求。

1. 毕设选题在线视频点播:一个能串起 SSM、Vue 和 MySQL 的完整闭环

如果你正在找毕业设计项目,又不想只做一个增删改查的“管理系统”,那在线视频点播是个很讨巧的选题:前端要处理视频列表、播放器、用户登录;后端要承担接口、权限、播放记录;数据库还要设计视频表、用户表和评论表。这套基于 java + ssm + vue + mysql 的源码正好把这三块都串起来了,拿到手不是零散代码,而是从数据库脚本到后端接口再到前端页面的完整链路,很适合用来当课程设计或期末大作业,也能作为面试时讲项目经历的素材。项目里附带了论文和数据库脚本,下载后按文档配置即可运行,适合熟悉 IDEA、Maven 和 Tomcat 基本操作的同学快速落地。

2. SSM + Vue 分层逻辑:从登录态到视频流的请求链路

2.1 请求先过 SpringMVC:DispatcherServlet 与 Controller 映射

SSM 是 Spring + SpringMVC + MyBatis 的组合,其中 SpringMVC 的核心是前端控制器 DispatcherServlet。所有前端请求会先经过它,再根据 URL 找到对应的 Controller 方法。我打开源码里的 Controller 层,典型的映射写法是 @RequestMapping("/video/list") ,配合 @ResponseBody 直接返回 JSON,而不是返回 JSP 页面。这部分和你在 Java 面试题里见到的流程一致:请求进入 DispatcherServlet → HandlerMapping 找到处理器 → 执行 Controller 方法 → 通过 Jackson 把对象序列化成 JSON。

一个实际例子是视频列表接口,控制层通过接收当前页码和每页条数,调用 Service 层进行分页查询,最终把结果封装成统一返回对象。代码大致长这样:

@Controller
@RequestMapping("/video")
public class VideoController {

    @Autowired
    private VideoService videoService;

    @RequestMapping("/list")
    @ResponseBody
    public ResultVO list(@RequestParam(defaultValue = "1") Integer pageNum,
                          @RequestParam(defaultValue = "10") Integer pageSize) {
        PageResult<Video> pageResult = videoService.getVideoPage(pageNum, pageSize);
        return ResultVO.success(pageResult);
    }
}

pageNum 默认值设为 1, pageSize 默认值设为 10,这样前端不传参数时也能正确返回首页数据。 ResultVO 是统一返回值包装类,里面一般包含 code、message 和 data 三个字段,前端拿到 code 为 0 或 200 时才会渲染数据。如果把视频列表接口直接暴露给未登录用户,需要在拦截器里做白名单配置,否则会被统一的登录校验拦下来。

2.2 MyBatis 查询视频列表与分页参数

MyBatis 在 SSM 项目里负责 SQL 映射。源码中视频表的 Mapper 接口与 XML 分离,你可以在 VideoMapper.xml 中看到动态 SQL 和分页条件。常见做法是使用 PageHelper 插件实现物理分页,而不是在 DAO 层手动拼接 limit 语句。使用 PageHelper 时,只需要在 Service 层调用静态方法 PageHelper.startPage(pageNum, pageSize) ,紧接着执行的查询就会被拦截并生成带 limit 的 SQL。

<select id="selectVideoList" resultType="com.example.entity.Video">
    select id, title, cover_url, play_url, category_id, play_count, status
    from video
    <where>
        <if test="keyword != null and keyword != ''">
            and title like concat('%', #{keyword}, '%')
        </if>
        <if test="categoryId != null">
            and category_id = #{categoryId}
        </if>
    </where>
    order by create_time desc
</select>

cover_url 对应视频封面, play_url 是实际播放地址,这两个字段在前端展示和播放时都会用到。如果项目里视频源用 M3U8 格式, play_url 存的是 .m3u8 的地址,前端需要配合播放器插件处理。Mapper 接口中的方法参数用 @Param 标注,主要是为了在 XML 中能直接引用 keyword 和 categoryId ,避免多个参数时出现的绑定异常。调试时如果遇到 Invalid bound statement (not found) ,基本是 XML 的 namespace 没有和接口全限定名对应上。

2.3 Vue 前端通过 axios 拿数据,路由与组件懒加载

前端部分基于 Vue 2 和 Element UI 搭建,目录结构里能看到 main.js 、 router/index.js 、 views/ 和 api/ 。页面跳转主要由 vue-router 控制,比如 /video/detail/:id 用来展示视频详情。播放器页面会先通过路由参数拿到视频 id,再调用 getVideoDetail(id) 接口获取播放地址和视频信息。

import request from '@/utils/request'

export function getVideoDetail(id) {
  return request({
    url: '/video/detail',
    method: 'get',
    params: { id }
  })
}

这里统一封装的 request 模块基于 axios 实例,设置了 baseURL 和超时时间。请求拦截器里会从 localStorage 读取 token 并放入 header,响应拦截器则统一处理 code 不等于 0 的情况,比如跳转登录页。Vue 路由中使用了组件懒加载,写法是 component: () => import('@/views/video/Detail.vue') ,这样首屏不会一次性加载所有页面,尤其在视频点播系统里图片和播放器资源很多的情况下,能明显减少白屏时间。调试时如果发现接口跨域,检查后端有没有配置 CORS 过滤器,或者查看前端 vite.config.js 中的 proxy 配置是否指向了 Tomcat 地址。

3. 数据库设计与初始化:MySQL 8 建库、用户表与视频表

3.1 从 SQL 脚本还原数据库

项目里附带数据库脚本,一般是一个 .sql 文件。我习惯用 Navicat 的“运行 SQL 文件”功能恢复数据库,而不是手动去复制粘贴每一行。注意 MySQL 8 默认字符集已经是 utf8mb4,但旧脚本可能还写着 DEFAULT CHARSET=utf8 ,如果视频标题里将来要存 emoji,最好改成 utf8mb4。另外,MySQL 8 的默认身份认证插件是 caching_sha2_password ,驱动必须用 com.mysql.cj.jdbc.Driver ,如果项目里还在用旧的 com.mysql.jdbc.Driver ,启动时会直接抛出异常。

CREATE DATABASE IF NOT EXISTS video_system DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
USE video_system;
source /path/to/video_system.sql;

source 命令在命令行客户端执行时也能导入脚本,但路径中不能有中文和空格。导入完成后,重点检查三张表: sys_user 用户表、 video_info 视频表和 play_record 播放记录表。如果脚本里没有管理员账号,你需要手动插入一条记录,密码字段通常存的是 MD5 值,比如 admin 对应的 MD5 是 21232f297a57a5a743894a0e4a801fc3 。

3.2 核心表结构与字段设计说明

在线视频点播系统涉及到的表一般包括用户表、视频表、评论表、播放记录表和轮播图表。下面列出视频表的主要字段设计,这套字段基本可以覆盖一个简单视频站点的核心需求:

字段名 类型 说明
id bigint 主键自增
title varchar(255) 视频标题
cover_url varchar(500) 封面地址
play_url varchar(500) 播放地址,支持 mp4/m3u8
category_id int 分类外键
play_count int 播放次数,用于热门排序
status tinyint 0 下架 1 上架
create_time datetime 上传时间

评论表和播放记录表都会以 video_id 为外键关联视频表,并在 video_id 和 user_id 上建立联合索引。如果项目里直接用了 play_count 作为排序字段,播放次数可以通过触发器或后端逻辑增加,实际部署时建议每 5 次播放批量更新一次,避免频繁 UPDATE 导致锁竞争。用户表里一般有 role 字段区分管理员和普通用户,管理端接口通过拦截器校验 role 权限。

3.3 调试时常见报错:时区、驱动与字符集

把项目连接 MySQL 8 时,最容易出问题的就是时区。数据库连接串如果只写 jdbc:mysql://localhost:3306/video_system ,会报 The server time zone value 'Öйú±ê׼ʱ¼ä' is unrecognized 。老一些的教程只让你加 useSSL=false ,却没人告诉你时区必须显式指定。我在 jdbc.properties 里一般这样配置:

jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/video_system?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false
jdbc.username=root
jdbc.password=123456

useUnicode=true&characterEncoding=utf8 解决中文乱码, serverTimezone=Asia/Shanghai 解决时区报错, useSSL=false 避免本地开发时 SSL 握手警告。如果把密码写在配置里,提交到 Git 的时候要记得忽略 application.properties 或 jdbc.properties ,否则密钥泄露出去,后续不管是不是毕设都会很麻烦。另一个常见问题是 Tomcat 8 和 MySQL 8 之间的驱动冲突,确保 pom.xml 里的 mysql-connector-java 版本是 8.x,并且和运行时 lib 里的 jar 不重复。

4. 项目配置与部署:IDEA、Maven、Tomcat 跑起来

4.1 IDEA 导入 Maven 工程并修改 JDK 版本

项目压缩包解压后,直接用 IDEA 的 Open 选择 pom.xml 所在目录,IDEA 会识别为 Maven 项目并自动下载依赖。如果一直卡在下载,检查本地 Maven 仓库的 settings.xml,换成阿里云镜像而不是默认中央仓库。

导入后第一件事是检查 Project Structure 里 SDK 版本。这个项目基础语言是 Java,很多学校环境装的是 JDK 8,那么就要确认 pom.xml 中 <source> 和 <target> 是 1.8,避免位脱变异常。可以看下编译插件:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>3.8.1</version>
    <configuration>
        <source>1.8</source>
        <target>1.8</target>
        <encoding>UTF-8</encoding>
    </configuration>
</plugin>

source 和 target 都设为 1.8 是把编译字节码版本锁定在 JDK 8。如果你的电脑装的是 JDK 11 甚至 17,这个配置还能继续跑,但要注意 Spring 版本,老 Spring 4.x 对高版本 JDK 兼容性差,容易在启动时报错。建议直接安装 JDK 8 最省事。

4.2 配置 Tomcat 8 并启动项目

这个项目建议部署环境用 Tomcat 7.x 或 8.x。Tomcat 10 之后将 Java EE 包名从 javax.* 改成了 jakarta.* ,如果你拿到的是修改前的代码,放到 Tomcat 10 里会直接抛 NoClassDefFoundError 。所以稳妥做法是使用 Tomcat 8.5,实测最稳定。

在 IDEA 中配置 Tomcat: Run -> Edit Configurations -> + -> Tomcat Server -> Local ,然后选中 Tomcat 8 的安装目录。Deployment 标签页里添加 war exploded 这个 artifact,Application context 填写 /video 。启动后访问 http://localhost:8080/video/ 就能看到前端页面了。如果端口被占用,修改 conf/server.xml 里的 Connector port 为 8081,IDEA 的 Server port 也要同步改。

curl http://localhost:8080/video/video/list?pageNum=1&pageSize=5

返回的 JSON 里应该有 code:200 和 data 字段,说明后端接口已经通了。如果前端页面登录后发请求报 404,多半是访问路径里少了项目 context,导致 axios 的 baseURL 配错。此时打开浏览器 F12 看 network 面板,实际请求地址与后端 Controller 映射对比一下就知道问题了。

4.3 Maven 打包 war 与外部 Tomcat 部署

想直接部署到服务器上,就用 Maven 打包。在 IDEA 右侧 Maven 面板执行 clean 再 package ,最终在 target 目录生成 .war 。把 war 包复制到 Tomcat 的 webapps/ 目录,启动 Tomcat 后它会自动解压。数据库连接配置在 resources/jdbc.properties 里,测试环境和生产环境要分开。打包时如果遇到测试类报错,在 pom.xml 里跳过测试:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-surefire-plugin</artifactId>
    <version>2.22.2</version>
    <configuration>
        <skipTests>true</skipTests>
    </configuration>
</plugin>

skipTests 的作用只是让打包时忽略测试代码,不影响正常业务功能。外部 Tomcat 部署要注意 Linux 服务器上的按端口权限,比如使用 80 端口需要先授权。我在部署时还会在 Tomcat 的 bin/startup.sh 前加上 JAVA_HOME 和 CATALINA_HOME ,避免服务器自动识别 JDK 路径错误。

5. 视频播放的进阶处理:M3U8 切片、防盗链与播放日志

5.1 前端用 vue 播放 m3u8 的常见做法

在线视频点播系统里如果播放地址是 .m3u8 ,原生 <video> 标签在 Chrome 和 Firefox 上无法直接播放,必须借助 hls.js 或 video.js 插件。很多视频平台都会把 mp4 转成 hls 再分发,好处是可以根据网速自适应码率。使用时在 Vue 的 Detail.vue 中引入 hls.js,并创建播放器实例,只需要十几行代码就能把 m3u8 视频播放起来。

if (videoSrc.endsWith('.m3u8') && Hls.isSupported()) {
  const hls = new Hls({ maxBufferLength: 30 })
  hls.loadSource(videoSrc)
  hls.attachMedia(this.$refs.video)
  hls.on(Hls.Events.MANIFEST_PARSED, () => {
    this.$refs.video.play()
  })
}

maxBufferLength 是缓冲区最大长度,网络抖动时可以减少卡顿,但设得太大内存占用也会变高。有些截图代码直接写 video.play() 会触发浏览器自动播放限制,最好在用户点击播放按钮后再调用。如果项目里兼容了 mp4 直接播放的地址,需要做一次判断,避免对 mp4 也走 hls 逻辑。

5.2 防盗链与播放次数落库

视频地址直接暴露会被别人拿去蹭流量,最轻量的防盗链手段是在后端拦截器里校验 Referer 请求头。对于 /video/play 接口,校验请求来源域名是否在白名单内,不是则返回 403。用 Spring 拦截器可以在 preHandle 方法里统一处理,不用改动每个 Controller。

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    String referer = request.getHeader("Referer");
    if (referer == null || !referer.contains("video.yourdomain.com")) {
        response.setStatus(HttpServletResponse.SC_FORBIDDEN);
        return false;
    }
    return true;
}

这段代码最关键的判断条件是 referer.contains("video.yourdomain.com") ,如果允许本地调试,可以在本地 hosts 里把域名解析到 127.0.0.1。播放一次就落一条播放记录也是溯源客户端行为的基础方式,表结构里存 user_id 、 video_id 、 play_time 和 ip_addr ,通过简单的 INSERT 即可完成。真的想统计不同地区的播放分布,可以后续把 ip_addr 做归属地分析,不过毕设项目不需要这么重的实现。

5.3 验证防盗链与播放日志是否生效的三步检查

先清空播放记录表,再到浏览器里点一次视频播放,然后到数据库里看 play_record 表有没有新记录。这一步验证的是接口是否被真实调用,而不只是页面加载。第二步用 curl 直接模拟防盗链请求:

curl -I -H "Referer: http://other.com" http://localhost:8080/video/video/play?id=1

这里期望拿到 HTTP/1.1 403 ,说明拦截器生效。不加 Referer 头再访问一次,如果返回 403 则是规则太严格,需要放行空来源请求;如果返回 200,则说明正常路径不受影响。第三步是验证 m3u8 子请求是否也走防盗链,很多防盗链只拦了第一次 m3u8 的请求,后续切片请求没拦,照样能被盗用。在浏览器开发者工具里筛选 m3u8 和 ts 类型的网络请求,检查每个状态码是否符合预期。

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

Logo

火山引擎视频云技术社区,是面向 AI 音视频开发者的技术交流平台。这里汇聚源自抖音、豆包等亿级 DAU 产品的 RTC、直播、点播、AI 媒体处理、音视频互动技术,提供接入指南、最佳实践、性能调优、场景案例、Demo 代码、开源项目、白皮书和 API 文档。社区汇聚官方工程师与一线开发者,为 AI 视频通话、数字人、AI 视频处理等应用的开发与落地提供技术支持。

更多推荐