diff --git a/.idea/MyBatisCodeHelperDatasource.xml b/.idea/MyBatisCodeHelperDatasource.xml
index efe123d..78337bc 100644
--- a/.idea/MyBatisCodeHelperDatasource.xml
+++ b/.idea/MyBatisCodeHelperDatasource.xml
@@ -7,15 +7,13 @@
-
-
-
-
-
-
-
+
+
+
+
+
@@ -146,7 +144,7 @@
-
+
@@ -429,7 +427,7 @@
-
+
diff --git a/han-note-comment/han-note-comment-biz/src/main/java/com/hanserwei/hannote/comment/biz/consumer/Comment2DBConsumer.java b/han-note-comment/han-note-comment-biz/src/main/java/com/hanserwei/hannote/comment/biz/consumer/Comment2DBConsumer.java
index 2880692..9f569dd 100644
--- a/han-note-comment/han-note-comment-biz/src/main/java/com/hanserwei/hannote/comment/biz/consumer/Comment2DBConsumer.java
+++ b/han-note-comment/han-note-comment-biz/src/main/java/com/hanserwei/hannote/comment/biz/consumer/Comment2DBConsumer.java
@@ -197,10 +197,12 @@ public class Comment2DBConsumer {
// 如果批量插入的行数大于 0
if (Objects.nonNull(insertedRows) && insertedRows > 0) {
// 构建发送给计数服务的 DTO 集合
- List countPublishCommentMqDTOS = publishCommentMqDTOS.stream()
- .map(publishCommentMqDTO -> CountPublishCommentMqDTO.builder()
- .noteId(publishCommentMqDTO.getNoteId())
- .commentId(publishCommentMqDTO.getCommentId())
+ List countPublishCommentMqDTOS = commentBOS.stream()
+ .map(commentBO -> CountPublishCommentMqDTO.builder()
+ .noteId(commentBO.getNoteId())
+ .commentId(commentBO.getId())
+ .level(commentBO.getLevel())
+ .parentId(commentBO.getParentId())
.build())
.toList();
diff --git a/han-note-comment/han-note-comment-biz/src/main/java/com/hanserwei/hannote/comment/biz/model/dto/CountPublishCommentMqDTO.java b/han-note-comment/han-note-comment-biz/src/main/java/com/hanserwei/hannote/comment/biz/model/dto/CountPublishCommentMqDTO.java
index bab5628..940eac2 100644
--- a/han-note-comment/han-note-comment-biz/src/main/java/com/hanserwei/hannote/comment/biz/model/dto/CountPublishCommentMqDTO.java
+++ b/han-note-comment/han-note-comment-biz/src/main/java/com/hanserwei/hannote/comment/biz/model/dto/CountPublishCommentMqDTO.java
@@ -21,4 +21,14 @@ public class CountPublishCommentMqDTO {
*/
private Long commentId;
+ /**
+ * 评论级别
+ */
+ private Integer level;
+
+ /**
+ * 父 ID
+ */
+ private Long parentId;
+
}
\ No newline at end of file
diff --git a/han-note-count/han-note-count-biz/src/main/java/com/hanserwei/hannote/count/biz/consumer/CountNoteChildCommentConsumer.java b/han-note-count/han-note-count-biz/src/main/java/com/hanserwei/hannote/count/biz/consumer/CountNoteChildCommentConsumer.java
new file mode 100644
index 0000000..46b7f4d
--- /dev/null
+++ b/han-note-count/han-note-count-biz/src/main/java/com/hanserwei/hannote/count/biz/consumer/CountNoteChildCommentConsumer.java
@@ -0,0 +1,80 @@
+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.CommentDOMapper;
+import com.hanserwei.hannote.count.biz.enums.CommentLevelEnum;
+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.Objects;
+import java.util.stream.Collectors;
+
+@Component
+@RocketMQMessageListener(consumerGroup = "han_note_group_child_comment_total" + MQConstants.TOPIC_COUNT_NOTE_COMMENT, // Group 组
+ topic = MQConstants.TOPIC_COUNT_NOTE_COMMENT // 主题 Topic
+)
+@Slf4j
+public class CountNoteChildCommentConsumer implements RocketMQListener {
+
+ @Resource
+ private CommentDOMapper commentDOMapper;
+
+ private final BufferTrigger bufferTrigger = BufferTrigger.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 bodys) {
+ log.info("==> 【笔记二级评论数】聚合消息, size: {}", bodys.size());
+ log.info("==> 【笔记二级评论数】聚合消息, {}", JsonUtils.toJsonString(bodys));
+
+ // 将聚合后的消息体 Json 转 List
+ List countPublishCommentMqDTOList = Lists.newArrayList();
+ bodys.forEach(body -> {
+ try {
+ List list = JsonUtils.parseList(body, CountPublishCommentMqDTO.class);
+ countPublishCommentMqDTOList.addAll(list);
+ } catch (Exception e) {
+ log.error("", e);
+ }
+ });
+
+ // 过滤出二级评论,并按 parent_id 分组
+ Map> groupMap = countPublishCommentMqDTOList.stream()
+ .filter(commentMqDTO -> Objects.equals(CommentLevelEnum.TWO.getCode(), commentMqDTO.getLevel()))
+ .collect(Collectors.groupingBy(CountPublishCommentMqDTO::getParentId)); // 按 parent_id 分组
+
+ // 若无二级评论,则直接 return
+ if (CollUtil.isEmpty(groupMap)) return;
+
+ // 循环分组字典
+ for (Map.Entry> entry : groupMap.entrySet()) {
+ // 一级评论 ID
+ Long parentId = entry.getKey();
+ // 评论数
+ int count = CollUtil.size(entry.getValue());
+
+ // 更新一级评论的下级评论总数,进行累加操作
+ commentDOMapper.updateChildCommentTotal(parentId, count);
+ }
+ }
+}
\ No newline at end of file
diff --git a/han-note-count/han-note-count-biz/src/main/java/com/hanserwei/hannote/count/biz/domain/dataobject/CommentDO.java b/han-note-count/han-note-count-biz/src/main/java/com/hanserwei/hannote/count/biz/domain/dataobject/CommentDO.java
new file mode 100644
index 0000000..7ca6daa
--- /dev/null
+++ b/han-note-count/han-note-count-biz/src/main/java/com/hanserwei/hannote/count/biz/domain/dataobject/CommentDO.java
@@ -0,0 +1,118 @@
+package com.hanserwei.hannote.count.biz.domain.dataobject;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.time.LocalDateTime;
+
+/**
+ * 评论表
+ */
+@Data
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+@TableName(value = "t_comment")
+public class CommentDO {
+ /**
+ * id
+ */
+ @TableId(value = "id", type = IdType.AUTO)
+ private Long id;
+
+ /**
+ * 关联的笔记ID
+ */
+ @TableField(value = "note_id")
+ private Long noteId;
+
+ /**
+ * 发布者用户ID
+ */
+ @TableField(value = "user_id")
+ private Long userId;
+
+ /**
+ * 评论内容UUID
+ */
+ @TableField(value = "content_uuid")
+ private String contentUuid;
+
+ /**
+ * 内容是否为空(0:不为空 1:为空)
+ */
+ @TableField(value = "is_content_empty")
+ private Boolean isContentEmpty;
+
+ /**
+ * 评论附加图片URL
+ */
+ @TableField(value = "image_url")
+ private String imageUrl;
+
+ /**
+ * 级别(1:一级评论 2:二级评论)
+ */
+ @TableField(value = "`level`")
+ private Integer level;
+
+ /**
+ * 评论被回复次数,仅一级评论需要
+ */
+ @TableField(value = "reply_total")
+ private Long replyTotal;
+
+ /**
+ * 评论被点赞次数
+ */
+ @TableField(value = "like_total")
+ private Long likeTotal;
+
+ /**
+ * 父ID (若是对笔记的评论,则此字段存储笔记ID; 若是二级评论,则此字段存储一级评论的ID)
+ */
+ @TableField(value = "parent_id")
+ private Long parentId;
+
+ /**
+ * 回复哪个的评论 (0表示是对笔记的评论,若是对他人评论的回复,则存储回复评论的ID)
+ */
+ @TableField(value = "reply_comment_id")
+ private Long replyCommentId;
+
+ /**
+ * 回复的哪个用户, 存储用户ID
+ */
+ @TableField(value = "reply_user_id")
+ private Long replyUserId;
+
+ /**
+ * 是否置顶(0:不置顶 1:置顶)
+ */
+ @TableField(value = "is_top")
+ private Boolean isTop;
+
+ /**
+ * 创建时间
+ */
+ @TableField(value = "create_time")
+ private LocalDateTime createTime;
+
+ /**
+ * 更新时间
+ */
+ @TableField(value = "update_time")
+ private LocalDateTime updateTime;
+
+ /**
+ * 二级评论总数(只有一级评论才需要统计)
+ */
+ @TableField(value = "child_comment_total")
+ private Long childCommentTotal;
+}
\ No newline at end of file
diff --git a/han-note-count/han-note-count-biz/src/main/java/com/hanserwei/hannote/count/biz/domain/mapper/CommentDOMapper.java b/han-note-count/han-note-count-biz/src/main/java/com/hanserwei/hannote/count/biz/domain/mapper/CommentDOMapper.java
new file mode 100644
index 0000000..5acd9fa
--- /dev/null
+++ b/han-note-count/han-note-count-biz/src/main/java/com/hanserwei/hannote/count/biz/domain/mapper/CommentDOMapper.java
@@ -0,0 +1,19 @@
+package com.hanserwei.hannote.count.biz.domain.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.hanserwei.hannote.count.biz.domain.dataobject.CommentDO;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+@Mapper
+public interface CommentDOMapper extends BaseMapper {
+
+ /**
+ * 更新一级评论的子评论总数
+ *
+ * @param parentId 一级评论 ID
+ * @param count 子评论数
+ * @return 更新结果
+ */
+ int updateChildCommentTotal(@Param("parentId") Long parentId, @Param("count") int count);
+}
\ No newline at end of file
diff --git a/han-note-count/han-note-count-biz/src/main/java/com/hanserwei/hannote/count/biz/enums/CommentLevelEnum.java b/han-note-count/han-note-count-biz/src/main/java/com/hanserwei/hannote/count/biz/enums/CommentLevelEnum.java
new file mode 100644
index 0000000..607bd25
--- /dev/null
+++ b/han-note-count/han-note-count-biz/src/main/java/com/hanserwei/hannote/count/biz/enums/CommentLevelEnum.java
@@ -0,0 +1,17 @@
+package com.hanserwei.hannote.count.biz.enums;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+@Getter
+@AllArgsConstructor
+public enum CommentLevelEnum {
+ // 一级评论
+ ONE(1),
+ // 二级评论
+ TWO(2),
+ ;
+
+ private final Integer code;
+
+}
\ No newline at end of file
diff --git a/han-note-count/han-note-count-biz/src/main/java/com/hanserwei/hannote/count/biz/model/dto/CountPublishCommentMqDTO.java b/han-note-count/han-note-count-biz/src/main/java/com/hanserwei/hannote/count/biz/model/dto/CountPublishCommentMqDTO.java
index c79ae7b..ea6c20d 100644
--- a/han-note-count/han-note-count-biz/src/main/java/com/hanserwei/hannote/count/biz/model/dto/CountPublishCommentMqDTO.java
+++ b/han-note-count/han-note-count-biz/src/main/java/com/hanserwei/hannote/count/biz/model/dto/CountPublishCommentMqDTO.java
@@ -21,4 +21,14 @@ public class CountPublishCommentMqDTO {
*/
private Long commentId;
+ /**
+ * 评论级别
+ */
+ private Integer level;
+
+ /**
+ * 父 ID
+ */
+ private Long parentId;
+
}
\ No newline at end of file
diff --git a/han-note-count/han-note-count-biz/src/main/resources/mapperxml/CommentDOMapper.xml b/han-note-count/han-note-count-biz/src/main/resources/mapperxml/CommentDOMapper.xml
new file mode 100644
index 0000000..e1074e1
--- /dev/null
+++ b/han-note-count/han-note-count-biz/src/main/resources/mapperxml/CommentDOMapper.xml
@@ -0,0 +1,51 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ id,
+ note_id,
+ user_id,
+ content_uuid,
+ is_content_empty,
+ image_url,
+ `level`,
+ reply_total,
+ like_total,
+ parent_id,
+ reply_comment_id,
+ reply_user_id,
+ is_top,
+ create_time,
+ update_time,
+ child_comment_total
+
+
+
+
\ No newline at end of file
diff --git a/http-client/gateApi.http b/http-client/gateApi.http
index d91cf32..9fce570 100644
--- a/http-client/gateApi.http
+++ b/http-client/gateApi.http
@@ -298,8 +298,9 @@ Authorization: Bearer {{token}}
{
"noteId": 1862481582414102549,
- "content": "这是一条测试评论计数的评论111",
- "imageUrl": "https://cdn.pixabay.com/photo/2025/10/05/15/06/autumn-9875155_1280.jpg"
+ "content": "这是一条测试评论计数的二级评论333",
+ "imageUrl": "https://cdn.pixabay.com/photo/2025/10/05/15/06/autumn-9875155_1280.jpg",
+ "replyCommentId": 4002
}
### 批量添加评论