
1. 项目概述这个毕业设计项目是一个基于SpringBootVue技术栈的移动端短视频社交平台。作为一名经历过多个短视频项目开发的老手我深知这类系统在当今移动互联网时代的重要性。这个平台不仅实现了基础的视频上传、播放功能还包含了社交互动和内容推荐等核心模块完全符合当下年轻人对短视频应用的期待。从技术架构来看系统采用前后端分离设计后端使用SpringBoot框架提供RESTful API前端使用Vue.js构建响应式界面数据库选用MySQL存储结构化数据。这种技术组合既保证了系统的可扩展性又能满足移动端对性能的高要求。2. 核心需求解析2.1 功能需求分解这个短视频平台需要实现以下核心功能模块用户系统注册/登录支持手机号验证码个人主页管理关注/粉丝关系维护私信功能实现视频模块短视频上传支持断点续传视频转码处理H.264编码封面自动生成多清晰度播放支持社交互动点赞/收藏/评论转发分享功能热门话题标签好友提醒推荐系统基于内容的推荐协同过滤算法热门视频排行个性化feed流2.2 非功能需求性能指标视频首屏加载时间1sAPI响应时间200ms支持1000并发用户安全要求视频内容审核机制防XSS/SQL注入敏感词过滤系统兼容性适配iOS/Android主流机型支持Chrome/Firefox/Safari响应式布局设计3. 技术架构设计3.1 系统架构图客户端层 - 网关层 - 业务服务层 - 数据存储层 ↑ ↑ │ └── 消息队列 └── CDN/OSS3.2 技术选型分析后端技术栈SpringBoot 2.7.x快速构建微服务MyBatis-Plus简化数据库操作Redis缓存热点数据Kafka异步消息处理FFmpeg视频转码工具前端技术栈Vue 3.x响应式前端框架Vant UI移动端组件库AxiosHTTP请求库Video.js视频播放器基础设施阿里云OSS对象存储腾讯云CDN内容分发MySQL 8.0关系型数据库Elasticsearch搜索服务3.3 数据库设计要点主要表结构设计用户表CREATE TABLE user ( id BIGINT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(32) UNIQUE, phone VARCHAR(11) UNIQUE, password VARCHAR(64), avatar VARCHAR(255), signature VARCHAR(100), status TINYINT DEFAULT 1, create_time DATETIME );视频表CREATE TABLE video ( id BIGINT PRIMARY KEY AUTO_INCREMENT, user_id BIGINT, title VARCHAR(100), cover_url VARCHAR(255), video_url VARCHAR(255), duration INT, width INT, height INT, like_count INT DEFAULT 0, comment_count INT DEFAULT 0, share_count INT DEFAULT 0, status TINYINT DEFAULT 1, create_time DATETIME, FOREIGN KEY (user_id) REFERENCES user(id) );关系表CREATE TABLE user_relation ( id BIGINT PRIMARY KEY AUTO_INCREMENT, user_id BIGINT, follower_id BIGINT, create_time DATETIME, FOREIGN KEY (user_id) REFERENCES user(id), FOREIGN KEY (follower_id) REFERENCES user(id), UNIQUE KEY uk_user_follower (user_id, follower_id) );4. 核心功能实现4.1 视频上传处理流程前端实现// 使用axios实现分片上传 const uploadVideo async (file) { const chunkSize 5 * 1024 * 1024; // 5MB const chunks Math.ceil(file.size / chunkSize); let uploaded 0; for (let i 0; i chunks; i) { const start i * chunkSize; const end Math.min(file.size, start chunkSize); const chunk file.slice(start, end); const formData new FormData(); formData.append(file, chunk); formData.append(chunkNumber, i 1); formData.append(totalChunks, chunks); formData.append(identifier, file.uniqueIdentifier); await axios.post(/api/video/upload, formData, { headers: { Content-Type: multipart/form-data }, onUploadProgress: (progressEvent) { const percent Math.round( ((uploaded progressEvent.loaded) / file.size) * 100 ); updateProgress(percent); } }); uploaded chunk.size; } };后端处理PostMapping(/upload) public ResponseEntity? uploadVideo( RequestParam(file) MultipartFile file, RequestParam int chunkNumber, RequestParam int totalChunks, RequestParam String identifier) { // 检查文件类型 String contentType file.getContentType(); if (!Arrays.asList(video/mp4, video/quicktime).contains(contentType)) { return ResponseEntity.badRequest().body(不支持的视频格式); } // 存储分片 String tempDir System.getProperty(java.io.tmpdir) /video-uploads/ identifier; File dir new File(tempDir); if (!dir.exists()) dir.mkdirs(); String chunkFilename chunkNumber .part; File chunkFile new File(dir, chunkFilename); try { file.transferTo(chunkFile); // 如果是最后一个分片开始合并 if (chunkNumber totalChunks) { mergeChunks(dir, totalChunks); processVideo(dir /merged.mp4); } return ResponseEntity.ok().build(); } catch (IOException e) { return ResponseEntity.status(500).body(上传失败); } }4.2 视频转码处理使用FFmpeg进行视频处理public void processVideo(String inputPath) { String outputDir /data/videos/; String outputName UUID.randomUUID().toString(); // 生成不同清晰度的视频 String[] resolutions {640x360, 854x480, 1280x720}; String[] bitrates {800k, 1500k, 3000k}; for (int i 0; i resolutions.length; i) { String cmd String.format( ffmpeg -i %s -c:v libx264 -b:v %s -vf scale%s -preset fast -movflags faststart %s%s_%s.mp4, inputPath, bitrates[i], resolutions[i], outputDir, outputName, resolutions[i] ); try { Process process Runtime.getRuntime().exec(cmd); process.waitFor(); } catch (Exception e) { logger.error(视频转码失败, e); } } // 生成封面 String coverCmd String.format( ffmpeg -i %s -ss 00:00:01 -vframes 1 %s%s_cover.jpg, inputPath, outputDir, outputName ); try { Process process Runtime.getRuntime().exec(coverCmd); process.waitFor(); } catch (Exception e) { logger.error(封面生成失败, e); } }4.3 推荐算法实现基于用户行为的协同过滤算法public ListVideo recommendVideos(Long userId, int limit) { // 1. 获取用户最近观看记录 ListWatchHistory histories watchHistoryMapper.selectByUser(userId, 50); // 2. 提取视频特征向量 MapLong, double[] videoFeatures new HashMap(); ListVideo watchedVideos histories.stream() .map(h - videoMapper.selectById(h.getVideoId())) .collect(Collectors.toList()); // 3. 计算用户偏好向量 double[] userVector new double[FEATURE_SIZE]; for (Video video : watchedVideos) { double[] features getVideoFeatures(video); for (int i 0; i features.length; i) { userVector[i] features[i]; } } // 4. 归一化处理 normalizeVector(userVector); // 5. 计算相似度并推荐 ListVideo candidates videoMapper.selectRecentVideos(1000); candidates.removeAll(watchedVideos); return candidates.stream() .sorted((v1, v2) - { double sim1 cosineSimilarity(userVector, getVideoFeatures(v1)); double sim2 cosineSimilarity(userVector, getVideoFeatures(v2)); return Double.compare(sim2, sim1); }) .limit(limit) .collect(Collectors.toList()); } private double cosineSimilarity(double[] v1, double[] v2) { double dotProduct 0.0; double norm1 0.0; double norm2 0.0; for (int i 0; i v1.length; i) { dotProduct v1[i] * v2[i]; norm1 Math.pow(v1[i], 2); norm2 Math.pow(v2[i], 2); } return dotProduct / (Math.sqrt(norm1) * Math.sqrt(norm2)); }5. 性能优化策略5.1 缓存设计Redis缓存策略Cacheable(value video, key #videoId) public Video getVideoById(Long videoId) { return videoMapper.selectById(videoId); } CachePut(value video, key #video.id) public Video updateVideo(Video video) { videoMapper.updateById(video); return video; } CacheEvict(value video, key #videoId) public void deleteVideo(Long videoId) { videoMapper.deleteById(videoId); }热点数据预加载Scheduled(fixedRate 30 * 60 * 1000) // 每30分钟执行一次 public void preloadHotVideos() { ListVideo hotVideos videoMapper.selectHotVideos(100); hotVideos.forEach(video - { redisTemplate.opsForValue().set( video: video.getId(), video, 1, TimeUnit.HOURS ); }); }5.2 数据库优化索引设计ALTER TABLE video ADD INDEX idx_user_status (user_id, status); ALTER TABLE comment ADD INDEX idx_video_time (video_id, create_time);分库分表策略# application-sharding.yml spring: shardingsphere: datasource: names: ds0,ds1 sharding: tables: video: actual-data-nodes: ds$-{0..1}.video_$-{0..15} table-strategy: inline: sharding-column: id algorithm-expression: video_$-{id % 16} database-strategy: inline: sharding-column: user_id algorithm-expression: ds$-{user_id % 2}5.3 前端性能优化懒加载实现template div classvideo-list div v-forvideo in visibleVideos :keyvideo.id classvideo-item img v-lazyvideo.coverUrl altvideo cover /div /div /template script import { useIntersectionObserver } from vueuse/core export default { data() { return { allVideos: [], visibleVideos: [], observer: null } }, mounted() { this.loadVideos() this.setupLazyLoad() }, methods: { async loadVideos() { this.allVideos await this.$api.getVideos() this.visibleVideos this.allVideos.slice(0, 5) }, setupLazyLoad() { const container document.querySelector(.video-list) this.observer useIntersectionObserver( container, ([entry]) { if (entry.isIntersecting) { const newCount Math.min( this.visibleVideos.length 3, this.allVideos.length ) this.visibleVideos this.allVideos.slice(0, newCount) } }, { threshold: 0.1 } ) } } } /script视频预加载策略// 预加载下一个视频 function preloadNextVideo() { const nextVideo getNextVideoInFeed(); const videoElement document.createElement(video); videoElement.preload auto; videoElement.src nextVideo.url; // 只预加载前3秒 videoElement.addEventListener(loadeddata, () { if (videoElement.readyState 2) { videoElement.currentTime 3; } }); }6. 部署方案6.1 容器化部署Dockerfile示例# 后端Dockerfile FROM openjdk:11-jdk ARG JAR_FILEtarget/*.jar COPY ${JAR_FILE} app.jar ENTRYPOINT [java,-jar,/app.jar] # 前端Dockerfile FROM node:16 as build WORKDIR /app COPY package*.json ./ RUN npm install COPY . . RUN npm run build FROM nginx:alpine COPY --frombuild /app/dist /usr/share/nginx/html COPY nginx.conf /etc/nginx/conf.d/default.confdocker-compose.ymlversion: 3 services: backend: build: ./backend ports: - 8080:8080 environment: - SPRING_PROFILES_ACTIVEprod depends_on: - redis - mysql frontend: build: ./frontend ports: - 80:80 mysql: image: mysql:8.0 environment: MYSQL_ROOT_PASSWORD: root MYSQL_DATABASE: video_db volumes: - mysql_data:/var/lib/mysql redis: image: redis:6 ports: - 6379:6379 volumes: mysql_data:6.2 CI/CD流程GitHub Actions配置name: Build and Deploy on: push: branches: [ main ] jobs: build-backend: runs-on: ubuntu-latest steps: - uses: actions/checkoutv2 - name: Set up JDK 11 uses: actions/setup-javav2 with: java-version: 11 distribution: adopt - name: Build with Maven run: mvn -B package --file pom.xml - name: Build Docker image run: docker build -t video-backend:${{ github.sha }} ./backend - name: Log in to Docker Hub uses: docker/login-actionv1 with: username: ${{ secrets.DOCKER_HUB_USERNAME }} password: ${{ secrets.DOCKER_HUB_TOKEN }} - name: Push Docker image run: | docker tag video-backend:${{ github.sha }} username/video-backend:latest docker push username/video-backend:latest deploy: needs: [build-backend] runs-on: ubuntu-latest steps: - name: SSH and deploy uses: appleboy/ssh-actionmaster with: host: ${{ secrets.SERVER_HOST }} username: ${{ secrets.SERVER_USER }} key: ${{ secrets.SERVER_SSH_KEY }} script: | cd ~/video-app docker-compose pull docker-compose up -d7. 常见问题与解决方案7.1 视频上传问题排查问题现象可能原因解决方案上传进度卡住网络不稳定或分片太大减小分片大小(2MB)增加重试机制上传后视频无法播放转码失败或格式不支持检查FFmpeg日志确保输入格式正确大文件上传超时Nginx默认限制调整Nginx配置client_max_body_size 100m;移动端上传失败某些机型兼容性问题使用第三方库如react-native-document-picker7.2 性能问题优化API响应慢检查慢查询日志优化SQL增加二级缓存使用连接池管理数据库连接视频卡顿启用CDN加速实现多码率自适应(ABR)预加载下一段视频内存泄漏// 示例修复视频处理中的资源泄漏 public void processVideo(String inputPath) { Process process null; try { process Runtime.getRuntime().exec(cmd); process.waitFor(); } catch (Exception e) { logger.error(处理失败, e); } finally { if (process ! null) { try { process.getInputStream().close(); process.getErrorStream().close(); process.getOutputStream().close(); } catch (IOException e) { logger.warn(关闭流失败, e); } } } }7.3 安全防护措施视频内容安全接入第三方内容审核API实现敏感帧检测建立用户举报机制接口防刷RateLimiter(value 10, key #userId) PostMapping(/like) public ResponseEntity? likeVideo(RequestParam Long videoId, RequestParam Long userId) { // 点赞逻辑 }XSS防护Configuration public class WebSecurityConfig extends WebSecurityConfigurerAdapter { Override protected void configure(HttpSecurity http) throws Exception { http .headers() .xssProtection() .and() .contentSecurityPolicy(script-src self); } }8. 项目扩展方向AI功能增强智能封面生成自动字幕添加内容分类打标社交功能扩展直播功能集成连麦互动虚拟礼物系统商业化功能广告投放系统付费内容订阅电商带货功能技术架构升级微服务化改造引入Service Mesh实现多活部署在实际开发过程中我特别建议重视监控系统的建设。我们团队使用PrometheusGrafana搭建了完整的监控体系对API响应时间、视频转码成功率、用户活跃度等关键指标进行实时监控这对快速定位和解决问题起到了至关重要的作用。