黑马点评--好友关注

好友关注:

关注和取关

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(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);
    }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值