7.Redis实战—好友关注

好友关注 :

关注和取关 :

关键是User之间的关系 , 是博主和粉丝之间的联系 , 数据库中有一张表 , tb_follow表来表示

  • 关注 : 在表中添加字段 ,
  • 取关 : 将表中字段删除
    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Override
    public Result follow(Long followUserId, Boolean isFollow) {
        // TODO 1.获取登录用户
        Long userId = UserHolder.getUser().getId();
        String key = "follows:" + userId;

        // TODO 1.判断是关注还是取关
        if (isFollow){
            // TODO 2.关注 , 新增数据
            Follow follow = new Follow();
            follow.setUserId(userId);
            follow.setFollowUserId(followUserId);
            boolean save = save(follow);
            // TODO 判断是否保存成功
            if (save){
                // TODO 保存成功 , 将关注用户的id保存在redis中
                //  sadd userId fallowUserId
                stringRedisTemplate.opsForSet().add(key,followUserId.toString());
            }
        }else{
            // TODO 3.取关 , 删除
            boolean isSuccess = remove(new QueryWrapper<Follow>()
                    .eq("user_id", userId)
                    .eq("follow_user_id", followUserId));
            if (isSuccess){
                // 把关注用户的id , 从redis集合中移除
                stringRedisTemplate.opsForSet().remove(key,followUserId.toString());
            }
        }
        return Result.ok();
    }

    @Override
    public Result isFollow(Long followUserId, Boolean isFollow) {
        // TODO 获取登录用户
        Long userId = UserHolder.getUser().getId();
        // TODO 查询是否关注 ,
        Integer count = query().eq("user_id", userId).eq("follow_user_id", followUserId).count();
        return Result.ok(count > 0) ;
    }

共同关注:

使用redis中的set , 可以查询两个缓存数据中的交集

使用的是intersect方法 , 可以查询两个key之间value的交集

@Override
public Result followCommons(Long id) {
    // TODO 1.获取当前用户
    // TODO 2.获取目标用户
    Long userId = UserHolder.getUser().getId();
    String key1 = "follows:" + userId;
    // TODO 3.求交集
    String key2 = "follows:" + id;
    Set<String> intersect = stringRedisTemplate.opsForSet().intersect(key1, key2);
    if (intersect == null || intersect.isEmpty()){
        // 无交集 , 返回
        return Result.ok(Collections.emptyList());
    }
    // TODO 4.解析出id
    List<Long> ids = intersect
            .stream()
            .map(Long::valueOf)
            .collect(Collectors.toList());
    // TODO 5.查询用户
    List<UserDTO> users = userService.listByIds(ids)
            .stream()
            .map(user -> BeanUtil.copyProperties(user, UserDTO.class)).collect(Collectors.toList());
    return Result.ok(users);
}

关注推送 :

image-20220419122218380

TimeLine模式 :

image-20220419123248051

  1. 拉模式 : 读扩散 , 只有用户在读的时候 , 才会去拉取对应的信息
    • image-20220419122537730
    • 优点 :
      • 节省内存空间 ;
    • 缺点 : 每次读都需要进行拉取 , 进行排序 ,
  2. 推模式 : 写扩散 , 用户在发消息的时候 , 将这些消息 直接发送到粉丝的收件箱中 ,
    • image-20220419122840141
    • 优点 :
      • 节省时间 ,
    • 缺点 : 占用空间大
  3. 推拉结合模式 :
    • image-20220419123214485

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

需求 :

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

修改新增探店笔记业务:

@Override
public Result saveBlog(Blog blog) {
    // TODO 获取登录用户
    UserDTO user = UserHolder.getUser();
    blog.setUserId(user.getId());
    // TODO 保存探店博文
    boolean isSuccess = save(blog);
    if (!isSuccess) {
        return Result.fail("新增笔记失败");
    }
    // TODO 查询笔记作者的所有粉丝
    List<Follow> follow_user_id = followService.query().eq("follow_user_id", user.getId()).list();
    for (Follow follow : follow_user_id) {
        // TODO 4.1 获取粉丝id
        Long userId = follow.getUserId();
        // TODO 4.2 推送到粉丝的收件箱 , 以feed加粉丝的id作为收件箱
        String key = "feed:" + userId;
        stringRedisTemplate.opsForZSet().add(key, blog.getId().toString(), System.currentTimeMillis());

    }
    // TODO 返回id
    return Result.ok(blog.getId());
}

Feed流的滚动分页查询 :

逻辑实现 :

image-20220419162849748

每次查询之后 , 记录当前查询的角标 , 下次查询从这个角标开始进行查询

参数总共有四个 :

max : 分数的最大值 : 设置为当前时间戳即可(第一次查询) | 上一次查询的最小值(不是第一次查询)

min : 分数的最小值 : 设置为 0 即可

offset : 0 (第一次查询) | 在上一次的结果中 , 与最小值一样的元素的个数 ()

count : 每次查询的数据量 : 固定的值


代码实现 :
//TODO 分页查询收件箱
@Override
public Result queryBlogOfFollow(Long max, Integer offset) {
    // TODO 1. 获取当前用户的id
    Long userId = UserHolder.getUser().getId();
    // TODO 2.查询收件箱
    String key = FEED_KEY + userId;
    Set<ZSetOperations.TypedTuple<String>> typedTuples = stringRedisTemplate.opsForZSet()
        .reverseRangeByScoreWithScores(key, 0, max, offset, 3);
    // TODO 3.非空判断
    if (typedTuples.isEmpty() || typedTuples == null) {
        return Result.ok();
    }
    // TODO 4.解析数据 , BlogId , minTime(时间戳) , offset
    List<Long> ids = new ArrayList<>(typedTuples.size());
    long minTime = 0;
    int os = 1;
    for (ZSetOperations.TypedTuple<String> typedTuple : typedTuples) {
        // TODO 4.1 获取id
        ids.add(Long.valueOf(typedTuple.getValue()));
        // TODO 4.2 获取分数(时间戳)
        long time = typedTuple.getScore().longValue();
        if (time == minTime){ //当有重复的时间戳时 , 就++
            os++;
        }else{ //不相等 , 说明出现了最新的最小值 , 将os重置为1即可
            minTime = time;
            os = 1;
        }
    }
    // TODO 4.根据id查询blog
    String idStr = StrUtil.join(",", ids);
    List<Blog> blogs = query().in("id", ids).last("ORDER BY FIELD(id," + idStr + ")").list();
    // TODO 每一个博客都需要查询点赞的用户 , 和是否被当前用户点赞
    for (Blog blog : blogs) {
        // TODO 2.查询blog有关的用户
        queryBlogUser(blog);
        // TODO 3. 查询blog是否被点赞
        isBLogLiked(blog);
    }
    // TODO 封装并返回
    ScrollResult scrollResult = new ScrollResult();
    scrollResult.setList(blogs);
    scrollResult.setOffset(os);
    scrollResult.setMinTime(minTime);
    return Result.ok(scrollResult);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值