From 7cc4454e14ce068af3eb2823d3fce376e37e79a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B0=95=ED=98=84=EC=9A=B1?= <43662405+hyunw9@users.noreply.github.com> Date: Tue, 16 Jul 2024 00:12:14 +0900 Subject: [PATCH] =?UTF-8?q?Feat=20:=20=EB=8B=B5=EB=B3=80=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84=20(#67)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Chore : Redis Listener 제거 * Feat : Controller 계층 구현 * Feat : Implementation 계층 구현 * Feat : Service 계층 구현 * Feat : Repository 계층 구현 --- .../application/reply/ReplyRetriever.java | 26 +++++++++ .../application/reply/ReplyService.java | 22 ++++++++ .../application/reply/ReplyUpdater.java | 18 ++++++ .../listener/RedisReplyMessageListener.java | 55 ------------------- .../listener/dto/DiaryListenedMessage.java | 12 ---- .../server/domain/reply/Reply.java | 4 ++ .../infrastructure/reply/ReplyRepository.java | 2 + .../presentation/api/ReplyController.java | 26 +++++++++ .../reply/ReplyControllerImpl.java | 34 ++++++++++++ .../reply/dto/response/ReplyResponse.java | 9 +++ .../server/support/config/RedisConfig.java | 20 ------- .../server/support/dto/type/ErrorType.java | 11 +++- 12 files changed, 149 insertions(+), 90 deletions(-) create mode 100644 src/main/java/com/donkeys_today/server/application/reply/ReplyRetriever.java create mode 100644 src/main/java/com/donkeys_today/server/application/reply/ReplyService.java create mode 100644 src/main/java/com/donkeys_today/server/application/reply/ReplyUpdater.java delete mode 100644 src/main/java/com/donkeys_today/server/application/reply/listener/RedisReplyMessageListener.java delete mode 100644 src/main/java/com/donkeys_today/server/application/reply/listener/dto/DiaryListenedMessage.java create mode 100644 src/main/java/com/donkeys_today/server/presentation/api/ReplyController.java create mode 100644 src/main/java/com/donkeys_today/server/presentation/reply/ReplyControllerImpl.java create mode 100644 src/main/java/com/donkeys_today/server/presentation/reply/dto/response/ReplyResponse.java diff --git a/src/main/java/com/donkeys_today/server/application/reply/ReplyRetriever.java b/src/main/java/com/donkeys_today/server/application/reply/ReplyRetriever.java new file mode 100644 index 0000000..9339d40 --- /dev/null +++ b/src/main/java/com/donkeys_today/server/application/reply/ReplyRetriever.java @@ -0,0 +1,26 @@ +package com.donkeys_today.server.application.reply; + +import com.donkeys_today.server.application.auth.JwtUtil; +import com.donkeys_today.server.domain.reply.Reply; +import com.donkeys_today.server.infrastructure.reply.ReplyRepository; +import com.donkeys_today.server.support.dto.type.ErrorType; +import com.donkeys_today.server.support.exception.NotFoundException; +import java.time.LocalDate; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +@Component +@RequiredArgsConstructor +public class ReplyRetriever { + + private final ReplyRepository replyRepository; + + public Reply findReplyByDate(String year, String month, String date) { + LocalDate localDate = LocalDate.of(Integer.parseInt(year), Integer.parseInt(month), + Integer.parseInt(date)); + Long userId = JwtUtil.getLoginMemberId(); + return replyRepository.findByUserIdAndDiaryCreatedDate(userId, localDate).orElseThrow( + () -> new NotFoundException(ErrorType.REPLY_NOT_FOUND) + ); + } +} diff --git a/src/main/java/com/donkeys_today/server/application/reply/ReplyService.java b/src/main/java/com/donkeys_today/server/application/reply/ReplyService.java new file mode 100644 index 0000000..17974d8 --- /dev/null +++ b/src/main/java/com/donkeys_today/server/application/reply/ReplyService.java @@ -0,0 +1,22 @@ +package com.donkeys_today.server.application.reply; + +import com.donkeys_today.server.domain.reply.Reply; +import com.donkeys_today.server.presentation.reply.dto.response.ReplyResponse; +import jakarta.transaction.Transactional; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class ReplyService { + + private final ReplyRetriever replyRetriever; + private final ReplyUpdater replyUpdater; + + @Transactional + public ReplyResponse readReply(String year, String month, String date) { + Reply reply = replyRetriever.findReplyByDate(year, month, date); + reply.readReply(); + return ReplyResponse.of(reply.getContent()); + } +} diff --git a/src/main/java/com/donkeys_today/server/application/reply/ReplyUpdater.java b/src/main/java/com/donkeys_today/server/application/reply/ReplyUpdater.java new file mode 100644 index 0000000..6612a65 --- /dev/null +++ b/src/main/java/com/donkeys_today/server/application/reply/ReplyUpdater.java @@ -0,0 +1,18 @@ +package com.donkeys_today.server.application.reply; + +import com.donkeys_today.server.domain.reply.Reply; +import com.donkeys_today.server.infrastructure.reply.ReplyRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +@Component +@RequiredArgsConstructor +public class ReplyUpdater { + + private final ReplyRepository replyRepository; + + public void readReply(Reply reply) { + reply.readReply(); + replyRepository.save(reply); + } +} diff --git a/src/main/java/com/donkeys_today/server/application/reply/listener/RedisReplyMessageListener.java b/src/main/java/com/donkeys_today/server/application/reply/listener/RedisReplyMessageListener.java deleted file mode 100644 index 5849223..0000000 --- a/src/main/java/com/donkeys_today/server/application/reply/listener/RedisReplyMessageListener.java +++ /dev/null @@ -1,55 +0,0 @@ -package com.donkeys_today.server.application.reply.listener; - -import com.donkeys_today.server.application.reply.listener.dto.DiaryListenedMessage; -import com.donkeys_today.server.support.dto.type.ErrorType; -import com.donkeys_today.server.support.exception.NotFoundException; -import com.fasterxml.jackson.databind.ObjectMapper; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.data.redis.connection.Message; -import org.springframework.data.redis.connection.MessageListener; -import org.springframework.data.redis.core.RedisTemplate; -import org.springframework.stereotype.Service; - -@Service -@RequiredArgsConstructor -@Slf4j -public class RedisReplyMessageListener implements MessageListener { - - private final ObjectMapper objectMapper; - private final RedisTemplate redisTemplate; - - private final String EXPIRED_MESSAGE_KEY_PREFIX = "diaryMessage_expiry:"; - private final String MESSAGE_KEY_PREFIX = "diaryMessage:"; - - @Override - public void onMessage(Message message, byte[] pattern) { - String expiredKey = new String(message.getBody()); - log.info("########## listen succeed ##########"); - - Long diaryId = Long.parseLong(expiredKey.split(":")[1]); - - String nonExpiredKey = MESSAGE_KEY_PREFIX + diaryId; - String value = redisTemplate.opsForValue().get(nonExpiredKey); - - validateMessagePrefix(expiredKey); - DiaryListenedMessage listenedMessage = parseStringToDiaryListenedMessage(value); - log.info("listenedMessage : userId = {}, message = {}, date = {}", listenedMessage.userId(), - listenedMessage.message(), listenedMessage.date().toString()); - } - - private void validateMessagePrefix(String expiredKey) { - if (expiredKey.startsWith(EXPIRED_MESSAGE_KEY_PREFIX)) { - return; - } - throw new NotFoundException(ErrorType.DIARY_MESSAGE_NOT_FOUND); - } - - private DiaryListenedMessage parseStringToDiaryListenedMessage(String value) { - try { - return objectMapper.readValue(value, DiaryListenedMessage.class); - } catch (Exception e) { - throw new RuntimeException(e); - } - } -} diff --git a/src/main/java/com/donkeys_today/server/application/reply/listener/dto/DiaryListenedMessage.java b/src/main/java/com/donkeys_today/server/application/reply/listener/dto/DiaryListenedMessage.java deleted file mode 100644 index 8077c36..0000000 --- a/src/main/java/com/donkeys_today/server/application/reply/listener/dto/DiaryListenedMessage.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.donkeys_today.server.application.reply.listener.dto; - -import com.fasterxml.jackson.annotation.JsonFormat; -import java.time.LocalDateTime; - -public record DiaryListenedMessage( - Long userId, - String message, - @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss") - LocalDateTime date -) { -} diff --git a/src/main/java/com/donkeys_today/server/domain/reply/Reply.java b/src/main/java/com/donkeys_today/server/domain/reply/Reply.java index 63ba4ea..898a614 100644 --- a/src/main/java/com/donkeys_today/server/domain/reply/Reply.java +++ b/src/main/java/com/donkeys_today/server/domain/reply/Reply.java @@ -37,4 +37,8 @@ public class Reply extends BaseEntity { @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "user_id") private User user; + + public void readReply() { + this.is_read = true; + } } diff --git a/src/main/java/com/donkeys_today/server/infrastructure/reply/ReplyRepository.java b/src/main/java/com/donkeys_today/server/infrastructure/reply/ReplyRepository.java index cd71c25..2b7cb54 100644 --- a/src/main/java/com/donkeys_today/server/infrastructure/reply/ReplyRepository.java +++ b/src/main/java/com/donkeys_today/server/infrastructure/reply/ReplyRepository.java @@ -3,6 +3,7 @@ import com.donkeys_today.server.domain.reply.Reply; import java.time.LocalDate; import java.util.List; +import java.util.Optional; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; @@ -11,4 +12,5 @@ public interface ReplyRepository extends JpaRepository { List findByUserIdAndDiaryCreatedDateBetween(Long userId, LocalDate start, LocalDate end); + Optional findByUserIdAndDiaryCreatedDate(Long userId, LocalDate date); } diff --git a/src/main/java/com/donkeys_today/server/presentation/api/ReplyController.java b/src/main/java/com/donkeys_today/server/presentation/api/ReplyController.java new file mode 100644 index 0000000..70a8326 --- /dev/null +++ b/src/main/java/com/donkeys_today/server/presentation/api/ReplyController.java @@ -0,0 +1,26 @@ +package com.donkeys_today.server.presentation.api; + +import com.donkeys_today.server.common.constants.Constants; +import com.donkeys_today.server.presentation.reply.dto.response.ReplyResponse; +import com.donkeys_today.server.support.dto.ApiResponse; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +@Tag(name = "답변 관련") +@RequestMapping("/api/v1") +@RestController +public interface ReplyController { + + @GetMapping("/reply") + ResponseEntity> getReply( + @RequestHeader(Constants.AUTHORIZATION) final String accessToken, + @RequestParam @Parameter(name = "연도", description = "조회할 연도", required = true) final String year, + @RequestParam @Parameter(name = "달", description = "조회할 달", required = true) final String month, + @RequestParam @Parameter(name = "일", description = "조회할 일", required = true) final String date); +} diff --git a/src/main/java/com/donkeys_today/server/presentation/reply/ReplyControllerImpl.java b/src/main/java/com/donkeys_today/server/presentation/reply/ReplyControllerImpl.java new file mode 100644 index 0000000..2601bc7 --- /dev/null +++ b/src/main/java/com/donkeys_today/server/presentation/reply/ReplyControllerImpl.java @@ -0,0 +1,34 @@ +package com.donkeys_today.server.presentation.reply; + +import com.donkeys_today.server.application.reply.ReplyService; +import com.donkeys_today.server.common.constants.Constants; +import com.donkeys_today.server.presentation.api.ReplyController; +import com.donkeys_today.server.presentation.reply.dto.response.ReplyResponse; +import com.donkeys_today.server.support.dto.ApiResponse; +import com.donkeys_today.server.support.dto.type.SuccessType; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/api/v1") +@RequiredArgsConstructor +public class ReplyControllerImpl implements ReplyController { + + private final ReplyService replyService; + + @GetMapping("/reply") + public ResponseEntity> getReply( + @RequestHeader(Constants.AUTHORIZATION) final String accessToken, + @RequestParam final String year, + @RequestParam final String month, + @RequestParam final String date) { + return ResponseEntity.status(HttpStatus.OK).body(ApiResponse.success( + SuccessType.READ_SUCCESS, replyService.readReply(year, month, date))); + } +} diff --git a/src/main/java/com/donkeys_today/server/presentation/reply/dto/response/ReplyResponse.java b/src/main/java/com/donkeys_today/server/presentation/reply/dto/response/ReplyResponse.java new file mode 100644 index 0000000..02a61be --- /dev/null +++ b/src/main/java/com/donkeys_today/server/presentation/reply/dto/response/ReplyResponse.java @@ -0,0 +1,9 @@ +package com.donkeys_today.server.presentation.reply.dto.response; + +public record ReplyResponse( + String content +) { + public static ReplyResponse of(String content) { + return new ReplyResponse(content); + } +} diff --git a/src/main/java/com/donkeys_today/server/support/config/RedisConfig.java b/src/main/java/com/donkeys_today/server/support/config/RedisConfig.java index 36ccf45..f2019dc 100644 --- a/src/main/java/com/donkeys_today/server/support/config/RedisConfig.java +++ b/src/main/java/com/donkeys_today/server/support/config/RedisConfig.java @@ -1,6 +1,5 @@ package com.donkeys_today.server.support.config; -import com.donkeys_today.server.application.reply.listener.RedisReplyMessageListener; import com.fasterxml.jackson.databind.ObjectMapper; import java.util.concurrent.Executor; import lombok.RequiredArgsConstructor; @@ -11,9 +10,6 @@ import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; -import org.springframework.data.redis.listener.PatternTopic; -import org.springframework.data.redis.listener.RedisMessageListenerContainer; -import org.springframework.data.redis.listener.adapter.MessageListenerAdapter; import org.springframework.data.redis.repository.configuration.EnableRedisRepositories; import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer; @@ -45,22 +41,6 @@ public Executor redisMessageTaskExecutor() { return threadPoolTaskExecutor; } - @Bean - public RedisMessageListenerContainer redisMessageListenerContainer( - RedisConnectionFactory redisConnectionFactory, - MessageListenerAdapter listenerAdapter) { - RedisMessageListenerContainer container = new RedisMessageListenerContainer(); - container.setConnectionFactory(redisConnectionFactory); - container.addMessageListener(listenerAdapter, new PatternTopic(EXPIRED_EVENT_PATTERN)); - container.setTaskExecutor(redisMessageTaskExecutor()); - return container; - } - - @Bean - public MessageListenerAdapter listenerAdapter(RedisReplyMessageListener listener) { - return new MessageListenerAdapter(listener); - } - @Bean @Primary public RedisTemplate redisStringTemplate( diff --git a/src/main/java/com/donkeys_today/server/support/dto/type/ErrorType.java b/src/main/java/com/donkeys_today/server/support/dto/type/ErrorType.java index d845684..a61b1d0 100644 --- a/src/main/java/com/donkeys_today/server/support/dto/type/ErrorType.java +++ b/src/main/java/com/donkeys_today/server/support/dto/type/ErrorType.java @@ -40,10 +40,15 @@ public enum ErrorType { FAIL_DIARY_ALARM_REGISTER(HttpStatus.INTERNAL_SERVER_ERROR.value(), "알람 설정에 실패했습니다."), INVALID_TIME_FORMAT(HttpStatus.BAD_REQUEST.value(), "올바르지 않은 시간 형식입니다."), - /* - ** 일기 관련 오류, + /** + * 일기 관련 오류, + */ + DIARY_MESSAGE_NOT_FOUND(HttpStatus.NOT_FOUND.value(), "일기 데이터가 존재하지 않습니다."), + + /** + * 답장 관련 오류, */ - DIARY_MESSAGE_NOT_FOUND(HttpStatus.NOT_FOUND.value(), "일기 데이터가 존재하지 않습니다.") + REPLY_NOT_FOUND(HttpStatus.NOT_FOUND.value(), "답장이 존재하지 않습니다."), ; private final int status; private final String message;