From 790923c9a0a9bff57b2b427be23df51382e4a00a Mon Sep 17 00:00:00 2001 From: enbraining Date: Tue, 10 Dec 2024 15:59:35 +0900 Subject: [PATCH 1/2] =?UTF-8?q?:recycle:=20FCM=20=EC=99=B8=EC=B6=9C?= =?UTF-8?q?=EC=A0=9C=20=EC=A0=84=20=EC=95=8C=EB=A6=BC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../scheduler/NotificationScheduler.kt | 12 +-- .../usecase/SendNotificationUseCase.kt | 78 ++++++------------- .../domain/notification/NotificationType.kt | 4 +- .../com/goms/v2/domain/notification/Topic.kt | 24 +++--- .../v2/thirdparty/notification/FcmAdapter.kt | 2 +- .../notification/config/FcmConfig.kt | 20 ++--- .../notification/property/FcmProperties.kt | 2 +- .../src/main/resources/application.yml | 2 +- .../notification/NotificationController.kt | 11 --- 9 files changed, 53 insertions(+), 102 deletions(-) diff --git a/goms-application/src/main/kotlin/com/goms/v2/domain/notification/scheduler/NotificationScheduler.kt b/goms-application/src/main/kotlin/com/goms/v2/domain/notification/scheduler/NotificationScheduler.kt index 5f6ca62c..c4fbfe71 100644 --- a/goms-application/src/main/kotlin/com/goms/v2/domain/notification/scheduler/NotificationScheduler.kt +++ b/goms-application/src/main/kotlin/com/goms/v2/domain/notification/scheduler/NotificationScheduler.kt @@ -9,11 +9,11 @@ import org.springframework.stereotype.Component class NotificationScheduler( private val sendNotificationUseCase: SendNotificationUseCase ) { + @Scheduled(cron = "0 30 17 ? * 1,3") // 매주 월요일, 수요일 5시 30분에 미리 공지한다. (1시간 10분 전) + fun sendFirstNotification() = + sendNotificationUseCase.execute(NotificationType.FIRST_NOTIFICATION) - @Scheduled(cron = "0 20 18 ? * 3") // 매주 수요일 6시 20분에 외출 여부 notification을 보낸다. - fun sendNotificationAtBeforeOuting() = sendNotificationUseCase.execute(NotificationType.BEFORE_OUTING) - - @Scheduled(cron = "0 20 19 ? * 3") // 매주 수요일 7시 20분에 외출 여부 notification을 보낸다. - fun sendNotificationAtAfterOuting() = sendNotificationUseCase.execute(NotificationType.AFTER_OUTING) - + @Scheduled(cron = "0 35 18 ? * 1,3") // 매주 월요일, 수요일 6시 35분에 공지한다. (5분 전) + fun sendSecondNotification() = + sendNotificationUseCase.execute(NotificationType.FINAL_NOTIFICATION) } \ No newline at end of file diff --git a/goms-application/src/main/kotlin/com/goms/v2/domain/notification/usecase/SendNotificationUseCase.kt b/goms-application/src/main/kotlin/com/goms/v2/domain/notification/usecase/SendNotificationUseCase.kt index 485b04a9..b7a07305 100644 --- a/goms-application/src/main/kotlin/com/goms/v2/domain/notification/usecase/SendNotificationUseCase.kt +++ b/goms-application/src/main/kotlin/com/goms/v2/domain/notification/usecase/SendNotificationUseCase.kt @@ -7,82 +7,54 @@ import com.goms.v2.domain.notification.spi.NotificationPort import com.goms.v2.repository.notification.DeviceTokenRepository import com.goms.v2.repository.outing.OutingBlackListRepository import com.goms.v2.repository.outing.OutingRepository +import com.goms.v2.repository.outingDate.DeniedOutingDateRepository +import java.time.LocalDate import java.util.* @UseCaseWithReadOnlyTransaction class SendNotificationUseCase( private val notificationPort: NotificationPort, private val deviceTokenRepository: DeviceTokenRepository, - private val outingRepository: OutingRepository, - private val outingBlackListRepository: OutingBlackListRepository + private val deniedOutingDateRepository: DeniedOutingDateRepository, ) { fun execute(notificationType: NotificationType) { + val isExistTodayOutingDate = deniedOutingDateRepository.existsByOutingDate(LocalDate.now()) + when (notificationType) { - // 외출 전 지난주 지각자 수에 따라서 외출 알림 발송 - NotificationType.BEFORE_OUTING -> { + NotificationType.FIRST_NOTIFICATION -> { runCatching { - val outingBlackList = outingBlackListRepository.findAll() - if (outingBlackList.isEmpty()) { - notificationPort.sendNotification( - deviceTokens = deviceTokenRepository.findAll().map { it.token }, - notification = Notification( - title = Topic.BEFORE_OUTING.title, - content = Topic.BEFORE_OUTING.content, - writer = Writer.GOMS - ) - ) - } else { - val outingBlackListToken = outingBlackList.map { findDeviceTokenByAccountIdx(it.accountIdx) }.map { it.token } - - notificationPort.sendNotification( - deviceTokens = outingBlackListToken, - notification = Notification( - title = Topic.GROUNDED.title, - content = Topic.GROUNDED.content, + notificationPort.sendNotification( + deviceTokens = deviceTokenRepository.findAll().map { it.token }, + notification = Notification( + title = if(isExistTodayOutingDate) Topic.DENIED_NOTIFICATION.title + else Topic.FIRST_NOTIFICATION.title, + content = if(isExistTodayOutingDate) Topic.DENIED_NOTIFICATION.content + else Topic.FIRST_NOTIFICATION.content, writer = Writer.GOMS ) ) + }.onFailure { + it.printStackTrace() + } + } + NotificationType.FINAL_NOTIFICATION -> { + if(!isExistTodayOutingDate){ + runCatching { notificationPort.sendNotification( - deviceTokens = deviceTokenRepository.findAll().filter { !outingBlackListToken.contains(it.token) }.map { it.token }, + deviceTokens = deviceTokenRepository.findAll().map { it.token }, notification = Notification( - title = Topic.BEFORE_OUTING.title, - content = Topic.BEFORE_OUTING.content, + title = Topic.FINAL_NOTIFICATION.title, + content = Topic.FINAL_NOTIFICATION.content, writer = Writer.GOMS ) ) + }.onFailure { + it.printStackTrace() } - }.onFailure { - it.printStackTrace() - } - - } - - // 외출 5분 전 아직 복귀하지 않은 학생들에게 알림 발송 - NotificationType.AFTER_OUTING -> { - runCatching { - val outingList = outingRepository.findAll() - // 외출자가 없으면 반환하고 끝낸다. - if (outingList.isEmpty()) return - - notificationPort.sendNotification( - deviceTokens = outingList.map { findDeviceTokenByAccountIdx(it.account.idx) }.map { it.token }, - notification = Notification( - title = Topic.AFTER_OUTING.title, - content = Topic.AFTER_OUTING.content, - writer = Writer.GOMS - ) - ) - }.onFailure { - it.stackTrace } } } } - - private fun findDeviceTokenByAccountIdx(accountIdx: UUID): DeviceToken = - deviceTokenRepository.findByIdxOrNull(accountIdx) - ?: throw DeviceTokenNotFoundException() - } diff --git a/goms-domain/src/main/kotlin/com/goms/v2/domain/notification/NotificationType.kt b/goms-domain/src/main/kotlin/com/goms/v2/domain/notification/NotificationType.kt index f9ec6b74..2d08d333 100644 --- a/goms-domain/src/main/kotlin/com/goms/v2/domain/notification/NotificationType.kt +++ b/goms-domain/src/main/kotlin/com/goms/v2/domain/notification/NotificationType.kt @@ -4,7 +4,5 @@ import com.goms.v2.common.annotation.Aggregate @Aggregate enum class NotificationType { - - BEFORE_OUTING, AFTER_OUTING - + FIRST_NOTIFICATION, FINAL_NOTIFICATION } \ No newline at end of file diff --git a/goms-domain/src/main/kotlin/com/goms/v2/domain/notification/Topic.kt b/goms-domain/src/main/kotlin/com/goms/v2/domain/notification/Topic.kt index cad251aa..01a25f46 100644 --- a/goms-domain/src/main/kotlin/com/goms/v2/domain/notification/Topic.kt +++ b/goms-domain/src/main/kotlin/com/goms/v2/domain/notification/Topic.kt @@ -8,20 +8,22 @@ enum class Topic( val content: String ) { - BEFORE_OUTING( - title = "오늘 수요 외출제 시행합니다!", - content = "꼭 나가기 전 후로 GOMS 앱을 통해 QR을 찍어주세요! \n" + FIRST_NOTIFICATION( + title = "[GOMS] 개발팀", + content = "✅ㅣ오늘 외출제 시행합니다!\n" + + "추가사항은 GSM 디스코드를 확인해주세요." ), - GROUNDED( - title = "외출제 금지", - content = "저번주 외출제 지각생이므로 외출 금지 대상입니다. \n" + FINAL_NOTIFICATION( + title = "[GOMS] 개발팀", + content = "⌛ㅣ잠시 후 외출제가 시작됩니다!\n" + + "신발을 꼭 착용해주세요. (크록스 X)" ), - AFTER_OUTING( - title = "금일 외출 시간이 5분 남았습니다.", - content = "외출중이신 분들은 빠르게 학교로 복귀해주세요\n! \n" - ); - + DENIED_NOTIFICATION( + title = "[GOMS] 개발팀", + content = "❌ㅣ오늘 외출제는 시행하지 않습니다!\n" + + "외출 시 무단 외출 처리됩니다." + ), } diff --git a/goms-infrastructure/src/main/kotlin/com/goms/v2/thirdparty/notification/FcmAdapter.kt b/goms-infrastructure/src/main/kotlin/com/goms/v2/thirdparty/notification/FcmAdapter.kt index 798469b6..a05acee1 100644 --- a/goms-infrastructure/src/main/kotlin/com/goms/v2/thirdparty/notification/FcmAdapter.kt +++ b/goms-infrastructure/src/main/kotlin/com/goms/v2/thirdparty/notification/FcmAdapter.kt @@ -16,7 +16,7 @@ class FcmAdapter: NotificationPort { val message = getMulticastMassageBuilderByNotification(notification) .addAllTokens(deviceTokens) .build() - firebaseInstance.sendMulticastAsync(message) + firebaseInstance.sendEachForMulticast(message) } private fun getMulticastMassageBuilderByNotification(notification: Notification) = diff --git a/goms-infrastructure/src/main/kotlin/com/goms/v2/thirdparty/notification/config/FcmConfig.kt b/goms-infrastructure/src/main/kotlin/com/goms/v2/thirdparty/notification/config/FcmConfig.kt index c472d818..ba2cb815 100644 --- a/goms-infrastructure/src/main/kotlin/com/goms/v2/thirdparty/notification/config/FcmConfig.kt +++ b/goms-infrastructure/src/main/kotlin/com/goms/v2/thirdparty/notification/config/FcmConfig.kt @@ -18,25 +18,15 @@ private val log = KotlinLogging.logger { } class FcmConfig( private val fcmProperties: FcmProperties ) { - - companion object { - const val PATH = "./credentials.json" - } - @PostConstruct fun init() { log.info("init start") runCatching { - URL(fcmProperties.fileUrl).openStream().use { - Files.copy(it, Paths.get(PATH)) - val file = File(PATH) - if (FirebaseApp.getApps().isEmpty()) { - val options = FirebaseOptions.builder() - .setCredentials(GoogleCredentials.fromStream(file.inputStream())) - .build() - FirebaseApp.initializeApp(options) - } - file.delete() + if (FirebaseApp.getApps().isEmpty()) { + val options = FirebaseOptions.builder() + .setCredentials(GoogleCredentials.fromStream(fcmProperties.credential.byteInputStream())) + .build() + FirebaseApp.initializeApp(options) } }.onFailure { it.printStackTrace() diff --git a/goms-infrastructure/src/main/kotlin/com/goms/v2/thirdparty/notification/property/FcmProperties.kt b/goms-infrastructure/src/main/kotlin/com/goms/v2/thirdparty/notification/property/FcmProperties.kt index 153c1d13..ef9f48d4 100644 --- a/goms-infrastructure/src/main/kotlin/com/goms/v2/thirdparty/notification/property/FcmProperties.kt +++ b/goms-infrastructure/src/main/kotlin/com/goms/v2/thirdparty/notification/property/FcmProperties.kt @@ -6,5 +6,5 @@ import org.springframework.boot.context.properties.ConstructorBinding @ConstructorBinding @ConfigurationProperties("fcm") class FcmProperties( - val fileUrl: String + val credential: String ) \ No newline at end of file diff --git a/goms-infrastructure/src/main/resources/application.yml b/goms-infrastructure/src/main/resources/application.yml index 5106bc5f..f4790c55 100644 --- a/goms-infrastructure/src/main/resources/application.yml +++ b/goms-infrastructure/src/main/resources/application.yml @@ -55,7 +55,7 @@ outing: expiredAt: ${OUTING_EXP} fcm: - fileUrl: ${FCM_FILE_URL} + credential: ${FCM_CREDENTIAL} cloud: aws: diff --git a/goms-presentation/src/main/kotlin/com/goms/v2/domain/notification/NotificationController.kt b/goms-presentation/src/main/kotlin/com/goms/v2/domain/notification/NotificationController.kt index e7ca7b5a..1be382f0 100644 --- a/goms-presentation/src/main/kotlin/com/goms/v2/domain/notification/NotificationController.kt +++ b/goms-presentation/src/main/kotlin/com/goms/v2/domain/notification/NotificationController.kt @@ -29,15 +29,4 @@ class NotificationController( fun deleteDeviceToken(): ResponseEntity = deleteDeviceTokenUseCase.execute() .run { ResponseEntity.status(HttpStatus.NO_CONTENT).build() } - - @GetMapping("outing/before") - fun beforeOutingTestApi(): ResponseEntity> = - sendNotificationUseCase.execute(NotificationType.BEFORE_OUTING) - .run { ResponseEntity.ok(mapOf("message" to "success")) } - - @GetMapping("outing/after") - fun afterOutingTestApi(): ResponseEntity> = - sendNotificationUseCase.execute(NotificationType.AFTER_OUTING) - .run { ResponseEntity.ok(mapOf("message" to "success")) } - } From 30cb0408455b10dbed0878d50465d81267c2d9d9 Mon Sep 17 00:00:00 2001 From: enbraining Date: Tue, 10 Dec 2024 16:15:57 +0900 Subject: [PATCH 2/2] =?UTF-8?q?:recycle:=20firebaseInstance=20=EB=A9=80?= =?UTF-8?q?=ED=8B=B0=EC=BA=90=EC=8A=A4=ED=8A=B8=20=ED=95=A8=EC=88=98=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kotlin/com/goms/v2/thirdparty/notification/FcmAdapter.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/goms-infrastructure/src/main/kotlin/com/goms/v2/thirdparty/notification/FcmAdapter.kt b/goms-infrastructure/src/main/kotlin/com/goms/v2/thirdparty/notification/FcmAdapter.kt index a05acee1..3064149e 100644 --- a/goms-infrastructure/src/main/kotlin/com/goms/v2/thirdparty/notification/FcmAdapter.kt +++ b/goms-infrastructure/src/main/kotlin/com/goms/v2/thirdparty/notification/FcmAdapter.kt @@ -16,7 +16,7 @@ class FcmAdapter: NotificationPort { val message = getMulticastMassageBuilderByNotification(notification) .addAllTokens(deviceTokens) .build() - firebaseInstance.sendEachForMulticast(message) + firebaseInstance.sendMulticast(message) } private fun getMulticastMassageBuilderByNotification(notification: Notification) =