飞道的博客

Redis——好友关注、共同关注、Feed流推送

371人阅读  评论(0)

1. 好友关注

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

进到探店笔记详情页,会发出两个请求,1是判断是否已经关注,2是尝试关注用户的请求。

 

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

其中userId是当前登录的用户id,follow_user_id是被关注的用户id

实现对应接口:

1. 判断是否关注:

Controller层:


  
  1. @GetMapping("/or/not/{id}")
  2. public Result isFollow (@PathVariable("id") Long followUserId) {
  3. return followService.isFollow(followUserId);
  4. }

Service层:


  
  1. @Override
  2. public Result isFollow (Long followUserId) {
  3. // 1.获取登录用户
  4. Long userId = UserHolder.getUser().getId();
  5. // 2.查询是否关注 select count(*) from tb_follow where user_id = ? and follow_user_id = ?
  6. Integer count = query().eq( "user_id", userId).eq( "follow_user_id", followUserId).count();
  7. // 3.判断
  8. return Result.ok(count > 0);
  9. }

2. 关注业务接口:如果已经关注,就在数据库中添加userid(当前登录用户的id)和关注的用户id(follow_user_id)等数据。

如果未关注,就删除数据。

Controller层:


  
  1. //关注
  2. @PutMapping("/{id}/{isFollow}")
  3. public Result follow (@PathVariable("id") Long followUserId, @PathVariable("isFollow") Boolean isFollow) {
  4. return followService.follow(followUserId, isFollow);
  5. }

Service层:


  
  1. 关注service
  2. @Override
  3. public Result follow (Long followUserId, Boolean isFollow) {
  4. // 1.获取登录用户
  5. Long userId = UserHolder.getUser().getId();
  6. // 1.判断到底是关注还是取关
  7. if (isFollow) {
  8. // 2.关注,新增数据
  9. Follow follow = new Follow();
  10. follow.setUserId(userId);
  11. follow.setFollowUserId(followUserId);
  12. boolean isSuccess = save(follow);
  13. } else {
  14. // 3.取关,删除 delete from tb_follow where user_id = ? and follow_user_id = ?
  15. remove( new QueryWrapper<Follow>()
  16. .eq( "user_id", userId).eq( "follow_user_id", followUserId));
  17. }
  18. return Result.ok();
  19. }

 2. 好友共同关注

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

我们可以使用Redis中的set集合,将每个用户关注的用户id,也就是follow_user_id,存入set集合中,每个用户对应一个关注博主的set集合,然后取他们之间的交集,就可以找到共同关注的博主。

所以,我们要先改造之前关注用户的业务逻辑,每次用户关注时,将关注的用户存入Set集合中。

 关注业务改造:


  
  1. @Override
  2. public Result follow (Long followUserId, Boolean isFollow) {
  3. // 1.获取登录用户
  4. Long userId = UserHolder.getUser().getId();
  5. String key = "follows:" + userId;
  6. // 1.判断到底是关注还是取关
  7. if (isFollow) {
  8. // 2.关注,新增数据
  9. Follow follow = new Follow();
  10. follow.setUserId(userId);
  11. follow.setFollowUserId(followUserId);
  12. boolean isSuccess = save(follow);
  13. if (isSuccess) {
  14. // 把关注用户的id,放入redis的set集合 sadd userId followerUserId
  15. stringRedisTemplate.opsForSet().add(key, followUserId.toString());
  16. }
  17. } else {
  18. // 3.取关,删除 delete from tb_follow where user_id = ? and follow_user_id = ?
  19. boolean isSuccess = remove( new QueryWrapper<Follow>()
  20. .eq( "user_id", userId).eq( "follow_user_id", followUserId));
  21. if (isSuccess) {
  22. // 把关注用户的id从Redis集合中移除
  23. stringRedisTemplate.opsForSet().remove(key, followUserId.toString());
  24. }
  25. }
  26. return Result.ok();
  27. }

然后,我们就可以实现共同关注的接口了。

 

  Controller层:


  
  1. @GetMapping("/common/{id}")
  2. public Result followCommons (@PathVariable("id") Long followUserId){
  3. return followService.followCommons(followUserId);
  4. }

Service层:

1.获取当前用户id和目标用户id在redis中的key

