diff --git a/src/main/java/com/daon/onjung/core/config/RabbitMQConfig.java b/src/main/java/com/daon/onjung/core/config/RabbitMQConfig.java index 2347746..041016c 100644 --- a/src/main/java/com/daon/onjung/core/config/RabbitMQConfig.java +++ b/src/main/java/com/daon/onjung/core/config/RabbitMQConfig.java @@ -52,7 +52,9 @@ public Jackson2JsonMessageConverter jsonMessageConverter() { @Bean public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) { RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory); - rabbitTemplate.setMessageConverter(jsonMessageConverter()); // JSON 컨버터 설정 + rabbitTemplate.setMessageConverter(jsonMessageConverter()); + rabbitTemplate.setReplyTimeout(5000); + rabbitTemplate.setUseDirectReplyToContainer(true); return rabbitTemplate; } } diff --git a/src/main/java/com/daon/onjung/suggestion/application/controller/command/SuggestionCommandV1Controller.java b/src/main/java/com/daon/onjung/suggestion/application/controller/command/SuggestionCommandV1Controller.java index 933b997..393648e 100644 --- a/src/main/java/com/daon/onjung/suggestion/application/controller/command/SuggestionCommandV1Controller.java +++ b/src/main/java/com/daon/onjung/suggestion/application/controller/command/SuggestionCommandV1Controller.java @@ -4,6 +4,8 @@ import com.daon.onjung.core.dto.ResponseDto; import com.daon.onjung.suggestion.application.dto.request.CreateBoardRequestDto; import com.daon.onjung.suggestion.application.dto.request.CreateCommentRequestDto; +import com.daon.onjung.suggestion.application.dto.response.CreateCommentResponseDto; +import com.daon.onjung.suggestion.application.dto.response.CreateOrDeleteLikeResponseDto; import com.daon.onjung.suggestion.application.usecase.CreateBoardUseCase; import com.daon.onjung.suggestion.application.usecase.CreateCommentUseCase; import com.daon.onjung.suggestion.application.usecase.CreateOrDeleteLikeUseCase; @@ -34,21 +36,19 @@ public ResponseDto createBoard( } @PostMapping("/api/v1/boards/{id}/comments") - public ResponseDto createComment( + public ResponseDto createComment( @AccountID UUID accountId, @PathVariable Long id, @RequestBody @Valid CreateCommentRequestDto requestDto ) { - createCommentUseCase.execute(accountId, id, requestDto); - return ResponseDto.created(null); + return ResponseDto.created(createCommentUseCase.execute(accountId, id, requestDto)); } @PutMapping("/api/v1/boards/{id}/likes") - public ResponseDto likeBoard( + public ResponseDto likeBoard( @AccountID UUID accountId, @PathVariable Long id ) { - createOrDeleteLikeUseCase.execute(accountId, id); - return ResponseDto.ok(null); + return ResponseDto.ok(createOrDeleteLikeUseCase.execute(accountId, id)); } } diff --git a/src/main/java/com/daon/onjung/suggestion/application/controller/consumer/CommentV1Consumer.java b/src/main/java/com/daon/onjung/suggestion/application/controller/consumer/CommentV1Consumer.java index 1a66719..7f2ada4 100644 --- a/src/main/java/com/daon/onjung/suggestion/application/controller/consumer/CommentV1Consumer.java +++ b/src/main/java/com/daon/onjung/suggestion/application/controller/consumer/CommentV1Consumer.java @@ -5,6 +5,7 @@ import com.daon.onjung.core.exception.error.ErrorCode; import com.daon.onjung.core.exception.type.CommonException; import com.daon.onjung.suggestion.application.dto.request.CommentMessage; +import com.daon.onjung.suggestion.application.dto.response.CreateCommentResponseDto; import com.daon.onjung.suggestion.domain.Board; import com.daon.onjung.suggestion.domain.Comment; import com.daon.onjung.suggestion.domain.service.BoardService; @@ -14,6 +15,7 @@ import lombok.RequiredArgsConstructor; import org.hibernate.dialect.lock.OptimisticEntityLockException; import org.springframework.amqp.rabbit.annotation.RabbitListener; +import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -28,9 +30,11 @@ public class CommentV1Consumer { private final CommentService commentService; private final BoardService boardService; + private final RabbitTemplate rabbitTemplate; + @Transactional @RabbitListener(queues = "comment-queue-1") - public void processCommentMessage1(CommentMessage commentMessage) { + public CreateCommentResponseDto processCommentMessage1(CommentMessage commentMessage) { try { // 게시글 조회 @@ -52,6 +56,8 @@ public void processCommentMessage1(CommentMessage commentMessage) { // 게시글 댓글 수 증가 board = boardService.addCommentCount(board); boardRepository.save(board); + + return CreateCommentResponseDto.of(user, true, comment); } catch (OptimisticEntityLockException e) { throw new CommonException(ErrorCode.OPTIMISTIC_EXCEPTION); } @@ -59,7 +65,7 @@ public void processCommentMessage1(CommentMessage commentMessage) { @Transactional @RabbitListener(queues = "comment-queue-2") - public void processCommentMessage2(CommentMessage commentMessage) { + public CreateCommentResponseDto processCommentMessage2(CommentMessage commentMessage) { try { // 게시글 조회 @@ -81,6 +87,8 @@ public void processCommentMessage2(CommentMessage commentMessage) { // 게시글 댓글 수 증가 board = boardService.addCommentCount(board); boardRepository.save(board); + + return CreateCommentResponseDto.of(user, true, comment); } catch (OptimisticEntityLockException e) { throw new CommonException(ErrorCode.OPTIMISTIC_EXCEPTION); } diff --git a/src/main/java/com/daon/onjung/suggestion/application/controller/consumer/LikeV1Consumer.java b/src/main/java/com/daon/onjung/suggestion/application/controller/consumer/LikeV1Consumer.java index e4a278d..7222abc 100644 --- a/src/main/java/com/daon/onjung/suggestion/application/controller/consumer/LikeV1Consumer.java +++ b/src/main/java/com/daon/onjung/suggestion/application/controller/consumer/LikeV1Consumer.java @@ -5,6 +5,7 @@ import com.daon.onjung.core.exception.error.ErrorCode; import com.daon.onjung.core.exception.type.CommonException; import com.daon.onjung.suggestion.application.dto.request.LikeMessage; +import com.daon.onjung.suggestion.application.dto.response.CreateOrDeleteLikeResponseDto; import com.daon.onjung.suggestion.domain.Board; import com.daon.onjung.suggestion.domain.Like; import com.daon.onjung.suggestion.domain.service.BoardService; @@ -30,7 +31,7 @@ public class LikeV1Consumer { @Transactional @RabbitListener(queues = "like-queue") - public void processLikeMessage(LikeMessage likeMessage) { + public Boolean processLikeMessage(LikeMessage likeMessage) { try { // 게시글 조회 @@ -52,6 +53,7 @@ public void processLikeMessage(LikeMessage likeMessage) { // 게시글 좋아요 수 감소 board = boardService.subtractLikeCount(board); boardRepository.save(board); + return false; } else { // 좋아요가 존재하지 않으면 생성 @@ -60,6 +62,7 @@ public void processLikeMessage(LikeMessage likeMessage) { // 게시글 좋아요 수 증가 board = boardService.addLikeCount(board); boardRepository.save(board); + return true; } } catch (OptimisticEntityLockException e) { throw new CommonException(ErrorCode.OPTIMISTIC_EXCEPTION); diff --git a/src/main/java/com/daon/onjung/suggestion/application/controller/producer/CommentV1Producer.java b/src/main/java/com/daon/onjung/suggestion/application/controller/producer/CommentV1Producer.java index 443ae49..0499064 100644 --- a/src/main/java/com/daon/onjung/suggestion/application/controller/producer/CommentV1Producer.java +++ b/src/main/java/com/daon/onjung/suggestion/application/controller/producer/CommentV1Producer.java @@ -1,6 +1,7 @@ package com.daon.onjung.suggestion.application.controller.producer; import com.daon.onjung.suggestion.application.dto.request.CommentMessage; +import com.daon.onjung.suggestion.application.dto.response.CreateCommentResponseDto; import com.daon.onjung.suggestion.application.usecase.SendCommentRequestUseCase; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @@ -11,8 +12,8 @@ public class CommentV1Producer { private final SendCommentRequestUseCase sendCommentRequestUseCase; - public void sendComment (CommentMessage commentMessage) { - sendCommentRequestUseCase.execute(commentMessage); + public CreateCommentResponseDto sendComment (CommentMessage commentMessage) { + return sendCommentRequestUseCase.execute(commentMessage); } } diff --git a/src/main/java/com/daon/onjung/suggestion/application/controller/producer/LikeV1Producer.java b/src/main/java/com/daon/onjung/suggestion/application/controller/producer/LikeV1Producer.java index 2da27c5..432bd77 100644 --- a/src/main/java/com/daon/onjung/suggestion/application/controller/producer/LikeV1Producer.java +++ b/src/main/java/com/daon/onjung/suggestion/application/controller/producer/LikeV1Producer.java @@ -1,6 +1,7 @@ package com.daon.onjung.suggestion.application.controller.producer; import com.daon.onjung.suggestion.application.dto.request.LikeMessage; +import com.daon.onjung.suggestion.application.dto.response.CreateOrDeleteLikeResponseDto; import com.daon.onjung.suggestion.application.usecase.SendLikeRequestUseCase; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @@ -11,8 +12,8 @@ public class LikeV1Producer { private final SendLikeRequestUseCase sendLikeRequestUseCase; - public void sendLike (LikeMessage likeMessage) { - sendLikeRequestUseCase.execute(likeMessage); + public CreateOrDeleteLikeResponseDto sendLike (LikeMessage likeMessage) { + return sendLikeRequestUseCase.execute(likeMessage); } } diff --git a/src/main/java/com/daon/onjung/suggestion/application/dto/response/CreateCommentResponseDto.java b/src/main/java/com/daon/onjung/suggestion/application/dto/response/CreateCommentResponseDto.java new file mode 100644 index 0000000..a7a59e8 --- /dev/null +++ b/src/main/java/com/daon/onjung/suggestion/application/dto/response/CreateCommentResponseDto.java @@ -0,0 +1,108 @@ +package com.daon.onjung.suggestion.application.dto.response; + +import com.daon.onjung.account.domain.User; +import com.daon.onjung.core.dto.SelfValidating; +import com.daon.onjung.core.utility.DateTimeUtil; +import com.daon.onjung.suggestion.domain.Comment; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Builder; +import lombok.Getter; + +@Getter +public class CreateCommentResponseDto extends SelfValidating { + + @JsonProperty("writer_info") + private final WriterInfoDto writerInfo; + + @JsonProperty("comment_info") + private final CommentInfoDto commentInfo; + + @Builder + public CreateCommentResponseDto(WriterInfoDto writerInfo, CommentInfoDto commentInfo) { + this.writerInfo = writerInfo; + this.commentInfo = commentInfo; + + this.validateSelf(); + } + + @Getter + public static class WriterInfoDto extends SelfValidating { + + @JsonProperty("profile_img_url") + private final String profileImgUrl; + + @JsonProperty("masked_nickname") + private final String maskedNickname; + + @JsonProperty("is_me") + private final Boolean isMe; + + @Builder + public WriterInfoDto(String profileImgUrl, String maskedNickname, Boolean isMe) { + this.profileImgUrl = profileImgUrl; + this.maskedNickname = maskedNickname; + this.isMe = isMe; + + this.validateSelf(); + } + + public static WriterInfoDto of(User user, Boolean isMe) { + String nickname = user.getNickName(); + String maskedNickname; + + if (nickname.length() == 1) { + maskedNickname = nickname; + } else if (nickname.length() == 2) { + maskedNickname = nickname.charAt(0) + "*"; + } else { + String firstChar = nickname.substring(0, 1); + String lastChar = nickname.substring(nickname.length() - 1); + String middleMask = "*".repeat(nickname.length() - 2); + maskedNickname = firstChar + middleMask + lastChar; + } + + return WriterInfoDto.builder() + .maskedNickname(maskedNickname) + .profileImgUrl(user.getProfileImgUrl()) + .isMe(isMe) + .build(); + } + } + + @Getter + public static class CommentInfoDto extends SelfValidating { + + @JsonProperty("id") + private final Long id; + + @JsonProperty("content") + private final String content; + + @JsonProperty("posted_at") + private final String postedAt; + + @Builder + public CommentInfoDto(Long id, String content, String postedAt) { + this.id = id; + this.content = content; + this.postedAt = postedAt; + + this.validateSelf(); + } + + public static CommentInfoDto fromEntity(Comment comment) { + return CommentInfoDto.builder() + .id(comment.getId()) + .content(comment.getContent()) + .postedAt(DateTimeUtil.calculatePostedAgo(comment.getCreatedAt())) + .build(); + } + } + + public static CreateCommentResponseDto of(User user, Boolean isMe, Comment comment) { + return CreateCommentResponseDto.builder() + .writerInfo(WriterInfoDto.of(user, isMe)) + .commentInfo(CommentInfoDto.fromEntity(comment)) + .build(); + } +} diff --git a/src/main/java/com/daon/onjung/suggestion/application/dto/response/CreateOrDeleteLikeResponseDto.java b/src/main/java/com/daon/onjung/suggestion/application/dto/response/CreateOrDeleteLikeResponseDto.java new file mode 100644 index 0000000..5ce7ea7 --- /dev/null +++ b/src/main/java/com/daon/onjung/suggestion/application/dto/response/CreateOrDeleteLikeResponseDto.java @@ -0,0 +1,26 @@ +package com.daon.onjung.suggestion.application.dto.response; + +import com.daon.onjung.core.dto.SelfValidating; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Builder; +import lombok.Getter; + +@Getter +public class CreateOrDeleteLikeResponseDto extends SelfValidating { + + @JsonProperty("is_like") + private final Boolean isLike; + + @Builder + public CreateOrDeleteLikeResponseDto(Boolean isLike) { + this.isLike = isLike; + + this.validateSelf(); + } + + public static CreateOrDeleteLikeResponseDto of(Boolean isLike) { + return CreateOrDeleteLikeResponseDto.builder() + .isLike(isLike) + .build(); + } +} diff --git a/src/main/java/com/daon/onjung/suggestion/application/service/CreateCommentService.java b/src/main/java/com/daon/onjung/suggestion/application/service/CreateCommentService.java index d90fc7c..d8cd275 100644 --- a/src/main/java/com/daon/onjung/suggestion/application/service/CreateCommentService.java +++ b/src/main/java/com/daon/onjung/suggestion/application/service/CreateCommentService.java @@ -7,6 +7,7 @@ import com.daon.onjung.suggestion.application.controller.producer.CommentV1Producer; import com.daon.onjung.suggestion.application.dto.request.CommentMessage; import com.daon.onjung.suggestion.application.dto.request.CreateCommentRequestDto; +import com.daon.onjung.suggestion.application.dto.response.CreateCommentResponseDto; import com.daon.onjung.suggestion.application.usecase.CreateCommentUseCase; import com.daon.onjung.suggestion.domain.Board; import com.daon.onjung.suggestion.repository.mysql.BoardRepository; @@ -27,7 +28,7 @@ public class CreateCommentService implements CreateCommentUseCase { @Override @Transactional - public void execute(UUID accountId, Long boardId, CreateCommentRequestDto requestDto) { + public CreateCommentResponseDto execute(UUID accountId, Long boardId, CreateCommentRequestDto requestDto) { // 유저 조회 User user = userRepository.findById(accountId) @@ -38,7 +39,7 @@ public void execute(UUID accountId, Long boardId, CreateCommentRequestDto reques .orElseThrow(() -> new CommonException(ErrorCode.NOT_FOUND_RESOURCE)); // 댓글 생성 요청 발송 - commentProducer.sendComment(CommentMessage.builder() + return commentProducer.sendComment(CommentMessage.builder() .content(requestDto.content()) .userId(user.getId()) .boardId(board.getId()) diff --git a/src/main/java/com/daon/onjung/suggestion/application/service/CreateOrDeleteLikeService.java b/src/main/java/com/daon/onjung/suggestion/application/service/CreateOrDeleteLikeService.java index 6c0089d..2e21dda 100644 --- a/src/main/java/com/daon/onjung/suggestion/application/service/CreateOrDeleteLikeService.java +++ b/src/main/java/com/daon/onjung/suggestion/application/service/CreateOrDeleteLikeService.java @@ -2,6 +2,7 @@ import com.daon.onjung.suggestion.application.controller.producer.LikeV1Producer; import com.daon.onjung.suggestion.application.dto.request.LikeMessage; +import com.daon.onjung.suggestion.application.dto.response.CreateOrDeleteLikeResponseDto; import com.daon.onjung.suggestion.application.usecase.CreateOrDeleteLikeUseCase; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @@ -17,9 +18,9 @@ public class CreateOrDeleteLikeService implements CreateOrDeleteLikeUseCase { @Override @Transactional - public void execute(UUID accountId, Long boardId) { + public CreateOrDeleteLikeResponseDto execute(UUID accountId, Long boardId) { - likeProducer.sendLike(LikeMessage.builder() + return likeProducer.sendLike(LikeMessage.builder() .boardId(boardId) .userId(accountId) .build()); diff --git a/src/main/java/com/daon/onjung/suggestion/application/service/SendCommentRequestService.java b/src/main/java/com/daon/onjung/suggestion/application/service/SendCommentRequestService.java index fbe176b..589215d 100644 --- a/src/main/java/com/daon/onjung/suggestion/application/service/SendCommentRequestService.java +++ b/src/main/java/com/daon/onjung/suggestion/application/service/SendCommentRequestService.java @@ -1,6 +1,7 @@ package com.daon.onjung.suggestion.application.service; import com.daon.onjung.suggestion.application.dto.request.CommentMessage; +import com.daon.onjung.suggestion.application.dto.response.CreateCommentResponseDto; import com.daon.onjung.suggestion.application.usecase.SendCommentRequestUseCase; import lombok.RequiredArgsConstructor; import org.springframework.amqp.rabbit.core.RabbitTemplate; @@ -13,12 +14,11 @@ public class SendCommentRequestService implements SendCommentRequestUseCase { private final RabbitTemplate rabbitTemplate; @Override - public void execute(CommentMessage commentMessage) { - + public CreateCommentResponseDto execute(CommentMessage commentMessage) { // 모듈러 연산을 사용하여 Queue 분리. boardId가 짝수인경우 board.0을 통해 Queue2로 라우팅 // 홀수인경우 board.1을 통해 Queue 1 로 라우팅 // Comment의 경우, 하나의 게시물에 대해 순서가 보장되어야 하므로, routingKey를 이용해 boardId를 기반으로 라우팅 String routingKey = "board." + (commentMessage.boardId() % 2); - rabbitTemplate.convertAndSend("board-exchange", routingKey, commentMessage); + return (CreateCommentResponseDto) rabbitTemplate.convertSendAndReceive("board-exchange", routingKey, commentMessage); } } diff --git a/src/main/java/com/daon/onjung/suggestion/application/service/SendLikeRequestService.java b/src/main/java/com/daon/onjung/suggestion/application/service/SendLikeRequestService.java index 7fea94b..e4833ea 100644 --- a/src/main/java/com/daon/onjung/suggestion/application/service/SendLikeRequestService.java +++ b/src/main/java/com/daon/onjung/suggestion/application/service/SendLikeRequestService.java @@ -1,6 +1,7 @@ package com.daon.onjung.suggestion.application.service; import com.daon.onjung.suggestion.application.dto.request.LikeMessage; +import com.daon.onjung.suggestion.application.dto.response.CreateOrDeleteLikeResponseDto; import com.daon.onjung.suggestion.application.usecase.SendLikeRequestUseCase; import lombok.RequiredArgsConstructor; import org.springframework.amqp.rabbit.core.RabbitTemplate; @@ -13,7 +14,9 @@ public class SendLikeRequestService implements SendLikeRequestUseCase { private final RabbitTemplate rabbitTemplate; @Override - public void execute(LikeMessage likeMessage) { - rabbitTemplate.convertAndSend("like-queue", likeMessage); + public CreateOrDeleteLikeResponseDto execute(LikeMessage likeMessage) { + + Boolean response = (Boolean) rabbitTemplate.convertSendAndReceive("like-queue", likeMessage); + return CreateOrDeleteLikeResponseDto.of(response); } } diff --git a/src/main/java/com/daon/onjung/suggestion/application/usecase/CreateCommentUseCase.java b/src/main/java/com/daon/onjung/suggestion/application/usecase/CreateCommentUseCase.java index dc132e1..ecba6dd 100644 --- a/src/main/java/com/daon/onjung/suggestion/application/usecase/CreateCommentUseCase.java +++ b/src/main/java/com/daon/onjung/suggestion/application/usecase/CreateCommentUseCase.java @@ -2,10 +2,11 @@ import com.daon.onjung.core.annotation.bean.UseCase; import com.daon.onjung.suggestion.application.dto.request.CreateCommentRequestDto; +import com.daon.onjung.suggestion.application.dto.response.CreateCommentResponseDto; import java.util.UUID; @UseCase public interface CreateCommentUseCase { - void execute(UUID accountId, Long boardId, CreateCommentRequestDto requestDto); + CreateCommentResponseDto execute(UUID accountId, Long boardId, CreateCommentRequestDto requestDto); } diff --git a/src/main/java/com/daon/onjung/suggestion/application/usecase/CreateOrDeleteLikeUseCase.java b/src/main/java/com/daon/onjung/suggestion/application/usecase/CreateOrDeleteLikeUseCase.java index 17bdde5..f50b048 100644 --- a/src/main/java/com/daon/onjung/suggestion/application/usecase/CreateOrDeleteLikeUseCase.java +++ b/src/main/java/com/daon/onjung/suggestion/application/usecase/CreateOrDeleteLikeUseCase.java @@ -1,11 +1,12 @@ package com.daon.onjung.suggestion.application.usecase; import com.daon.onjung.core.annotation.bean.UseCase; +import com.daon.onjung.suggestion.application.dto.response.CreateOrDeleteLikeResponseDto; import java.util.UUID; @UseCase public interface CreateOrDeleteLikeUseCase { - void execute(UUID accountId, Long boardId); + CreateOrDeleteLikeResponseDto execute(UUID accountId, Long boardId); } diff --git a/src/main/java/com/daon/onjung/suggestion/application/usecase/SendCommentRequestUseCase.java b/src/main/java/com/daon/onjung/suggestion/application/usecase/SendCommentRequestUseCase.java index 5115830..bb2f104 100644 --- a/src/main/java/com/daon/onjung/suggestion/application/usecase/SendCommentRequestUseCase.java +++ b/src/main/java/com/daon/onjung/suggestion/application/usecase/SendCommentRequestUseCase.java @@ -2,8 +2,9 @@ import com.daon.onjung.core.annotation.bean.UseCase; import com.daon.onjung.suggestion.application.dto.request.CommentMessage; +import com.daon.onjung.suggestion.application.dto.response.CreateCommentResponseDto; @UseCase public interface SendCommentRequestUseCase { - void execute(CommentMessage commentMessage); + CreateCommentResponseDto execute(CommentMessage commentMessage); } diff --git a/src/main/java/com/daon/onjung/suggestion/application/usecase/SendLikeRequestUseCase.java b/src/main/java/com/daon/onjung/suggestion/application/usecase/SendLikeRequestUseCase.java index 4742a64..333567d 100644 --- a/src/main/java/com/daon/onjung/suggestion/application/usecase/SendLikeRequestUseCase.java +++ b/src/main/java/com/daon/onjung/suggestion/application/usecase/SendLikeRequestUseCase.java @@ -2,8 +2,9 @@ import com.daon.onjung.core.annotation.bean.UseCase; import com.daon.onjung.suggestion.application.dto.request.LikeMessage; +import com.daon.onjung.suggestion.application.dto.response.CreateOrDeleteLikeResponseDto; @UseCase public interface SendLikeRequestUseCase { - void execute(LikeMessage likeMessage); + CreateOrDeleteLikeResponseDto execute(LikeMessage likeMessage); }