黑马Redis实战项目——黑马点评笔记06 | 好友关注

news2024/11/17 9:58:20

黑马Redis实战项目——黑马点评笔记06 | 好友关注

  • 1、关注和取关
  • 2、共同关注
    • 2.1 查看他人主页
    • 2.2 查询共同关注
      • A 改造关注和取关功能
      • B 求交集
  • 3、关注推送
    • 3.1 Feed 流分析
      • 3.1.1、拉模式(读扩散)
      • 3.1.2、推模式(写扩散)
      • 3.1.3、推拉结合模式(读写混合)
    • 3.2 推送到粉丝收件箱
    • 3.3 滚动分页

在这里插入图片描述


1、关注和取关

在这里插入图片描述在这里插入图片描述
1、在FollowController中调用followService.follow和followService.isFollow方法

@RestController
@RequestMapping("/follow")
public class FollowController {
    @Resource
    private IFollowService followService;

    @PutMapping("/{id}/{isFollow}")
    public Result follow(@PathVariable("id") Long followUserId, @PathVariable("isFollow") Boolean isFollow){
        return followService.follow(followUserId,isFollow);
    }
    @GetMapping("/or/not/{id}")
    public Result isFollow(@PathVariable("id") Long followUserId){
        return followService.isFollow(followUserId);
    }
}

2、在IFollowService接口中声明 follow 和 isFollow 方法

public interface IFollowService extends IService<Follow> {
    Result follow(Long followUserId, Boolean isFollow);
    Result isFollow(Long followUserId);
}

3、在FollowServiceImpl类中实现方法

@Service
public class FollowServiceImpl extends ServiceImpl<FollowMapper, Follow> implements IFollowService {

    /**
     * 关注和取关
     * @param followUserId
     * @param isFollow
     * @return
     */
    @Override
    public Result follow(Long followUserId, Boolean isFollow) {
        //获取当前用户Id
        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 userId = ? and follow_user_id = ?
            QueryWrapper<Follow> followQueryWrapper = new QueryWrapper<>();
            followQueryWrapper.eq("user_id",userId).eq("follow_user_id",followUserId);
            remove(followQueryWrapper);
        }
        return Result.ok();
    }

	/**
     * 查询是否已经关注
     * @param followUserId
     * @return
     */
    @Override
    public Result isFollow(Long followUserId) {
        //获取当前用户Id
        Long userId = UserHolder.getUser().getId();
        //1 查询是否关注
        Integer count = query().eq("user_id", userId).eq("follow_user_id", followUserId).count();
        return Result.ok(count>0);
    }
}


2、共同关注

2.1 查看他人主页

在这里插入图片描述
1、在UserController新增根据id查询用户功能

    @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);
    }

2、在BlogController新增根据用户id查询blog功能

    @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);
    }

2.2 查询共同关注

利用redis中set数据操作的求交集功能实现共同关注。

在这里插入图片描述

A 改造关注和取关功能

@Service
public class FollowServiceImpl extends ServiceImpl<FollowMapper, Follow> implements IFollowService {
    @Resource
    private StringRedisTemplate stringRedisTemplate;
    /**
     * 关注和取关
     * @param followUserId
     * @param isFollow
     * @return
     */
    @Override
    public Result follow(Long followUserId, Boolean isFollow) {
        //获取当前用户Id
        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集合
                stringRedisTemplate.opsForSet().add(key,followUserId.toString());
            }
        }else{
            //3 已关注,删除delete from tb_follow where userId = ? and follow_user_id = ?
            QueryWrapper<Follow> followQueryWrapper = new QueryWrapper<>();
            followQueryWrapper.eq("user_id",userId).eq("follow_user_id",followUserId);
            boolean isSuccess = remove(followQueryWrapper);
            if(isSuccess){
                //把关注用户的id从Redis的set集合中移除
                stringRedisTemplate.opsForSet().remove(key,followUserId.toString());
            }
        }
        return Result.ok();
    }
}

B 求交集

1、FollowController类中调用followService.followCommons

    @GetMapping("/common/{id}")
    public Result followCommons(@PathVariable("id") Long id){
        return followService.followCommons(id);
    }

2、IFollowService接口中声明followCommons方法

    Result followCommons(Long id);

