点评项目——好友关注模块

2023.12.12

        本章实现好友关注模块,包含以下功能的实现:关注与取关、共同关注、关注推送。

关注与取关        

        当点击某个用户的主页时,会调用如下接口:

        该接口是用来判断是否已经关注该用户,最后一个参数是该用户的id。

        在我们点击关注之后,又会调用一个接口:

        该接口就是用来实现关注或者取关的操作的,倒数第二个参数是该用户的id,最后一个参数是当前是否已经关注该用户,根据这个bool值来判断是执行关注操作还是取关操作。

       关注与取关操作 和 判断是否关注 的实现类方法定义为:follow和isFollow,代码如下:

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

    @Override
    public Result follow(Long followUserId, Boolean isFollow) {
        //1.获取登陆用户
        Long userId = UserHolder.getUser().getId();
        //2.判断是关注还是取关
        if(isFollow){
            //2.1 关注,新增数据
            Follow follow = new Follow();
            follow.setUserId(userId);
            follow.setFollowUserId(followUserId);
            save(follow);
        }else {
            //2.2取关 直接删除表中的数据即可
            remove(new QueryWrapper<Follow>()
                    .eq("user_id",userId).eq("follow_user_id",followUserId));
        }
        return Result.ok();
    }

    @Override
    public Result isFollow(Long followUserId) {
        //1.获取登陆用户
        Long userId = UserHolder.getUser().getId();
        //2.查询数据库看是否关注
        Integer count = query().eq("user_id", userId).eq("follow_user_id", followUserId).count();

        return Result.ok(count > 0);
    }
}

共同关注

        接下来实现共同关注功能。当点进一个用户的主页时,会显示该用户的基本信息(头像、id),还会显示该用户发布的博客。

         右边还有一个共同关注的列表,显示的内容是我与这个用户共同关注的用户,点击共同关注,接口信息如下:

        1022为当前操作用户的id,这里需要实现当前操作用户与点击的这个用户的共同关注列表,思路就是将两个用户的关注列表取一个交集即可,那么适合此操作的数据结构为redis中的set集合,set集合有取交集的操作。

        这里得先修改我们之前关注和取关操作的逻辑,在关注博主的同时,需要将数据放到set集合中,方便后期我们实现共同关注,当取消关注时,也需要将数据从set集合中删除。

        原代码改为:

    @Override
    public Result follow(Long followUserId, Boolean isFollow) {
        //1.获取登陆用户
        Long userId = UserHolder.getUser().getId();
        String key = "follows:" + userId;
        //2.判断是关注还是取关
        if(isFollow){
            //2.1 关注,新增数据
            Follow follow = new Follow();
            follow.setUserId(userId);
            follow.setFollowUserId(followUserId);
            boolean isSuccess = save(follow);
            if(isSuccess){
                //把关注用户的id,存入redis的set集合中
                stringRedisTemplate.opsForSet().add(key,followUserId.toString());
            }
        }else {
            //2.2取关 直接删除表中的数据即可
            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();
    }

        即在修改数据库之后,对redis中的set集合数据进行保存或者移除。

        接下来,实现共同关注的功能,大致思路为:从redis中取出两个用户关注列表,再使用redis中的intersect操作取出两个关注列表的集合,再返回给前端页面。代码如下:

    @Override
    public 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> users = userService.listByIds(ids)
                .stream()
                .map(user -> BeanUtil.copyProperties(user, UserDTO.class))
                .collect(Collectors.toList());
        return Result.ok(users);
    }

