laitimes

Like function, analysis and implementation, practical!

author:IT Knowledge Sharing Officer

1ãåæ

ç¹èμåè1/2æç ̧ä¿¡å§å¶é1/2ä ̧éçï1/4æå1/4æåãB ç«ãæé³ãå°çº¹¦çå ³äºç¤3/4äºç ̧å ³çåè1/2åºæ¬é1/2æç¹èμè¿ä ̧åè1/2ï1/4æ以æ¬ç ̄ï1/4å±ä»¬å°±æ¥åæä ̧ä ̧¹èμåè1/2å¦ä1/2æ¥å®®ç°ã

é¦to â¹èμè ̄to ̄¹æä ̧ç±»æ°æ è¿è¡ç¹èμï1/4å¦ï1/4B ç«oh±æ§é¢ç¹èμãè ̄è ºç¹èμãå ̈æç¹èμçãæ 以ï1/4æç §æ们to®̧è§å¥· ̄ï1/4æ ̄ä ̧æ ̄å°±ohºè ̄¥æ§é¢ùèμæ¥ãï1/4è ̄觹èμæ®®®¥ãï1/4å ̈æç¹èμæ¥å£çï1/4ä1/2äºå æç宦æ¤ï1/4

ä1/2 çæè¿æ ·è®3/4è¡ï1/4æ ̄å¦å ̄计ï1/4

  • æ°æ®idï1/4è§é¢ãè ̄论çidï1/4
  • æ°æ®ç±»åï1/4è§é¢ãè ̄论çï1/4
  • ç¹èμç¶æï1/4trueï1/4falseï1/4

è¿æ ·éè¿ä ̧åæ°ï1/4å¤ä ̧êä°æ â¹èμæ¥å£å1/2å°ä ̧ä ̧êæ¥å£å1/2ä ̧ðï1®/4è¿æ ·æ ̄ä ̧å°úä ̧å¿ è¦çåä1/2代ç ãä1/2éä¹èæ¥çé é¢ä¹æ ̄æ3/4èæ§ï1/4以åç¹èμåè1/2çæμéè®®¢ « yæ°ä ̧ä ̧ªæå¤ä ̧ªææ¥å1/2ä ̧ï1/4èç°å ̈é1/2å1/2å°ä ̧ä ̧ªææ¥ææ¥å ç°ï1/4é£æçæμéå°±é1/2ä1/4æå°ä ̧ä ̧ªç¹èμæ¥å£å£å1/2ä ̧ï1/4è¿å ̄¹æ£æ§è1/2å®°±è¦æ±å3/4é«äºã

ç°å ̈ç¥éæ¥å£æä¹è 3/4è¡äºï1/4é£åæ¥ççç¹èμè¡ ̈å¦ä1/2è 3/4è®®®®¡ï1/4

æ 殥ï1/4æä"¬å ̄ä"¥á ®®è¿å ä̧ªåæ®μ

Æææ ID箹èμç¶æ

ä ̧ºå¥æ²¡ææ°æ®ç±»åï1/4

æä"¬yæ³ä ̧ä ̧ï1/4æ±"åæ ̧°æ ±"ç"åæ ̄ä ̧ºúéé æ ́å¤çä ̧âæ°æ çùçâ°æ ̧å¤ ̧ä ̧êç¹èμçæ°æ IDï1/4å°±å ̄ä"¥éå é£æ¡æ°æ 碫ç¹èμäºï1/4æ以æ éæ°æ®®®®®ç®±»åIDã®

å1/2ç¶ä ̧ºäºç¡®ä¿ç¥éç ̈æ·ç¹èμäºé£æ¡æ°æ ï1/4æ以è¿éè¦å¦ä ̧åæ®®μï1/4

ç ̈æ·ID

æ以æåï1/4æ们çç¹å ̈è¡ ̈ SQL å°±æ ̄ä ̧é¢è¿æ ·ï1/4

sqlåå¶ä»£ç

