diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/v2/gift/application/GiftPushService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/v2/gift/application/GiftPushService.kt new file mode 100644 index 00000000..ef976e69 --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/v2/gift/application/GiftPushService.kt @@ -0,0 +1,126 @@ +package kr.co.vividnext.sodalive.v2.gift.application + +import kr.co.vividnext.sodalive.fcm.FcmDeepLinkValue +import kr.co.vividnext.sodalive.fcm.FcmEvent +import kr.co.vividnext.sodalive.fcm.FcmEventType +import kr.co.vividnext.sodalive.fcm.notification.PushNotificationCategory +import org.springframework.context.ApplicationEventPublisher +import org.springframework.stereotype.Service + +@Service +class GiftPushService( + private val applicationEventPublisher: ApplicationEventPublisher +) { + fun sendTrackingDeadlineReminder(applicationNo: String, memberId: Long) { + publishGiftPush( + memberId = memberId, + applicationNo = applicationNo, + title = "운송장 번호를 입력해 주세요", + message = "24시간 이내에 운송장 번호를 등록해 주세요.\n기한 내 등록하지 않으면 신청이 자동 취소됩니다." + ) + } + + fun sendRecipientAddressDeadlineReminder(applicationNo: String, memberId: Long) { + publishGiftPush( + memberId = memberId, + applicationNo = applicationNo, + title = "배송지 입력 기한이 1일 남았어요", + message = "24시간 안에 배송지를 입력해 주세요.\n입력하지 않으면 선물은 반송되지 않고 폐기돼요" + ) + } + + fun sendAutoCanceledToSender(applicationNo: String, memberId: Long) { + publishGiftPush( + memberId = memberId, + applicationNo = applicationNo, + title = "선물 신청이 자동 취소됐어요", + message = "신청 후 3일 이내에 운송장 번호가 등록되지 않아 신청이 취소됐어요.\n사용한 캔은 모두 환불되었습니다." + ) + } + + fun sendTrackingRegisteredToRecipient(applicationNo: String, memberId: Long) { + publishGiftPush( + memberId = memberId, + applicationNo = applicationNo, + title = "팬이 보낸 선물이 있어요!", + message = "7일 이내에 배송지를 입력해 주세요.\n기간이 지나면 선물을 받을 수 없어요" + ) + } + + fun sendArrivedAtMailboxToSender(applicationNo: String, memberId: Long) { + publishGiftPush( + memberId = memberId, + applicationNo = applicationNo, + title = "선물이 사서함에 도착했어요", + message = "보내주신 선물이 소다라이브 사서함에 도착했어요.\n안전하게 전달할 수 있도록 선물을 확인하고 있어요." + ) + } + + fun sendArrivedAtMailboxToRecipient(applicationNo: String, memberId: Long) { + publishGiftPush( + memberId = memberId, + applicationNo = applicationNo, + title = "선물이 사서함에 도착했어요", + message = "팬이 보낸 선물을 검수하고 있어요.\n검수가 끝나면 다시 알려드릴게요" + ) + } + + fun sendInspectionCompleted(applicationNo: String, memberId: Long) { + publishGiftPush( + memberId = memberId, + applicationNo = applicationNo, + title = "선물 검수가 완료됐어요", + message = "확인된 선물을 빠른 시일 내에 입력하신 배송지로 보내드릴게요." + ) + } + + fun sendUndeliverableToSender(applicationNo: String, memberId: Long) { + publishGiftPush( + memberId = memberId, + applicationNo = applicationNo, + title = "선물을 전달할 수 없어요", + message = "도착한 선물이 선물 접수 규격에 맞지 않아 크리에이터에게 전달할 수 없어요.\n자세한 사유는 신청 상세에서 확인해 주세요." + ) + } + + fun sendUndeliverableToRecipient(applicationNo: String, memberId: Long) { + publishGiftPush( + memberId = memberId, + applicationNo = applicationNo, + title = "전달할 수 없는 선물이 있어요", + message = "검수 결과 선물 정책에 맞지 않아 해당 선물은 배송되지 않아요.\n자세한 사유를 확인해 주세요." + ) + } + + fun sendDeliveredToSender(applicationNo: String, memberId: Long) { + publishGiftPush( + memberId = memberId, + applicationNo = applicationNo, + title = "선물이 전달됐어요", + message = "보내주신 선물이 크리에이터에게 안전하게 전달됐어요." + ) + } + + fun sendDeliveredToRecipient(applicationNo: String, memberId: Long) { + publishGiftPush( + memberId = memberId, + applicationNo = applicationNo, + title = "선물이 도착했어요!", + message = "팬이 보낸 선물이 배송지에 도착했어요.\n지금 확인해 보세요." + ) + } + + private fun publishGiftPush(memberId: Long, applicationNo: String, title: String, message: String) { + applicationEventPublisher.publishEvent( + FcmEvent( + type = FcmEventType.INDIVIDUAL, + category = PushNotificationCategory.GIFT, + title = title, + message = message, + deepLinkValue = FcmDeepLinkValue.GIFT_DETAIL, + deepLinkId = applicationNo, + recipients = listOf(memberId) + ) + ) + } +} diff --git a/src/test/kotlin/kr/co/vividnext/sodalive/v2/gift/application/GiftPushServiceTest.kt b/src/test/kotlin/kr/co/vividnext/sodalive/v2/gift/application/GiftPushServiceTest.kt new file mode 100644 index 00000000..915a161c --- /dev/null +++ b/src/test/kotlin/kr/co/vividnext/sodalive/v2/gift/application/GiftPushServiceTest.kt @@ -0,0 +1,145 @@ +package kr.co.vividnext.sodalive.v2.gift.application + +import kr.co.vividnext.sodalive.fcm.FcmDeepLinkValue +import kr.co.vividnext.sodalive.fcm.FcmEvent +import kr.co.vividnext.sodalive.fcm.FcmEventType +import kr.co.vividnext.sodalive.fcm.notification.PushNotificationCategory +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test +import org.mockito.ArgumentCaptor +import org.mockito.Mockito +import org.springframework.context.ApplicationEventPublisher + +class GiftPushServiceTest { + private val eventPublisher = Mockito.mock(ApplicationEventPublisher::class.java) + private val service = GiftPushService(eventPublisher) + + @Test + fun shouldPublishTrackingDeadlineReminderToSender() { + service.sendTrackingDeadlineReminder(APPLICATION_NO, MEMBER_ID) + + val event = capturePublishedEvent() + assertGiftEvent(event) + assertEquals("운송장 번호를 입력해 주세요", event.title) + assertEquals("24시간 이내에 운송장 번호를 등록해 주세요.\n기한 내 등록하지 않으면 신청이 자동 취소됩니다.", event.message) + } + + @Test + fun shouldPublishRecipientAddressDeadlineReminderToRecipient() { + service.sendRecipientAddressDeadlineReminder(APPLICATION_NO, MEMBER_ID) + + val event = capturePublishedEvent() + assertGiftEvent(event) + assertEquals("배송지 입력 기한이 1일 남았어요", event.title) + assertEquals("24시간 안에 배송지를 입력해 주세요.\n입력하지 않으면 선물은 반송되지 않고 폐기돼요", event.message) + } + + @Test + fun shouldPublishAutoCanceledToSender() { + service.sendAutoCanceledToSender(APPLICATION_NO, MEMBER_ID) + + val event = capturePublishedEvent() + assertGiftEvent(event) + assertEquals("선물 신청이 자동 취소됐어요", event.title) + assertEquals("신청 후 3일 이내에 운송장 번호가 등록되지 않아 신청이 취소됐어요.\n사용한 캔은 모두 환불되었습니다.", event.message) + } + + @Test + fun shouldPublishTrackingRegisteredToRecipient() { + service.sendTrackingRegisteredToRecipient(APPLICATION_NO, MEMBER_ID) + + val event = capturePublishedEvent() + assertGiftEvent(event) + assertEquals("팬이 보낸 선물이 있어요!", event.title) + assertEquals("7일 이내에 배송지를 입력해 주세요.\n기간이 지나면 선물을 받을 수 없어요", event.message) + } + + @Test + fun shouldPublishArrivedAtMailboxToSender() { + service.sendArrivedAtMailboxToSender(APPLICATION_NO, MEMBER_ID) + + val event = capturePublishedEvent() + assertGiftEvent(event) + assertEquals("선물이 사서함에 도착했어요", event.title) + assertEquals("보내주신 선물이 소다라이브 사서함에 도착했어요.\n안전하게 전달할 수 있도록 선물을 확인하고 있어요.", event.message) + } + + @Test + fun shouldPublishArrivedAtMailboxToRecipient() { + service.sendArrivedAtMailboxToRecipient(APPLICATION_NO, MEMBER_ID) + + val event = capturePublishedEvent() + assertGiftEvent(event) + assertEquals("선물이 사서함에 도착했어요", event.title) + assertEquals("팬이 보낸 선물을 검수하고 있어요.\n검수가 끝나면 다시 알려드릴게요", event.message) + } + + @Test + fun shouldPublishInspectionCompletedToRecipient() { + service.sendInspectionCompleted(APPLICATION_NO, MEMBER_ID) + + val event = capturePublishedEvent() + assertGiftEvent(event) + assertEquals("선물 검수가 완료됐어요", event.title) + assertEquals("확인된 선물을 빠른 시일 내에 입력하신 배송지로 보내드릴게요.", event.message) + } + + @Test + fun shouldPublishUndeliverableToSender() { + service.sendUndeliverableToSender(APPLICATION_NO, MEMBER_ID) + + val event = capturePublishedEvent() + assertGiftEvent(event) + assertEquals("선물을 전달할 수 없어요", event.title) + assertEquals("도착한 선물이 선물 접수 규격에 맞지 않아 크리에이터에게 전달할 수 없어요.\n자세한 사유는 신청 상세에서 확인해 주세요.", event.message) + } + + @Test + fun shouldPublishUndeliverableToRecipient() { + service.sendUndeliverableToRecipient(APPLICATION_NO, MEMBER_ID) + + val event = capturePublishedEvent() + assertGiftEvent(event) + assertEquals("전달할 수 없는 선물이 있어요", event.title) + assertEquals("검수 결과 선물 정책에 맞지 않아 해당 선물은 배송되지 않아요.\n자세한 사유를 확인해 주세요.", event.message) + } + + @Test + fun shouldPublishDeliveredToSender() { + service.sendDeliveredToSender(APPLICATION_NO, MEMBER_ID) + + val event = capturePublishedEvent() + assertGiftEvent(event) + assertEquals("선물이 전달됐어요", event.title) + assertEquals("보내주신 선물이 크리에이터에게 안전하게 전달됐어요.", event.message) + } + + @Test + fun shouldPublishDeliveredToRecipient() { + service.sendDeliveredToRecipient(APPLICATION_NO, MEMBER_ID) + + val event = capturePublishedEvent() + assertGiftEvent(event) + assertEquals("선물이 도착했어요!", event.title) + assertEquals("팬이 보낸 선물이 배송지에 도착했어요.\n지금 확인해 보세요.", event.message) + } + + private fun assertGiftEvent(event: FcmEvent) { + assertEquals(FcmEventType.INDIVIDUAL, event.type) + assertEquals(PushNotificationCategory.GIFT, event.category) + assertEquals(FcmDeepLinkValue.GIFT_DETAIL, event.deepLinkValue) + assertEquals(APPLICATION_NO, event.deepLinkId) + assertEquals(listOf(MEMBER_ID), event.recipients) + } + + private fun capturePublishedEvent(): FcmEvent { + val captor = ArgumentCaptor.forClass(FcmEvent::class.java) + Mockito.verify(eventPublisher).publishEvent(captor.capture()) + return captor.value + } + + private companion object { + const val APPLICATION_NO = "G20260929000001" + const val MEMBER_ID = 1L + } +}