黑马点评--好友关注

news2024/11/23 1:12:16

好友关注:

关注和取关

在探店图文的详情页面中,可以关注发布笔记的作者:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1iJs9ZBz-1669627950228)(C:\Users\20745\AppData\Roaming\Typora\typora-user-images\image-20221126142526129.png)]

需求:基于该数据结构,实现两个接口

  1. 关注和取关接口

      @Override
        public Result follow(Long id, Boolean isFollow) {
            // 1.获取登录用户
            Long userId = UserHolder.getUser().getId();
            // 2.判断到底是关注还是取关
            if (isFollow){
                // 2.1.关注,新增数据
                Follow follow =new Follow();
                //当前用户
                follow.setUserId(userId);
                //被关注的用户
                follow.setFollowUserId(id);
                save(follow);
                return Result.ok();
            }
            // 2.2.取关,删除 delete from tb_follow where userId = ? and follow_user_id =?
            LambdaQueryWrapper<Follow> queryWrapper=new LambdaQueryWrapper<>();
            queryWrapper.eq(Follow::getUserId,userId).eq(Follow::getFollowUserId,id);
            this.remove(queryWrapper);
            return Result.ok();
        }
    
  2. 判断是否关注的接口

     @Override
        public Result isFollow(Long id) {
            // 1.获取登录用户
            Long userId = UserHolder.getUser().getId();
            //查询是否关注   select count(*) from tb_follow where userId = ? and follow_user_id =?
            LambdaQueryWrapper<Follow> queryWrapper=new LambdaQueryWrapper<>();
            queryWrapper.eq(Follow::getUserId,userId).eq(Follow::getFollowUserId,id);
            int count = count(queryWrapper);
            //判断count
            return Result.ok(count>0);
        }
    

关注是User之间的关系,是博主与粉丝的关系,数据库中有一张tb_follow表来标示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YWAq0h58-1669627950229)(C:\Users\20745\AppData\Roaming\Typora\typora-user-images\image-20221126142859456.png)]

分页查询用户博客信息

 //根据用户查询
        Page<Blog> page=new Page<>(current,MAX_PAGE_SIZE);
        LambdaQueryWrapper<Blog> queryWrapper=new LambdaQueryWrapper<>();
        queryWrapper.eq(Blog::getUserId,id) ;
        page(page,queryWrapper);
        //获取当前页数据
        List<Blog> records = page.getRecords();
        return Result.ok(records);

查询用户信息

User user = getById(id);
        if (user == null){
            return Result.ok();
        }
        UserDTO userDTO =BeanUtil.copyProperties(user,UserDTO.class);
        //返回
        return Result.ok(userDTO);

共同关注:

需求:利用Redis中恰当的数据结构,实现共同关注功能。在博主个人页面展示出当前用户与博主的共同好友。

思路:

  1. 将用户的关注保存到redis中用set集合
  2. 在前端发送请求时将当前用户和目标用户set集合求交集并返回

改造关注与取关接口:

在每次添加关注时把数据不仅放到数据库中,还应该放到redis中。

    public Result follow(Long id, Boolean isFollow) {
        // 1.获取登录用户
        Long userId = UserHolder.getUser().getId();
        // 2.判断到底是关注还是取关
        if (isFollow){
            // 2.1.关注,新增数据
            Follow follow =new Follow();
            //当前用户
            follow.setUserId(userId);
            //被关注的用户
            follow.setFollowUserId(id);
            boolean isSuccess = save(follow);
            if (isSuccess){
                //把关注用户的id,放入redis的set集合  sadd userId followerUserId
               stringRedisTemplate.opsForSet().add(FOLLOWS+userId,id.toString());
            }
            return Result.ok();
        }
        // 2.2.取关,删除 delete from tb_follow where userId = ? and follow_user_id =?
        LambdaQueryWrapper<Follow> queryWrapper=new LambdaQueryWrapper<>();
        queryWrapper.eq(Follow::getUserId,userId).eq(Follow::getFollowUserId,id);
        boolean isSuccess = this.remove(queryWrapper);
        //把关注用户的id从Redis集合中移除
        if (isSuccess){
            stringRedisTemplate.opsForSet().remove(FOLLOWS+userId,id.toString());
        }
        return Result.ok();
    }

