feat(gift): 보낸 선물 상세에 받을 주소를 노출한다

This commit is contained in:
2026-10-01 22:58:11 +09:00
parent 8a95dc171d
commit 322b07d794
3 changed files with 124 additions and 2 deletions
@@ -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.GiftCategoryRepository
import kr.co.vividnext.sodalive.v2.gift.adapter.out.persistence.GiftDelivery 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.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.GiftRepository
import kr.co.vividnext.sodalive.v2.gift.adapter.out.persistence.GiftSizePriceRepository import kr.co.vividnext.sodalive.v2.gift.adapter.out.persistence.GiftSizePriceRepository
import kr.co.vividnext.sodalive.v2.gift.domain.GiftDirection import kr.co.vividnext.sodalive.v2.gift.domain.GiftDirection
@@ -105,6 +107,7 @@ class GiftQueryService(
private val giftRepository: GiftRepository, private val giftRepository: GiftRepository,
private val deliveryRepository: GiftDeliveryRepository, private val deliveryRepository: GiftDeliveryRepository,
private val memberRepository: MemberRepository, private val memberRepository: MemberRepository,
private val giftMailboxRepository: GiftMailboxRepository,
@Value("\${cloud.aws.cloud-front.host}") @Value("\${cloud.aws.cloud-front.host}")
private val cloudFrontHost: String private val cloudFrontHost: String
) { ) {
@@ -186,7 +189,7 @@ class GiftQueryService(
), ),
senderInfo = delivery.toSenderInfo().takeIf { direction == GiftDirection.SENT }, senderInfo = delivery.toSenderInfo().takeIf { direction == GiftDirection.SENT },
recipientAddress = recipientAddress, recipientAddress = recipientAddress,
mailbox = null, mailbox = mailboxFor(direction, gift.status),
trackingRequired = direction == GiftDirection.SENT && gift.status == GiftStatus.RECEIVED, trackingRequired = direction == GiftDirection.SENT && gift.status == GiftStatus.RECEIVED,
recipientAddressRequired = direction == GiftDirection.RECEIVED && recipientAddress == null && recipientAddressRequired = direction == GiftDirection.RECEIVED && recipientAddress == null &&
gift.status !in TERMINAL_STATUSES, gift.status !in TERMINAL_STATUSES,
@@ -247,6 +250,17 @@ class GiftQueryService(
return GiftAddressResult(name, phoneNumber, formatAddress(zipCode, address, recipientAddressDetail)) 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) { private fun GiftDelivery.tracking() = if (courierCompanyName != null && trackingNumber != null) {
"${courierCompanyName}_$trackingNumber" "${courierCompanyName}_$trackingNumber"
} else { } else {
@@ -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.GiftCategoryRepository
import kr.co.vividnext.sodalive.v2.gift.adapter.out.persistence.GiftDelivery 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.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.GiftRepository
import kr.co.vividnext.sodalive.v2.gift.adapter.out.persistence.GiftSizePrice import kr.co.vividnext.sodalive.v2.gift.adapter.out.persistence.GiftSizePrice
import kr.co.vividnext.sodalive.v2.gift.adapter.out.persistence.GiftSizePriceRepository 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 giftDeliveryRepository: GiftDeliveryRepository,
private val sizePriceRepository: GiftSizePriceRepository, private val sizePriceRepository: GiftSizePriceRepository,
private val memberRepository: MemberRepository, private val memberRepository: MemberRepository,
private val giftMailboxRepository: GiftMailboxRepository,
private val objectMapper: ObjectMapper private val objectMapper: ObjectMapper
) { ) {
@MockBean @MockBean
@@ -190,6 +193,48 @@ class GiftControllerTest @Autowired constructor(
.andExpect(jsonPath("$.data.statusTimeline[1].occurredAt").value("2026-09-30T12:00:00Z")) .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 @Test
@DisplayName("팬은 POST /api/v2/gifts로 선물을 신청하고 접수 결과를 받는다") @DisplayName("팬은 POST /api/v2/gifts로 선물을 신청하고 접수 결과를 받는다")
fun shouldRegisterGift() { fun shouldRegisterGift() {
@@ -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.GiftCategoryRepository
import kr.co.vividnext.sodalive.v2.gift.adapter.out.persistence.GiftDelivery 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.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.GiftRepository
import kr.co.vividnext.sodalive.v2.gift.adapter.out.persistence.GiftSizePrice import kr.co.vividnext.sodalive.v2.gift.adapter.out.persistence.GiftSizePrice
import kr.co.vividnext.sodalive.v2.gift.adapter.out.persistence.GiftSizePriceRepository 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 sizePriceRepository: GiftSizePriceRepository,
private val giftRepository: GiftRepository, private val giftRepository: GiftRepository,
private val deliveryRepository: GiftDeliveryRepository, private val deliveryRepository: GiftDeliveryRepository,
private val memberRepository: MemberRepository private val memberRepository: MemberRepository,
private val giftMailboxRepository: GiftMailboxRepository
) { ) {
@Test @Test
fun `폼 옵션은 활성 카테고리와 활성 사이즈 가격만 반환한다`() { fun `폼 옵션은 활성 카테고리와 활성 사이즈 가격만 반환한다`() {
@@ -230,6 +233,65 @@ class GiftQueryServiceTest @Autowired constructor(
assertNull(result.statusTimeline[1].occurredAt) 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 @Test
@DisplayName("수신자는 노출된 선물 상세에서 자신의 배송지와 발신자 닉네임만 조회한다") @DisplayName("수신자는 노출된 선물 상세에서 자신의 배송지와 발신자 닉네임만 조회한다")
fun shouldReturnReceivedGiftDetailWithoutSenderInformation() { fun shouldReturnReceivedGiftDetailWithoutSenderInformation() {
@@ -450,6 +512,7 @@ class GiftQueryServiceTest @Autowired constructor(
giftRepository, giftRepository,
deliveryRepository, deliveryRepository,
memberRepository, memberRepository,
giftMailboxRepository,
"https://cdn.test" "https://cdn.test"
) )