测试结果如下:

 关注推送

        当我们关注了用户之后,这个用户发布了动态,那我们应该把这些动态推送给粉丝,即关注推送,关注推送也叫作Feed流,直译为投喂,为用户提供沉浸式体验,通过无限下拉刷新获取新的信息。

  • Feed流的实现有两种模式
    1. Timeline:不做内容筛选,简单的按照内容发布时间排序,常用于好友或关注(B站关注的up,朋友圈等)
      • 优点:信息全面,不会有缺失,并且实现也相对简单
      • 缺点:信息噪音较多,用户不一定感兴趣,内容获取效率低
    2. 智能排序:利用智能算法屏蔽掉违规的、用户不感兴趣的内容,推送用户感兴趣的信息来吸引用户
      • 优点:投喂用户感兴趣的信息,用户粘度很高,容易沉迷
      • 缺点:如果算法不精准,可能会起到反作用(给你推的你都不爱看)

        这里我们采用的是Timeline方式,有三种方案:

  1. 拉模式:也叫读扩散,博主将发布的博客保存至发件箱,粉丝需要将关注的内容拉到自己的收件箱中。
  2. 推模式:也叫写扩散,博主会将发布的博客发布至所有粉丝的收件箱中。
  3. 推拉结合:上述两种方式的结合版。

        这里考虑到粉丝量不会有很多,采取推模式。需要修改新增探店博客的业务,在保存blog到数据库的同时,推送到粉丝的收件箱,收件箱需要满足可以根据时间戳排序,必须用Redis的数据结构实现,这里使用ZSet集合来充当收件箱。

        这里为什么使用redis中的Zset集合呢?Zset支持滚动分页,传统的分页模式在这里会有问题,因为feed流中的数据是不断变化的,数据的角标也在变化,这里需要使用滚动分页。

        改造新增探店博客的代码:

    @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.查询该博客作者的所有粉丝
        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推送
            String key = "feed:" + userId;
            stringRedisTemplate.opsForZSet().add(key,blog.getId().toString(),System.currentTimeMillis());
        }
        // 5.返回id
        return Result.ok(blog.getId());
    }

        点击关注栏,会发送请求:

        实现该接口的实现类:

    @Override
    public Result queryBlogOfFollow(Long max, Integer offset) {
        // 1.获取当前用户
        Long userId = UserHolder.getUser().getId();
        // 2.查询收件箱 ZREVRANGEBYSCORE key Max Min LIMIT offset count
        String key = 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、minTime(时间戳)、offset
        List<Long> ids = new ArrayList<>(typedTuples.size());
        long minTime = 0; // 2
        int os = 1; // 2
        for (ZSetOperations.TypedTuple<String> tuple : typedTuples) { // 5 4 4 2 2
            // 4.1.获取id
            ids.add(Long.valueOf(tuple.getValue()));
            // 4.2.获取分数(时间戳)
            long time = tuple.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) {
            // 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);
    }

        最终效果如图:

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

1,使用IDLE开启我们第一个Python程序

前面我们已经安装好了Python&#xff0c;安装了Python后&#xff0c;他会自动帮我们安装一个IDLE。IDLE是一个Python自带的非常简洁的集成开发环境&#xff08;IDE&#xff09;。他是一个Python Shell&#xff0c;我们可以利用Python Shell与Python交互。下面我们就利用IDLE开发…

如何学习网络安全

我们应该怎么学习网络安全 首先&#xff0c;必须&#xff08;时刻&#xff09;意识到你是在学习一门可以说是最难的课程&#xff0c;是网络专业领域的顶尖课程&#xff0c;不是什么人、随随便便就能学好的。不然&#xff0c;大家都是黑客&#xff0c;也就没有黑客和网络安全的概…

【玩转TableAgent数据智能分析】TableAgent全功能详解及多领域数据分析实践(中)不同领域数据分析实践

3 电影点评数据分析实践 利用本身自带的电影点评数据&#xff0c;来具体看一下TableAgent的分析能力&#xff0c;选择电影点评数据&#xff0c;智能体会自动导入该数据DMSC20000.csv&#xff0c;大小为3.3 MB。在数据信息展示区&#xff0c;就会显示出该数据&#xff0c;并提供…

C/C++,图算法——Dinic最大流量算法

1 文本格式 // C implementation of Dinics Algorithm #include<bits/stdc.h> using namespace std; // A structure to represent a edge between // two vertex struct Edge { int v; // Vertex v (or "to" vertex) // of a directed edge u…

微服务实战系列之通信

前言 掰个指头数一数&#xff0c;博主的“微服务实战系列”从无到有&#xff0c;从零走到了十五。如果比作时钟&#xff0c;刚好走过了一刻度。 当初为什么要做这个系列&#xff0c;博主想了又想&#xff0c;私以为作为当下软件领域的几个“hot spot”之一&#xff0c;又乘着…

urllib 的 get 请求和 post 请求(二)

目录 一、爬取网页、图片视频 二、请求对象的定制 三、get请求的urlencode方法 四、post 请求英文翻译 一、爬取网页、图片视频 目标&#xff1a;下载数据 知识点&#xff1a;urllib.request.urlretrieve()下载 使用urllib下载网页、图片和视频 下载网页&#xff1a; #…

六、ZGC深度剖析

一、引言 对于Java 程序员来说&#xff0c;JVM 帮助我们做了很多事情。 JVM是虚拟机&#xff0c;能够识别字节码&#xff0c;就是class文件或者你打包的jar文件&#xff0c;运行在操作系统上。 JVM帮我们实现了跨平台&#xff0c;你只需要编译一次&#xff0c;就可以在不同的…

traj_dist 笔记 源代码解析(python部分)

1distance.py 1.1 METRIC_DIC 不同实现方法对应的函数路径 1.2 sspd 功能&#xff1a; 计算轨迹 traj_1 和 traj_2 之间的对称化段路径距离。 参数&#xff1a; traj_1&#xff1a;一个二维 numpy 数组&#xff0c;代表第一个轨迹。traj_2&#xff1a;一个二维 numpy 数组…

FreeRTOS的三处栈空间设置分析

1、汇编启动代码中设置栈 这个栈空间只有300字节&#xff0c;是用于汇编启动代码早期&#xff0c;以及调用C语言的main函数&#xff08;创建任务等&#xff09;在创建好任务&#xff0c;启动调取器后&#xff0c;这个栈空间就被抛弃掉&#xff0c;后续不会使用到等调度器开启后…

星际飞船大战

欢迎来到程序小院 星际飞船大战 玩法&#xff1a;滑动鼠标控制方向&#xff0c;点击鼠标左键射击&#xff0c;生命值100分&#xff0c;被敌船击中减去20&#xff0c; 5次生命复活机会&#xff0c;统计分数&#xff0c;快去星际飞船大战吧^^。开始游戏https://www.ormcc.com/pl…

Dijkstra求最短路 II(堆优化Dijkstra算法)

给定一个 n 个点 m 条边的有向图&#xff0c;图中可能存在重边和自环&#xff0c;所有边权均为非负值。 请你求出 11 号点到 n 号点的最短距离&#xff0c;如果无法从 11 号点走到 n 号点&#xff0c;则输出 −1−1。 输入格式 第一行包含整数 n 和 m。 接下来 m 行每行包含…

鱼骨探矿的题解

原题描述&#xff1a; 题目描述&#xff1a; 众所周知&#xff0c;《我的世界》中一种非常流行的探矿方式就是鱼骨探矿。 我的世界的地图可以看作一个的正方形世界。 经过初步探测&#xff0c;在第 i 行&#xff0c;[li, ri] 区间内可能存在宝藏。为了探索效率&#xff0c;…

C# | 对比不同种类的锁

文章目录 C# 对比不同种类的锁异同点对比表使用方法lock语句Monitor类Mutex类Semaphore类ReaderWriterLock类 结语 C# 对比不同种类的锁 Hi&#xff0c;在C#编程中&#xff0c;想要保护共享资源&#xff0c;通常会用到各种类型的锁。今天我们就来一起看看C#中不同种类的锁&…

geolife 笔记:将所有轨迹放入一个DataFrame

单条轨迹的处理&#xff1a;geolife笔记&#xff1a;整理处理单条轨迹-CSDN博客 1 加载数据 import pandas as pd import numpy as np import datetime as dt import osdata_dir Geolife Trajectories 1.3/Data/ 1.1 列出所有文件夹 dirlist os.listdir(data_dir) dirlist…

基于Qt开发的闹钟

#include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this);speecher new QTextToSpeech(this); }Widget::~Widget() {delete ui; }//定时器超时时&#xff0c;自动执行的…

Ubuntu22.04中用户的全名

概要&#xff1a; 用户的全名有别于用户名username username可以理解为账户名&#xff0c;或者说用户ID&#xff0c;用于确定身份&#xff0c;显然是必需的 用户全名则不是必需的&#xff0c;用户全名也叫做注释&#xff0c;是一种辅助信息&#xff0c;如果没有填写用户全名…

docker 资源控制

Docker的资源控制 对容器使用宿主机的资源进行限制&#xff0c;如cpu&#xff0c;内存&#xff0c;磁盘I/O Docker使用linux自带的功能cgroup(control grouos)是linux内核系统提供的一种可以限制&#xff0c;记录&#xff0c;隔离进程组使用的物理资源 Docker借助这个机制&…

MySQL执行流程_执行一条select语句,期间发生了什么

文章目录 执行一条select语句&#xff0c;期间发生了什么MySQL执行流程第一步&#xff1a;连接器第二步&#xff1a;查询缓存第三步&#xff1a;解析SQL第四步&#xff1a;执行SQL 执行一条select语句&#xff0c;期间发生了什么 MySQL执行流程 server层负责建立连接、分析和执…

Banana Pi BPI-R4 SBC/路由器推出,带双 10G SFP+ 端口+Wifi7支持

Banana Pi BPI-R4 wifi7路由器开发板 香蕉派 Banana Pi BPI-R4 根据著名Banana Pi品牌背后的公司Sinovoip提供的初步信息&#xff0c;他们即将推出的Banana Pi BPI-R4路由器板目前已经正式发售。与之前的 Banana Pi R3 板相比&#xff0c;这在规格上将有显着提升。这就是我们…

99基于matlab的小波分解和小波能量熵函数

基于matlab的小波分解和小波能量熵函数&#xff0c;通过GUI界面导入西储大学轴承故障数据&#xff0c;以可视化的图对结果进行展现。数据可更换自己的&#xff0c;程序已调通&#xff0c;可直接运行。 99小波分解和小波能量熵函数 (xiaohongshu.com)https://www.xiaohongshu.co…