From 322b07d794e40323aaf038a57fd7d57bb355e7d9 Mon Sep 17 00:00:00 2001 From: Klaus Date: Thu, 1 Oct 2026 22:58:11 +0900 Subject: [PATCH] =?UTF-8?q?feat(gift):=20=EB=B3=B4=EB=82=B8=20=EC=84=A0?= =?UTF-8?q?=EB=AC=BC=20=EC=83=81=EC=84=B8=EC=97=90=20=EB=B0=9B=EC=9D=84=20?= =?UTF-8?q?=EC=A3=BC=EC=86=8C=EB=A5=BC=20=EB=85=B8=EC=B6=9C=ED=95=9C?= =?UTF-8?q?=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../v2/gift/application/GiftQueryService.kt | 16 ++++- .../gift/adapter/in/web/GiftControllerTest.kt | 45 +++++++++++++ .../gift/application/GiftQueryServiceTest.kt | 65 ++++++++++++++++++- 3 files changed, 124 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/v2/gift/application/GiftQueryService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/v2/gift/application/GiftQueryService.kt index f84b6890..8bd5c35b 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/v2/gift/application/GiftQueryService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/v2/gift/application/GiftQueryService.kt @@ -7,6 +7,8 @@ import kr.co.vividnext.sodalive.v2.gift.adapter.out.persistence.Gift import kr.co.vividnext.sodalive.v2.gift.adapter.out.persistence.GiftCategoryRepository import kr.co.vividnext.sodalive.v2.gift.adapter.out.persistence.GiftDelivery import kr.co.vividnext.sodalive.v2.gift.adapter.out.persistence.GiftDeliveryRepository +import kr.co.vividnext.sodalive.v2.gift.adapter.out.persistence.GiftMailbox +import kr.co.vividnext.sodalive.v2.gift.adapter.out.persistence.GiftMailboxRepository import kr.co.vividnext.sodalive.v2.gift.adapter.out.persistence.GiftRepository import kr.co.vividnext.sodalive.v2.gift.adapter.out.persistence.GiftSizePriceRepository import kr.co.vividnext.sodalive.v2.gift.domain.GiftDirection @@ -105,6 +107,7 @@ class GiftQueryService( private val giftRepository: GiftRepository, private val deliveryRepository: GiftDeliveryRepository, private val memberRepository: MemberRepository, + private val giftMailboxRepository: GiftMailboxRepository, @Value("\${cloud.aws.cloud-front.host}") private val cloudFrontHost: String ) { @@ -186,7 +189,7 @@ class GiftQueryService( ), senderInfo = delivery.toSenderInfo().takeIf { direction == GiftDirection.SENT }, recipientAddress = recipientAddress, - mailbox = null, + mailbox = mailboxFor(direction, gift.status), trackingRequired = direction == GiftDirection.SENT && gift.status == GiftStatus.RECEIVED, recipientAddressRequired = direction == GiftDirection.RECEIVED && recipientAddress == null && gift.status !in TERMINAL_STATUSES, @@ -247,6 +250,17 @@ class GiftQueryService( return GiftAddressResult(name, phoneNumber, formatAddress(zipCode, address, recipientAddressDetail)) } + private fun mailboxFor(direction: GiftDirection, status: GiftStatus): GiftMailboxResult? { + if (direction != GiftDirection.SENT || status != GiftStatus.RECEIVED) return null + return giftMailboxRepository.findByIdOrNull(GiftMailbox.SINGLETON_ID)?.let { + GiftMailboxResult( + name = it.name, + address = formatAddress(it.zipCode, it.address, it.addressDetail), + phoneNumber = it.phoneNumber + ) + } + } + private fun GiftDelivery.tracking() = if (courierCompanyName != null && trackingNumber != null) { "${courierCompanyName}_$trackingNumber" } else { diff --git a/src/test/kotlin/kr/co/vividnext/sodalive/v2/api/gift/adapter/in/web/GiftControllerTest.kt b/src/test/kotlin/kr/co/vividnext/sodalive/v2/api/gift/adapter/in/web/GiftControllerTest.kt index 9bc22f37..e159ca08 100644 --- a/src/test/kotlin/kr/co/vividnext/sodalive/v2/api/gift/adapter/in/web/GiftControllerTest.kt +++ b/src/test/kotlin/kr/co/vividnext/sodalive/v2/api/gift/adapter/in/web/GiftControllerTest.kt @@ -14,6 +14,8 @@ import kr.co.vividnext.sodalive.v2.gift.adapter.out.persistence.GiftCategory import kr.co.vividnext.sodalive.v2.gift.adapter.out.persistence.GiftCategoryRepository import kr.co.vividnext.sodalive.v2.gift.adapter.out.persistence.GiftDelivery import kr.co.vividnext.sodalive.v2.gift.adapter.out.persistence.GiftDeliveryRepository +import kr.co.vividnext.sodalive.v2.gift.adapter.out.persistence.GiftMailbox +import kr.co.vividnext.sodalive.v2.gift.adapter.out.persistence.GiftMailboxRepository import kr.co.vividnext.sodalive.v2.gift.adapter.out.persistence.GiftRepository import kr.co.vividnext.sodalive.v2.gift.adapter.out.persistence.GiftSizePrice import kr.co.vividnext.sodalive.v2.gift.adapter.out.persistence.GiftSizePriceRepository @@ -60,6 +62,7 @@ class GiftControllerTest @Autowired constructor( private val giftDeliveryRepository: GiftDeliveryRepository, private val sizePriceRepository: GiftSizePriceRepository, private val memberRepository: MemberRepository, + private val giftMailboxRepository: GiftMailboxRepository, private val objectMapper: ObjectMapper ) { @MockBean @@ -190,6 +193,48 @@ class GiftControllerTest @Autowired constructor( .andExpect(jsonPath("$.data.statusTimeline[1].occurredAt").value("2026-09-30T12:00:00Z")) } + @Test + @DisplayName("발신자는 운송장 등록 전 선물 상세에서 받을 주소를 조회한다") + fun shouldGetMailboxForSentReceivedGiftDetail() { + val sender = memberRepository.save(Member(password = "password", nickname = "fan")) + val recipient = memberRepository.save(Member(password = "password", nickname = "creator")) + val gift = saveGift( + applicationNo = "G-CONTROLLER-MAILBOX", + senderMemberId = sender.id!!, + recipientMemberId = recipient.id!!, + status = GiftStatus.RECEIVED + ) + giftDeliveryRepository.save( + GiftDelivery( + giftId = gift.id!!, + senderName = "홍길동", + senderPhoneNumber = "01012345678", + senderZipCode = "06234", + senderAddress = "서울시 강남구", + trackingDeadlineAt = LocalDateTime.of(2026, 10, 2, 12, 0) + ) + ) + giftMailboxRepository.save( + GiftMailbox( + name = "소다라이브 선물 담당자", + phoneNumber = "01012345678", + zipCode = "06234", + address = "서울시 강남구", + addressDetail = "3층 선물 접수처" + ) + ) + + mockMvc.perform( + get("/api/v2/gifts/{applicationNo}", gift.applicationNo) + .with(user(MemberAdapter(sender))) + ) + .andExpect(status().isOk) + .andExpect(jsonPath("$.success").value(true)) + .andExpect(jsonPath("$.data.mailbox.name").value("소다라이브 선물 담당자")) + .andExpect(jsonPath("$.data.mailbox.phoneNumber").value("01012345678")) + .andExpect(jsonPath("$.data.mailbox.address").value("(06234) 서울시 강남구, 3층 선물 접수처")) + } + @Test @DisplayName("팬은 POST /api/v2/gifts로 선물을 신청하고 접수 결과를 받는다") fun shouldRegisterGift() { diff --git a/src/test/kotlin/kr/co/vividnext/sodalive/v2/gift/application/GiftQueryServiceTest.kt b/src/test/kotlin/kr/co/vividnext/sodalive/v2/gift/application/GiftQueryServiceTest.kt index 2e4cb51b..c44e8c7b 100644 --- a/src/test/kotlin/kr/co/vividnext/sodalive/v2/gift/application/GiftQueryServiceTest.kt +++ b/src/test/kotlin/kr/co/vividnext/sodalive/v2/gift/application/GiftQueryServiceTest.kt @@ -9,6 +9,8 @@ import kr.co.vividnext.sodalive.v2.gift.adapter.out.persistence.GiftCategory import kr.co.vividnext.sodalive.v2.gift.adapter.out.persistence.GiftCategoryRepository import kr.co.vividnext.sodalive.v2.gift.adapter.out.persistence.GiftDelivery import kr.co.vividnext.sodalive.v2.gift.adapter.out.persistence.GiftDeliveryRepository +import kr.co.vividnext.sodalive.v2.gift.adapter.out.persistence.GiftMailbox +import kr.co.vividnext.sodalive.v2.gift.adapter.out.persistence.GiftMailboxRepository import kr.co.vividnext.sodalive.v2.gift.adapter.out.persistence.GiftRepository import kr.co.vividnext.sodalive.v2.gift.adapter.out.persistence.GiftSizePrice import kr.co.vividnext.sodalive.v2.gift.adapter.out.persistence.GiftSizePriceRepository @@ -39,7 +41,8 @@ class GiftQueryServiceTest @Autowired constructor( private val sizePriceRepository: GiftSizePriceRepository, private val giftRepository: GiftRepository, private val deliveryRepository: GiftDeliveryRepository, - private val memberRepository: MemberRepository + private val memberRepository: MemberRepository, + private val giftMailboxRepository: GiftMailboxRepository ) { @Test fun `폼 옵션은 활성 카테고리와 활성 사이즈 가격만 반환한다`() { @@ -230,6 +233,65 @@ class GiftQueryServiceTest @Autowired constructor( assertNull(result.statusTimeline[1].occurredAt) } + @Test + @DisplayName("보낸 선물의 운송장 등록 전 상세만 받을 주소를 반환한다") + fun shouldReturnMailboxOnlyForSentReceivedGiftDetail() { + val sender = saveMember("fan") + val recipient = saveMember("creator") + val receivedGift = saveGift( + applicationNo = "G-MAILBOX-RECEIVED", + senderMemberId = sender.id!!, + recipientMemberId = recipient.id!!, + status = GiftStatus.RECEIVED + ) + saveDelivery(receivedGift) + val trackingGift = saveGift( + applicationNo = "G-MAILBOX-TRACKING", + senderMemberId = sender.id!!, + recipientMemberId = recipient.id!!, + status = GiftStatus.TRACKING_REGISTERED + ) + saveDelivery(trackingGift).trackingRegisteredAt = NOW + giftMailboxRepository.save( + GiftMailbox( + name = "소다라이브 선물 담당자", + phoneNumber = "01012345678", + zipCode = "06234", + address = "서울시 강남구", + addressDetail = "3층 선물 접수처" + ) + ) + val service = queryService() + + val receivedResult = service.getGiftDetail(sender.id!!, receivedGift.applicationNo) + val trackingSenderResult = service.getGiftDetail(sender.id!!, trackingGift.applicationNo) + val trackingRecipientResult = service.getGiftDetail(recipient.id!!, trackingGift.applicationNo) + + assertEquals("소다라이브 선물 담당자", receivedResult.mailbox?.name) + assertEquals("01012345678", receivedResult.mailbox?.phoneNumber) + assertEquals("(06234) 서울시 강남구, 3층 선물 접수처", receivedResult.mailbox?.address) + assertNull(trackingSenderResult.mailbox) + assertNull(trackingRecipientResult.mailbox) + } + + @Test + @DisplayName("받을 주소가 없으면 운송장 등록 전 상세도 mailbox가 null이다") + fun shouldReturnNullMailboxWhenMailboxNotConfigured() { + val sender = saveMember("fan") + val recipient = saveMember("creator") + val gift = saveGift( + applicationNo = "G-MAILBOX-EMPTY", + senderMemberId = sender.id!!, + recipientMemberId = recipient.id!!, + status = GiftStatus.RECEIVED + ) + saveDelivery(gift) + + val result = queryService().getGiftDetail(sender.id!!, gift.applicationNo) + + assertNull(result.mailbox) + } + @Test @DisplayName("수신자는 노출된 선물 상세에서 자신의 배송지와 발신자 닉네임만 조회한다") fun shouldReturnReceivedGiftDetailWithoutSenderInformation() { @@ -450,6 +512,7 @@ class GiftQueryServiceTest @Autowired constructor( giftRepository, deliveryRepository, memberRepository, + giftMailboxRepository, "https://cdn.test" )