使用Redis优化用户点赞功能

2.4k 词

目前的点赞功能

同一用户可以给同一篇博客多次点赞,且直接与数据库交互,代码如下:

1
2
3
4
5
6
7
@PutMapping("/like/{id}")
public Result likeBlog(@PathVariable("id") Long id) {
// 修改点赞数量
blogService.update()
.setSql("liked = liked + 1").eq("id", id).update();
return Result.ok();
}

希望实现的效果

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

实现步骤

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

新增isLike字段

因为该字段不在数据库中,所以应该添加注解
注:此处更加优雅的写法应该是创建一个新的BlogVO

1
2
3
4
5
/**
* 是否点赞过了
*/
@TableField(exist = false)
private Boolean isLike;

修改点赞功能

点赞功能修改流程如下:

  1. 获取当前用户
  2. 判断当前用户是否已经点赞
  3. 如果未点赞
    1. 数据库点赞数+1
    2. 保存用户到Redis的set集合
  4. 如果已点赞
    1. 数据库-1
    2. 把用户从Redis的set中删除
      代码实现:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      @Override
      @Transactional
      public Result likeBlog(Long blogId) {
      // 获取当前用户
      Long userId = UserHolder.getUser().getId();
      // 判断是否已点赞
      String key = RedisConstants.BLOG_LIKED_KEY + blogId;
      Boolean isLiked = redisTemplate.opsForSet().isMember(key, userId.toString());
      if (BooleanUtil.isFalse(isLiked)) {
      // 数据库+1
      boolean success = lambdaUpdate().setSql("liked = liked + 1").eq(Blog::getId, blogId).update();
      // 更新Redis
      if (success) {
      redisTemplate.opsForSet().add(key, userId.toString());
      }else {
      throw new MySqlException(MessageConstant.LIKE_FAILED);
      }
      }else {
      // 数据库-1
      boolean success = lambdaUpdate().setSql("liked = liked - 1").eq(Blog::getId, blogId).update();
      // 更新Redis
      if (success) {
      redisTemplate.opsForSet().remove(key, userId.toString());
      }else {
      throw new MySqlException(MessageConstant.LIKE_FAILED);
      }
      }
      return Result.ok();
      }

修改根据id查询

只需在查询时,多在Redis中查询一次是否点赞

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Override
public Blog queryById(Long id) {
Blog blog = getById(id);
queryBlogUser(blog);
setIsLiked(blog);
return blog;
}

private void setIsLiked(Blog blog) {
String key = RedisConstants.BLOG_LIKED_KEY + blog.getId();
Long userId = UserHolder.getUser().getId();
Boolean isLiked = redisTemplate.opsForSet().isMember(key, userId.toString());
blog.setIsLike(isLiked);
}

这么写有一个bug,用户在未登录时,加载首页的博客信息时,也会去查询当前用户是否点赞,但UserHolder.getUser()返回值为null,所以此处会抛出NullPointerException
修改如下:

1
2
3
4
5
6
7
8
private void setIsLiked(Blog blog) {
String key = RedisConstants.BLOG_LIKED_KEY + blog.getId();
// 处理用户未登录的情况
if (UserHolder.getUser() == null) return;
Long userId = UserHolder.getUser().getId();
Boolean isLiked = redisTemplate.opsForSet().isMember(key, userId.toString());
blog.setIsLike(isLiked);
}

分页查询同理,就不再赘述了