当前位置: 首页 > news >正文

黑马点评8——好友关注-SortedSet

文章目录

  • 关注和取关
  • 共同关注
  • Feed流实现方案分析
  • 推送到粉丝收件箱
    • Feed流
    • 基于推模式实现关注推送功能
  • 滚动分页查询收件箱的思路
  • 实现滚动分页查询

关注和取关

在这里插入图片描述

在这里插入图片描述
所以关注和取关就是简单的插入和删除数据库。

  @Overridepublic Result isFollow(Long followUserId) {// 1. 获取登录用户Long userId = UserHolder.getUser().getId();// 2.查询是否关注,select count(*) from tb_follow where user_id = ? and follow_user_id = ?Integer count = query().eq("user_id", userId).eq("follow_user_id", followUserId).count();// 3.判断return Result.ok(count > 0);}@Overridepublic Result follow(Long followUserId, Boolean isFollow) {// 获取登录用户Long userId = UserHolder.getUser().getId();// 1. 判断到底是关注还是取关if(isFollow){// 2.关注,新增数据Follow follow = new Follow();follow.setFollowUserId(followUserId);follow.setUserId(userId);save(follow);}else{// 3.取关,删除 delete from tb_follow where user_id = ? and follow_user_id = ?remove(new QueryWrapper<Follow>().eq("user_id",userId).eq("follow_user_id", followUserId));}return Result.ok();}

共同关注

在这里插入图片描述
这两个接口主要是点击头像,查询用户和查询用户的博客
直接上写接口就行

@GetMapping("/{id}")public Result queryUserById(@PathVariable("id") Long userId){// 查询详情User user = userService.getById(userId);if(user == null){return Result.ok();}UserDTO userDTO = BeanUtil.copyProperties(user, UserDTO.class);// 返回return Result.ok(userDTO);}
    @GetMapping("/of/user")public Result queryBlogByUserId(@RequestParam(value = "current", defaultValue = "1") Integer current,@RequestParam("id") Long id){// 根据用户查询Page<Blog> page = blogService.query().eq("user_id", id).page(new Page<>(current, SystemConstants.MAX_PAGE_SIZE));// 获取当前页数据List<Blog> records = page.getRecords();return Result.ok(records);}

其实查询共同关注就是查询当前登录用户和查询的用户他们的关注的集合的交集
在这里插入图片描述
我们可以把当前用户的所有关注全部放在redis中,直接使用redis的set的求交集的功能

 @Overridepublic Result follow(Long followUserId, Boolean isFollow) {// 获取登录用户Long userId = UserHolder.getUser().getId();String key = "follows:" + userId;// 1. 判断到底是关注还是取关if(isFollow){// 2.关注,新增数据Follow follow = new Follow();follow.setFollowUserId(followUserId);follow.setUserId(userId);boolean isSuccess = save(follow);if(isSuccess){// 把关注用户的id, 放入redis的set集合,sadd userId followerUserIdstringRedisTemplate.opsForSet().add(key, followUserId.toString());}}else{// 3.取关,删除 delete from tb_follow where user_id = ? and follow_user_id = ?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();}@Overridepublic Result followCommons(Long id) {// 1. 获取当前用户Long userId = UserHolder.getUser().getId();String key = "follows:" + userId;// 2. 求交集String key2 = "follows:" + id;Set<String> intersect = stringRedisTemplate.opsForSet().intersect(key, key2);if(intersect == null || intersect.isEmpty()){// 没有交集return Result.ok(Collections.emptyList());}// 3.解析id集合List<Long> ids = intersect.stream().map(Long::valueOf).collect(Collectors.toList());// 4. 查询用户List<UserDTO> userDTOS = userService.listByIds(ids).stream().map(user -> BeanUtil.copyProperties(user, UserDTO.class)).collect(Collectors.toList());return Result.ok(userDTOS);}

Feed流实现方案分析

在这里插入图片描述

推送到粉丝收件箱

Feed流

在这里插入图片描述
我们的业务是把关注的用户发布的博客展示出来,比较适合使用Timeline模式
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
我们就使用推模式

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

在这里插入图片描述
Feed流的分页问题:
在这里插入图片描述
分页流不能采用传统的分页模式,因为我们的数据会动态变化,比如上面的情况就会出现重复读取6的情况,
因为我们采用滚动分页
在这里插入图片描述
因为他的查询不依赖角标,因此他的查询不会因为角标的变化、数据的变化而改变
list支持滚动分页吗?不行,因为list只支持角标的查询,或者首尾,都不行。
SortedSet可以吗?SortedSet会按照score值排序,然后有一个排名,如果按照排名查询,那和角标查询没什么区别,但是,我们的SortedSet也支持范围查询,score值就是我们的时间戳嘛,我们把时间戳从大到小的顺序进行一个排列,每一次查询的时候,记录下最小的时间戳,然后下次查询的时候,找比这个更小的,这样就实现了滚动分页了。

我们先实现,每当有人发布笔记时,把笔记保存到SortedSet中

 @Overridepublic Result saveBlog(Blog blog) {// 获取登录用户UserDTO user = UserHolder.getUser();blog.setUserId(user.getId());// 保存探店笔记boolean isSuccess = save(blog);if(!isSuccess){return Result.fail("新增笔记失败!");}// 3.查询笔记作者的所有粉丝 select * from tb_follow where follow_user_id = ?List<Follow> follows = followService.query().eq("follow_user_id", user.getId()).list();// 4,推送笔记id给所有粉丝for (Follow follow : follows) {// 4.1. 获取粉丝idLong userId = follow.getUserId();// 4.2 推送String key = "feed:" + userId;stringRedisTemplate.opsForZSet().add(key, blog.getId().toString(), System.currentTimeMillis());}// 返回idreturn Result.ok(blog.getId());}

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

在这里插入图片描述
在这里插入图片描述

实现滚动分页查询

 @GetMapping("/of/follow")public Result queryBlogOfFollow(@RequestParam("lastId") Long max, @RequestParam(value = "offset", defaultValue = "0") Integer offset){return blogService.queryBlogOfFollow(max, offset);}
@Overridepublic Result queryBlogOfFollow(Long max, Integer offset) {// 1. 获取当前用户idLong userId = UserHolder.getUser().getId();// 2. 查询收件箱 ZREVRANGEBYSCORE key Max Min LIMIT offset countString key = RedisConstants.FEED_KEY + userId;Set<ZSetOperations.TypedTuple<String>> typedTuples = stringRedisTemplate.opsForZSet().reverseRangeByScoreWithScores(key, 0, max, offset, 2);// 3. 非空判断if(typedTuples == null || typedTuples.isEmpty()){return Result.ok();}// 4. 解析数据: blogId、score(时间戳minTime), offsetList<Long> ids = new ArrayList<>(typedTuples.size());long minTime = 0;int os = 1;for (ZSetOperations.TypedTuple<String> typedTuple : typedTuples) {// 4.1 获取idids.add(Long.valueOf(typedTuple.getValue()));// 4.2 获取分数long time = typedTuple.getScore().longValue();if(time == minTime){os++;}else{minTime = time;os = 1;}}// 5. 根据id查询blogString idStr = StrUtil.join(",", ids);List<Blog> blogs = query().in("id", ids).last("ORDER BY FIELD(id," + idStr + ")").list();for (Blog blog : blogs) {// 5.1 查询blog有关的用户queryBlogUser(blog);// 5.2 查询blog是否被点赞isBlogLiked(blog);}// 6. 封装并返回ScrollResult r = new ScrollResult();r.setList(blogs);r.setOffset(os);r.setMinTime(minTime);return Result.ok(r);}

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • spring入门(二)IOC入门案例和DI入门案例
  • Android Dialog:Dialog和DialogFragment的区别?DialogFragment如何使用?源码解析
  • MATLAB进行天线阵列方向图综合
  • 海外域名自动手动免费续费ssl证书
  • 对象存储数据库minio的持久化存储
  • 模拟算法专题——算法介绍算法讲解力扣实战应用
  • Android中使用eBPF跟踪 FD打开与关闭
  • HTTP“请求”和“响应”的报头及正文详解
  • BUUCTF—[网鼎杯 2020 朱雀组]phpweb
  • 【Spring Boot 3】【Web】解析获取HTTP请求参数
  • 828华为云征文|部署私有云和文档管理系统 Kodcloud
  • 【C++】static作用总结
  • Harmony TextInput实现带有提示语的Text效果
  • Linux之MySQL日志
  • java 中简单实现异步的几种方法
  • 【跃迁之路】【669天】程序员高效学习方法论探索系列(实验阶段426-2018.12.13)...
  • ➹使用webpack配置多页面应用(MPA)
  • create-react-app做的留言板
  • ERLANG 网工修炼笔记 ---- UDP
  • express.js的介绍及使用
  • Java 23种设计模式 之单例模式 7种实现方式
  • leetcode-27. Remove Element
  • Node.js 新计划:使用 V8 snapshot 将启动速度提升 8 倍
  • Sass 快速入门教程
  • 阿里云容器服务区块链解决方案全新升级 支持Hyperledger Fabric v1.1
  • 从0实现一个tiny react(三)生命周期
  • 分布式熔断降级平台aegis
  • 警报:线上事故之CountDownLatch的威力
  • 聊聊redis的数据结构的应用
  • 如何胜任知名企业的商业数据分析师?
  • 网络应用优化——时延与带宽
  • 没有任何编程基础可以直接学习python语言吗?学会后能够做什么? ...
  • 如何通过报表单元格右键控制报表跳转到不同链接地址 ...
  • ​决定德拉瓦州地区版图的关键历史事件
  • ​七周四次课(5月9日)iptables filter表案例、iptables nat表应用
  • # 飞书APP集成平台-数字化落地
  • #android不同版本废弃api,新api。
  • #我与Java虚拟机的故事#连载06:收获颇多的经典之作
  • ${ }的特别功能
  • (C++哈希表01)
  • (pytorch进阶之路)CLIP模型 实现图像多模态检索任务
  • (Repost) Getting Genode with TrustZone on the i.MX
  • (八)c52学习之旅-中断实验
  • (二)学习JVM —— 垃圾回收机制
  • (论文阅读11/100)Fast R-CNN
  • (四)【Jmeter】 JMeter的界面布局与组件概述
  • (五)关系数据库标准语言SQL
  • (五)网络优化与超参数选择--九五小庞
  • (转)编辑寄语:因为爱心,所以美丽
  • (转)机器学习的数学基础(1)--Dirichlet分布
  • 、写入Shellcode到注册表上线
  • .Net Core和.Net Standard直观理解
  • .net core开源商城系统源码,支持可视化布局小程序
  • .net framework4与其client profile版本的区别
  • .NET文档生成工具ADB使用图文教程