计算机毕业设计263—基于Springboot+Vue的影视推荐和评分系统(源代码+数据库)

毕设所有选题:
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、管理员:







五、浏览地址

前台地址:http://localhost:8080

  • 用户账号密码:1/123456

  • 导演账号密码:1/123456

  • 管理员账户密码:admin/123456

六、部署教程

  1. 使用Navicat或者其它工具,在mysql中创建对应名称的数据库,并执行项目的sql文件

  2. 使用IDEA/Eclipse导入springboot项目,若为maven项目请选择maven,等待依赖下载完成

  3. 修改application.yml里面的数据库配置,src/main/java/com/example/SpringbootApplication.java启动后端项目

  4. vscode或idea打开vue项目

  5. 在编译器中打开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;
    }
}
相关推荐
一 乐2 小时前
在线考试|基于springboot + vue在线考试系统(源码+数据库+文档)
java·数据库·vue.js·spring boot·后端·课程设计
qq_12498707532 小时前
基于spring boot的调查问卷系统的设计与实现(源码+论文+部署+安装)
java·vue.js·spring boot·后端·spring·毕业设计·计算机毕业设计
让我上个超影吧3 小时前
SpringAI会话记忆实现——基于MYSQL进行存储
java·spring boot·ai
码界奇点3 小时前
基于Beego v2与Go语言的网站管理后台系统设计与实现
开发语言·golang·毕业设计·go语言·源代码管理·beego
EstherNi3 小时前
小程序中,下拉多选的组件,有写死的三级下拉,样式需要修改
javascript·小程序·vue
WangYaolove13143 小时前
基于python的漏洞扫描系统(源码+文档)
python·mysql·django·毕业设计·源码
小北方城市网14 小时前
Redis 分布式锁高可用实现:从原理到生产级落地
java·前端·javascript·spring boot·redis·分布式·wpf
毕设源码-钟学长16 小时前
【开题答辩全过程】以 基于SpringBoot的智能书城推荐系统的设计与实现为例,包含答辩的问题和答案
java·spring boot·后端
冉冰学姐17 小时前
SSM医院预约挂号管理系统q9ig2(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面
数据库·毕业设计·医院预约挂号系统·ssm 框架