实现共同关注接口:

 @Override
    public Result followCommons(Long id) {
        //1.获取当前用户
        Long userId = UserHolder.getUser().getId();
        //2.求交集
        Set<String> intersect = stringRedisTemplate.opsForSet()
                .intersect(FOLLOWS + userId, FOLLOWS + id);
        //3.解析id集合
        if (intersect==null || intersect.isEmpty()) {
            return Result.ok(Collections.emptyList());
        }
        List<Long> userIdList = intersect.stream().
                map(Long::valueOf).collect(Collectors.toList());
        List<User> users = userService.listByIds(userIdList);
        List<UserDTO> dtoList = users.stream()
                .map(user -> BeanUtil.copyProperties(user, UserDTO.class))
                .collect(Collectors.toList());
        return Result.ok(dtoList);
    }

关注推送:

关注推送也叫Feed流,直译为投喂。为用户持续的提供”沉浸式”的体验,通过无限下拉刷新获取新的信息。

  • Timeline:不做内容筛选,简单的按照内容发布时间排序,常用于好友或关注。例如朋友圈

    • 优点:信息全面,不会有缺失。并且实现也相对简单
    • 缺点:信息噪音较多,用户不一定感兴趣,内容获取效率低
  • **智能排序:**利用智能算法屏蔽掉违规的,用户不感兴趣的内容。推送用户感兴趣信息来吸引用户

    • 优点:投喂用户感兴趣信息,用户粘度很高,容易沉迷
    • 缺点:如果算法不清楚,可能起到反作用

    本例中的个人页面,是基于关注的好友来做Feed流,因此采用Timeline的模式。该模式的实现方案有三种:

    1. 拉模式

      • [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ISNV7wwQ-1669627950230)(C:\Users\20745\AppData\Roaming\Typora\typora-user-images\image-20221127151019086.png)]
    2. 推模式

      • [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Hu2UGv6W-1669627950231)(C:\Users\20745\AppData\Roaming\Typora\typora-user-images\image-20221127151257127.png)]
    3. 推拉结合

      • [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OLOS8GKl-1669627950231)(C:\Users\20745\AppData\Roaming\Typora\typora-user-images\image-20221127151646487.png)]

总结:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZVVdlsZN-1669627950232)(C:\Users\20745\AppData\Roaming\Typora\typora-user-images\image-20221127151818233.png)]

基于推模式实现关注推送功能:

需求:

  1. 修改新增探店笔记业务,在保存blog到数据库的同时,推送到分数的收件箱
  2. 收件箱满足可以根据时间戳排序,必须用Redis的数据结构实现
  3. 查询收件箱数据时,可以实现分页查询

在redis中用scoreSet还是list

Feed流中的数据会不断更新,所以数据的角标也在变化,因此不能采用传统的分页模式。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MYiYmDv9-1669627950233)(C:\Users\20745\AppData\Roaming\Typora\typora-user-images\image-20221127161456573.png)]

采用滚动分页模式:该方式查询不依赖角标所以list不支持,因为list只能通过角标查询。而scoreSet是按排名查询。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HrbXyOUh-1669627950234)(C:\Users\20745\AppData\Roaming\Typora\typora-user-images\image-20221127161630262.png)]

