feat(comment): 实现评论异步消费与内容存储

- 新增评论内容批量存储接口与实现
- 实现MQ消息消费端处理评论发布逻辑
- 支持一级与二级评论的层级关系构建
- 添加评论内容与元数据分离存储机制
- 集成分布式ID生成服务用于评论ID生成
- 完善评论相关DTO、DO、BO模型类
- 添加Cassandra数据库操作支持
- 实现Feign接口调用与事务控制
This commit is contained in:
2025-11-05 19:19:19 +08:00
parent c37b16ff42
commit a37e76c87c
22 changed files with 575 additions and 4 deletions

View File

@@ -3,6 +3,7 @@ package com.hanserwei.hannote.kv.api;
import com.hanserwei.framework.common.response.Response;
import com.hanserwei.hannote.kv.constant.ApiConstants;
import com.hanserwei.hannote.kv.dto.req.AddNoteContentReqDTO;
import com.hanserwei.hannote.kv.dto.req.BatchAddCommentContentReqDTO;
import com.hanserwei.hannote.kv.dto.req.DeleteNoteContentReqDTO;
import com.hanserwei.hannote.kv.dto.req.FindNoteContentReqDTO;
import com.hanserwei.hannote.kv.dto.resp.FindNoteContentRspDTO;
@@ -24,4 +25,7 @@ public interface KeyValueFeignApi {
@PostMapping(value = PREFIX + "/note/content/delete")
Response<?> deleteNoteContent(@RequestBody DeleteNoteContentReqDTO deleteNoteContentReqDTO);
@PostMapping(value = PREFIX + "/comment/content/batchAdd")
Response<?> batchAddCommentContent(@RequestBody BatchAddCommentContentReqDTO batchAddCommentContentReqDTO);
}

View File

@@ -0,0 +1,21 @@
package com.hanserwei.hannote.kv.dto.req;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotEmpty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class BatchAddCommentContentReqDTO {
@Valid
@NotEmpty(message = "评论内容集合不能为空")
private List<CommentContentReqDTO> comments;
}

View File

@@ -0,0 +1,26 @@
package com.hanserwei.hannote.kv.dto.req;
import jakarta.validation.constraints.NotNull;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class CommentContentReqDTO {
@NotNull(message = "笔记noteId不能为空")
private Long noteId;
@NotNull(message = "发布年月不能为空")
private String yearMonth;
@NotNull(message = "评论正文id不能为空")
private String contentId;
@NotNull(message = "评论正文内容不能为空")
private String content;
}

View File

@@ -49,6 +49,16 @@
<artifactId>spring-boot-starter-data-cassandra</artifactId>
</dependency>
<dependency>
<groupId>com.hanserwei</groupId>
<artifactId>hanserwei-spring-boot-starter-biz-operationlog</artifactId>
</dependency>
<dependency>
<groupId>com.hanserwei</groupId>
<artifactId>hanserwei-spring-boot-starter-jackson</artifactId>
</dependency>
<dependency>
<groupId>com.hanserwei</groupId>
<artifactId>han-note-kv-api</artifactId>

View File

@@ -0,0 +1,29 @@
package com.hanserwei.hannote.kv.biz.controller;
import com.hanserwei.framework.biz.operationlog.aspect.ApiOperationLog;
import com.hanserwei.framework.common.response.Response;
import com.hanserwei.hannote.kv.biz.service.CommentContentService;
import com.hanserwei.hannote.kv.dto.req.BatchAddCommentContentReqDTO;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/kv")
@Slf4j
public class CommentContentController {
@Resource
private CommentContentService commentContentService;
@PostMapping("/comment/content/batchAdd")
@ApiOperationLog(description = "批量添加评论内容")
public Response<?> batchAddCommentContent(@Validated @RequestBody BatchAddCommentContentReqDTO batchAddCommentContentReqDTO) {
return commentContentService.batchAddCommentContent(batchAddCommentContentReqDTO);
}
}

View File

@@ -0,0 +1,21 @@
package com.hanserwei.hannote.kv.biz.domain.dataobject;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.data.cassandra.core.mapping.PrimaryKey;
import org.springframework.data.cassandra.core.mapping.Table;
@Table("comment_content")
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class CommentContentDO {
@PrimaryKey
private CommentContentPrimaryKey primaryKey;
private String content;
}

View File

@@ -0,0 +1,37 @@
package com.hanserwei.hannote.kv.biz.domain.dataobject;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.data.cassandra.core.cql.PrimaryKeyType;
import org.springframework.data.cassandra.core.mapping.PrimaryKeyClass;
import org.springframework.data.cassandra.core.mapping.PrimaryKeyColumn;
import java.util.UUID;
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
@PrimaryKeyClass
public class CommentContentPrimaryKey {
/**
* 分区键1
*/
@PrimaryKeyColumn(name = "note_id", type = PrimaryKeyType.PARTITIONED)
private Long noteId;
/**
* 分区键2
*/
@PrimaryKeyColumn(name = "year_month", type = PrimaryKeyType.PARTITIONED)
private String yearMonth;
/**
* 聚簇键
*/
@PrimaryKeyColumn(name = "content_id", type = PrimaryKeyType.PARTITIONED)
private UUID contentId;
}

View File

@@ -0,0 +1,9 @@
package com.hanserwei.hannote.kv.biz.service;
import com.hanserwei.framework.common.response.Response;
import com.hanserwei.hannote.kv.dto.req.BatchAddCommentContentReqDTO;
public interface CommentContentService {
Response<?> batchAddCommentContent(BatchAddCommentContentReqDTO batchAddCommentContentReqDTO);
}

View File

@@ -0,0 +1,50 @@
package com.hanserwei.hannote.kv.biz.service.impl;
import com.hanserwei.framework.common.response.Response;
import com.hanserwei.hannote.kv.biz.domain.dataobject.CommentContentDO;
import com.hanserwei.hannote.kv.biz.domain.dataobject.CommentContentPrimaryKey;
import com.hanserwei.hannote.kv.biz.service.CommentContentService;
import com.hanserwei.hannote.kv.dto.req.BatchAddCommentContentReqDTO;
import com.hanserwei.hannote.kv.dto.req.CommentContentReqDTO;
import jakarta.annotation.Resource;
import org.springframework.data.cassandra.core.CassandraTemplate;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.UUID;
@Service
public class CommentContentServiceImpl implements CommentContentService {
@Resource
private CassandraTemplate cassandraTemplate;
@Override
public Response<?> batchAddCommentContent(BatchAddCommentContentReqDTO batchAddCommentContentReqDTO) {
List<CommentContentReqDTO> comments = batchAddCommentContentReqDTO.getComments();
//DTO转DO
List<CommentContentDO> contentDOS = comments.stream()
.map(comment -> {
// 构建主键类
CommentContentPrimaryKey commentContentPrimaryKey = CommentContentPrimaryKey.builder()
.noteId(comment.getNoteId())
.yearMonth(comment.getYearMonth())
.contentId(UUID.fromString(comment.getContentId()))
.build();
// 构建DO
return CommentContentDO.builder()
.primaryKey(commentContentPrimaryKey)
.content(comment.getContent())
.build();
}).toList();
// 批量插入数据
cassandraTemplate.batchOps()
.insert(contentDOS)
.execute();
return Response.success();
}
}