1. 项目概述作为一名有10年Java全栈开发经验的工程师最近我完成了一个基于Java的短剧推荐系统毕业设计项目。这个系统采用了当前主流的SpringBootVue前后端分离架构整合了推荐算法、用户行为分析等核心功能模块。在实际开发过程中我遇到了不少技术难点和业务逻辑的挑战今天就来详细分享一下这个项目的设计思路和实现过程。短剧推荐系统本质上是一个内容分发平台核心目标是通过分析用户的观看历史和偏好为其推荐可能感兴趣的短剧内容。相比传统的影视推荐系统短剧具有时长更短、更新频率更快的特点这对推荐算法的实时性和准确性提出了更高要求。2. 系统架构设计2.1 技术栈选型在技术选型上我主要考虑了以下几个因素开发效率作为毕业设计项目需要在有限时间内完成学习成本使用主流技术栈便于后续维护和扩展性能要求需要支撑一定的并发访问量基于这些考虑最终确定的技术方案如下后端技术栈基础框架Spring Boot 2.7.xORM框架MyBatis-Plus 3.5.x安全框架Spring Security缓存Redis消息队列RabbitMQ用于异步处理用户行为日志搜索引擎Elasticsearch用于短剧内容检索前端技术栈核心框架Vue 3.xUI组件库Element Plus状态管理Pinia路由Vue RouterHTTP客户端Axios数据库主库MySQL 8.0事务型数据从库MySQL 8.0读写分离文档数据库MongoDB存储用户行为日志2.2 系统架构图整个系统采用标准的微服务架构分为以下几个核心服务[用户端] → [API Gateway] → [用户服务] → [MySQL] ↘ [推荐服务] → [Redis/ES] ↘ [内容服务] → [MySQL] ↘ [日志服务] → [MongoDB]API Gateway使用Spring Cloud Gateway实现负责路由转发、限流和鉴权。各服务之间通过OpenFeign进行通信使用Nacos作为服务注册中心。2.3 数据库设计数据库设计遵循第三范式主要包含以下几张核心表用户表(user)CREATE TABLE user ( id bigint NOT NULL AUTO_INCREMENT, username varchar(50) NOT NULL COMMENT 用户名, password varchar(100) NOT NULL COMMENT 密码, phone varchar(20) DEFAULT NULL COMMENT 手机号, avatar varchar(255) DEFAULT NULL COMMENT 头像, gender tinyint DEFAULT 0 COMMENT 性别(0-未知 1-男 2-女), birthday date DEFAULT NULL COMMENT 生日, status tinyint DEFAULT 1 COMMENT 状态(0-禁用 1-正常), create_time datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, update_time datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY idx_username (username), KEY idx_phone (phone) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT用户表;短剧表(drama)CREATE TABLE drama ( id bigint NOT NULL AUTO_INCREMENT, title varchar(100) NOT NULL COMMENT 短剧标题, cover varchar(255) NOT NULL COMMENT 封面图, description text COMMENT 描述, category_id int NOT NULL COMMENT 分类ID, tags varchar(255) DEFAULT NULL COMMENT 标签逗号分隔, episode_count int NOT NULL DEFAULT 0 COMMENT 集数, duration int DEFAULT NULL COMMENT 单集时长(秒), score decimal(3,1) DEFAULT 0.0 COMMENT 评分, status tinyint NOT NULL DEFAULT 1 COMMENT 状态(0-下架 1-上架), create_time datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, update_time datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_category (category_id), KEY idx_score (score), FULLTEXT KEY ft_title_desc (title,description) /* 全文索引 */ ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT短剧表;用户行为表(user_behavior)CREATE TABLE user_behavior ( id bigint NOT NULL AUTO_INCREMENT, user_id bigint NOT NULL, drama_id bigint NOT NULL, behavior_type tinyint NOT NULL COMMENT 1-浏览 2-播放 3-点赞 4-收藏 5-分享, duration int DEFAULT NULL COMMENT 观看时长(秒), create_time datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_user_drama (user_id,drama_id), KEY idx_create_time (create_time) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT用户行为表;3. 核心功能实现3.1 推荐算法实现推荐系统是短剧平台的核心竞争力我实现了以下几种推荐策略基于内容的推荐public ListDrama recommendByContent(Long userId, int size) { // 1. 获取用户最近观看的短剧 ListLong recentDramas behaviorMapper.selectRecentWatchedDramas(userId, 5); if (recentDramas.isEmpty()) { return recommendHotDramas(size); // 退回热门推荐 } // 2. 提取这些短剧的特征标签 SetString tags dramaMapper.selectTagsByDramaIds(recentDramas); // 3. 根据标签相似度推荐其他短剧 return dramaMapper.selectByTags(tags, size); }协同过滤推荐public ListDrama recommendByCF(Long userId, int size) { // 1. 获取相似用户 ListLong similarUsers userSimilarityService.findSimilarUsers(userId, 5); // 2. 获取这些用户喜欢但当前用户未看过的短剧 ListLong candidateDramas behaviorMapper.selectDramasByUsers( similarUsers, Collections.singletonList(userId), size * 3 ); // 3. 按热度排序 return dramaMapper.selectByIdsOrderByScore(candidateDramas, size); }实时推荐RabbitListener(queues behavior.queue) public void processBehavior(BehaviorMessage message) { // 1. 记录用户行为 behaviorMapper.insert(message.toBehavior()); // 2. 更新用户兴趣模型 userProfileService.updateUserInterest(message.getUserId(), message.getDramaId(), message.getBehaviorType()); // 3. 更新短剧热度 dramaStatService.incrementCount(message.getDramaId(), message.getBehaviorType()); }3.2 用户行为分析用户行为数据是推荐系统的重要输入我设计了完善的行为采集和分析流程行为类型定义public enum BehaviorType { VIEW(1, 浏览), // 浏览详情页 PLAY(2, 播放), // 开始播放 LIKE(3, 点赞), // 点赞 COLLECT(4, 收藏), // 收藏 SHARE(5, 分享); // 分享 private final int code; private final String desc; // constructor getters }行为权重配置# 行为权重配置 behavior.weight.view1 behavior.weight.play3 behavior.weight.like5 behavior.weight.collect8 behavior.weight.share10兴趣度计算public void updateUserInterest(Long userId, Long dramaId, BehaviorType behaviorType) { // 1. 获取行为权重 int weight getBehaviorWeight(behaviorType); // 2. 获取短剧标签 SetString tags dramaService.getDramaTags(dramaId); // 3. 更新用户兴趣向量 userInterestService.incrementTagsWeight(userId, tags, weight); // 4. 更新Redis缓存 String cacheKey user:interest: userId; redisTemplate.opsForValue().set(cacheKey, userInterestService.getUserInterest(userId), 1, TimeUnit.HOURS); }3.3 性能优化为了提升系统性能我实施了以下几项优化措施多级缓存设计public Drama getDramaById(Long id) { // 1. 查询本地缓存 Drama drama localCache.get(id); if (drama ! null) { return drama; } // 2. 查询Redis String redisKey drama: id; drama (Drama) redisTemplate.opsForValue().get(redisKey); if (drama ! null) { localCache.put(id, drama); // 回填本地缓存 return drama; } // 3. 查询数据库 drama dramaMapper.selectById(id); if (drama ! null) { redisTemplate.opsForValue().set(redisKey, drama, 30, TimeUnit.MINUTES); localCache.put(id, drama); } return drama; }数据库读写分离spring: datasource: master: url: jdbc:mysql://master-host:3306/drama username: root password: 123456 slave: url: jdbc:mysql://slave-host:3306/drama username: root password: 123456Elasticsearch搜索优化public SearchResponse searchDramas(String keyword, int page, int size) { NativeSearchQueryBuilder queryBuilder new NativeSearchQueryBuilder() .withQuery(QueryBuilders.multiMatchQuery(keyword, title, description, tags)) .withPageable(PageRequest.of(page - 1, size)) .withSort(SortBuilders.scoreSort()) .withHighlightFields( new HighlightBuilder.Field(title).preTags(em).postTags(/em), new HighlightBuilder.Field(description).preTags(em).postTags(/em) ); // 添加分类和评分过滤 if (categoryId ! null) { queryBuilder.withFilter(QueryBuilders.termQuery(category_id, categoryId)); } if (minScore ! null) { queryBuilder.withFilter(QueryBuilders.rangeQuery(score).gte(minScore)); } return elasticsearchTemplate.search(queryBuilder.build(), Drama.class); }4. 系统部署与测试4.1 部署架构系统采用Docker容器化部署整体架构如下[Nginx] → [Spring Boot Apps] → [MySQL Cluster] ↘ [Vue App] ↘ [Redis Cluster] ↘ [Elasticsearch Cluster]使用Docker Compose编排服务version: 3 services: nginx: image: nginx:1.21 ports: - 80:80 volumes: - ./nginx.conf:/etc/nginx/nginx.conf depends_on: - gateway - frontend gateway: build: ./gateway ports: - 8080:8080 environment: - SPRING_PROFILES_ACTIVEprod depends_on: - redis - nacos frontend: build: ./frontend ports: - 3000:3000 redis: image: redis:6.2 ports: - 6379:6379 volumes: - redis_data:/data mysql: image: mysql:8.0 ports: - 3306:3306 environment: - MYSQL_ROOT_PASSWORD123456 - MYSQL_DATABASEdrama volumes: - mysql_data:/var/lib/mysql # 其他服务配置...4.2 压力测试使用JMeter进行压力测试主要测试以下几个场景用户登录接口线程数100循环次数10平均响应时间128ms错误率0%短剧推荐接口线程数200循环次数20平均响应时间235ms错误率0.2%短剧搜索接口线程数150循环次数15平均响应时间189ms错误率0%测试结果表明系统在常规负载下表现良好但在推荐接口高并发时出现了少量错误后续通过增加Redis节点和优化SQL查询解决了这个问题。4.3 安全测试系统进行了全面的安全测试主要包括SQL注入测试使用SQLMap工具扫描所有接口均未发现注入漏洞XSS测试尝试在前端输入框中注入脚本后端过滤和前端转义双重防护CSRF测试所有修改操作都要求携带Token关键操作需要二次验证权限测试普通用户无法访问管理员接口垂直权限和水平权限检查完善5. 项目总结与经验分享在开发这个短剧推荐系统的过程中我积累了一些宝贵的经验关于推荐算法不要一开始就追求复杂的算法简单的基于内容的推荐也能取得不错的效果用户冷启动问题可以通过热门推荐和随机推荐来解决实时性对短剧推荐特别重要用户的最新行为应该尽快影响推荐结果关于性能优化缓存是提升性能的利器但要处理好缓存一致性问题数据库查询一定要用EXPLAIN分析执行计划适当的反范式设计可以显著减少联表查询关于工程实践接口设计要遵循RESTful规范代码分层要清晰Controller只做参数校验和结果封装日志记录要完整便于问题排查遇到的坑与解决方案问题MySQL连接数不足 解决调整连接池配置增加从库分担读压力问题Redis缓存穿透 解决对空结果也进行缓存设置较短的过期时间问题Elasticsearch分片不均 解决手动调整分片分配策略设置合适的分片数这个项目从技术选型到最终部署上线历时约3个月时间。通过这个项目我不仅巩固了Java全栈开发的技能还对推荐系统、高并发处理有了更深的理解。希望我的经验分享对正在做类似项目的同学有所帮助。