3、FollowServiceImpl中实现followCommons方法

	@Resource
    private IUserService userService;
    
	@Override
    public Result followCommons(Long id) {
        //1 获取当前用户Id
        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> users = userService.listByIds(ids)
                .stream()
                .map(user -> BeanUtil.copyProperties(user, UserDTO.class))
                .collect(Collectors.toList());
        return Result.ok(users);
    }

3、关注推送

3.1 Feed 流分析

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

3.1.1、拉模式(读扩散)

在这里插入图片描述

3.1.2、推模式(写扩散)

在这里插入图片描述

3.1.3、推拉结合模式(读写混合)

在这里插入图片描述
在这里插入图片描述本案例用户不多且无v,使用推模式.

3.2 推送到粉丝收件箱

在这里插入图片描述
传统角标分页:在这里插入图片描述
滚动分页:在这里插入图片描述如果是数据有变化的情况,尽量不要使用List。因为List不支持滚动分页,只能按照角标查询,而sortedSet虽然根据socre排名查询和按角标查是一样的,但它可以按照score值的范围查询。每次查询记录最小的score(时间戳),下一次查询时查更小的时间戳则实现了滚动分页,不会重复查询。

1、修改新增博客的功能接口,使博客发布就能推送到粉丝

//修改BlogController中saveBlog
    @PostMapping
    public Result saveBlog(@RequestBody Blog blog) {
        return blogService.saveBlog(blog);
    }
    
//IBlogService接口中声明saveBlog
    Result saveBlog(Blog blog);
    
//类中实现saveBlog
    @Resource
    private IFollowService followService;

	@Override
    public Result saveBlog(Blog blog) {
        //1 获取登录用户
        UserDTO user = UserHolder.getUser();
        blog.setUserId(user.getId());
        //2 保存探店笔记
        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 获取粉丝id
            Long userId = follow.getUserId();
            //4.2 推送笔记到每个粉丝的收件箱(sortedSet)
            String key = "feed:" + userId;
            stringRedisTemplate.opsForZSet().add(key, blog.getId().toString(), System.currentTimeMillis());
        }
        //5 返回id
        return Result.ok(blog.getId());
    }

3.3 滚动分页

展示推送的blog消息,使用的滚动分页,稍许复杂
在这里插入图片描述

基于Redis的sortedSet实现滚动分页所需参数:
在这里插入图片描述基于Redis的sortedSet实现滚动分页代码实现:
1、一个dto类封装滚动分页返回值

@Data
public class ScrollResult {
    private List<?> list;
    private Long minTime;
    private Integer offset;
}

2、滚动查询,展示博主推送的笔记

//BlogController中调用queryBlogOfFollow方法
	@GetMapping("/of/follow")
    public Result queryBlogOfFollow(//如果offset为空说明是第一次传入,设置默认值0
            @RequestParam("lastId") Long max, @RequestParam(value = "offset",defaultValue = 0) Integer offset){
        return blogService.queryBlogOfFollow(max,offset);
    }

//IBlogService接口声明queryBlogOfFollow方法
    Result queryBlogOfFollow(Long max, Integer offset);

//BlogServiceImpl类实现queryBlogOfFollow方法
    /**
     * 滚动查询,展示博主推送的笔记,
     * 新发布的滚动查询查不到,但是往上滚,前端做了处理,就是刷新重新查询,开始位置在当前最新位置
     * @param max
     * @param offset
     * @return
     */
    @Override
    public Result queryBlogOfFollow(Long max, Integer offset) {
        //1 获取当前用户
        Long userId = UserHolder.getUser().getId();
        //2 查询收件箱 ZREVRANGEBYSCORE key Max Min LIMIT offset count
        // limit是小于等于的意思,小于等于查询的最后时间戳
        String key = FEED_KEY + userId;
        Set<ZSetOperations.TypedTuple<String>> typedTuples = stringRedisTemplate.opsForZSet()
                .reverseRangeByScoreWithScores(key, 0, max, offset, 5);
        //3 非空判断
        if (typedTuples == null || typedTuples.isEmpty()){
            return Result.ok();
        }
        //4 解析数据: blogId,minTime(时间戳),offset
        ArrayList<Long> ids = new ArrayList<>(typedTuples.size());
        long minTime = 0;  //这个minTime是上次查询的最小时间戳,作为当次查询的最大时间戳来开始查
        int os = 1;//os->offset
        for (ZSetOperations.TypedTuple<String> typedTuple : typedTuples) {
            //4.1 获取博客id转换为Long型并存入ids数组中
            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,先把前面保存id的ids数组转为字符串
        String idStr = StrUtil.join(",", ids); 
        //由于用mp提供的listByIds是用in方法查,不能保证顺序,需要orderby手动排序
        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);
    }

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

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

