【Redis】hmdp点赞、排行榜、分页功能的实现

【Redis】hmdp点赞、排行榜、分页功能的实现

1.点赞功能实现


1.1 需求

需求:

  • 同一个用户只能点赞一次,再次点击则取消点赞
  • 如果当前用户已经点赞,则点赞按钮高亮显示(前端已实现,判断字段Blog类的isLike属性)

1.2 实现步骤

实现步骤:

  1. 给Blog类中添加一个isLike字段,标示是否被当前用户点赞
  2. 修改点赞功能,利用Redis的set集合判断是否点赞过,未点赞过则点赞数+1,已点赞过则点赞数-1
  3. 修改根据id查询Blog的业务,判断当前登录用户是否点赞过,赋值给isLike字段
  4. 修改分页查询Blog业务,判断当前登录用户是否点赞过,赋值给isLike字段

1.3 思路分析

在实现点赞功能前,我们还需要确定如何使用Redis来存储用户点赞记录?我们知道Redis存储的是键值对数据,那么我们可以先确定“键”的取值为:blog:liked:用户id ,“值”的取值为 用户id。确定好存储的数据之后,我们还需要考虑究竟是我们使用redis的哪种数据结构?因为点赞是有先后顺序的,所以我们采用 SortSet 结构,需求和实现步骤都明晰了后我们开始实现。

点击点赞按钮,发送请求:

image-20230131145255229


1.4 代码实现

代码如下:

@Override
public Result likeBlog(Long id) {
    //1.获取当前用户
    Long userId = UserHolder.getUser().getId();
    //2.判断当前用户是否已经点赞
    String key = "blog:liked:" + id;
    Double score = stringRedisTemplate.opsForZSet().score(key, userId.toString());
    if (score == null) {
        //3.如果未点赞,可以点赞
        //3.1数据库点赞+1
        boolean isSuccess = lambdaUpdate().setSql("liked=liked+1").eq(Blog::getId, id).update();
        //3.2.保存用户到redis的SortSet集合 ZADD KEY VALUE SCORE
        if (isSuccess) {
            stringRedisTemplate.opsForZSet().add(key, userId.toString(), System.currentTimeMillis());
        }
    } else {
        //4.如果已点赞,取消点赞
        //4.1.数据库点赞数-1
        boolean isSuccess = lambdaUpdate().setSql("liked=liked-1").eq(Blog::getId, id).update();
        if (isSuccess) {
            //4.2.把用户从Redis的set集合移除
            stringRedisTemplate.opsForZSet().remove(key, userId.toString());
        }
    }
    return Result.ok();
}

1.5 逻辑解析

代码逻辑:首先获得笔记id,拼接获得key值,再获得当前登录用户的id,根据用户id和key值查看redis中是否有数据,如果没有数据,说明未点赞,此时可以点赞,数据库对应字段+1,并且将用户id存入redis当中,score值存入当前时间戳,表示递增;如果有数据,说明已点赞,此时取消点赞,数据库对应字段-1,并且将用户id从redis中移除。


2. 排行榜功能实现

在笔记的详情页面,应该把给该笔记点赞的人显示出来,比如最早点赞的TOP5,形成点赞排行榜:

image-20230131150514923

按照点赞顺序,将用户的头像先后展示。


2.1 需求

需求:按照点赞时间先后排序,返回Top5的用户

ListSetSortedSet
排序方式按添加顺序排序无法排序根据score值排序
唯一性不唯一唯一唯一
查找方式按索引查找 或首尾查找根据元素查找根据元素查找

2.2 实现步骤

实现步骤:

  1. 去redis中查询top5的点赞用户
  2. 解析出其中的用户id
  3. 根据用户id去数据库查询用户

2.3 思路分析

没啥思路需要理清,很清晰。


2.4 代码实现

代码如下:

@Override
public Result queryBlogLikes(Long id) {
    String key = BLOG_LIKED_KEY + id;
    //1.查询top5的点赞用户 zrange key 0 4
    Set<String> top5 = stringRedisTemplate.opsForZSet().range(key, 0, 4);
    if (top5 == null || top5.isEmpty()) {
        return Result.ok(Collections.emptyList());
    }
    //2.解析出其中的用户id
    List<Long> ids = top5.stream().map(Long::valueOf).collect(Collectors.toList());
    String idStr = StrUtil.join(",", ids);
    //3.根据用户id查询用户 WHERE id IN (5,1) ORDER BY FIELD(id,5,1)
    List<UserDTO> userDTOS = userService.lambdaQuery()
            .in(User::getId, ids).last("ORDER BY FIELD(id," + idStr + ")").list()
            .stream()
            .map(user -> BeanUtil.copyProperties(user, UserDTO.class))
            .collect(Collectors.toList());
    return Result.ok(userDTOS);
}

2.5 逻辑解析

代码逻辑:我们在实现点赞功能时,score值存储的是当前时间戳,一定是递增的,所以用户id的存储顺序也是按照时间先后顺序存储的。于是我们直接采用 range 方法得到前五个点赞用户的id。然后去到数据库中查询这前五个用户的信息,在查询语句中加上 ORDER BY FIELD(id,5,1) 是因为如果不加的话会按照id的数值大小依次返回用户数据,这样就无法实现排行榜的功能了。


3. 分页功能实现

我们已经实现了点赞功能和点赞排行榜功能,现在我们需要进一步改进分页功能。

3.1 需求

需求:按照点赞数量的多少进行分页查询,点赞量多的排在前面,点赞量少的排在后面。


3.2 实现步骤

实现步骤:

  1. 根据笔记的点赞量倒序排序并分页
  2. 获取当前页的数据
  3. 查询每条数据对应的用户信息和点赞信息

3.3 代码实现

代码如下:

@Override
public Result queryHotBlog(Integer current) {
    // 根据用户查询
    Page<Blog> page = query()
            .orderByDesc("liked")
            .page(new Page<>(current, SystemConstants.MAX_PAGE_SIZE));
    // 获取当前页数据
    List<Blog> records = page.getRecords();
    // 查询用户
    records.forEach(blog -> {
        this.queryBlogUser(blog);
        this.isBlogLiked(blog);
    });
    return Result.ok(records);
}

private void queryBlogUser(Blog blog) {
    Long userId = blog.getUserId();
    User user = userService.getById(userId);
    blog.setName(user.getNickName());
    blog.setIcon(user.getIcon());
}

private void isBlogLiked(Blog blog) {
    //1.获取当前用户
    UserDTO user = UserHolder.getUser();
    if (user == null) {
        return;
    }
    Long userId = user.getId();
    //2.判断当前用户是否已经点赞
    String key = "blog:liked:" + blog.getId();
    Double score = stringRedisTemplate.opsForZSet().score(key, userId.toString());
    blog.setIsLike(score != null);
}

3.4 逻辑解析

代码逻辑:如果已经看懂了前两个功能的实现,那么分页功能的逻辑也就明白了。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值