feat(gift): 선물 푸시 서비스를 추가한다

This commit is contained in:
2026-09-30 13:36:43 +09:00
parent 4ac203be18
commit 71e7195414
2 changed files with 271 additions and 0 deletions
@@ -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)
)
)
}
}
@@ -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
}
}