2.使用set中求交集的方法,得到结果

3.解析结果,得到id组合

4.通过id组合获取用户组合。

5.返回用户组合给前端。


  
  1. @Override
  2. public Result followCommons (Long id) {
  3. // 1.获取当前用户
  4. Long userId = UserHolder.getUser().getId();
  5. String key = "follows:" + userId;
  6. // 2.求交集
  7. String key2 = "follows:" + id;
  8. Set<String> intersect = stringRedisTemplate.opsForSet().intersect(key, key2);
  9. if (intersect == null || intersect.isEmpty()) {
  10. // 无交集
  11. return Result.ok(Collections.emptyList());
  12. }
  13. // 3.解析id集合
  14. List<Long> ids = intersect.stream().map(Long::valueOf).collect(Collectors.toList());
  15. // 4.查询用户
  16. List<UserDTO> users = userService.listByIds(ids)
  17. .stream()
  18. .map(user -> BeanUtil.copyProperties(user, UserDTO.class))
  19. .collect(Collectors.toList());
  20. return Result.ok(users);
  21. }

共同关注功能实现。

3. 关注推送(Feed流) 

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

传统的推送模式是用户自己查找内容,而Feed这是根据内容信息来取查找对应用户并推送

 

Feed流产品有两种常见模式:

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

  • 优点:信息全面,不会有缺失。并且实现也相对简单

  • 缺点:信息噪音较多,用户不一定感兴趣,内容获取效率低

智能排序:利用智能算法屏蔽掉违规的、用户不感兴趣的内容。推送用户感兴趣信息来吸引用户

  • 优点:投喂用户感兴趣信息,用户粘度很高,容易沉迷

  • 缺点:如果算法不精准,可能起到反作用

 

这里我们的关注推送,就采用第一种 Timeline。

该模式的实现方法同样有三种:

1. 拉模式

也叫做读扩散。它是每个博主发出博客信息后,都有一个发件箱,这个时候,粉丝要来读它的信息,就从这个发件箱里拉取信息到自己的收件箱中并读取,所以叫拉模式。

 缺点:可能会有延迟

2.推模式

也叫作写扩散。它是一个博主发布博客信息后,直接将博客推送到粉丝的收件箱中。这种方法,解决了拉模式的延迟问题,但也会导致内存问题。

 

3.推拉结合

也叫做读写混合,兼具推和拉两种模式的优点。

现在有三个粉丝,同时关注了大V博主,有两个粉丝关注了普通博主张三。 

这时候,张三发送了博客信息,但由于它是普通博主,粉丝基数不大,不用担心内存问题,所以直接采用推模式,发送给粉丝。

 而对于大V博主来说,将粉丝分为活跃粉丝和普通粉丝,活跃粉丝采用推模式,而普通粉丝采用拉模式,将消息放进收件箱中。

这就是三种实现模式,我们做个对比。 

 

这里,我们采用推模式来实现好友推送功能。

这里提一嘴分页问题:

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

传统分页模式:

所以,我们必须采用Feed流的滚动分页。

1. 首先,修改新增探店笔记的业务,在保存blog到数据库的同时,推送到粉丝的收件箱


  
  1. @Override
  2. public Result saveBlog (Blog blog) {
  3. // 获取登录用户
  4. UserDTO user = UserHolder.getUser();
  5. blog.setUserId(user.getId());
  6. // 保存探店博文
  7. boolean isSuccess = save(blog);
  8. if (!isSuccess){
  9. //没有保存成功,返回错误信息
  10. return Result.fail( "新增失败!");
  11. }
  12. //成功,将博客信息放到粉丝收件箱
  13. //1.先获取粉丝群体 select * from tb_follow where follow_user_id = ?
  14. QueryChainWrapper<Follow> follows = followService.query().eq( "follow_user_id", user.getId());
  15. //2.将消息推送至粉丝收件箱
  16. String key = "feeds:"+user.getId();
  17. stringRedisTemplate.opsForZSet().add(key,blog.getId().toString(),System.currentTimeMillis());
  18. // 返回id
  19. return Result.ok(blog.getId());
  20. }

 

 

 

 

 


转载:https://blog.csdn.net/qq_59212867/article/details/128347694
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场