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 @@ 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 + + + + update t_comment + set child_comment_total = child_comment_total + #{count}, + update_time = now() + where id = #{parentId} + and level = 1 + + \ 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 } ### 批量添加评论