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

代码如下:
@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数据库点赞+1boolean isSuccess = lambdaUpdate().setSql("liked=liked+1").eq(Blog::getId, id).update();//3.2.保存用户到redis的SortSet集合 ZADD KEY VALUE SCOREif (isSuccess) {stringRedisTemplate.opsForZSet().add(key, userId.toString(), System.currentTimeMillis());}} else {//4.如果已点赞,取消点赞//4.1.数据库点赞数-1boolean 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();
}
代码逻辑:首先获得笔记id,拼接获得key值,再获得当前登录用户的id,根据用户id和key值查看redis中是否有数据,如果没有数据,说明未点赞,此时可以点赞,数据库对应字段+1,并且将用户id存入redis当中,score值存入当前时间戳,表示递增;如果有数据,说明已点赞,此时取消点赞,数据库对应字段-1,并且将用户id从redis中移除。
在笔记的详情页面,应该把给该笔记点赞的人显示出来,比如最早点赞的TOP5,形成点赞排行榜:

按照点赞顺序,将用户的头像先后展示。
需求:按照点赞时间先后排序,返回Top5的用户
| List | Set | SortedSet | |
|---|---|---|---|
| 排序方式 | 按添加顺序排序 | 无法排序 | 根据score值排序 |
| 唯一性 | 不唯一 | 唯一 | 唯一 |
| 查找方式 | 按索引查找 或首尾查找 | 根据元素查找 | 根据元素查找 |
实现步骤:
没啥思路需要理清,很清晰。
代码如下:
@Override
public Result queryBlogLikes(Long id) {String key = BLOG_LIKED_KEY + id;//1.查询top5的点赞用户 zrange key 0 4Set top5 = stringRedisTemplate.opsForZSet().range(key, 0, 4);if (top5 == null || top5.isEmpty()) {return Result.ok(Collections.emptyList());}//2.解析出其中的用户idList 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 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);
}
代码逻辑:我们在实现点赞功能时,score值存储的是当前时间戳,一定是递增的,所以用户id的存储顺序也是按照时间先后顺序存储的。于是我们直接采用 range 方法得到前五个点赞用户的id。然后去到数据库中查询这前五个用户的信息,在查询语句中加上 ORDER BY FIELD(id,5,1) 是因为如果不加的话会按照id的数值大小依次返回用户数据,这样就无法实现排行榜的功能了。
我们已经实现了点赞功能和点赞排行榜功能,现在我们需要进一步改进分页功能。
需求:按照点赞数量的多少进行分页查询,点赞量多的排在前面,点赞量少的排在后面。
实现步骤:
代码如下:
@Override
public Result queryHotBlog(Integer current) {// 根据用户查询Page page = query().orderByDesc("liked").page(new Page<>(current, SystemConstants.MAX_PAGE_SIZE));// 获取当前页数据List 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);
}
代码逻辑:如果已经看懂了前两个功能的实现,那么分页功能的逻辑也就明白了。