feat(count): 实现评论计数功能支持二级评论统计
- 新增评论数据对象 CommentDO 及其 MyBatis 映射配置 - 新增评论级别枚举 CommentLevelEnum 区分一级与二级评论 - 新增 CountNoteChildCommentConsumer 消费 MQ 消息并更新子评论总数 - 修改 CountPublishCommentMqDTO 增加 level 和 parentId 字段以支持层级识别 - 调整 Comment2DBConsumer 中构造 CountPublishCommentMqDTO 的逻辑,使用 commentBO 提取完整信息 - 配置 MyBatis Code Helper 插件指向新的 han-note-count 模块路径 - 更新 gateApi.http 测试接口示例,添加 replyCommentId 参数用于模拟二级评论发布
This commit is contained in:
16
.idea/MyBatisCodeHelperDatasource.xml
generated
16
.idea/MyBatisCodeHelperDatasource.xml
generated
@@ -7,15 +7,13 @@
|
||||
<option name="customizedLombokAnnotation" value="true" />
|
||||
<option name="customizedLombokValue" value="@lombok.Builder" />
|
||||
<option name="deleteByPrimayKeyEnabled" value="false" />
|
||||
<option name="generateService" value="true" />
|
||||
<option name="generateServiceInterface" value="true" />
|
||||
<option name="insertMethodEnabled" value="false" />
|
||||
<option name="insertSelectiveMethodEnabled" value="false" />
|
||||
<option name="javaMapperPackage" value="com.hanserwei.hannote.comment.biz.domain.mapper" />
|
||||
<option name="javaMapperPath" value="$PROJECT_DIR$/han-note-comment/han-note-comment-biz/src/main/java" />
|
||||
<option name="javaModelPackage" value="com.hanserwei.hannote.comment.biz.domain.dataobject" />
|
||||
<option name="javaModelPath" value="$PROJECT_DIR$/han-note-comment/han-note-comment-biz/src/main/java" />
|
||||
<option name="lastDatabaseCrudChooseModuleName" value="han-note-comment-biz" />
|
||||
<option name="javaMapperPackage" value="com.hanserwei.hannote.count.biz.domain.mapper" />
|
||||
<option name="javaMapperPath" value="$PROJECT_DIR$/han-note-count/han-note-count-biz/src/main/java" />
|
||||
<option name="javaModelPackage" value="com.hanserwei.hannote.count.biz.domain.dataobject" />
|
||||
<option name="javaModelPath" value="$PROJECT_DIR$/han-note-count/han-note-count-biz/src/main/java" />
|
||||
<option name="lastDatabaseCrudChooseModuleName" value="han-note-count-biz" />
|
||||
<option name="lombokAllArgConstructor" value="true" />
|
||||
<option name="lombokDataAnnotation" value="true" />
|
||||
<option name="lombokNoArgsConstructor" value="true" />
|
||||
@@ -146,7 +144,7 @@
|
||||
<option name="insertMethodEnabled" value="false" />
|
||||
<option name="insertSelectiveMethodEnabled" value="false" />
|
||||
<option name="javaModelName" value="CommentDO" />
|
||||
<option name="moduleName" value="han-note-comment-biz" />
|
||||
<option name="moduleName" value="han-note-count-biz" />
|
||||
<option name="mybatisplusIdType" value="AUTO" />
|
||||
<option name="selectByPrimaryKeyEnabled" value="false" />
|
||||
<option name="sequenceColumn" value="" />
|
||||
@@ -429,7 +427,7 @@
|
||||
<option name="updateByPrimaykeyEnabled" value="false" />
|
||||
<option name="userMybatisPlus" value="true" />
|
||||
<option name="xmlMapperPackage" value="mapperxml" />
|
||||
<option name="xmlMapperPath" value="$PROJECT_DIR$/han-note-comment/han-note-comment-biz/src/main/resources" />
|
||||
<option name="xmlMapperPath" value="$PROJECT_DIR$/han-note-count/han-note-count-biz/src/main/resources" />
|
||||
</ProjectProfile>
|
||||
</option>
|
||||
</component>
|
||||
|
||||
@@ -197,10 +197,12 @@ public class Comment2DBConsumer {
|
||||
// 如果批量插入的行数大于 0
|
||||
if (Objects.nonNull(insertedRows) && insertedRows > 0) {
|
||||
// 构建发送给计数服务的 DTO 集合
|
||||
List<CountPublishCommentMqDTO> countPublishCommentMqDTOS = publishCommentMqDTOS.stream()
|
||||
.map(publishCommentMqDTO -> CountPublishCommentMqDTO.builder()
|
||||
.noteId(publishCommentMqDTO.getNoteId())
|
||||
.commentId(publishCommentMqDTO.getCommentId())
|
||||
List<CountPublishCommentMqDTO> countPublishCommentMqDTOS = commentBOS.stream()
|
||||
.map(commentBO -> CountPublishCommentMqDTO.builder()
|
||||
.noteId(commentBO.getNoteId())
|
||||
.commentId(commentBO.getId())
|
||||
.level(commentBO.getLevel())
|
||||
.parentId(commentBO.getParentId())
|
||||
.build())
|
||||
.toList();
|
||||
|
||||
|
||||
@@ -21,4 +21,14 @@ public class CountPublishCommentMqDTO {
|
||||
*/
|
||||
private Long commentId;
|
||||
|
||||
/**
|
||||
* 评论级别
|
||||
*/
|
||||
private Integer level;
|
||||
|
||||
/**
|
||||
* 父 ID
|
||||
*/
|
||||
private Long parentId;
|
||||
|
||||
}
|
||||
@@ -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<String> {
|
||||
|
||||
@Resource
|
||||
private CommentDOMapper commentDOMapper;
|
||||
|
||||
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);
|
||||
}
|
||||
});
|
||||
|
||||
// 过滤出二级评论,并按 parent_id 分组
|
||||
Map<Long, List<CountPublishCommentMqDTO>> 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<Long, List<CountPublishCommentMqDTO>> entry : groupMap.entrySet()) {
|
||||
// 一级评论 ID
|
||||
Long parentId = entry.getKey();
|
||||
// 评论数
|
||||
int count = CollUtil.size(entry.getValue());
|
||||
|
||||
// 更新一级评论的下级评论总数,进行累加操作
|
||||
commentDOMapper.updateChildCommentTotal(parentId, count);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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<CommentDO> {
|
||||
|
||||
/**
|
||||
* 更新一级评论的子评论总数
|
||||
*
|
||||
* @param parentId 一级评论 ID
|
||||
* @param count 子评论数
|
||||
* @return 更新结果
|
||||
*/
|
||||
int updateChildCommentTotal(@Param("parentId") Long parentId, @Param("count") int count);
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -21,4 +21,14 @@ public class CountPublishCommentMqDTO {
|
||||
*/
|
||||
private Long commentId;
|
||||
|
||||
/**
|
||||
* 评论级别
|
||||
*/
|
||||
private Integer level;
|
||||
|
||||
/**
|
||||
* 父 ID
|
||||
*/
|
||||
private Long parentId;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.hanserwei.hannote.count.biz.domain.mapper.CommentDOMapper">
|
||||
<resultMap id="BaseResultMap" type="com.hanserwei.hannote.count.biz.domain.dataobject.CommentDO">
|
||||
<!--@mbg.generated-->
|
||||
<!--@Table t_comment-->
|
||||
<id column="id" jdbcType="BIGINT" property="id"/>
|
||||
<result column="note_id" jdbcType="BIGINT" property="noteId"/>
|
||||
<result column="user_id" jdbcType="BIGINT" property="userId"/>
|
||||
<result column="content_uuid" jdbcType="VARCHAR" property="contentUuid"/>
|
||||
<result column="is_content_empty" jdbcType="BIT" property="isContentEmpty"/>
|
||||
<result column="image_url" jdbcType="VARCHAR" property="imageUrl"/>
|
||||
<result column="level" jdbcType="TINYINT" property="level"/>
|
||||
<result column="reply_total" jdbcType="BIGINT" property="replyTotal"/>
|
||||
<result column="like_total" jdbcType="BIGINT" property="likeTotal"/>
|
||||
<result column="parent_id" jdbcType="BIGINT" property="parentId"/>
|
||||
<result column="reply_comment_id" jdbcType="BIGINT" property="replyCommentId"/>
|
||||
<result column="reply_user_id" jdbcType="BIGINT" property="replyUserId"/>
|
||||
<result column="is_top" jdbcType="TINYINT" property="isTop"/>
|
||||
<result column="create_time" jdbcType="TIMESTAMP" property="createTime"/>
|
||||
<result column="update_time" jdbcType="TIMESTAMP" property="updateTime"/>
|
||||
<result column="child_comment_total" jdbcType="BIGINT" property="childCommentTotal"/>
|
||||
</resultMap>
|
||||
<sql id="Base_Column_List">
|
||||
<!--@mbg.generated-->
|
||||
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
|
||||
</sql>
|
||||
|
||||
<update id="updateChildCommentTotal" parameterType="map">
|
||||
update t_comment
|
||||
set child_comment_total = child_comment_total + #{count},
|
||||
update_time = now()
|
||||
where id = #{parentId}
|
||||
and level = 1
|
||||
</update>
|
||||
</mapper>
|
||||
@@ -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
|
||||
}
|
||||
|
||||
### 批量添加评论
|
||||
|
||||
Reference in New Issue
Block a user