From f07adec658b6573ded5c871c20650df4f31a2fe0 Mon Sep 17 00:00:00 2001 From: forely <1605769034@qq.com> Date: Sat, 10 May 2025 00:31:10 +0800 Subject: [PATCH] =?UTF-8?q?=E9=87=8D=E6=9E=84=E9=83=A8=E5=88=86=E8=A1=A8?= =?UTF-8?q?=E4=B8=8E=E6=8E=A5=E5=8F=A3=EF=BC=8C=E6=96=B0=E5=A2=9E=E5=B8=96?= =?UTF-8?q?=E5=AD=90=EF=BC=8C=E8=AF=84=E8=AE=BA=E7=9A=84=E7=9B=B8=E5=85=B3?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../luojia-island/pom.xml | 6 + .../message/config/WebSocketConfig.java | 17 +++ .../mq/AbstractSendProduceTemplate.java | 44 ++++++ .../modules/message/mq/BaseSendExtendDTO.java | 18 +++ .../modules/message/mq/MessageWrapper.java | 41 ++++++ .../post/controller/CommentController.java | 41 +++--- .../post/controller/PostController.java | 3 +- .../post/dto/req/CommentPageQueryDTO.java | 3 +- .../modules/post/dto/req/PostSaveDTO.java | 1 + .../modules/post/dto/resp/CommentInfoDTO.java | 6 +- .../modules/post/dto/resp/PostInfoDTO.java | 22 --- .../modules/post/entity/Comment.java | 3 +- .../modules/post/service/CommentService.java | 9 +- .../modules/post/service/PostService.java | 3 +- .../post/service/impl/CommentServiceImpl.java | 128 +++++++++++++----- .../post/service/impl/PostServiceImpl.java | 46 +++++-- .../modules/post/utils/ValidatePostUtil.java | 35 ++++- .../src/main/resources/db/luojia_channel.sql | 5 +- 18 files changed, 331 insertions(+), 100 deletions(-) create mode 100644 珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/message/config/WebSocketConfig.java create mode 100644 珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/message/mq/AbstractSendProduceTemplate.java create mode 100644 珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/message/mq/BaseSendExtendDTO.java create mode 100644 珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/message/mq/MessageWrapper.java delete mode 100644 珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/post/dto/resp/PostInfoDTO.java diff --git a/珞珈岛-项目相关文件/luojia-island/pom.xml b/珞珈岛-项目相关文件/luojia-island/pom.xml index 984db35..c0249b9 100644 --- a/珞珈岛-项目相关文件/luojia-island/pom.xml +++ b/珞珈岛-项目相关文件/luojia-island/pom.xml @@ -106,6 +106,12 @@ commons-codec 1.15 + + + + org.springframework.boot + spring-boot-starter-websocket + diff --git a/珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/message/config/WebSocketConfig.java b/珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/message/config/WebSocketConfig.java new file mode 100644 index 0000000..e039eb2 --- /dev/null +++ b/珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/message/config/WebSocketConfig.java @@ -0,0 +1,17 @@ +package com.luojia_channel.modules.message.config; + +import org.springframework.context.annotation.Configuration; +import org.springframework.web.socket.config.annotation.EnableWebSocket; +import org.springframework.web.socket.config.annotation.WebSocketConfigurer; +import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry; + +@Configuration +@EnableWebSocket +public class WebSocketConfig implements WebSocketConfigurer { + @Override + public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { + registry.addHandler(new MyWebSocketHandler(), "/ws") + .setAllowedOrigins("*"); // 允许跨域 + } + +} diff --git a/珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/message/mq/AbstractSendProduceTemplate.java b/珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/message/mq/AbstractSendProduceTemplate.java new file mode 100644 index 0000000..343a078 --- /dev/null +++ b/珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/message/mq/AbstractSendProduceTemplate.java @@ -0,0 +1,44 @@ +package com.luojia_channel.modules.message.mq; + +import com.alibaba.fastjson.JSON; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.amqp.rabbit.core.RabbitTemplate; +import org.springframework.messaging.MessageHeaders; + +@Slf4j +@RequiredArgsConstructor +// 模板方法类,提供消息发送的模板方法 +public abstract class AbstractSendProduceTemplate { + + private final RabbitTemplate rabbitTemplate; + private static final String KEYS = "keys"; + + protected abstract BaseSendExtendDTO buildBaseSendParam(T messageSendEvent); + + public void sendMessage(T messageSendEvent) { + BaseSendExtendDTO baseSendDTO = buildBaseSendParam(messageSendEvent); + try { + // 发送消息 + rabbitTemplate.convertAndSend( + baseSendDTO.getExchange(), + baseSendDTO.getRoutingKey(), + new MessageWrapper(baseSendDTO.getKeys(), messageSendEvent), + m -> { + // 设置消息头 + m.getMessageProperties().setHeader(KEYS, baseSendDTO.getKeys()); + // 设置消息属性(如延迟时间) TODO 若需要延迟消息,需安装延时插件 + if (baseSendDTO.getDelay() != null) { + // m.getMessageProperties().setDelay(baseSendDTO.getDelay()); + } + return m; + } + ); + log.info("[{}] 消息发送成功,Exchange:{},Routing Key:{}", + baseSendDTO.getEventName(), baseSendDTO.getExchange(), baseSendDTO.getRoutingKey()); + } catch (Throwable ex) { + log.error("[{}] 消息发送失败,消息体:{}", baseSendDTO.getEventName(), JSON.toJSONString(messageSendEvent), ex); + throw ex; + } + } +} diff --git a/珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/message/mq/BaseSendExtendDTO.java b/珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/message/mq/BaseSendExtendDTO.java new file mode 100644 index 0000000..4893713 --- /dev/null +++ b/珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/message/mq/BaseSendExtendDTO.java @@ -0,0 +1,18 @@ +package com.luojia_channel.modules.message.mq; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +public final class BaseSendExtendDTO { + private String eventName; //业务名称 + private String exchange; //交换机 + private String routingKey; //路由键 + private String keys; // 消息唯一标识 + private Long delay; // 延迟时间(毫秒),需RabbitMQ安装延迟插件 +} diff --git a/珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/message/mq/MessageWrapper.java b/珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/message/mq/MessageWrapper.java new file mode 100644 index 0000000..2d07daf --- /dev/null +++ b/珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/message/mq/MessageWrapper.java @@ -0,0 +1,41 @@ +package com.luojia_channel.modules.message.mq; + +import lombok.*; + +import java.io.Serializable; +import java.util.UUID; + +/** + * 消息体包装器 + */ +@Data +@Builder +@NoArgsConstructor(force = true) +@AllArgsConstructor +@RequiredArgsConstructor +public final class MessageWrapper implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * 消息发送 Keys + */ + @NonNull + private String keys; + + /** + * 消息体 + */ + @NonNull + private T message; + + /** + * 唯一标识,用于客户端幂等验证 + */ + private String uuid = UUID.randomUUID().toString(); + + /** + * 消息发送时间 + */ + private Long timestamp = System.currentTimeMillis(); +} diff --git a/珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/post/controller/CommentController.java b/珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/post/controller/CommentController.java index 9e6cc5a..616cb10 100644 --- a/珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/post/controller/CommentController.java +++ b/珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/post/controller/CommentController.java @@ -4,61 +4,58 @@ import com.luojia_channel.common.domain.Result; import com.luojia_channel.common.domain.page.PageResponse; import com.luojia_channel.common.utils.UserContext; import com.luojia_channel.modules.post.dto.req.CommentPageQueryDTO; +import com.luojia_channel.modules.post.dto.req.CommentSaveDTO; import com.luojia_channel.modules.post.dto.resp.CommentInfoDTO; import com.luojia_channel.modules.post.entity.Comment; import com.luojia_channel.modules.post.service.CommentService; +import com.luojia_channel.modules.post.utils.ValidatePostUtil; +import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import java.util.List; - +@RequiredArgsConstructor @RestController @RequestMapping("/comments") public class CommentController { private final CommentService commentService; - @Autowired - public CommentController(CommentService commentService) { - this.commentService = commentService; - } - // 创建评论 @PostMapping - public Result saveComment(@RequestBody Comment comment) { - commentService.saveComment(comment); + public Result saveComment(@RequestBody CommentSaveDTO commentSaveDTO) { + commentService.saveComment(commentSaveDTO); return Result.success(); } // 更新评论 - @PutMapping("/{id}") - public Result updateComment(@PathVariable Long id, @RequestBody Comment comment) { - Long currentUserId = UserContext.getUserId(); - commentService.updateComment(comment, currentUserId); + @PutMapping + public Result updateComment(@RequestBody CommentSaveDTO commentSaveDTO) { + commentService.updateComment(commentSaveDTO); return Result.success(); } // 删除评论 - @DeleteMapping("/{id}") - public Result deleteComment(@PathVariable Long id) { - Long currentUserId = UserContext.getUserId(); - commentService.deleteComment(id, currentUserId); + @DeleteMapping + public Result deleteComment(@RequestParam("id") Long id) { + commentService.deleteComment(id); return Result.success(); } - // 根据帖子ID分页获取评论 + // 根据帖子ID分页获取根评论 @GetMapping("/list") public Result> getCommentsByPostId(@RequestBody CommentPageQueryDTO commentPageQueryDTO) { PageResponse commentList = commentService.getCommentsByPostId(commentPageQueryDTO); return Result.success(commentList); } - // 根据帖子ID获取嵌套评论 - @GetMapping("/nested/post/{postId}") - public Result> getNestedCommentsByPostId(@PathVariable Long postId) { - List nestedComments = commentService.getNestedCommentsByPostId(postId); - return Result.success(nestedComments); + + // 根据评论ID获取回复 + @GetMapping("/list/reply") + public Result> getReplyById(@RequestBody CommentPageQueryDTO commentPageQueryDTO) { + PageResponse commentInfoDTOList = commentService.getReplyById(commentPageQueryDTO); + return Result.success(commentInfoDTOList); } } diff --git a/珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/post/controller/PostController.java b/珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/post/controller/PostController.java index 944385a..1dd7fff 100644 --- a/珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/post/controller/PostController.java +++ b/珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/post/controller/PostController.java @@ -6,6 +6,7 @@ import com.luojia_channel.common.domain.page.PageResponse; import com.luojia_channel.modules.post.dto.req.PostSaveDTO; import com.luojia_channel.modules.post.dto.req.PostPageQueryDTO; import com.luojia_channel.modules.post.dto.resp.PostBasicInfoDTO; +import com.luojia_channel.modules.post.dto.resp.PostInfoDTO; import com.luojia_channel.modules.post.entity.Post; import com.luojia_channel.modules.post.service.PostService; import lombok.RequiredArgsConstructor; @@ -48,7 +49,7 @@ public class PostController { // 根据ID获取帖子详情 @GetMapping("/detail") - public Post getPostDetail(@RequestParam("id") Long id) { + public PostInfoDTO getPostDetail(@RequestParam("id") Long id) { return postService.getPostDetail(id); } diff --git a/珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/post/dto/req/CommentPageQueryDTO.java b/珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/post/dto/req/CommentPageQueryDTO.java index b83ac08..3af2096 100644 --- a/珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/post/dto/req/CommentPageQueryDTO.java +++ b/珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/post/dto/req/CommentPageQueryDTO.java @@ -5,5 +5,6 @@ import lombok.Data; @Data public class CommentPageQueryDTO extends PageRequest { - private Integer postId; + private Long postId; + private Long commentId; } diff --git a/珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/post/dto/req/PostSaveDTO.java b/珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/post/dto/req/PostSaveDTO.java index f23957a..de2299a 100644 --- a/珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/post/dto/req/PostSaveDTO.java +++ b/珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/post/dto/req/PostSaveDTO.java @@ -4,6 +4,7 @@ import lombok.Data; @Data public class PostSaveDTO { + private Long id; private String title; private String image; private String content; diff --git a/珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/post/dto/resp/CommentInfoDTO.java b/珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/post/dto/resp/CommentInfoDTO.java index 335ef53..b2e0663 100644 --- a/珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/post/dto/resp/CommentInfoDTO.java +++ b/珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/post/dto/resp/CommentInfoDTO.java @@ -15,12 +15,16 @@ import java.util.List; public class CommentInfoDTO { private Long id; private String content; + private Long likeCount; + private Long replyCount; private Long userId; - private String postType; private Long postId; private Long parentCommentId; private Long topId; private LocalDateTime createTime; + private String userName; + private String userAvatar; + private List commentInfoDTOList; } diff --git a/珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/post/dto/resp/PostInfoDTO.java b/珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/post/dto/resp/PostInfoDTO.java deleted file mode 100644 index 6680971..0000000 --- a/珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/post/dto/resp/PostInfoDTO.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.luojia_channel.modules.post.dto.resp; - -import com.luojia_channel.modules.post.entity.Comment; - -import java.util.List; - -public class PostInfoDTO { - private Long id; - private String image; - private String title; - private Integer likeCount; - private Integer commentCount; - private Integer favoriteCount; - private String content; - - private Boolean isLike; - private Long userId; - private String userName; - private String userAvatar; - - private List commentList; -} diff --git a/珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/post/entity/Comment.java b/珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/post/entity/Comment.java index bb55476..a00ac09 100644 --- a/珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/post/entity/Comment.java +++ b/珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/post/entity/Comment.java @@ -16,8 +16,9 @@ import java.time.LocalDateTime; public class Comment { private Long id; private String content; + private Long likeCount; + private Long replyCount; private Long userId; - private String postType; private Long postId; private Long parentCommentId; private Long topId; diff --git a/珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/post/service/CommentService.java b/珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/post/service/CommentService.java index 9523485..3ebeab1 100644 --- a/珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/post/service/CommentService.java +++ b/珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/post/service/CommentService.java @@ -2,6 +2,7 @@ package com.luojia_channel.modules.post.service; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.luojia_channel.common.domain.page.PageResponse; import com.luojia_channel.modules.post.dto.req.CommentPageQueryDTO; +import com.luojia_channel.modules.post.dto.req.CommentSaveDTO; import com.luojia_channel.modules.post.dto.resp.CommentInfoDTO; import com.luojia_channel.modules.post.entity.Comment; import org.springframework.stereotype.Service; @@ -11,13 +12,13 @@ import java.util.List; @Service public interface CommentService { - void saveComment(Comment comment); + void saveComment(CommentSaveDTO commentSaveDTO); - void updateComment(Comment comment, Long userId); + void updateComment(CommentSaveDTO commentSaveDTO); - void deleteComment(Long id, Long userId); + void deleteComment(Long id); PageResponse getCommentsByPostId(CommentPageQueryDTO commentPageQueryDTO); - List getNestedCommentsByPostId(Long postId); + PageResponse getReplyById(CommentPageQueryDTO commentPageQueryDTO); } diff --git a/珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/post/service/PostService.java b/珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/post/service/PostService.java index 8c7136e..68cecb0 100644 --- a/珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/post/service/PostService.java +++ b/珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/post/service/PostService.java @@ -5,6 +5,7 @@ import com.luojia_channel.common.domain.page.PageResponse; import com.luojia_channel.modules.post.dto.req.PostSaveDTO; import com.luojia_channel.modules.post.dto.req.PostPageQueryDTO; import com.luojia_channel.modules.post.dto.resp.PostBasicInfoDTO; +import com.luojia_channel.modules.post.dto.resp.PostInfoDTO; import com.luojia_channel.modules.post.entity.Post; import org.springframework.web.multipart.MultipartFile; @@ -17,7 +18,7 @@ public interface PostService extends IService { void deletePost(Long id); - Post getPostDetail(Long id); + PostInfoDTO getPostDetail(Long id); PageResponse pagePost(PostPageQueryDTO postPageQueryDTO); diff --git a/珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/post/service/impl/CommentServiceImpl.java b/珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/post/service/impl/CommentServiceImpl.java index f78d52a..794da2b 100644 --- a/珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/post/service/impl/CommentServiceImpl.java +++ b/珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/post/service/impl/CommentServiceImpl.java @@ -1,91 +1,150 @@ package com.luojia_channel.modules.post.service.impl; +import cn.hutool.core.bean.BeanUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.toolkit.Wrappers; -import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.luojia_channel.common.domain.page.PageResponse; import com.luojia_channel.common.exception.PostException; import com.luojia_channel.common.utils.PageUtil; +import com.luojia_channel.common.utils.UserContext; import com.luojia_channel.modules.post.dto.req.CommentPageQueryDTO; +import com.luojia_channel.modules.post.dto.req.CommentSaveDTO; import com.luojia_channel.modules.post.dto.resp.CommentInfoDTO; import com.luojia_channel.modules.post.dto.resp.PostBasicInfoDTO; import com.luojia_channel.modules.post.entity.Comment; import com.luojia_channel.modules.post.entity.Post; import com.luojia_channel.modules.post.mapper.CommentMapper; import com.luojia_channel.modules.post.service.CommentService; +import com.luojia_channel.modules.post.utils.ValidatePostUtil; +import com.luojia_channel.modules.user.entity.User; +import com.luojia_channel.modules.user.mapper.UserMapper; import lombok.RequiredArgsConstructor; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.stream.Collectors; @Service @RequiredArgsConstructor public class CommentServiceImpl extends ServiceImpl implements CommentService { - private CommentMapper commentMapper; + private final ValidatePostUtil validatePostUtil; + private final CommentMapper commentMapper; + private final UserMapper userMapper; @Override - public void saveComment(Comment comment) { - comment.setCreateTime(LocalDateTime.now()); - comment.setUpdateTime(LocalDateTime.now()); - commentMapper.insert(comment); + @Transactional(rollbackFor = Exception.class) + public void saveComment(CommentSaveDTO commentSaveDTO) { + validatePostUtil.validateComment(commentSaveDTO); + Comment comment = BeanUtil.copyProperties(commentSaveDTO, Comment.class); + if(!save(comment)){ + throw new PostException("创建评论失败"); + } + Long parentCommentId = commentSaveDTO.getParentCommentId(); + if(parentCommentId != null){ + LambdaUpdateWrapper updateWrapper = Wrappers.lambdaUpdate(Comment.class) + .eq(Comment::getId, parentCommentId) + .setSql("reply_count = reply_count + 1"); + int update = commentMapper.update(null, updateWrapper); + if(update <= 0) { + throw new PostException("回复评论失败"); + } + Long topId = commentSaveDTO.getTopId(); + // 更新顶级评论回复数 + if(!parentCommentId.equals(topId)){ + updateWrapper = Wrappers.lambdaUpdate(Comment.class) + .eq(Comment::getId, topId) + .setSql("reply_count = reply_count + 1"); + update = commentMapper.update(null, updateWrapper); + if(update <= 0) { + throw new PostException("回复顶级评论失败"); + } + } + + // TODO 消息通知 + + } + } @Override - public void updateComment(Comment comment, Long userId) { - validatePostOwnership(comment.getId(), userId); + public void updateComment(CommentSaveDTO commentSaveDTO) { + validatePostUtil.validateComment(commentSaveDTO); + validatePostUtil.validateCommentOwnership(commentSaveDTO.getId()); + Comment comment = BeanUtil.copyProperties(commentSaveDTO, Comment.class); comment.setUpdateTime(LocalDateTime.now()); if(!updateById(comment)){ - throw new PostException("更新帖子失败"); + throw new PostException("更新评论失败"); } } @Override - public void deleteComment(Long id, Long userId) { - validatePostOwnership(id, userId); - commentMapper.deleteById(id); + public void deleteComment(Long id) { + validatePostUtil.validateCommentOwnership(id); + LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(Comment.class) + .eq(Comment::getTopId, id); + int delete = commentMapper.delete(queryWrapper); + if(delete <= 0) { + throw new PostException("删除评论失败"); + } + // TODO 如果根评论删除,那么其他评论怎么办,目前做法是删除其下所有的子评论 } + // 分页查询一系列根评论 @Override public PageResponse getCommentsByPostId(CommentPageQueryDTO commentPageQueryDTO) { + if(commentPageQueryDTO.getPostId() == null || commentPageQueryDTO.getPostId() < 0){ + throw new PostException("帖子id不合法"); + } LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(Comment.class) .eq(Comment::getPostId, commentPageQueryDTO.getPostId()) + .eq(Comment::getParentCommentId, 0L) .orderByDesc(Comment::getCreateTime); - IPage commentPage = commentMapper.selectPage(PageUtil.convert(commentPageQueryDTO), queryWrapper); - return PageUtil.convert(commentPage, CommentInfoDTO.class); + return getCommentInfoDTOPageResponse(commentPageQueryDTO, queryWrapper); } @Override - public List getNestedCommentsByPostId(Long postId) { - LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); - queryWrapper.eq(Comment::getPostId, postId) - .orderByAsc(Comment::getCreateTime); - - List comments = commentMapper.selectList(queryWrapper); - return buildNestedComments(comments); + public PageResponse getReplyById(CommentPageQueryDTO commentPageQueryDTO) { + if(commentPageQueryDTO.getCommentId() == null || commentPageQueryDTO.getCommentId() < 0){ + throw new PostException("评论id不合法"); + } + LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(Comment.class) + .eq(Comment::getTopId, commentPageQueryDTO.getCommentId()) + .orderByDesc(Comment::getCreateTime); + return getCommentInfoDTOPageResponse(commentPageQueryDTO, queryWrapper); } - private void validatePostOwnership(Long commentId, Long userId) { - Comment comment = commentMapper.selectById(commentId); - if (comment == null) { - throw new PostException("评论不存在"); - } - if (!userId.equals(comment.getUserId())) { - throw new PostException("你无权操作他人的评论"); - } + private PageResponse getCommentInfoDTOPageResponse(CommentPageQueryDTO commentPageQueryDTO, LambdaQueryWrapper queryWrapper) { + IPage commentPage = commentMapper.selectPage(PageUtil.convert(commentPageQueryDTO), queryWrapper); + List userIds = new ArrayList<>(); + commentPage.getRecords().forEach(comment -> { + userIds.add(comment.getUserId()); + }); + List users = userMapper.selectBatchIds(userIds); + Map userMap = users.stream() + .collect(Collectors.toMap(User::getId, user -> user)); + return PageUtil.convert(commentPage, (comment) -> { + CommentInfoDTO commentInfoDTO = BeanUtil.copyProperties(comment, CommentInfoDTO.class); + User user = userMap.getOrDefault(comment.getUserId(), new User()); + commentInfoDTO.setUserAvatar(user.getAvatar()); + commentInfoDTO.setUserName(user.getUsername()); + return commentInfoDTO; + }); } - private List convertToDTO(List comments) { + private List convertToDTO(List commentList) { List dtos = new ArrayList<>(); - for (Comment comment : comments) { + for (Comment comment : commentList) { CommentInfoDTO dto = new CommentInfoDTO(); BeanUtils.copyProperties(comment, dto); dtos.add(dto); @@ -93,16 +152,14 @@ public class CommentServiceImpl extends ServiceImpl impl return dtos; } - private List buildNestedComments(List comments) { + // 根据评论创建嵌套评论,目前没用到 + private List buildNestedCommentList(List comments) { Map map = new HashMap<>(); List rootComments = new ArrayList<>(); - for (Comment comment : comments) { CommentInfoDTO dto = new CommentInfoDTO(); BeanUtils.copyProperties(comment, dto); - map.put(comment.getId(), dto); - if (comment.getParentCommentId() == null) { rootComments.add(dto); } else { @@ -115,7 +172,6 @@ public class CommentServiceImpl extends ServiceImpl impl } } } - return rootComments; } } diff --git a/珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/post/service/impl/PostServiceImpl.java b/珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/post/service/impl/PostServiceImpl.java index 0f31108..7c80b53 100644 --- a/珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/post/service/impl/PostServiceImpl.java +++ b/珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/post/service/impl/PostServiceImpl.java @@ -11,18 +11,28 @@ import com.luojia_channel.common.utils.PageUtil; import com.luojia_channel.common.utils.RedisUtil; import com.luojia_channel.common.utils.UserContext; import com.luojia_channel.modules.file.service.impl.FileServiceImpl; +import com.luojia_channel.modules.post.dto.req.CommentPageQueryDTO; import com.luojia_channel.modules.post.dto.req.PostSaveDTO; import com.luojia_channel.modules.post.dto.req.PostPageQueryDTO; +import com.luojia_channel.modules.post.dto.resp.CommentInfoDTO; import com.luojia_channel.modules.post.dto.resp.PostBasicInfoDTO; +import com.luojia_channel.modules.post.dto.resp.PostInfoDTO; +import com.luojia_channel.modules.post.entity.Comment; import com.luojia_channel.modules.post.entity.Post; import com.luojia_channel.modules.post.mapper.PostMapper; import com.luojia_channel.modules.post.service.PostService; import com.luojia_channel.modules.post.utils.ValidatePostUtil; +import com.luojia_channel.modules.user.entity.User; +import com.luojia_channel.modules.user.mapper.UserMapper; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.web.multipart.MultipartFile; import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; @Service @RequiredArgsConstructor @@ -32,13 +42,12 @@ public class PostServiceImpl extends ServiceImpl implements Po private final FileServiceImpl fileService; private final ValidatePostUtil validatePostUtil; private final RedisUtil redisUtil; + private final UserMapper userMapper; @Override public void createPost(PostSaveDTO postSaveDTO) { validatePostUtil.validatePost(postSaveDTO); - Long userId = UserContext.getUserId(); Post post = BeanUtil.copyProperties(postSaveDTO, Post.class); - post.setUserId(userId); if(!save(post)){ throw new PostException("创建帖子失败"); } @@ -52,6 +61,7 @@ public class PostServiceImpl extends ServiceImpl implements Po @Override public void updatePost(PostSaveDTO postSaveDTO) { validatePostUtil.validatePost(postSaveDTO); + validatePostUtil.validatePostOwnership(postSaveDTO.getId()); Post post = BeanUtil.copyProperties(postSaveDTO, Post.class); post.setUpdateTime(LocalDateTime.now()); if(!updateById(post)){ @@ -69,15 +79,21 @@ public class PostServiceImpl extends ServiceImpl implements Po } @Override - public Post getPostDetail(Long id) { - return redisUtil.safeGet("post:detail" + id.toString(), Post.class, + public PostInfoDTO getPostDetail(Long id) { + return redisUtil.safeGet("post:detail" + id.toString(), PostInfoDTO.class, () -> { Post post = getById(id); if(post == null){ throw new PostException("帖子不存在或被删除"); } - return post; - }, 10, TimeUnit.MINUTES); + PostInfoDTO postInfoDTO = BeanUtil.copyProperties(post, PostInfoDTO.class); + User user = userMapper.selectOne(Wrappers.lambdaQuery(User.class) + .eq(User::getId, post.getUserId())); + postInfoDTO.setUserAvatar(user.getAvatar()); + postInfoDTO.setUserName(user.getUsername()); + return postInfoDTO; + }, + 60, TimeUnit.MINUTES); } @Override @@ -86,10 +102,23 @@ public class PostServiceImpl extends ServiceImpl implements Po LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(Post.class) .orderByDesc(Post::getCreateTime); IPage postPage = postMapper.selectPage(PageUtil.convert(postPageQueryDTO), queryWrapper); - return PageUtil.convert(postPage, PostBasicInfoDTO.class); + List userIds = new ArrayList<>(); + postPage.getRecords().forEach(comment -> { + userIds.add(comment.getUserId()); + }); + List users = userMapper.selectBatchIds(userIds); + Map userMap = users.stream() + .collect(Collectors.toMap(User::getId, user -> user)); + // 组装用户头像与名称,批量查询只要查一次数据库 + return PageUtil.convert(postPage, post -> { + PostBasicInfoDTO postBasicInfoDTO = BeanUtil.copyProperties(post, PostBasicInfoDTO.class); + User user = userMap.getOrDefault(post.getUserId(), new User()); + postBasicInfoDTO.setUserAvatar(user.getAvatar()); + postBasicInfoDTO.setUserName(user.getUsername()); + return postBasicInfoDTO; + }); } - @Override public PageResponse pagePostOfMe(PostPageQueryDTO postPageQueryDTO) { Long userId = UserContext.getUserId(); @@ -99,4 +128,5 @@ public class PostServiceImpl extends ServiceImpl implements Po IPage postPage = postMapper.selectPage(PageUtil.convert(postPageQueryDTO), queryWrapper); return PageUtil.convert(postPage, PostBasicInfoDTO.class); } + } diff --git a/珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/post/utils/ValidatePostUtil.java b/珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/post/utils/ValidatePostUtil.java index 9a0c2ba..ba5236b 100644 --- a/珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/post/utils/ValidatePostUtil.java +++ b/珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/post/utils/ValidatePostUtil.java @@ -3,8 +3,11 @@ package com.luojia_channel.modules.post.utils; import cn.hutool.core.util.StrUtil; import com.luojia_channel.common.exception.PostException; import com.luojia_channel.common.utils.UserContext; +import com.luojia_channel.modules.post.dto.req.CommentSaveDTO; import com.luojia_channel.modules.post.dto.req.PostSaveDTO; +import com.luojia_channel.modules.post.entity.Comment; import com.luojia_channel.modules.post.entity.Post; +import com.luojia_channel.modules.post.mapper.CommentMapper; import com.luojia_channel.modules.post.mapper.PostMapper; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Component; @@ -16,6 +19,8 @@ public class ValidatePostUtil { private final PostMapper postMapper; + private final CommentMapper commentMapper; + public void validatePost(PostSaveDTO postSaveDTO) { // 非空字段检验 if (StrUtil.isBlank(postSaveDTO.getTitle())) { @@ -34,6 +39,9 @@ public class ValidatePostUtil { public void validatePostOwnership(Long id){ Long userId = UserContext.getUserId(); + if(id == null){ + throw new PostException("传入id不能为空"); + } Post post = postMapper.selectById(id); if(post == null){ throw new PostException("帖子不存在"); @@ -43,7 +51,32 @@ public class ValidatePostUtil { } } - public void AIValidate(PostSaveDTO postSaveDTO){ + public void validateComment(CommentSaveDTO commentSaveDTO) { + if (StrUtil.isBlank(commentSaveDTO.getContent())) { + throw new PostException("内容不能为空"); + } + if (commentSaveDTO.getPostId() == null || commentSaveDTO.getPostId() < 0){ + throw new PostException("帖子id不合法"); + } + if(commentSaveDTO.getParentCommentId() != null && commentSaveDTO.getParentCommentId() < 0){ + throw new PostException("父评论id不合法"); + } + if(commentSaveDTO.getTopId() != null && commentSaveDTO.getTopId() <= 0){ + throw new PostException("顶级评论id不合法"); + } + } + public void validateCommentOwnership(Long id) { + Long userId = UserContext.getUserId(); + if(id == null){ + throw new PostException("传入id不能为空"); + } + Comment comment = commentMapper.selectById(id); + if(comment == null){ + throw new PostException("评论不存在"); + } + if(!userId.equals(comment.getUserId())){ + throw new PostException("你无权操作他人的评论"); + } } } diff --git a/珞珈岛-项目相关文件/luojia-island/service/src/main/resources/db/luojia_channel.sql b/珞珈岛-项目相关文件/luojia-island/service/src/main/resources/db/luojia_channel.sql index b801e0d..d3da488 100644 --- a/珞珈岛-项目相关文件/luojia-island/service/src/main/resources/db/luojia_channel.sql +++ b/珞珈岛-项目相关文件/luojia-island/service/src/main/resources/db/luojia_channel.sql @@ -101,10 +101,11 @@ DROP TABLE IF EXISTS `comment`; CREATE TABLE `comment` ( `id` BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '主键ID', `content` TEXT NOT NULL COMMENT '评论内容', + `like_count` INT DEFAULT 0 COMMENT '点赞数', + `reply_count` INT DEFAULT 0 COMMENT '回复数', `user_id` BIGINT NOT NULL COMMENT '评论用户ID', - `post_type` VARCHAR(20) NOT NULL COMMENT '帖子类型(post/video)', `post_id` BIGINT NOT NULL COMMENT '关联的帖子ID', - `parent_comment_id` BIGINT COMMENT '父评论ID', + `parent_comment_id` BIGINT DEFAULT 0 COMMENT '父评论ID', `top_id` BIGINT COMMENT '顶层评论ID', `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',