摘要
随着短视频行业的快速发展,传统泥人制作技艺的传承与推广面临新的机遇与挑战。本文设计并实现了一个基于SpringBoot的泥人制作短视频管理系统,旨在为泥人制作爱好者、非遗传承人和文化机构提供一个集视频上传、分类管理、在线观看、互动评论于一体的综合管理平台。系统采用SpringBoot作为后端开发框架,结合MyBatis-Plus进行数据持久化,前端使用Vue.js和Element UI构建用户界面,实现了用户管理、视频管理、分类管理、评论管理、数据统计等核心功能。本文详细阐述了系统的技术选型、需求分析、架构设计、数据库设计以及核心功能模块的实现过程,并对系统进行了功能测试和性能测试。测试结果表明,系统运行稳定、响应迅速,能够满足泥人制作短视频管理的实际需求,对推动传统手工艺的数字化传播具有积极的实践意义。
1. 引言
1.1 项目背景
泥人制作作为中国传统民间手工艺的重要代表,承载着深厚的历史文化底蕴。然而,随着现代生活方式的改变,传统泥人制作技艺的传承面临受众面窄、传播渠道单一等困境。近年来,短视频平台的兴起为传统文化的传播提供了全新的渠道,越来越多的非遗传承人通过短视频展示泥人制作过程,吸引了大量年轻用户的关注。
然而,当前主流的短视频平台主要面向大众娱乐内容,缺乏针对传统手工艺领域的专业化管理功能。泥人制作技艺的短视频内容分散在各个平台,缺乏系统化的分类整理和知识沉淀,不利于传统文化的系统传播与教学传承。因此,开发一个专门面向泥人制作领域的短视频管理系统,具有重要的现实意义。
1.2 研究意义
本系统的设计与实现具有以下多方面的意义:
- 文化传承价值:通过数字化手段系统化地整理和保存泥人制作技艺视频,为非遗文化的传承提供可持续的数字档案。
- 教学推广价值:为泥人制作爱好者提供系统化的学习资源,降低学习门槛,扩大传统技艺的受众群体。
- 平台管理价值:为文化机构和传承人提供专业的视频内容管理工具,提升内容组织效率和传播效果。
- 技术实践价值:综合运用SpringBoot、Vue.js等主流技术栈,为同类文化类短视频管理系统的开发提供可参考的技术方案。
2. 系统需求分析
2.1 功能需求分析
通过对泥人制作短视频管理业务场景的深入调研,本系统主要划分为以下功能模块:
| 功能模块 | 功能描述 |
|---|---|
| 用户管理 | 实现用户的注册、登录、个人信息维护、角色权限管理等功能 |
| 视频管理 | 支持视频的上传、编辑、删除、审核、上下架等全生命周期管理 |
| 分类管理 | 对泥人制作视频按技法、题材、难度等维度进行分类管理 |
| 评论互动 | 支持用户对视频进行评论、点赞、收藏等互动操作 |
| 数据统计 | 对视频播放量、用户活跃度、分类分布等数据进行统计分析 |
| 系统管理 | 包含系统参数配置、操作日志管理、数据备份等基础功能 |
2.2 非功能需求分析
- 性能需求:系统应支持至少500人同时在线访问,页面响应时间不超过3秒。
- 安全需求:用户密码需加密存储,接口需进行身份验证和权限控制,防止未授权访问。
- 可用性需求:系统界面应简洁友好,操作流程清晰,具备良好的用户体验。
- 可扩展性需求:系统架构应具备良好的扩展性,便于后续功能模块的添加和升级。
3. 系统技术栈
3.1 后端技术栈
| 技术组件 | 版本 | 用途说明 |
|---|---|---|
| SpringBoot | 2.7.x | 核心开发框架,提供自动配置和快速开发能力 |
| Spring Security | 5.7.x | 用户认证与授权管理 |
| MyBatis-Plus | 3.5.x | 数据持久层框架,简化数据库操作 |
| MySQL | 8.0 | 关系型数据库,存储业务数据 |
| Redis | 6.x | 缓存热点数据,提升系统性能 |
| MinIO | 8.4.x | 对象存储服务,存储视频文件 |
| JWT | 0.9.1 | 无状态身份认证令牌 |
3.2 前端技术栈
| 技术组件 | 版本 | 用途说明 |
|---|---|---|
| Vue.js | 2.6.x | 前端渐进式框架,构建用户界面 |
| Element UI | 2.15.x | 桌面端组件库,提供丰富的UI组件 |
| Axios | 0.21.x | HTTP请求库,与后端进行数据交互 |
| ECharts | 5.x | 数据可视化图表库,展示统计报表 |
4. 系统设计
4.1 系统架构设计
本系统采用前后端分离的B/S架构,整体分为表现层、业务逻辑层和数据访问层三层架构。前端通过HTTP协议与后端RESTful API进行数据交互,后端采用SpringBoot框架实现业务逻辑,通过MyBatis-Plus操作MySQL数据库,使用Redis缓存热点数据,视频文件存储在MinIO对象存储服务中。
flowchart TD A[浏览器前端 Vue.js] -->|HTTP/JSON| B[SpringBoot后端] B --> C[业务逻辑层 Service] C --> D[数据访问层 Mapper] D --> E[(MySQL数据库)] C --> F[Redis缓存] C --> G[MinIO对象存储] B --> H[Spring Security 认证授权]4.2 数据库设计
根据系统需求分析,数据库主要包含以下核心数据表:用户表、视频信息表、视频分类表、评论表、点赞记录表、收藏记录表、操作日志表等。以下为部分核心表结构设计:
用户表(sys_user)
| 字段名 | 数据类型 | 约束 | 说明 |
|---|---|---|---|
| id | bigint | 主键,自增 | 用户ID |
| username | varchar(50) | 唯一,非空 | 用户名 |
| password | varchar(100) | 非空 | 加密后的密码 |
| nickname | varchar(50) | 昵称 | |
| avatar | varchar(255) | 头像地址 | |
| role | tinyint | 默认1 | 角色:0管理员,1普通用户 |
| status | tinyint | 默认1 | 状态:0禁用,1正常 |
| create_time | datetime | 创建时间 |
视频信息表(video_info)
| 字段名 | 数据类型 | 约束 | 说明 |
|---|---|---|---|
| id | bigint | 主键,自增 | 视频ID |
| title | varchar(100) | 非空 | 视频标题 |
| description | text | 视频描述 | |
| category_id | bigint | 外键 | 分类ID |
| user_id | bigint | 外键 | 上传用户ID |
| video_url | varchar(255) | 非空 | 视频存储地址 |
| cover_url | varchar(255) | 封面图地址 | |
| play_count | bigint | 默认0 | 播放次数 |
| like_count | bigint | 默认0 | 点赞次数 |
| status | tinyint | 默认0 | 状态:0待审核,1已发布,2已下架 |
| create_time | datetime | 上传时间 |
5. 系统核心功能实现
5.1 用户认证模块实现
用户认证模块采用Spring Security结合JWT实现无状态认证。用户登录成功后,后端生成JWT令牌返回给前端,前端在后续请求中携带该令牌,后端通过过滤器对令牌进行校验,实现身份认证和权限控制。
@Service public class UserServiceImpl implements UserService { @Autowired private UserMapper userMapper; @Autowired private PasswordEncoder passwordEncoder; @Autowired private JwtUtils jwtUtils; @Override public LoginResult login(String username, String password) { // 根据用户名查询用户 LambdaQueryWrapper<SysUser> wrapper = new LambdaQueryWrapper<>(); wrapper.eq(SysUser::getUsername, username); SysUser user = userMapper.selectOne(wrapper); // 用户不存在或密码错误 if (user == null || !passwordEncoder.matches(password, user.getPassword())) { throw new BusinessException("用户名或密码错误"); } // 用户被禁用 if (user.getStatus() == 0) { throw new BusinessException("账号已被禁用,请联系管理员"); } // 生成JWT令牌 String token = jwtUtils.generateToken(user.getId(), user.getUsername(), user.getRole()); LoginResult result = new LoginResult(); result.setToken(token); result.setUserId(user.getId()); result.setNickname(user.getNickname()); result.setRole(user.getRole()); return result; } @Override public void register(SysUser user) { // 检查用户名是否已存在 LambdaQueryWrapper<SysUser> wrapper = new LambdaQueryWrapper<>(); wrapper.eq(SysUser::getUsername, user.getUsername()); if (userMapper.selectCount(wrapper) > 0) { throw new BusinessException("用户名已存在"); } // 密码加密存储 user.setPassword(passwordEncoder.encode(user.getPassword())); user.setRole(1); user.setStatus(1); user.setCreateTime(LocalDateTime.now()); userMapper.insert(user); } }5.2 视频上传与播放模块实现
视频上传模块采用分片上传策略,将大文件分割为多个分片依次上传,支持断点续传,有效解决了大视频文件上传超时和失败的问题。视频文件最终存储在MinIO对象存储服务中,数据库仅保存视频的访问地址。
@RestController @RequestMapping("/api/video") public class VideoController { @Autowired private VideoService videoService; @Autowired private MinioService minioService; /** 初始化分片上传 */ @PostMapping("/upload/init") public Result<UploadInitVO> initUpload(@RequestBody UploadInitDTO dto) { String uploadId = minioService.initMultipartUpload(dto.getFileName()); UploadInitVO vo = new UploadInitVO(); vo.setUploadId(uploadId); vo.setChunkSize(5 * 1024 * 1024); // 每个分片5MB vo.setTotalChunks((int) Math.ceil((double) dto.getFileSize() / vo.getChunkSize())); return Result.success(vo); } /** 上传视频分片 */ @PostMapping("/upload/chunk") public Result<Boolean> uploadChunk(@RequestParam("uploadId") String uploadId, @RequestParam("chunkIndex") Integer chunkIndex, @RequestParam("file") MultipartFile file) { minioService.uploadPart(uploadId, chunkIndex, file); return Result.success(true); } /** 合并分片并保存视频信息 */ @PostMapping("/upload/complete") public Result<Long> completeUpload(@RequestBody UploadCompleteDTO dto) { String videoUrl = minioService.completeMultipartUpload(dto.getUploadId(), dto.getFileName()); Long videoId = videoService.saveVideoInfo(dto, videoUrl); return Result.success(videoId); } /** 获取视频播放地址 */ @GetMapping("/play/{id}") public Result<String> getPlayUrl(@PathVariable Long id) { VideoInfo video = videoService.getById(id); if (video == null || video.getStatus() != 1) { throw new BusinessException("视频不存在或未发布"); } // 播放量加一 videoService.incrementPlayCount(id); // 生成临时访问地址 String playUrl = minioService.getPresignedUrl(video.getVideoUrl(), 3600); return Result.success(playUrl); } }5.3 视频分类管理模块实现
视频分类管理模块实现了对泥人制作视频的多维度分类管理,支持按制作技法、题材内容、难度等级等维度进行分类。管理员可以对分类进行增删改查操作,用户在前端可以按分类浏览和筛选视频。
@Service public class CategoryServiceImpl implements CategoryService { @Autowired private CategoryMapper categoryMapper; @Override public List<CategoryVO> getCategoryTree() { // 查询全部分类 List<VideoCategory> categories = categoryMapper.selectList( new LambdaQueryWrapper<VideoCategory>() .orderByAsc(VideoCategory::getSort) ); // 构建树形结构 Map&lt;Long, CategoryVO&gt; voMap = new HashMap&lt;&gt;(); List&lt;CategoryVO&gt; rootList = new ArrayList&lt;&gt;(); for (VideoCategory category : categories) { CategoryVO vo = new CategoryVO(); BeanUtils.copyProperties(category, vo); vo.setChildren(new ArrayList&lt;&gt;()); voMap.put(category.getId(), vo); } for (VideoCategory category : categories) { CategoryVO vo = voMap.get(category.getId()); if (category.getParentId() == 0) { rootList.add(vo); } else { CategoryVO parent = voMap.get(category.getParentId()); if (parent != null) { parent.getChildren().add(vo); } } } return rootList; } @Override public void addCategory(CategoryDTO dto) { VideoCategory category = new VideoCategory(); BeanUtils.copyProperties(dto, category); category.setCreateTime(LocalDateTime.now()); categoryMapper.insert(category); } @Override public void updateCategory(CategoryDTO dto) { VideoCategory category = new VideoCategory(); BeanUtils.copyProperties(dto, category); categoryMapper.updateById(category); } @Override public void deleteCategory(Long id) { // 检查是否存在子分类 Long childCount = categoryMapper.selectCount( new LambdaQueryWrapper<VideoCategory>()