相关文章

C++ JPEG编码

依据上一篇的JPEG编码所得到的RGB信息&#xff0c;我们可以重新对RGB图片进行编码&#xff0c;也可对其他图片如BMP所得到的RGB信息进行编码,来得到*.jpg文件&#xff0c;注意我这里实现的JPEG编码不知道为啥编码出来的文件比原来大了好多。 还有要注意的地方&#xff0c;下面会…

【计算机三级网络技术】 第六篇 交换机及其配置

文章目录 IPS&#xff08;入侵防护系统&#xff09;相关知识点蓝牙服务器技术DNS 服务器WWW 服务器FTP 服务器邮件&#xff08;Winmail 邮件服务器&#xff09;生成树协议IEEEVLAN 标识的描述DHCP 服务器 IPS&#xff08;入侵防护系统&#xff09;相关知识点 1、入侵防护系统&…

迅为i.MX6ULL开发板生成 KEY 文件,并安装

使用“ssh-keygen” 生成个四个 key 文件“ssh_host_rsa_key” “ssh_host_dsa_key” “ssh_host_ecdsa_key” 和“ssh_host_ed25519_key” 。 1 在虚拟机 Ubuntu 控制台&#xff0c; “ /home/ssh/openssh-4.6p1” 目录下&#xff0c; 使用命 令“ssh-keygen -t rsa -f ssh…

帮助客户实现自助服务,企业可以打造产品知识库来解决

随着科技的不断发展&#xff0c;越来越多的企业开始将自助服务作为一种解决客户问题的方式。自助服务不仅可以提高客户满意度&#xff0c;还可以减少企业的工作量和成本。为了帮助客户实现自助服务&#xff0c;企业可以打造产品知识库来解决客户问题。本文将介绍产品知识库的定…

shell脚本----函数

文章目录 一、函数的定义1.1 shell函数:1.2函数如何定义 二、函数的返回值三、函数的传参四、函数变量的作用范围五、函数的递归六、函数库 一、函数的定义 1.1 shell函数: 使用函数可以避免代码重复使用函数可以将大的工程分割为若干小的功能模块&#xff0c;代码的可读性更…

数字农业农村解决方案(ppt可编辑)

本资料来源公开网络&#xff0c;仅供个人学习&#xff0c;请勿商用&#xff0c;如有侵权请联系删除。 数字农业农村发展现状 数据基础薄弱&#xff1a;数据资源分散&#xff0c;天空地一体化数据获取能力弱&#xff1b;资源数字化、产业数字化水平不高&#xff0c;部分农业数…

“玲珑”编解码融合架构助力视频多元化需求

随着近年来 AI 技术的兴起&#xff0c;视频监控、汽车、智能家居、移动设备及数据中心等对高清视频处理有了越来越高的要求。安谋科技全新视频处理器——“玲珑”V6/V8&#xff0c;针对主流市场的视频流媒体技术进行了大量投入&#xff0c;通过一系列智能权衡实现了极大优化&am…

常用数据处理方式

文章目录 缺失值处理删除法填充法基于统计学变量填充基于插值填充基于模型填充基于预测填充 不处理 异常值处理基于统计分析的方法基于聚类的方法基于树的方法基于预测的方法 数据重采样标准化min-max标准化&#xff08;归一化&#xff09;z-score标准化&#xff08;规范化&…

基于Web的电竞赛事管理系统的设计与实现(论文+源码)_kaic

摘要 迅猛发展并日益成熟的网络已经彻底的影响了我们的方方面面。人们也确实真切的体会到了网络带给我们的便捷。本网站的设计理念在于作为一个大学生电竞赛事联盟推广网&#xff0c;就是能够尽可能详细地展示、介绍电竞赛事联盟资讯信息&#xff0c;播放视频&#xff0c;同时…

WhatsApp 营销:获得更多潜在客户和销售(一)

