diff --git a/src/main/java/com/kustacks/kuring/admin/adapter/in/web/AdminCommandApiV2.java b/src/main/java/com/kustacks/kuring/admin/adapter/in/web/AdminCommandApiV2.java index d4518413e..43e352ffc 100644 --- a/src/main/java/com/kustacks/kuring/admin/adapter/in/web/AdminCommandApiV2.java +++ b/src/main/java/com/kustacks/kuring/admin/adapter/in/web/AdminCommandApiV2.java @@ -1,6 +1,7 @@ package com.kustacks.kuring.admin.adapter.in.web; import com.google.firebase.database.annotations.NotNull; +import com.kustacks.kuring.admin.adapter.in.web.dto.AcademicTestNotificationRequest; import com.kustacks.kuring.admin.adapter.in.web.dto.AdminAlertCreateRequest; import com.kustacks.kuring.admin.adapter.in.web.dto.RealNotificationRequest; import com.kustacks.kuring.admin.adapter.in.web.dto.TestNotificationRequest; @@ -38,6 +39,7 @@ import static com.kustacks.kuring.common.dto.ResponseCodeAndMessages.ADMIN_EMBEDDING_NOTICE_SUCCESS; import static com.kustacks.kuring.common.dto.ResponseCodeAndMessages.ADMIN_REAL_NOTICE_CREATE_SUCCESS; +import static com.kustacks.kuring.common.dto.ResponseCodeAndMessages.ADMIN_TEST_ACADEMIC_CREATE_SUCCESS; import static com.kustacks.kuring.common.dto.ResponseCodeAndMessages.ADMIN_TEST_NOTICE_CREATE_SUCCESS; @Tag(name = "Admin-Command", description = "관리자가 주체가 되는 정보 수정") @@ -75,6 +77,18 @@ public ResponseEntity> createRealNotice( return ResponseEntity.ok().body(new BaseResponse<>(ADMIN_REAL_NOTICE_CREATE_SUCCESS, null)); } + @Operation(summary = "테스트 학사일정 알림 전송", description = "테스트 학사일정 알림을 전송합니다, 실제 운영시 사용하지 않습니다") + @SecurityRequirement(name = "JWT") + @Secured(AdminRole.ROLE_ROOT) + @PostMapping("/academic/dev") + public ResponseEntity> sendAcademicTest( + @RequestBody AcademicTestNotificationRequest request + ) { + adminCommandUseCase.createAcademicTestNotification(request.toCommand()); + return ResponseEntity.ok().body(new BaseResponse<>(ADMIN_TEST_ACADEMIC_CREATE_SUCCESS, null)); + } + + @Operation(summary = "예약 알림 등록", description = "서버에 알림 시간을 yyyy-MM-dd HH:mm:ss 형태로 요청시 예약 알림을 등록한다") @SecurityRequirement(name = "JWT") @Secured(AdminRole.ROLE_ROOT) diff --git a/src/main/java/com/kustacks/kuring/admin/adapter/in/web/dto/AcademicTestNotificationRequest.java b/src/main/java/com/kustacks/kuring/admin/adapter/in/web/dto/AcademicTestNotificationRequest.java new file mode 100644 index 000000000..ee98817f8 --- /dev/null +++ b/src/main/java/com/kustacks/kuring/admin/adapter/in/web/dto/AcademicTestNotificationRequest.java @@ -0,0 +1,12 @@ +package com.kustacks.kuring.admin.adapter.in.web.dto; + +import com.kustacks.kuring.message.application.port.in.dto.AcademicTestNotificationCommand; + +public record AcademicTestNotificationRequest( + String title, + String body +) { + public AcademicTestNotificationCommand toCommand() { + return new AcademicTestNotificationCommand(title, body); + } +} \ No newline at end of file diff --git a/src/main/java/com/kustacks/kuring/admin/adapter/out/event/AdminFirebaseMessageAdapter.java b/src/main/java/com/kustacks/kuring/admin/adapter/out/event/AdminFirebaseMessageAdapter.java index 80b04e495..61d238e89 100644 --- a/src/main/java/com/kustacks/kuring/admin/adapter/out/event/AdminFirebaseMessageAdapter.java +++ b/src/main/java/com/kustacks/kuring/admin/adapter/out/event/AdminFirebaseMessageAdapter.java @@ -2,6 +2,7 @@ import com.kustacks.kuring.admin.application.port.out.AdminEventPort; import com.kustacks.kuring.common.domain.Events; +import com.kustacks.kuring.message.adapter.in.event.dto.AcademicTestNotificationEvent; import com.kustacks.kuring.message.adapter.in.event.dto.AdminNotificationEvent; import com.kustacks.kuring.message.adapter.in.event.dto.AdminTestNotificationEvent; import com.kustacks.kuring.message.application.service.exception.FirebaseMessageSendException; @@ -37,4 +38,9 @@ public void sendTestNotificationByAdmin( .build() ); } + + @Override + public void sendAcademicTestNotification(String title, String body) { + Events.raise(new AcademicTestNotificationEvent(title, body)); + } } diff --git a/src/main/java/com/kustacks/kuring/admin/application/port/in/AdminCommandUseCase.java b/src/main/java/com/kustacks/kuring/admin/application/port/in/AdminCommandUseCase.java index 005e3f051..68ae6de59 100644 --- a/src/main/java/com/kustacks/kuring/admin/application/port/in/AdminCommandUseCase.java +++ b/src/main/java/com/kustacks/kuring/admin/application/port/in/AdminCommandUseCase.java @@ -4,6 +4,7 @@ import com.kustacks.kuring.admin.application.port.in.dto.TestNotificationCommand; import com.kustacks.kuring.alert.application.port.in.dto.AlertCreateCommand; import com.kustacks.kuring.alert.application.port.in.dto.DataEmbeddingCommand; +import com.kustacks.kuring.message.application.port.in.dto.AcademicTestNotificationCommand; public interface AdminCommandUseCase { @@ -18,4 +19,7 @@ public interface AdminCommandUseCase { void embeddingCustomData(DataEmbeddingCommand command); void resubscribeAllUsersToTopics(); + + void createAcademicTestNotification(AcademicTestNotificationCommand command); + } diff --git a/src/main/java/com/kustacks/kuring/admin/application/port/out/AdminEventPort.java b/src/main/java/com/kustacks/kuring/admin/application/port/out/AdminEventPort.java index 3eeb8a351..0b8e5a9e7 100644 --- a/src/main/java/com/kustacks/kuring/admin/application/port/out/AdminEventPort.java +++ b/src/main/java/com/kustacks/kuring/admin/application/port/out/AdminEventPort.java @@ -13,4 +13,7 @@ void sendTestNotificationByAdmin( String korName, String url ) throws FirebaseMessageSendException; + + void sendAcademicTestNotification(String title, String body); + } diff --git a/src/main/java/com/kustacks/kuring/admin/application/service/AdminCommandService.java b/src/main/java/com/kustacks/kuring/admin/application/service/AdminCommandService.java index 1e6b78b91..e34225d91 100644 --- a/src/main/java/com/kustacks/kuring/admin/application/service/AdminCommandService.java +++ b/src/main/java/com/kustacks/kuring/admin/application/service/AdminCommandService.java @@ -12,6 +12,7 @@ import com.kustacks.kuring.auth.userdetails.UserDetailsServicePort; import com.kustacks.kuring.common.annotation.UseCase; import com.kustacks.kuring.common.properties.ServerProperties; +import com.kustacks.kuring.message.application.port.in.dto.AcademicTestNotificationCommand; import com.kustacks.kuring.message.application.port.out.FirebaseSubscribePort; import com.kustacks.kuring.notice.domain.CategoryName; import com.kustacks.kuring.user.application.port.out.UserQueryPort; @@ -82,6 +83,14 @@ public void createRealNoticeForAllUser(RealNotificationCommand command) { adminEventPort.sendNotificationByAdmin(command.title(), command.body(), command.url()); } + @Override + public void createAcademicTestNotification(AcademicTestNotificationCommand command) { + adminEventPort.sendAcademicTestNotification( + command.title(), + command.body() + ); + } + @Override public void addAlertSchedule(AlertCreateCommand command) { adminAlertEventPort.addAlertSchedule(command.title(), command.content(), command.alertTime()); diff --git a/src/main/java/com/kustacks/kuring/calendar/application/service/AcademicEventNotificationService.java b/src/main/java/com/kustacks/kuring/calendar/application/service/AcademicEventNotificationService.java index 454c164eb..7ecef0004 100644 --- a/src/main/java/com/kustacks/kuring/calendar/application/service/AcademicEventNotificationService.java +++ b/src/main/java/com/kustacks/kuring/calendar/application/service/AcademicEventNotificationService.java @@ -16,6 +16,7 @@ import java.util.List; import static com.kustacks.kuring.message.application.service.FirebaseSubscribeService.ACADEMIC_EVENT_TOPIC; +import static com.kustacks.kuring.message.domain.MessageType.ACADEMIC; @Slf4j @UseCase @@ -99,6 +100,7 @@ private Message makeMessage(String title, String body) { .setTopic(serverProperties.ifDevThenAddSuffix(ACADEMIC_EVENT_TOPIC)) .putData("title", title) .putData("body", body) + .putData("messageType", ACADEMIC.getValue()) .build(); } } \ No newline at end of file diff --git a/src/main/java/com/kustacks/kuring/common/dto/ResponseCodeAndMessages.java b/src/main/java/com/kustacks/kuring/common/dto/ResponseCodeAndMessages.java index 406454886..5c019a824 100644 --- a/src/main/java/com/kustacks/kuring/common/dto/ResponseCodeAndMessages.java +++ b/src/main/java/com/kustacks/kuring/common/dto/ResponseCodeAndMessages.java @@ -30,6 +30,7 @@ public enum ResponseCodeAndMessages { ADMIN_LOAD_BAD_WORDS(HttpStatus.OK.value(), "금칙어 로드에 성공했습니다."), ADMIN_LOAD_WHITELIST_WORDS(HttpStatus.OK.value(), "허용 단어 로드에 성공했습니다."), ADMIN_USER_SUBSCRIPTION_UPDATE_SUCCESS(HttpStatus.OK.value(), "사용자들의 구독 정보를 성공적으로 재설정했습니다."), + ADMIN_TEST_ACADEMIC_CREATE_SUCCESS(HttpStatus.OK.value(), "테스트 학사일정 알림 생성에 성공하였습니다"), /* User */ USER_REGISTER_SUCCESS(HttpStatus.OK.value(), "회원가입에 성공하였습니다"), @@ -52,7 +53,7 @@ public enum ResponseCodeAndMessages { /* Email */ EMAIL_SEND_SUCCESS(HttpStatus.OK.value(), "이메일 전송에 성공했습니다."), - EMAIL_CODE_VERIFY_SUCCESS(HttpStatus.OK.value(),"인증에 성공했습니다."), + EMAIL_CODE_VERIFY_SUCCESS(HttpStatus.OK.value(), "인증에 성공했습니다."), REPORT_SEARCH_SUCCESS(HttpStatus.OK.value(), "신고 목록 조회에 성공하였습니다"), REPORT_COMMENT_SUCCESS(HttpStatus.CREATED.value(), "댓글 신고에 성공했습니다"), diff --git a/src/main/java/com/kustacks/kuring/message/adapter/in/event/MessageAdminEventListener.java b/src/main/java/com/kustacks/kuring/message/adapter/in/event/MessageAdminEventListener.java index 5103ccc84..500a8d988 100644 --- a/src/main/java/com/kustacks/kuring/message/adapter/in/event/MessageAdminEventListener.java +++ b/src/main/java/com/kustacks/kuring/message/adapter/in/event/MessageAdminEventListener.java @@ -1,5 +1,6 @@ package com.kustacks.kuring.message.adapter.in.event; +import com.kustacks.kuring.message.adapter.in.event.dto.AcademicTestNotificationEvent; import com.kustacks.kuring.message.adapter.in.event.dto.AdminNotificationEvent; import com.kustacks.kuring.message.adapter.in.event.dto.AdminTestNotificationEvent; import com.kustacks.kuring.message.adapter.in.event.dto.AlertSendEvent; @@ -37,4 +38,13 @@ public void sendAlertEvent( ) { firebaseWithAdminUseCase.sendNotificationByAdmin(event.toCommand()); } + + @Async + @EventListener + public void sendAcademicTestNotificationEvent( + AcademicTestNotificationEvent event + ) { + firebaseWithAdminUseCase.sendAcademicTestNotification(event.toCommand()); + } + } diff --git a/src/main/java/com/kustacks/kuring/message/adapter/in/event/dto/AcademicTestNotificationEvent.java b/src/main/java/com/kustacks/kuring/message/adapter/in/event/dto/AcademicTestNotificationEvent.java new file mode 100644 index 000000000..64c724c79 --- /dev/null +++ b/src/main/java/com/kustacks/kuring/message/adapter/in/event/dto/AcademicTestNotificationEvent.java @@ -0,0 +1,12 @@ +package com.kustacks.kuring.message.adapter.in.event.dto; + +import com.kustacks.kuring.message.application.port.in.dto.AcademicTestNotificationCommand; + +public record AcademicTestNotificationEvent( + String title, + String body +) { + public AcademicTestNotificationCommand toCommand() { + return new AcademicTestNotificationCommand(title, body); + } +} diff --git a/src/main/java/com/kustacks/kuring/message/application/port/in/FirebaseWithAdminUseCase.java b/src/main/java/com/kustacks/kuring/message/application/port/in/FirebaseWithAdminUseCase.java index 76f9ed305..22970b96b 100644 --- a/src/main/java/com/kustacks/kuring/message/application/port/in/FirebaseWithAdminUseCase.java +++ b/src/main/java/com/kustacks/kuring/message/application/port/in/FirebaseWithAdminUseCase.java @@ -1,9 +1,14 @@ package com.kustacks.kuring.message.application.port.in; +import com.kustacks.kuring.message.application.port.in.dto.AcademicTestNotificationCommand; import com.kustacks.kuring.message.application.port.in.dto.AdminNotificationCommand; import com.kustacks.kuring.message.application.port.in.dto.AdminTestNotificationCommand; public interface FirebaseWithAdminUseCase { void sendNotificationByAdmin(AdminNotificationCommand command); + void sendTestNotificationByAdmin(AdminTestNotificationCommand command); + + void sendAcademicTestNotification(AcademicTestNotificationCommand command); + } diff --git a/src/main/java/com/kustacks/kuring/message/application/port/in/dto/AcademicTestNotificationCommand.java b/src/main/java/com/kustacks/kuring/message/application/port/in/dto/AcademicTestNotificationCommand.java new file mode 100644 index 000000000..706bf7147 --- /dev/null +++ b/src/main/java/com/kustacks/kuring/message/application/port/in/dto/AcademicTestNotificationCommand.java @@ -0,0 +1,7 @@ +package com.kustacks.kuring.message.application.port.in.dto; + +public record AcademicTestNotificationCommand( + String title, + String body +) { +} diff --git a/src/main/java/com/kustacks/kuring/message/application/service/FirebaseNotificationService.java b/src/main/java/com/kustacks/kuring/message/application/service/FirebaseNotificationService.java index 6bf7f55e3..d736732e4 100644 --- a/src/main/java/com/kustacks/kuring/message/application/service/FirebaseNotificationService.java +++ b/src/main/java/com/kustacks/kuring/message/application/service/FirebaseNotificationService.java @@ -9,6 +9,7 @@ import com.kustacks.kuring.common.exception.code.ErrorCode; import com.kustacks.kuring.common.properties.ServerProperties; import com.kustacks.kuring.message.application.port.in.FirebaseWithAdminUseCase; +import com.kustacks.kuring.message.application.port.in.dto.AcademicTestNotificationCommand; import com.kustacks.kuring.message.application.port.in.dto.AdminNotificationCommand; import com.kustacks.kuring.message.application.port.in.dto.AdminTestNotificationCommand; import com.kustacks.kuring.message.application.port.out.FirebaseMessagingPort; @@ -22,7 +23,11 @@ import java.util.Map; import java.util.function.UnaryOperator; +import static com.kustacks.kuring.message.application.service.FirebaseSubscribeService.ACADEMIC_EVENT_TOPIC; import static com.kustacks.kuring.message.application.service.FirebaseSubscribeService.ALL_DEVICE_SUBSCRIBED_TOPIC; +import static com.kustacks.kuring.message.domain.MessageType.ACADEMIC; +import static com.kustacks.kuring.message.domain.MessageType.ADMIN; +import static com.kustacks.kuring.message.domain.MessageType.NOTICE; @Slf4j @UseCase @@ -62,6 +67,29 @@ public void sendNotifications(List noticeList) { sendNotices(notificationDtoList); } + @Override + public void sendAcademicTestNotification(AcademicTestNotificationCommand command) { + try { + Message newMessage = Message.builder() + .setNotification(Notification.builder() + .setTitle(command.title()) + .setBody(command.body()) + .build()) + .putAllData(Map.of( + "title", command.title(), + "body", command.body(), + "messageType", ACADEMIC.getValue() + )) + .setTopic(serverProperties.addDevSuffix(ACADEMIC_EVENT_TOPIC)) + .build(); + + firebaseMessagingPort.send(newMessage); + } catch (FirebaseMessagingException exception) { + throw new FirebaseMessageSendException(); + } + } + + private void sendNotices(List notificationDtoList) { try { loggingNoticeSendInfo(notificationDtoList); @@ -127,6 +155,7 @@ private Message createMessageFromCommand(AdminNotificationCommand command) { .setBody(command.body()) .build()) .putAllData(objectMapper.convertValue(command, Map.class)) + .putData("messageType", ADMIN.getValue()) .setTopic(serverProperties.ifDevThenAddSuffix(ALL_DEVICE_SUBSCRIBED_TOPIC)) .build(); } @@ -139,6 +168,7 @@ private Message createMessageFromDto(NoticeMessageDto messageDto, UnaryOperator< .setBody(messageDto.getSubject()) .build()) .putAllData(objectMapper.convertValue(messageDto, Map.class)) + .putData("messageType", NOTICE.getValue()) .setTopic(suffixUtil.apply(messageDto.getCategory())) .build(); } diff --git a/src/main/java/com/kustacks/kuring/message/domain/MessageType.java b/src/main/java/com/kustacks/kuring/message/domain/MessageType.java new file mode 100644 index 000000000..35f830d68 --- /dev/null +++ b/src/main/java/com/kustacks/kuring/message/domain/MessageType.java @@ -0,0 +1,15 @@ +package com.kustacks.kuring.message.domain; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@RequiredArgsConstructor +public enum MessageType { + + NOTICE("notice"), + ADMIN("admin"), + ACADEMIC("academic"); + + private final String value; +} diff --git a/src/test/java/com/kustacks/kuring/acceptance/AdminAcceptanceTest.java b/src/test/java/com/kustacks/kuring/acceptance/AdminAcceptanceTest.java index 836512b83..8d32f08f9 100644 --- a/src/test/java/com/kustacks/kuring/acceptance/AdminAcceptanceTest.java +++ b/src/test/java/com/kustacks/kuring/acceptance/AdminAcceptanceTest.java @@ -1,5 +1,6 @@ package com.kustacks.kuring.acceptance; +import com.kustacks.kuring.admin.adapter.in.web.dto.AcademicTestNotificationRequest; import com.kustacks.kuring.admin.adapter.in.web.dto.AdminAlertCreateRequest; import com.kustacks.kuring.admin.adapter.in.web.dto.RealNotificationRequest; import com.kustacks.kuring.admin.adapter.in.web.dto.TestNotificationRequest; @@ -27,6 +28,7 @@ import static com.kustacks.kuring.acceptance.AdminStep.알림_예약; import static com.kustacks.kuring.acceptance.AdminStep.예약_알림_삭제; import static com.kustacks.kuring.acceptance.AdminStep.예약_알림_조회; +import static com.kustacks.kuring.acceptance.AdminStep.테스트_학사일정_알림_발송; import static com.kustacks.kuring.acceptance.AdminStep.피드백_조회_확인; import static com.kustacks.kuring.acceptance.AdminStep.허용_단어_로드_요청; import static com.kustacks.kuring.acceptance.AuthStep.로그인_되어_있음; @@ -150,6 +152,28 @@ void role_root_admin_create_real_notification() { ); } + @DisplayName("[v2] 테스트 학사일정 알림 발송") + @Test + void role_root_admin_send_academic_test_notification() { + // given + String accessToken = 로그인_되어_있음(ADMIN_LOGIN_ID, ADMIN_PASSWORD); + + // when + var response = 테스트_학사일정_알림_발송( + accessToken, + new AcademicTestNotificationRequest("테스트-수강신청 일정", "오늘은 수강신청 일정이 있어요") + ); + + // then + assertAll( + () -> assertThat(response.statusCode()).isEqualTo(HttpStatus.OK.value()), + () -> assertThat(response.jsonPath().getInt("code")).isEqualTo(200), + () -> assertThat(response.jsonPath().getString("message")) + .isEqualTo("테스트 학사일정 알림 생성에 성공하였습니다"), + () -> assertThat(response.jsonPath().getString("data")).isNull() + ); + } + /** * Given : 등록된 ROLE_ROOT의 Admin이 있다. * When : 원하는 시간에 예약 알림을 등록한다 diff --git a/src/test/java/com/kustacks/kuring/acceptance/AdminStep.java b/src/test/java/com/kustacks/kuring/acceptance/AdminStep.java index 7707fc430..1fbb62573 100644 --- a/src/test/java/com/kustacks/kuring/acceptance/AdminStep.java +++ b/src/test/java/com/kustacks/kuring/acceptance/AdminStep.java @@ -1,5 +1,6 @@ package com.kustacks.kuring.acceptance; +import com.kustacks.kuring.admin.adapter.in.web.dto.AcademicTestNotificationRequest; import com.kustacks.kuring.admin.adapter.in.web.dto.AdminAlertCreateRequest; import io.restassured.RestAssured; import io.restassured.response.ExtractableResponse; @@ -137,4 +138,20 @@ public class AdminStep { () -> assertThat(response.jsonPath().getString("data")).isNull() ); } + + public static ExtractableResponse 테스트_학사일정_알림_발송( + String accessToken, + AcademicTestNotificationRequest request + ) { + return RestAssured + .given().log().all() + .header("Authorization", "Bearer " + accessToken) + .contentType(MediaType.APPLICATION_JSON_VALUE) + .accept(MediaType.APPLICATION_JSON_VALUE) + .body(request) + .when().post("/api/v2/admin/academic/dev") + .then().log().all() + .extract(); + } + }