推送到粉丝邮件箱–存到redis中

    public Result saveBlog(Blog blog) {
        // 获取登录用户
        UserDTO user = UserHolder.getUser();
        blog.setUserId(user.getId());
        // 保存探店博文
        boolean isSuccess = save(blog);
        if (isSuccess){
            return Result.fail("新增笔记失败!");
        }
        // 查询笔记作者的所有粉丝
        LambdaQueryWrapper<Follow> queryWrapper=new LambdaQueryWrapper<>();
        queryWrapper.eq(Follow::getFollowUserId,blog.getUserId());
        List<Follow> follows = followService.list(queryWrapper);
        // 推送笔记id给所有粉丝
        for (Follow follow : follows) {
            //获取粉丝id
            Long userId = follow.getUserId();
            //推送到redis
            stringRedisTemplate.opsForZSet()
                    .add(FEED_KEY+userId,blog.getId().toString(),System.currentTimeMillis());
        }
        // 返回id
        return Result.ok(blog.getId());
    }

滚动分页查询收件箱的思路:

需求:在个人主页的“关注”卡片中,查询并展示推送的Blog的信息

ZREVRANGEBYSCORE z1 max min WITHSCORES LIMIT offset count

分页查询参数

  1. max:上一次查询的最小时间戳 | 当前时间
  2. min:0
  3. offset:与上一次查询最小时间戳一致的所有元素个数 | 0
  4. count:分页大小 3

实现关注推送页面的滚动分页查询

定义数据模型

//滚动分页结果
@Data
public class ScrollResult {
    private List<?> list;
    private Long minTime;
    private Integer offset;
}

业务代码实现:

   @Override
    public Result getBlogOfFollow(Long max, Integer offset) {
        //1.获取当前用户
        Long userId = UserHolder.getUser().getId();
        //2.查询收件箱
        Set<ZSetOperations.TypedTuple<String>> typedTuples = stringRedisTemplate.opsForZSet()
                .reverseRangeByScoreWithScores(FEED_KEY + userId, 0, max, offset, 2);
        //3.非空判断
        if (typedTuples ==null || typedTuples.isEmpty()) {
            return Result.ok(Collections.emptyList());
        }
        //4.解析数据: blogId、score(时间戳)、offset
        List<Long> ids =new ArrayList<>(typedTuples.size());
        long minTime = 0;
        int os = 1;
        for (ZSetOperations.TypedTuple<String> typedTuple : typedTuples) {
            //4.1.获取id
            ids.add(Long.valueOf(typedTuple.getValue()));
            //4.2.获取分数(时间戳)
            long time = typedTuple.getScore().longValue();
            if (time == minTime){
                os++;
            }else {
                minTime = time;
                os = 1;
            }
        }
        //5.根据id查询blog
        String idStr =StrUtil.join(",",ids);
        List<Blog> blogs = query()
                .in("id",ids).last("order by FIELD(id,"+idStr+")").list();

        for (Blog blog : blogs) {
            //2.查询blog有关的用户
            queryBlogUser(blog);
            //3.查询blog是否被点赞
            isBlogLiked(blog);
        }
        //6.封装并返回
        ScrollResult result =new ScrollResult();
        result.setList(blogs);
        result.setOffset(os);
        result.setMinTime(minTime);
        return Result.ok(result);
    }

	 private void queryBlogUser(Blog blog) {
        Long userId = blog.getUserId();
        User user = userService.getById(userId);
        blog.setName(user.getNickName());
        blog.setIcon(user.getIcon());
    }

	private void isBlogLiked(Blog blog) {
        //1.获取登录用户
        Long userId = UserHolder.getUser().getId();
        //2.判断当前登录用户是否已经点赞
//        Boolean isMember = stringRedisTemplate.opsForSet().isMember(BLOG_LIKED_KEY + blog.getId(), userId.toString());
        Double score = stringRedisTemplate.opsForZSet().score(BLOG_LIKED_KEY + blog.getId(), userId.toString());
        blog.setIsLike(score != null);
    }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/43678.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

软件设计师教程(二)计算机系统知识-计算机体系结构

软件设计师教程 软件设计师教程&#xff08;一&#xff09;计算机系统知识-计算机系统基础知识 计算机体系结构软件设计师教程计算机体系结构的发展计算机系统结构概述计算机体系结构分类指令系统指令集体系结构的分类CISC和RISC指令的流水处理阵列处理机、并行处理机和多处理…

