毕设所有选题:
https://blog.csdn.net/2303_76227485/article/details/131104075
基于Springboot+Vue的影视推荐和评分系统(源代码+数据库)
项目编号:263
一、系统介绍
本项目前后端分离,分为用户、导演、管理员3种角色。
1、用户:
- 注册登录、影视协同过滤推荐、热映Top15、近期好评、电影搜索、电影(收藏、加入我想看的、标为已看过)、影视评论、个人信息、密码修改
2、导演:
- 注册登录、电影管理、评论查看
3、管理员:
- 统计分析:前十导演作品数量统计,电影数量统计,电影评分分布,电影分类占比统计
- 导演管理:导演增删改查、导演审核
- 用户管理:
- 分类管理:
- 电影管理:增删改查、查看成绩
- 评论管理:评论查看,评论删除
3、亮点:
- 使用协同过滤算法为用户推荐影视作品
- 后台首页大屏使用echarts图表统计,更直观的看出系统的运行数据
二、所用技术
后端技术栈:
- Springboot
- mybatis
- Jwt
- Mysql
- Maven
前端技术栈:
- Vue
- Vue-router
- axios
- elementUi
- echarts
三、环境介绍
基础环境 :IDEA/eclipse, JDK1.8, Mysql5.7及以上, Maven3.6, node16, navicat
所有项目以及源代码本人均调试运行无问题 可支持远程调试运行
四、页面截图
1、用户:














2、导演:




3、管理员:







五、浏览地址
-
用户账号密码:1/123456
-
导演账号密码:1/123456
-
管理员账户密码:admin/123456
六、部署教程
-
使用Navicat或者其它工具,在mysql中创建对应名称的数据库,并执行项目的sql文件
-
使用IDEA/Eclipse导入springboot项目,若为maven项目请选择maven,等待依赖下载完成
-
修改application.yml里面的数据库配置,src/main/java/com/example/SpringbootApplication.java启动后端项目
-
vscode或idea打开vue项目
-
在编译器中打开terminal,执行npm install 依赖下载完成后执行 npm run serve,执行成功后会显示访问地址
七、协同过滤推荐部分代码
java
public List<Movie> recommend() {
Account currentUser = TokenUtils.getCurrentUser();
if (ObjectUtil.isEmpty(currentUser)) {
return new ArrayList<>();
}
List<Collect> allCollect = collectMapper.selectAll(null);
List<Towatch> allTowatch = towatchMapper.selectAll(null);
List<Seen> allSeen = seenMapper.selectAll(null);
List<Comment> allComment = commentMapper.selectAll(null);
List<User> allUser = userMapper.selectAll(null);
List<Movie> allMovie = movieMapper.selectAll(null);
List<RelateDTO> data = new ArrayList<>();
for (Movie movie : allMovie) {
Integer movieId = movie.getId();
for (User user : allUser) {
Integer userId = user.getId();
int index = 1;
Optional<Collect> collectOptional = allCollect.stream().filter(x -> x.getMovieId().equals(movieId) && x.getUserId().equals(userId)).findFirst();
if (collectOptional.isPresent()) {
index += 2;
}
Optional<Towatch> towatchOptional = allTowatch.stream().filter(x -> x.getMovieId().equals(movieId) && x.getUserId().equals(userId)).findFirst();
if (towatchOptional.isPresent()) {
index += 3;
}
Optional<Seen> seenOptional = allSeen.stream().filter(x -> x.getMovieId().equals(movieId) && x.getUserId().equals(userId)).findFirst();
if (seenOptional.isPresent()) {
index += 4;
}
Optional<Comment> commentOptional = allComment.stream().filter(x -> x.getMovieId().equals(movieId) && x.getUserId().equals(userId)).findFirst();
if (commentOptional.isPresent()) {
index += 1;
}
if (index > 1) {
RelateDTO relateDTO = new RelateDTO(userId, movieId, index);
data.add(relateDTO);
}
}
}
List<Integer> movieIds = UserCF.recommend(currentUser.getId(), data);
List<Movie> recommendResult = movieIds.stream().map(movieId -> allMovie.stream()
.filter(x -> x.getId().equals(movieId)).findFirst().orElse(null))
.limit(10).collect(Collectors.toList());
if (CollectionUtil.isEmpty(recommendResult)) {
return getRandomMovie(10, null);
}
Set<Integer> recommendedIds = recommendResult.stream()
.map(Movie::getId)
.collect(Collectors.toSet());
if (recommendResult.size() < 10) {
int num = 10 - recommendResult.size();
List<Movie> randomMovies = getRandomMovie(num, recommendedIds); // 传入已存在的ID集合
recommendResult.addAll(randomMovies);
}
return recommendResult;
}
public class UserCF {
/**
* 方法描述: 推荐id列表
*
* @param userId 当前用户
* @param list 评分数据
* @return {@link List<Integer>}
*/
public static List<Integer> recommend(Integer userId, List<RelateDTO> list) {
// 按用户分组
Map<Integer, List<RelateDTO>> userMap = list.stream().collect(Collectors.groupingBy(RelateDTO::getUserId));
// 获取其他用户与当前用户的关系值
Map<Integer, Double> userDisMap = CoreMath.computeNeighbor(userId, userMap, 0);
if (CollectionUtil.isEmpty(userDisMap)) {
return Collections.emptyList();
}
// 获取关系最近的用户
double maxValue = Collections.max(userDisMap.values());
Set<Integer> userIds = userDisMap.entrySet().stream().filter(e -> e.getValue() == maxValue).map(Map.Entry::getKey).collect(Collectors.toSet());
// 取关系最近的用户
Integer nearestUserId = userIds.stream().findAny().orElse(null);
if (nearestUserId == null) {
return Collections.emptyList();
}
// 最近邻用户看过
List<Integer> neighborItems = userMap.get(nearestUserId).stream().map(RelateDTO::getMovieId).collect(Collectors.toList());
// 指定用户看过
List<Integer> userItems = userMap.get(userId).stream().map(RelateDTO::getMovieId).collect(Collectors.toList());
// 找到最近邻看过,但是该用户没看过的
neighborItems.removeAll(userItems);
return neighborItems;
}
}
public class CoreMath {
public static Map<Integer, Double> computeNeighbor(Integer key, Map<Integer, List<RelateDTO>> map, int type) {
Map<Integer, Double> distMap = new TreeMap<>();
List<RelateDTO> userItems = map.get(key);
if (CollectionUtil.isNotEmpty(userItems)) {
map.forEach((k, v) -> {
//排除此用户
if (!k.equals(key)) {
//关系系数
double coefficient = relateDist(v, userItems, type);
//关系距离
double distance = Math.abs(coefficient);
distMap.put(k, distance);
}
});
}
return distMap;
}
/**
* 计算两个序列间的相关系数
*/
private static double relateDist(List<RelateDTO> xList, List<RelateDTO> yList, int type) {
List<Integer> xs = new ArrayList<>();
List<Integer> ys = new ArrayList<>();
xList.forEach(x -> yList.forEach(y -> {
if (type == 0) {
if (x.getMovieId().equals(y.getMovieId())) {
xs.add(x.getIndex());
ys.add(y.getIndex());
}
} else {
if (x.getUserId().equals(y.getUserId())) {
xs.add(x.getIndex());
ys.add(y.getIndex());
}
}
}));
return getRelate(xs, ys);
}
/**
* 方法描述: 皮尔森(pearson)相关系数计算
* @param xs x集合
* @param ys y集合
* @Return {@link double}
public static double getRelate(List<Integer> xs, List<Integer> ys) {
int n = xs.size();
//至少有两个元素
if (n < 2) {
return 0D;
}
double Ex = xs.stream().mapToDouble(x -> x).sum();
double Ey = ys.stream().mapToDouble(y -> y).sum();
double Ex2 = xs.stream().mapToDouble(x -> Math.pow(x, 2)).sum();
double Ey2 = ys.stream().mapToDouble(y -> Math.pow(y, 2)).sum();
double Exy = IntStream.range(0, n).mapToDouble(i -> xs.get(i) * ys.get(i)).sum();
double numerator = Exy - Ex * Ey / n;
double denominator = Math.sqrt((Ex2 - Math.pow(Ex, 2) / n) * (Ey2 - Math.pow(Ey, 2) / n));
if (denominator == 0) {
return 0D;
}
return numerator / denominator;
}
*/
public static double getRelate(List<Integer> xs, List<Integer> ys) {
int n = xs.size();
// 至少有两个元素
if (n < 2) {
return 0D;
}
double sumX = 0, sumY = 0, sumX2 = 0, sumY2 = 0, sumXY = 0;
for (int i = 0; i < n; i++) {
int x = xs.get(i);
int y = ys.get(i);
sumX += x;
sumY += y;
sumX2 += x * x;
sumY2 += y * y;
sumXY += x * y;
}
double Ex = sumX / n;
double Ey = sumY / n;
double Ex2 = sumX2 / n;
double Ey2 = sumY2 / n;
double Exy = sumXY / n;
double numerator = Exy - Ex * Ey;
double denominator = Math.sqrt((Ex2 - Ex * Ex) * (Ey2 - Ey * Ey));
// 检查分母是否为零,以避免除以零的异常
if (denominator == 0) {
return 0D;
}
return numerator / denominator;
}
}