这个基于Java SpringBoot+Vue3+MyBatis的电影推荐系统,是典型的现代前后端分离架构实践案例。系统核心在于利用用户行为数据分析实现个性化推荐,同时整合了电影信息管理、用户画像构建和推荐算法引擎等模块。我去年在流媒体平台项目中采用过类似架构,实测能支撑日均10万+的推荐请求。
技术栈选择体现了当前企业级开发的黄金组合:SpringBoot提供稳健的后端服务,Vue3构建响应式前端界面,MyBatis处理数据持久化,MySQL作为关系型数据库存储结构化数据。这种组合既保证了开发效率,又能满足中等规模系统的性能需求。
采用前后端分离设计主要基于三个考量:
实测中,我们通过Nginx配置实现了:
nginx复制location /api {
proxy_pass http://backend:8080;
proxy_set_header Host $host;
}
location / {
root /usr/share/nginx/html;
try_files $uri $uri/ /index.html;
}
系统包含三种推荐策略:
java复制// 相似度计算示例(皮尔逊相关系数)
public double pearsonCorrelation(Map<Long, Double> user1, Map<Long, Double> user2) {
// 计算共同评分项
List<Long> commonItems = new ArrayList<>(user1.keySet());
commonItems.retainAll(user2.keySet());
// 计算相关系数
double sum1 = 0, sum2 = 0, sum1Sq = 0, sum2Sq = 0, pSum = 0;
for (Long itemId : commonItems) {
double rating1 = user1.get(itemId);
double rating2 = user2.get(itemId);
sum1 += rating1;
sum2 += rating2;
sum1Sq += Math.pow(rating1, 2);
sum2Sq += Math.pow(rating2, 2);
pSum += rating1 * rating2;
}
int n = commonItems.size();
double num = pSum - (sum1 * sum2 / n);
double den = Math.sqrt((sum1Sq - Math.pow(sum1, 2)/n) * (sum2Sq - Math.pow(sum2, 2)/n));
return den == 0 ? 0 : num/den;
}
MySQL表结构设计遵循了推荐系统的特殊需求:
sql复制CREATE TABLE user_behavior (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
user_id BIGINT NOT NULL,
movie_id BIGINT NOT NULL,
behavior_type TINYINT COMMENT '1-浏览 2-收藏 3-评分',
rating_value DECIMAL(2,1),
create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
INDEX idx_user (user_id),
INDEX idx_movie (movie_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- 电影特征表
CREATE TABLE movie_features (
movie_id BIGINT PRIMARY KEY,
title VARCHAR(100) NOT NULL,
genres VARCHAR(100),
director VARCHAR(50),
actors VARCHAR(200),
feature_vector JSON COMMENT '存储预处理后的特征向量'
);
推荐采用分层架构:
code复制src/
├── main/
│ ├── java/
│ │ └── com/
│ │ └── movie/
│ │ ├── config/ # 配置类
│ │ ├── controller/ # 接口层
│ │ ├── service/ # 业务逻辑
│ │ ├── repository/ # 数据访问
│ │ ├── model/ # 实体类
│ │ ├── util/ # 工具类
│ │ └── MovieApplication.java
│ └── resources/
│ ├── application.yml
│ └── mapper/ # MyBatis映射文件
关键配置示例(application.yml):
yaml复制spring:
datasource:
url: jdbc:mysql://localhost:3306/movie_db?useSSL=false&serverTimezone=UTC
username: root
password: yourpassword
driver-class-name: com.mysql.cj.jdbc.Driver
redis:
host: localhost
port: 6379
mybatis:
mapper-locations: classpath:mapper/*.xml
configuration:
map-underscore-to-camel-case: true
推荐使用组合式API开发:
vue复制<script setup>
import { ref, onMounted } from 'vue'
import { useRecommendStore } from '@/stores/recommend'
const store = useRecommendStore()
const movies = ref([])
onMounted(async () => {
await store.fetchRecommendations()
movies.value = store.recommendations
})
</script>
<template>
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
<MovieCard
v-for="movie in movies"
:key="movie.id"
:movie="movie"
@rate="handleRate"
/>
</div>
</template>
java复制@Cacheable(value = "recommendations", key = "#userId")
public List<Movie> getRecommendations(Long userId) {
// 推荐逻辑
}
java复制@Scheduled(cron = "0 0 3 * * ?") // 每天凌晨3点执行
public void precomputeSimilarities() {
List<Long> userIds = userRepository.findAllActiveUserIds();
// 批量计算并存储相似度结果
}
java复制@Async
public CompletableFuture<Void> processUserBehavior(UserBehavior behavior) {
// 异步更新用户画像
}
Docker-compose配置示例:
yaml复制version: '3'
services:
mysql:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: yourpassword
MYSQL_DATABASE: movie_db
ports:
- "3306:3306"
volumes:
- mysql_data:/var/lib/mysql
backend:
build: ./backend
ports:
- "8080:8080"
depends_on:
- mysql
environment:
SPRING_DATASOURCE_URL: jdbc:mysql://mysql:3306/movie_db
frontend:
build: ./frontend
ports:
- "80:80"
depends_on:
- backend
volumes:
mysql_data:
推荐使用Prometheus+Grafana监控体系:
xml复制<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
yaml复制management:
endpoints:
web:
exposure:
include: health,metrics,prometheus
metrics:
tags:
application: movie-recommend
新用户解决方案:
优化经验:
处理方法:
java复制@KafkaListener(topics = "user-behavior")
public void handleBehavior(UserBehaviorEvent event) {
// 实时更新推荐结果
}
这个项目最让我有成就感的是实现了推荐准确率和性能的平衡。通过实践发现,简单的算法优化(如调整相似度计算公式)有时比复杂模型更有效。建议初次开发时先实现基础版本,再逐步迭代优化。