CREATE TABLE `sb_like` ( `id` bigint(20) NOT NULL, `item_id` bigint(20) NOT NULL COMMENT '点赞条目id', `user_id` bigint(20) NOT NULL COMMENT '用户id', `like` tinyint(1) NOT NULL COMMENT '是否点赞,true点赞,false未点赞', `create_time` datetime DEFAULT NULL, `update_time` datetime DEFAULT NULL, PRIMARY KEY (`id`), UNIQUE KEY `un` (`item_id`,`user_id`), KEY `k` (`item_id`,`user_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_german2_ci

è ̄ ́æï1/4

item_idï1/4å¿ é¡»æ ̄å ̈å±å ̄ä ̧IDï1/4ä3/4å¦ï1/4éªè±IDï1/4ï1/4ä¹å³ä ̧è1/2æ ̄èªå¢ï1/4å ̧ºè¿å1/4 ç¹èμè¡ ̈æ ̈æ ̧è¿å1/4 �æä ̧å°æ â¹èμè°å1/2ï1/4讧é¢ãè ̄论ãå ̈æçï1/4ãidï1/4åæ μæå¥1/2ä¹æ®®̄å ̈å±å ̄ä ̧IDï1/4å ̧ºyy ̄è1/2éè¦è¿è¡è¡ ̈ï1/4æ ̄ç«èμè®°å1/2æ ̄ä ̧äªå¢é¿è ̄å¿æ®éï1/4å¦æç ̈èªå¢IDï1/4åæå3/4é3/4è¿ç§»ã

2nd®ç°

§°å ̈æ ¹æ ̧é¢çåæï1/4å ̄以å3/4åºç¹èμè ̄·æ±çç®åæ°æ®®æμyy¦ä ̧å3/4ï1/4

Like function, analysis and implementation, practical!

to ̄以çå¦æç³»ä ̧ä ̧áæ°æ è¶æ¥è¶å§è ̄ï1/4ç¹èμæ¥å£çè¿é éå°ä1/4åå3/4è¶æ¥è¶§ï1/4ä»èåæä ̧ä ̧ªé«é¢è¿é æ¥å£ï1/4å1/2ç¶ï1/4ç¹èμæ®®¬èº®«oh°±æ ̄ä ̧ä̧ªé«é®®¢çå ̈ä1/2ï1/4ã

æ以ï1/4é¢å ̄¹ä ̧ä ̧ªé«é¢æ£ï1/4å±ä»¬ç¹èμè®°å1/2è ̄å æ ̄ä ̧è1/2ç ́æ¥å ¥ MySQL çï1/4æ以è¿éå±ä»¬ç¹èμæ°æ ̧殥è ̄å®æ ̄宥 Redisã

ä ̧ä ̧ªæ ̄åºäºç£çIOæä1/2ï1/4MySQLï1/4ï1/4a ̧ä ̧§ªæ ̄åºäºå åæä1/2ï1/4Redisï1/4ã

é£ï1/4æ¢ç¶å1/4å ¥ Redisï1/4è ̄å åä1/4å1/4ç³åºä ̧äºå é®®é¢ï1/4宦ï1/4

  • ä1/2¿ç ̈ä»ä¹æ°æ ç±»yyy ̈ç¹èμæ°æ®®
  • Redis æ°æ å¦ä1/2åæ®¥å° MySQL
  • æ¥è ̄¢ç ̈æ·ç¹èμï1/4ä ̧å¡æ°æ®çç¹èμéä1/4åå3/4å¤æ3/4夤

éå ̄¹è¿ä ̧ä ̧ªé®é¢ï1/4ä¹å¥1/2解å³ï1/4æ槣å³ç大è ́æ· ̄è±äºå1/4 å3/4ï1/4ç ̧ä¿¡ä ̧å3/4èåè ̈ï1/4

Like function, analysis and implementation, practical!

æ³ ̈ï1/4Redis çyy ̈æ°æ®ç±»yä ̧º hash

okï1/4ä ̧é¢å°±æ ̄æ们çç1/4ç to ðç ̄èäºï1/4⧪éå ̧̧å£1/®2å ç®°ã

2.1 ç¹èμæ¥ãå®ç°

1ï1/4controller

ä1/2ç1/2®ï1/4cn.j3code.community.api.v1.controller

java复制代码@Slf4j
@ResponseResult
@AllArgsConstructor
@RestController
@RequestMapping(UrlPrefixConstants.WEB_V1 + "/like")
public class LikeController {

    private final LikeService likeService;

    /**
     * 点赞
     * @param request
     */
    @PostMapping("/")
    public void like(@Validated @RequestBody LikeRequest request) {
        likeService.like(request);
    }
}
           

LikeRequest to ̄¹è±¡

ä1/2ç1/2®ï1/4cn.j3code.community.api.v1.request

java复制代码@Data
public class LikeRequest {
    @NotNull(message = "条目id不为空")
    private Long itemId;
    @NotNull(message = "点赞不为空")
    private Boolean like;
    @NotNull(message = "类型不为空")
    private CommentTypeEnum type;
}
           

CommentTypeEnum æä ̧3/4

ä1/2ç1/2®ï1/4cn.j3code.config.enums

java复制代码@Getter
public enum CommentTypeEnum {
    COMMODITY(1, "商品评论"),

    POST_COMMENT(2, "帖子评论"),

    POST(3, "帖子"),
    ;

    @EnumValue
    private Integer value;

    private String description;

    CommentTypeEnum(Integer value, String description) {
        this.value = value;
        this.description = description;
    }
}
           

2ï1/4service

ä1/2ç1/2®ï1/4cn.j3code.community.service

java复制代码public interface LikeService extends IService<Like> {
    void like(LikeRequest request);
}
@Slf4j
@AllArgsConstructor
@Service
public class LikeServiceImpl extends ServiceImpl<LikeMapper, Like>
    implements LikeService {

    private final RedisTemplate<String, Object> redisTemplate;

    @Override
    public void like(LikeRequest request) {
        redisTemplate.opsForHash().put(
            SbUtil.getItemLikeKey(request.getType().getValue() + ":" + request.getItemId()),
            Objects.requireNonNull(SecurityUtil.getUserId(), "获取登录人信息出错!").toString(),
            request.getLike());
    }
}
           

æ ̄ä ̧æ ̄éå ̧ç åï1/4åªéè¦ç®»è å£1/2 hash ç»æçæ°æ®ï1/4访é®ä ̧ä ̧ Redis ̙å ̄ã

2.2 ç¹èμæ°æ yæ°æ®®úå®ç°

åé¢æ们氿ï1/4æä¹ åæ ̄騿å æ¶ä»»å®¡è¿è¡çï1/4æ以è¿éæä ̧ùç¹é é¢å°±æ ̦̄æå ̈å æ¶ä»»»å¨¿æ§è§áçæ¶åï1/4Redis æºï1/4é£è¿æ®μæ¶é®́秹èμè®°å1/2å°ä1/4ä ̧¢å®¤±ãä ̧ú¿çæ¶åï1/4Redis çæä¹ yyy1/2è¦è®°å3/4é å¥1/2ï1/4AOF/RDBï1/4ã

§°å ̈ï1/4æä"¬æææç¹èμæ°æ yæ°æ°æ åºçæμç®®̈ï1/4

  1. è·yy ̄¹åºæ°æ®ç±»YÇÆÆǹèμ Key
  2. æ ¹æ è·yyİç keyï1/4è·åæçç¹èμè°å1/2ï1/4æ°æ æ 1/4å1/4ä ̧º Map<ä ̧å¡æ°æ IDï1/4Map<ç ̈æ·IDï1/4ç¹èμç¶æ®®®>>®
  3. ç»to Redis ç¹èμè°å1/2 + MySQL æä¹ yè°å1/2ï1/4è¡ç åºyä ̧æ°æ°æ çæç»ç¹èμç®®®¶ææ°æ ç箹èμæ®®°é
  4. ç§»é¤ redis ä ̧ï1/4å·²ç»åæ¥çæ°æ®
  5. æ°ä ̧å¡æ°æ ç¹èμæ®°é
  6. æå ¥ç ̈æ·ç¹èμè®°å1/2æ°æ®

è¿éï1/4å ̄è1/22ã3ùå§å¶æùä ̧宥1/2ç解ï1/4æ²¡å ³ç³»ï1/4æå å1/4 æμç ̈å3/4æ ́ä1/2æ¥çæ¿ä ̧ªæμç ̈ï1/4åæ¥åæä1/2 们çæç¹ï1/4

Like function, analysis and implementation, practical!

to ̈å3/4ä ̧ï1/4æå·²ç»æè1/2解éçé é¢é1/2å·²ç»è§£éæ ̧ æ¥äºï1/4ä ̧鮢就ç代ç çï1®/4

to ä ̧ºç¹èμä ̧å¡æå3/4å¤ï1/4æ以å ̄¹åºçå æ¶ä»»å¡è ̄ùæ ̄ä®̧åçï1/4è¿éæ以æ¬é¡¹ç®̧åä®̧ºä3/4ï1/4楮å çåæ°æ ç¹èμæ°æ çåæ®®¥ã

1ï1/4schedule

ä1/2ç1/2®ï1/4cn.j3code.community.schedule

java复制代码@Slf4j
@Component
@AllArgsConstructor
public class PostLikeSchedule {

    private final PostService postService;

    /**
     * 同步帖子点赞
     */
    @DistributedLock
    @Scheduled(cron = "11 0/9 * * * ?")
    public void syncPostLike(){
        postService.syncPostLike();
    }
}
           

2ï1/4service

ä1/2ç1/2®ï1/4cn.j3code.community.service

java复制代码public interface PostService extends IService<Post> {
    void syncPostLike();
}
@Slf4j
@AllArgsConstructor
@Service
public class PostServiceImpl extends ServiceImpl<PostMapper, Post>
    implements PostService {
    
    private final LikeServiceImpl likeService;
    private final RedisTemplate<String, Object> redisTemplate;
    private final TransactionTemplate transactionTemplate;

    @Override
    public void syncPostLike() {
        // 获取帖子点赞 key
        List<String> keys = new ArrayList<>(redisTemplate.keys(SbUtil.getItemLikeKey(CommentTypeEnum.POST.getValue() + ":*")));
        if (CollectionUtils.isEmpty(keys)) {
            return;
        }

        Set<Long> commentIdList = keys.stream().map(key -> Long.valueOf(key.substring(key.lastIndexOf(":") + 1)))
            .collect(Collectors.toSet());

        // 批量查看 redis 评论id,的点赞数据
        ItemLikeBO itemLikeBO = likeService.getItemLikeCount(new ArrayList<>(commentIdList), CommentTypeEnum.POST, Boolean.TRUE);
        Map<Long, Integer> itemLikeCount = itemLikeBO.getItemLikeCount();

        /**
         * 帖子id 对应,用户id 和 点赞状态 的 map
         */
        Map<Long, Map<Long, Boolean>> postToUserLikeMap = itemLikeBO.getItemIdToUserLikeMap();

        // 待修改的评论的点赞数量集合
        List<Post> updatePostList = new ArrayList<>();
        // 待插入的点赞集合
        List<Like> saveLikeList = new ArrayList<>();

        postToUserLikeMap.forEach((postId, likeMap) -> {
            Post post = new Post();
            post.setId(postId);
            post.setLikeCount(itemLikeCount.get(postId));
            updatePostList.add(post);

            likeMap.forEach((key, value) -> {
                Like like = new Like();
                like.setId(SnowFlakeUtil.getId());
                like.setItemId(postId);
                like.setUserId(key);
                like.setLike(value);
                like.setCreateTime(LocalDateTime.now());
                like.setUpdateTime(LocalDateTime.now());
                saveLikeList.add(like);
            });
        });

        Map<Long, Post> postMap = lambdaQuery()
            .in(Post::getId, postToUserLikeMap.keySet()).list()
            .stream().collect(Collectors.toMap(Post::getId, item -> item));
        // 点赞数量等于 数据库 + redis
        updatePostList.forEach(item ->
                               item.setLikeCount(item.getLikeCount() + postMap.get(item.getId()).getLikeCount()));

        String format = LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME);
        log.info(format + "-同步点赞数据:updatePostList={},saveLikeList={}",
                 JSON.toJSONString(updatePostList),
                 JSON.toJSONString(saveLikeList));

        // 修改数据库
        MyTransactionTemplate.execute(transactionTemplate, accept -> {
            CollUtil.split(updatePostList, 100).forEach(this::updateBatchById);
            CollUtil.split(saveLikeList, 100).forEach(likeService::saveOrUpdateByDuplicate);
        }, format + "-同步帖子点赞逻辑出错!");
    }
}
           

to ¶ä ̧ï1/4likeService.getItemLikeCount ç代ç ð±æ ̄ä ̧å3/4ä ̧第3ã4ã5çå ç°ï1/4å æ ̄ç¹èμé»è3/4çå ¬ç ̈æ¹æ³ï1/4åæ¹éç¹èμåæ¥ããå ¶å ç±»yyy¥å ̄¹å§3ã4ã5é»è3/4é1®/2æ°ç ̈è ̄¹æ³å®®®ç°çã代ç æä ̧ï1/4

3ï1/4getItemLikeCount æ¹æ³å®ç°

ä1/2ç1/2®ï1/4cn.j3code.community.service

java复制代码public interface LikeService extends IService<Like> {
    /**
     *
     * @param itemIdList 条目id集合
     * @param type 条目的数据类型
     * @param redisDataRemove 统计完后,是否移除redis中的数据
     * @return
     */
    ItemLikeBO getItemLikeCount(List<Long> itemIdList, CommentTypeEnum type, Boolean redisDataRemove);
}
@Slf4j
@AllArgsConstructor
@Service
public class LikeServiceImpl extends ServiceImpl<LikeMapper, Like>
    implements LikeService {

    private final RedisTemplate<String, Object> redisTemplate;

    @Override
    public ItemLikeBO getItemLikeCount(List<Long> itemIdList, CommentTypeEnum type, Boolean redisDataRemove) {
        ItemLikeBO itemLikeBO = new ItemLikeBO();
        Map<Long, Integer> result = new HashMap<>();
        itemIdList.forEach(itemId -> result.put(itemId, 0));

        // 批量查看 redis 评论id,的点赞数据
        List<Object> executePipelined = redisTemplate.executePipelined(new SessionCallback<>() {
            @Override
            public <K, V> Object execute(RedisOperations<K, V> redisOperations) throws DataAccessException {
                for (Long itemId : itemIdList) {
                    redisOperations.opsForHash().entries((K) SbUtil.getItemLikeKey(type.getValue() + ":" + itemId));
                }
                return null;
            }
        });

        /**
         * 评论id 对应,用户id 和 点赞状态 的 map
         */
        Map<Long, Map<Long, Boolean>> commentToUserLikeMap = new HashMap<>();
        for (int i = 0; i < itemIdList.size(); i++) {
            if (Objects.isNull(executePipelined.get(i))) {
                continue;
            }
            Long commentId = itemIdList.get(i);
            Map<String, Boolean> likeMap = (Map<String, Boolean>) executePipelined.get(i);

            Map<Long, Boolean> lb = new HashMap<>();
            likeMap.forEach((k, v) -> lb.put(Long.valueOf(k), v));

            commentToUserLikeMap.put(commentId, lb);


            if (redisDataRemove) {
                /**
                 * 这里会有一点点问题,就是如果在获取点赞 与 删除点赞 的时间空隙之间,同一个用户又操作了同一个评论的点赞
                 * 那这将会导致数据丢失
                 * 解决方法:
                 * 1、加锁
                 * 2、改用 MQ 方式
                 */
                // 删除一下 redis 中的 点赞 记录
                redisTemplate.opsForHash().delete(
                    SbUtil.getItemLikeKey(type.getValue() + ":" + commentId),
                    likeMap.keySet().stream().map(Object::toString).toArray(Object[]::new));
            }
        }

        for (int i = 0; i < itemIdList.size(); i++) {
            Map<Long, Boolean> redisUserLikeMap = commentToUserLikeMap.get(itemIdList.get(i));
            if (Objects.nonNull(redisUserLikeMap) && CollectionUtils.isNotEmpty(redisUserLikeMap.keySet())) {
                // 查询数据库中,该评论的点赞记录
                Map<Long, Boolean> dbUserLikeMap = lambdaQuery()
                    .eq(Like::getItemId, itemIdList.get(i))
                    .eq(Like::getLike, Boolean.TRUE)
                    .in(Like::getUserId, redisUserLikeMap.keySet())
                    .list().stream().collect(Collectors.toMap(Like::getUserId, Like::getLike));
                // redis 与 数据库点赞记录 结合
                AtomicInteger redisLikeCount = new AtomicInteger(0);
                for (Map.Entry<Long, Boolean> entry : redisUserLikeMap.entrySet()) {
                    // 数据库存在点赞,redis 取消点赞,那点赞数量减一
                    if (dbUserLikeMap.containsKey(entry.getKey()) && Boolean.FALSE.equals(entry.getValue())) {
                        redisLikeCount.set(redisLikeCount.get() - 1);
                    }
                    // 数据库存在点赞,redis 存在点赞,那 Redis 点赞状态变为 false,不记录总点赞数量中
                    if (dbUserLikeMap.containsKey(entry.getKey()) && Boolean.TRUE.equals(entry.getValue())) {
                        entry.setValue(Boolean.FALSE);
                    }
                }

                redisLikeCount.set(redisLikeCount.get() +
                                   Integer.parseInt(redisUserLikeMap.values().stream().filter(like -> like).count() + "")
                                  );
                result.put(itemIdList.get(i), redisLikeCount.get());
            }
        }

        itemLikeBO.setItemLikeCount(result);
        itemLikeBO.setItemIdToUserLikeMap(commentToUserLikeMap);
        return itemLikeBO;
    }
}
           

代ç å ç°çé»è3/4åºæ¬åæä ̧å3/4ä ̧ç»çæμç ̈ä ̧è ́ï1/4èä ̧æ代ç æ³ ̈éä¹åå3®/4æ ̧ æ¥ï1/4ç ̧ä¿¡ä1/2 们åºè ̄¥è1/2çæã

2.3 æ°æ®æ¥è ̄¢yy¡«ç¹èμè®°å1/2

é£æä ̧ä ̧ªåè1/2å°±æ ̄æ¥è ̄¢ä ̧å°æ çæ¶åæä"¬ä ̧ä" è¦å§MySQLä ̧çç¹èμæ°æ æºæ¥ï1/4è¿è¦æ Redis ä ̧çæ°æ ä ̧åæºæ¥è¿è¡ç»è£ ï1/4æ°æ°æ®®®®åæ3/4å°é¡μé¢ã

ä ̧è¿ï1/4è¿ä ̧ªé»è3/4ä ̧æ ̄å3/4é3/4ï1/4å ̧ºæäº 2.2 èçå ±æ¹æ³çå ç°ï1/4æ以è¿ä ̧æ¥å®°ä1/4åå3/4ç®åºã

åæè ̄¥é»è3/4ä¹åï1/4æ们å æ¥ççï1/4é¡μé¢è¦æ3/4示ç¹èμçé£äºæ°æ®ï1/4é¡μé¢å¦ä ̧ï1/4

Like function, analysis and implementation, practical!
  • å1/2åç»å1/2人ç¹èμç¶æ
  • ä ̧å¡æ°æ çç¹èμæ®°é

okï1/4æ们å æ¥åæç ̈æ·ç¹èμç¶æçè·åæμç ̈ï1/4

  1. å°æ¥è ̄¢å°çä ̧å°æ°æ éåè1/2¬ä ̧º Map<ä ̧å¡IDï1/4ç ̈æ·ç¹èμç¶æ>ï1/4yå§æ®åμä ̧ï1/4ç¶æé1/2ä ̧º false
  2. ®I don't want to IDãç ̈æ· IDãç¹èμç¶æï1/4trueï1/4ï1/4æ¥è ̄¢MySQLçç¹èμè®°å1/2ï1/4å°å ̄¹yºçæ°æ yy¡«oh° 第ä ̧殥ç Map ̧
  3. åéè¿ç¡éæä1/2ï1/4æ¹éè¿é® Redisï1/4æ3/4å° hash keyä ̧ºä ̧å¡idï1/4å±æ§ key ä ̧º ç ̈æ·id çùèμè°å1/2ï1/4å°æ3/4å°çæ°æ ç® ́殥议¦çå° Map ä ̧
  4. æç»ï1/4Map ä ̧çæ°æ ï1/4å°±æ ̄ç ̈æ·æ ̄å¦ç¹èμä ̧å¡çæ®è ̄äºã

èç¹èμæ°éæå°±ä ̧åæä ̧å¡æμç ̈äºï1/4å ̧º 2.2 èå·²ç»yæ¿ä°±æ ̄ getItemLikeCount æ¹æ³çå ðï1/4åªä ̧è¿éç redisDataRemove åæ°ä ̧º false ï1/4ä ̧éè¦ç§»é¤ redis çæ°æ ï1/4å®̧ºè¿æ ̄ä ̧ä ̧ªæ¥è ̄¢ï1/4èä ̧æ ̄å殥ã

ä ̧é¢ï1/4æ¥ççæçæ¥è ̄¢ä ̧å¡æ°æ®åè¡ ̈ä1/4ªä»£ç å®ç°ï1/4

ç ̈ä1/4ªä»£ç to®ðæ ̄to ̧ºæ¥è ̄¢çæμç ̈åºæ¬å°±æ ̄å æ¥ä̧å¡æ°æ ï1/4ç¶yyy¡«ç¹èμæ°æ ï1/4èåä ̧æ¥ oh±æ²¡y¿ èy¶® è ̄ ́äºï1/4æç ̈ä1/4ªä»£ç ä1/2ç°yyġ«ç¹èμçæ°æ å³å ̄ï1/4ç̧ä¿¡Ť§å®¶é1/2æã®®®
java复制代码public IPage<PostVO> page(PostPageRequest request) {

    // 先查询 MySQL 的业务数据

    // 用户评论点赞状态
    Map<Long, Boolean> itemIdToLikeMap = likeService.getItemLikeState(voiPage.getRecords().stream().map(PostVO::getId).collect(Collectors.toList()), CommentTypeEnum.POST);
    // redis 中评论点赞数量
    Map<Long, Integer> itemIdToLikeCountMap = likeService.getItemLikeCount(voiPage.getRecords().stream().map(PostVO::getId).collect(Collectors.toList()), CommentTypeEnum.POST, Boolean.FALSE)
        .getItemLikeCount();

    // 填充评论点赞数量及当前用户点赞状态,用户信息
    业务数据列表.forEach(postVO -> {
        设置业务数据点赞数据(业务MySQL点赞数量 + itemIdToLikeCountMap.get(postVO.getId()));
        设置业务数据用户点赞状态(itemIdToLikeMap.get(postVO.getId()));
    });

}
           

getItemLikeState æ¹æ³å®ç°ï1/4

java复制代码public Map<Long, Boolean> getItemLikeState(List<Long> itemIdList, CommentTypeEnum type) {
    Map<Long, Boolean> result = new HashMap<>();
    itemIdList.forEach(itemId -> result.put(itemId, Boolean.FALSE));

    if (Objects.isNull(SecurityUtil.getUserId())) {
        // 未登录
        return result;
    }

    if (CollectionUtils.isEmpty(itemIdList)) {
        return result;
    }

    // 查看数据库中是否有用户点赞记录
    lambdaQuery()
        .eq(Like::getUserId, SecurityUtil.getUserId())
        .eq(Like::getLike, Boolean.TRUE)
        .in(Like::getItemId, itemIdList)
        .list().forEach(likeObj -> {
        if (likeObj.getLike()) {
            result.put(likeObj.getItemId(), Boolean.TRUE);
        }
    });

    // 查看 redis 用户点赞记录
    List<Object> executePipelined = redisTemplate.executePipelined(new SessionCallback<>() {
        @Override
        public <K, V> Object execute(RedisOperations<K, V> redisOperations) throws DataAccessException {
            for (Long itemId : itemIdList) {
                redisOperations.opsForHash().get(
                    (K) SbUtil.getItemLikeKey(type.getValue() + ":" + itemId),
                    SecurityUtil.getUserId().toString());
            }
            return null;
        }
    });

    for (int i = 0; i < itemIdList.size(); i++) {
        if (Objects.nonNull(executePipelined.get(i))) {
            result.put(itemIdList.get(i), (Boolean) executePipelined.get(i));
        }
    }

    return result;
}
           

è¿ä ̧ªæ¹æ³ï1/4å°±æ ̄æä ̧é¢åæç代ç å®ç°äºã

å°æ¤ï1/4æä"¬æ ́ä ̧ªçä ̧ä ̧ªä ̧å¡ç¹èμåè1/2çå ç°å°±ç to æäºï1/4å1/2ç¶å ¶ä ̧è ̄å æ ̄æä®̧宥1/2çç¹åéè¦æ©to®ç箹ï1/4

  • ä ̧å¥1/2ç¹ï1/4å¦ä1/2解å³æ¥è ̄¢ Redis æ°æ ä ̧ç§»é¤ Redis æ°æ ä¹é ́yňçæ°æ å·®ï1/4æè è ̄ ́y°æ®®®¶é ́é ́é
  • ä ̧å¥1/2çç¹ï1/4å¦ä1/2解å³y楹èμæ°æ å°MySQLåºéï1/4èå ̄1/4è ́ç¹èμè®°å1/2ä ̧¢å¤±é 鮢ï1/4ç®åï1/4殥å¿æ¢å¤ï1/4
  • ©æ å çç¹ï1/4ç ̈æ·ç¹èμä¹åï1/4å ̄以åéç ̧å ³ç MQ æ¶æ ̄ï1/4åç¥ç ̈æ·ï1/4æ示ç ̈æ·ä¿¡æ ̄æç¥åº¦

y¦æ¿ä§å¶æå¥1/2ç建è ï1/4å® ̄以è ̄论éèèã®®