wy的leetcode刷题记录_Day51

wy的leetcode刷题记录_Day51 声明 本文章的所有题目信息都来源于leetcode 如有侵权请联系我删掉! 时间&#xff1a;2022-11-24 前言 目录wy的leetcode刷题记录_Day51声明前言795. 区间子数组个数题目介绍思路代码收获98. 验证二叉搜索树题目介绍思路代码收获795. 区间子数组…

Linux中修改环境变量的几种方法比较分析

修改环境变量的作用 使得命令可以在命令行直接运行 第一种方式&#xff0c;在终端直接使用export **特点&#xff1a;**即可生效&#xff0c;无需重启或刷新文件&#xff1b;终端关闭则失效 第二种方式&#xff0c;修改/etc/profile文件 特点&#xff1a;对所有用户永久有…

算法刷题打卡第29天:省份数量---并查集

省份数量 难度&#xff1a;中等 有 n 个城市&#xff0c;其中一些彼此相连&#xff0c;另一些没有相连。如果城市 a 与城市 b 直接相连&#xff0c;且城市 b 与城市 c 直接相连&#xff0c;那么城市 a 与城市 c 间接相连。 省份 是一组直接或间接相连的城市&#xff0c;组内不…

SQL注入漏洞 | iwebsec

文章目录靶场搭建SQL注入漏洞靶场搭建 配置 云服务器&#xff1a;阿里云 系统&#xff1a;CentOS 7.6 靶场&#xff1a;iwebsec CentOS-7.6安装docker 安装iwebsec 启动靶场 docker run --restartalways --name iwebsec -it -dp 8001:80 iwebsec/iwebsec访问不成功 可能是…

希望流程挖掘成为撬动企服市场的突破口 | 专访凡得科技CEO海广跃、首席技术顾问刘聪

2022年&#xff0c;全球流程挖掘市场规模预计将达70多亿人民币&#xff0c;而目前中国流程挖掘行业尚处于市场启蒙期&#xff0c;仅少数大型企业与机构对流程挖掘进行了初步或尝试性的投入。从目前来看&#xff0c;原生流程挖掘厂商会直接面向客户输出流程挖掘能力&#xff0c;…

Spring集合注入