你需要了解客户的世界观才能进行有效的营销&#xff0c;你应该投入时间和精力来学习和实施你的业务WhatsApp营销 -因为你的客户出现在WhatsApp上&#xff0c;他们希望在那里联系&#xff0c;而不是在他们讨厌被打断的电子邮件或电话中。 SaleSmartly&#xff08;ss客服&#x…

基于磁盘的Kafka为什么这么快

基于磁盘的Kafka为什么这么快 原创 Wyman 大数据技术架构 2019-05-23 18:04 Kafka是大数据领域无处不在的消息中间件&#xff0c;目前广泛使用在企业内部的实时数据管道&#xff0c;并帮助企业构建自己的流计算应用程序。Kafka虽然是基于磁盘做的数据存储&#xff0c;但却具有…

代码随想录算法训练营day27 | 39. 组合总和,40.组合总和II,131.分割回文串

代码随想录算法训练营day27 | 39. 组合总和&#xff0c;40.组合总和II&#xff0c;131.分割回文串 39. 组合总和解法一&#xff1a;回溯解法二&#xff1a;回溯排序剪枝 40.组合总和II解法一&#xff1a;回溯&#xff08;使用used标记数组&#xff09;解法一&#xff1a;回溯&a…

1688获取商品api接口

作为一名技术爱好者&#xff0c;我们总会遇到各种各样的技术问题&#xff0c;需要寻找合适的技术解决方案。而在互联网时代&#xff0c;我们可以快速通过搜索引擎获取丰富的技术资源和解决方案。然而&#xff0c;在不同的技术分享中&#xff0c;我们常常会遇到质量参差不齐的文…

虹科方案 | 视频和广播专业人士的存储和存档解决方案

虹科HK & Overland-Tandberg 为所有视频和广播工作流阶段提供全面的数字媒体存储解决方案组合&#xff0c;包括创建、复制、传输、存储、保护和归档数据和内容。 一、后期制作工作流程 后期制作是一个多任务过程&#xff0c;通过掌握处理原始视频和声音元素。 这个过程的前…

c++ 11标准模板(STL) std::vector (十)

定义于头文件 <vector> template< class T, class Allocator std::allocator<T> > class vector;(1)namespace pmr { template <class T> using vector std::vector<T, std::pmr::polymorphic_allocator<T>>; }(2)(C17…

adb bugreport 与adb shell getprop 详解

&#x1f604;作者简介&#xff1a; 小曾同学.com,一个致力于测试开发的博主⛽️&#xff0c; 如果文章知识点有错误的地方&#xff0c;还请大家指正&#xff0c;让我们一起学习&#xff0c;一起进步。&#x1f60a; 座右铭&#xff1a;不想当开发的测试&#xff0c;不是一个好…

低代码行业未来如何?大家都真的看好低代码开发吗?

低代码行业未来如何&#xff1f;大家都真的看好低代码开发吗&#xff1f; 是否一定需要开发人员&#xff1f;低代码和无代码平台会取代传统编程吗&#xff1f;低代码/无代码真的是未来吗&#xff1f; 无疑是需要且重要的。今天就来解答为什么低/零代码工具越来越受欢迎&#xf…

第1章计算机系统漫游

文章目录 1、信息就是位上下文2、程序被其他程序翻译成不同的格式3、了解编译系统如何工作的益处4、处理器读并解释储存在存储器中的指令4.1 系统的硬件组成4.2 执行 hello 程序 5、高速缓存6、形成层次结构的存储设备7、操作系统管理硬件7.1 进程7.2 线程7.3 虚拟存储器7.4 文…

领英退出中国,谷歌Bard集成进安卓,陆奇最新演讲,HuggingFace网传遭禁|今日科技圈要闻

夕小瑶科技说 原创 作者 | 智商掉了一地、兔子酱 AI 新闻速递来咯&#xff01;搬好小板凳&#xff0c;一起了解近期发生了什么新鲜事~ 领英职场退出中国 领英是一个专注于职业发展、招聘和营销等方面的社交平台。Linkdein 官方公众号发布公告称&#xff0c;由于面临日趋激烈的…

Spring MVC——Rest风格

REST&#xff08;Representational State Transfer&#xff09; 当我们想表示一个网络资源的时候&#xff0c;可以使用两种方式: 我们分别用查询id为1的用户信息与保存用户信息举例传统风格资源描述形式 http://localhost/user/getById?id1http://localhost/user/saveUser RES…