feat(count): 实现评论发布后异步更新笔记评论数功能

- 新增 CountPublishCommentMqDTO 用于传输评论计数消息
- 在评论服务中添加异步发送评论计数消息逻辑
- 新建 CountNoteCommentConsumer 消费评论计数消息并批量更新笔记评论数
- 扩展 t_comment 表结构,新增 child_comment_total 字段
- 更新 MQ 常量配置,添加评论计数相关 Topic 定义
- 调整 LIKE/UNLIKE 和 COLLECT/UNCOLLECT 消费者中的注解使用(防止循环依赖)
- 修改 gateApi.http 中的测试用例内容以适配新功能
This commit is contained in:
2025-11-07 17:13:01 +08:00
parent f49d0e6b76
commit 63495b4938
12 changed files with 196 additions and 18 deletions

View File

@@ -2,6 +2,11 @@ package com.hanserwei.hannote.count.biz.constant;
public interface MQConstants {
/**
* Topic: 笔记评论总数计数
*/
String TOPIC_COUNT_NOTE_COMMENT = "CountNoteCommentTopic";
/**
* Topic: 计数 - 笔记点赞数
*/
@@ -32,21 +37,11 @@ public interface MQConstants {
*/
String TOPIC_COUNT_FOLLOWING_2_DB = "CountFollowing2DBTopic";
/**
* Topic: 计数 - 笔记点赞数
*/
String TOPIC_COUNT_NOTE_LIKE = "CountNoteLikeTopic";
/**
* Topic: 计数 - 笔记点赞数落库
*/
String TOPIC_COUNT_NOTE_LIKE_2_DB = "CountNoteLike2DBTTopic";
/**
* Topic: 计数 - 笔记收藏数
*/
String TOPIC_COUNT_NOTE_COLLECT = "CountNoteCollectTopic";
/**
* Topic: 计数 - 笔记收藏数落库
*/

View File

@@ -0,0 +1,76 @@
package com.hanserwei.hannote.count.biz.consumer;
import cn.hutool.core.collection.CollUtil;
import com.github.phantomthief.collection.BufferTrigger;
import com.google.common.collect.Lists;
import com.hanserwei.framework.common.utils.JsonUtils;
import com.hanserwei.hannote.count.biz.constant.MQConstants;
import com.hanserwei.hannote.count.biz.domain.mapper.NoteCountDOMapper;
import com.hanserwei.hannote.count.biz.model.dto.CountPublishCommentMqDTO;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
import org.apache.rocketmq.spring.core.RocketMQListener;
import org.springframework.stereotype.Component;
import java.time.Duration;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@Component
@RocketMQMessageListener(consumerGroup = "han_note_group_" + MQConstants.TOPIC_COUNT_NOTE_COMMENT, // Group 组
topic = MQConstants.TOPIC_COUNT_NOTE_COMMENT // 主题 Topic
)
@Slf4j
public class CountNoteCommentConsumer implements RocketMQListener<String> {
@Resource
private NoteCountDOMapper noteCountDOMapper;
private final BufferTrigger<String> bufferTrigger = BufferTrigger.<String>batchBlocking()
.bufferSize(50000) // 缓存队列的最大容量
.batchSize(1000) // 一批次最多聚合 1000 条
.linger(Duration.ofSeconds(1)) // 多久聚合一次1s 一次)
.setConsumerEx(this::consumeMessage) // 设置消费者方法
.build();
@Override
public void onMessage(String body) {
// 往 bufferTrigger 中添加元素
bufferTrigger.enqueue(body);
}
private void consumeMessage(List<String> bodys) {
log.info("==> 【笔记评论数】聚合消息, size: {}", bodys.size());
log.info("==> 【笔记评论数】聚合消息, {}", JsonUtils.toJsonString(bodys));
// 将聚合后的消息体 Json 转 List<CountPublishCommentMqDTO>
List<CountPublishCommentMqDTO> countPublishCommentMqDTOList = Lists.newArrayList();
bodys.forEach(body -> {
try {
List<CountPublishCommentMqDTO> list = JsonUtils.parseList(body, CountPublishCommentMqDTO.class);
countPublishCommentMqDTOList.addAll(list);
} catch (Exception e) {
log.error("", e);
}
});
// 按笔记 ID 进行分组
Map<Long, List<CountPublishCommentMqDTO>> groupMap = countPublishCommentMqDTOList.stream()
.collect(Collectors.groupingBy(CountPublishCommentMqDTO::getNoteId));
// 循环分组字典
for (Map.Entry<Long, List<CountPublishCommentMqDTO>> entry : groupMap.entrySet()) {
// 笔记 ID
Long noteId = entry.getKey();
// 评论数
int count = CollUtil.size(entry.getValue());
// 若评论数大于零,则执行更新操作:累加评论总数
if (count > 0) {
noteCountDOMapper.insertOrUpdateCommentTotalByNoteId(count, noteId);
}
}
}
}

View File

@@ -25,4 +25,13 @@ public interface NoteCountDOMapper extends BaseMapper<NoteCountDO> {
* @return 影响行数
*/
int insertOrUpdateCollectTotalByNoteId(@Param("count") Integer count, @Param("noteId") Long noteId);
/**
* 添加记录或更新笔记评论数
*
* @param count 评论数
* @param noteId 笔记ID
* @return 影响行数
*/
int insertOrUpdateCommentTotalByNoteId(@Param("count") int count, @Param("noteId") Long noteId);
}

View File

@@ -0,0 +1,24 @@
package com.hanserwei.hannote.count.biz.model.dto;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class CountPublishCommentMqDTO {
/**
* 笔记 ID
*/
private Long noteId;
/**
* 评论 ID
*/
private Long commentId;
}

View File

@@ -26,4 +26,10 @@
VALUES (#{noteId}, #{count})
ON DUPLICATE KEY UPDATE collect_total = collect_total + (#{count});
</insert>
<insert id="insertOrUpdateCommentTotalByNoteId" parameterType="map">
INSERT INTO t_note_count (note_id, comment_total)
VALUES (#{noteId}, #{count})
ON DUPLICATE KEY UPDATE comment_total = comment_total + (#{count});
</insert>
</mapper>