一、环境准备 创建一个Maven项目pom.xml添加Spring依赖resources下添加spring的配置文件applicationContext.xml项目中添加BookDao、BookDaoImpl类 public interface BookDao {public void save(); }public class BookDaoImpl implements BookDao {private int[] array;priva…

容器云平台初始化(harbor的安装部署)

1.虚拟机规划 设备说明主机名接口IP地址虚拟机1MasterEth010.0.0.10/24虚拟机2Node1Eth010.0.0.20/24虚拟机3HarborEth010.0.0.30/24 2.容器云平台初始化(harbor的安装部署) 1.根据规划的IP地址&#xff0c;创建虚拟机&#xff0c;确保网络正常通信。按照规划表修改主机名并关…

Linux-ACL权限

ACL权限简介 ACL&#xff1a;access control list 允许给任何用户或者用户组设置任何文件或者目录的访问权限 查看Linux是否开启ACL dumpe2fs Linux一般都开启了ACL权限&#xff0c;可以使用下面的命令查看分区的ACL权限是否打开 首先可以使用df -h或者lsblk来查看Linux系统…

JioNLP上的那些好用的冷门工具

大家好&#xff0c;JioNLP(https://github.com/dongrixinyu/JioNLP)目前已经在Github上有了1600星的点赞&#xff0c;下载安装量大概有几万了。 被使用最多的功能&#xff0c;也是被问得最多的&#xff0c;主要是关键短语抽取、时间语义解析、地址按省市县三级解析等等。其它功…

程序员迷茫:30岁以上的“大龄程码农”出路在哪?java码农该怎么办?

程序员生存、成功、制胜的法则源自IT精英的职业发展秘诀热爱工作&#xff0c;享受生活 为什么程序员过了30就不行了&#xff1f; 我们被固定在“敲代码”的坑里&#xff0c;一干就是10年&#xff0c;再干别的早已不会。敲代码已经成了一项流水线般的工作&#xff0c;就像搬砖工…

Postman非GUI运行脚本工具Newman的安装简介

一、Newman简介 Newman是为Postman而生&#xff0c; 专门用来运行Postman编写好的脚本&#xff1b;使用Newman&#xff0c; 你可以很方便的用命令行来执行postman collections 二、Newman安装 1.先下载Node.js https://nodejs.org/zh-cn/download/ 根据自己电脑系统及位数…

数字验证学习笔记——SystemVerilog芯片验证12 ——句柄的使用包的使用

一、句柄的使用 句柄可以作为形式参数通过方法来完成对象指针的传递&#xff0c;从外部传入方法内部。 句柄也可以在方法内部首先完成修改&#xff0c;而后再由外部完成使用 最后会报错&#xff0c;因为create&#xff08;t&#xff09;定义的是function&#xff0c;没有返回…

锁机制之 Condition 接口

1. 前言 本节内容主要是对 Java 锁机制之 Condition 接口进行讲解&#xff0c;Condition 接口是配合 Lock 接口使用的&#xff0c;我们已经学习过 Lock 接口的相关知识&#xff0c;那么接下来对 Condition 接口进行讲解。本节内容的知识点如下&#xff1a; 2. Condition 接口…

cookie、localStorage 和sessionStorage

文章目录Cookie1.什么是 Cookie&#xff1f;2.cookie的工作机制&#xff0c;运作流程cookie属性项3.读取cookie4.修改cookie5.删除cookielocalStorage 和sessionStorage1.生存期2.数据结构3.API 不管是 localStorage&#xff0c;还是 sessionStorage&#xff0c;可使用的API都相…

基于FPGA+MPU+MCU全自动血细胞分析仪解决方案

全自动血细胞分析仪是医院临床检验应用非常广泛的仪器之一&#xff0c;用来检测红细胞、血红蛋白、白细胞、血小板等项目。是基于电子技术和自动化技术的全自动智能设备&#xff0c;功能齐全&#xff0c;操作简单&#xff0c;依托相关计算机系统在数据处理和数据分析等方面具有…

蓝牙学习三(GAP)

1.简介 GAP&#xff08;Generic Access Profile-通用访问配置文件&#xff09;与应用层紧密相连&#xff0c;所以要想了解BLE&#xff0c;GAP是必须认识的东西。 在第一章中我们说过GAP层&#xff0c;GAP层目前主要用来进行广播、扫描和发起连接。GAP保证了不同的BLE设备可以互…

深度学习入门(7)误差反向传播计算方式及简单计算层的实现

在上一节中《深度学习入门&#xff08;6&#xff09;误差反向传播基础---计算图与链式法则》&#xff0c;我们介绍了误差反向传播的计算图与导数计算的链式法则&#xff0c;这一节主要介绍计算图中各计算节点的误差反向传播计算方式&#xff0c;以及加法与乘法层的实现。 目录…

Scala系列-4、scala中特质、柯里化、闭包等

版权声明&#xff1a;本文为博主原创文章&#xff0c;遵循 CC 4.0 BY-SA 版权协议&#xff0c;转载请附上原文出处链接和本声明。 传送门&#xff1a;大数据系列文章目录 目录scala中特质特质作为接口使用特质中放置非抽象的成员特质的模板操作特质的混入对象操作特质的执行链…

T31开发笔记: 使用FTP上传下载文件

若该文为原创文章&#xff0c;转载请注明原文出处 一、前言 最段时间&#xff0c;在开发IPC时&#xff0c;突然想到把录制好的MP4文件上传到服务器&#xff0c;考虑了一些方法&#xff0c;感觉用TFP方式比较好&#xff0c;可以下载和上传文件&#xff0c;只需要搭建一个简单的…