《分布式中间件技术实战:Java版》学习笔记(三):Redis实现点赞、取消赞功能

用户在发布内容(包括博客、想法、日记等等)时,后台数据入库后,要往Redis的有序集合添加一条分数为0的记录。这个有序集合是用来对内容点赞量做排序的。同时,可以记录用户操作日志。

@Override
public String insertArticle(ArticleParams articleParams) {
    String uuid = UUID.randomUUID().toString();

    Article article = new Article();
    article.setAuthor(articleParams.getAuthor());
    article.setContent(articleParams.getContent());
    article.setTitle(articleParams.getTitle());
    article.setArticleUuid(uuid);
    save(article);

    //写redis
    redisTemplate.opsForZSet().add(ARTICLE_LIKE_COUNT, uuid, 0);

    UserLog userLog = new UserLog();
    userLog.setUserType("publish");
    userLog.setArticleUuid(uuid);
    userLog.setUserId(articleParams.getAuthor());
    userLogMapper.insert(userLog);

    return uuid;
}

一般地,用户点击点赞图标,如果之前点赞过,就是取消点赞;如果之前没有点赞过,就是点赞。后台怎么判断是否点赞过呢?

点赞时,后台会把用户ID加入到该篇内容的点赞用户集合中,然后内容点击量加一。取消点赞时,后台将用户ID从集合中移除,内容点击量减一。

用户刷新到这篇内容时,可以调用后台接口,根据用户ID是否在该篇内容的点赞用户集合中,判断是否点赞过。

我这里把判断用户ID是否在集合中、添加/移除用户ID、点击量加/减一这三个操作放在一个lua脚本中,作为一个原子性操作。

Redis点赞、取消赞

@Override
public String likeArticle(LikeArticleParams likeArticleParams) {
    String articleUuid = likeArticleParams.getArticleUuid();
    int userId = new Random().nextInt(100000);

    //异步发送到消息队列,限流 保证原子性
    boolean result = likeArticle(articleUuid, userId);

    //数据库写操作记录
    UserLog userLog = new UserLog();
    userLog.setUserType("like");
    userLog.setArticleUuid(articleUuid);
    userLog.setUserId(userId);

    if(result) {
        //点赞需要推送消息给文章作者
    } else {
        userLog.setUserType("unlike");
    }
    userLogMapper.insert(userLog);

    return "success";
}

public boolean likeArticle(String articleUuid, int userId) {
    String likeArticleByUuidSet = "article:like:set:" + articleUuid;
    String redisScript = "if redis.call('exists', KEYS[1]) == 1 and redis.call('sismember', KEYS[1], ARGV[2]) == 1" +
            " then " +
            "   redis.call('zincrby', KEYS[2], -1, ARGV[1])" +
            "   redis.call('srem', KEYS[1], ARGV[2])" +
            "   return 0 " +
            " else " +
            "   redis.call('zincrby', KEYS[2], 1, ARGV[1])" +
            "   redis.call('sadd', KEYS[1], ARGV[2])" +
            "   return 1 " +
            " end ";
    DefaultRedisScript defaultRedisScript = new DefaultRedisScript(redisScript, Boolean.class);
    List<String> keys = new ArrayList();
    keys.add(likeArticleByUuidSet);
    keys.add(ARTICLE_LIKE_COUNT);
    boolean result = (boolean) redisTemplate.execute(defaultRedisScript, keys, articleUuid, userId);
    return result;
}

article:like:set是Redis的set集合,scard查询多少用户点赞了该内容,srandmember随机返回一个点赞用户。
article:like:count是Redis的sorted set集合,srange返回指定排序位的成员,zscore返回成员的点赞量。
《分布式中间件技术实战:Java版》学习笔记(三):Redis实现点赞、取消赞功能_第1张图片
附上我用的数据库表

CREATE TABLE `article`  (
  `id` int(0) NOT NULL AUTO_INCREMENT,
  `article_uuid` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '文章唯一标识',
  `title` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '标题',
  `content` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '内容',
  `author` int(0) NULL DEFAULT NULL COMMENT '用户主键',
  `create_time` timestamp(0) NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `enable_flag` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '1' COMMENT '有效标识 1有效 0无效',
  `like_count` int(0) NULL DEFAULT NULL COMMENT '点赞数',
  `comment_count` int(0) NULL DEFAULT NULL COMMENT '评论数',
  `update_time` timestamp(0) NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP(0) COMMENT '更新时间',
  PRIMARY KEY (`id`) USING BTREE
) 
CREATE TABLE `user_log`  (
  `id` int(0) NOT NULL AUTO_INCREMENT,
  `user_id` int(0) NULL DEFAULT NULL,
  `article_uuid` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `user_type` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '用户类型like 点赞 unlike取消点赞 pulish发布文章',
  `comment` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '评论内容',
  `create_time` timestamp(0) NULL DEFAULT CURRENT_TIMESTAMP,
  `enable_flag` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '1' COMMENT '有效1 无效0',
  PRIMARY KEY (`id`) USING BTREE
)

你可能感兴趣的:(redis)