feat(gift): 선물함 리스트 배송지 상태를 응답한다
This commit is contained in:
@@ -62,6 +62,9 @@ data class GiftListResponse(
|
|||||||
counterpartMemberId = it.counterpartMemberId,
|
counterpartMemberId = it.counterpartMemberId,
|
||||||
counterpartNickname = it.counterpartNickname,
|
counterpartNickname = it.counterpartNickname,
|
||||||
counterpartProfileImageUrl = it.counterpartProfileImageUrl,
|
counterpartProfileImageUrl = it.counterpartProfileImageUrl,
|
||||||
|
recipientAddressRegistered = it.recipientAddressRegistered,
|
||||||
|
recipientAddressDeadlineAt = it.recipientAddressDeadlineAt?.toUtcIso(),
|
||||||
|
canceledAt = it.canceledAt?.toUtcIso(),
|
||||||
createdAt = it.createdAt?.toUtcIso()
|
createdAt = it.createdAt?.toUtcIso()
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
@@ -83,6 +86,9 @@ data class GiftListItemResponse(
|
|||||||
val counterpartMemberId: Long,
|
val counterpartMemberId: Long,
|
||||||
val counterpartNickname: String,
|
val counterpartNickname: String,
|
||||||
val counterpartProfileImageUrl: String,
|
val counterpartProfileImageUrl: String,
|
||||||
|
val recipientAddressRegistered: Boolean,
|
||||||
|
val recipientAddressDeadlineAt: String?,
|
||||||
|
val canceledAt: String?,
|
||||||
val createdAt: String?
|
val createdAt: String?
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -46,6 +46,9 @@ data class GiftListItemResult(
|
|||||||
val counterpartMemberId: Long,
|
val counterpartMemberId: Long,
|
||||||
val counterpartNickname: String,
|
val counterpartNickname: String,
|
||||||
val counterpartProfileImageUrl: String,
|
val counterpartProfileImageUrl: String,
|
||||||
|
val recipientAddressRegistered: Boolean,
|
||||||
|
val recipientAddressDeadlineAt: LocalDateTime?,
|
||||||
|
val canceledAt: LocalDateTime?,
|
||||||
val createdAt: LocalDateTime?
|
val createdAt: LocalDateTime?
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -153,10 +156,13 @@ class GiftQueryService(
|
|||||||
val membersById = memberRepository.findAllById(
|
val membersById = memberRepository.findAllById(
|
||||||
gifts.content.flatMap { listOf(it.senderMemberId, it.recipientMemberId) }.toSet()
|
gifts.content.flatMap { listOf(it.senderMemberId, it.recipientMemberId) }.toSet()
|
||||||
).associateBy { it.id ?: throw invalidRequest() }
|
).associateBy { it.id ?: throw invalidRequest() }
|
||||||
|
val deliveriesByGiftId = deliveryRepository.findByGiftIdIn(
|
||||||
|
gifts.content.mapNotNull { it.id }
|
||||||
|
).associateBy { it.giftId }
|
||||||
|
|
||||||
return GiftListResult(
|
return GiftListResult(
|
||||||
totalCount = gifts.totalElements,
|
totalCount = gifts.totalElements,
|
||||||
items = gifts.content.map { it.toGiftListItem(memberId, membersById) },
|
items = gifts.content.map { it.toGiftListItem(memberId, membersById, deliveriesByGiftId) },
|
||||||
page = normalizedPage,
|
page = normalizedPage,
|
||||||
size = normalizedSize,
|
size = normalizedSize,
|
||||||
hasNext = gifts.hasNext()
|
hasNext = gifts.hasNext()
|
||||||
@@ -203,10 +209,16 @@ class GiftQueryService(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun Gift.toGiftListItem(memberId: Long, membersById: Map<Long, Member>): GiftListItemResult {
|
private fun Gift.toGiftListItem(
|
||||||
|
memberId: Long,
|
||||||
|
membersById: Map<Long, Member>,
|
||||||
|
deliveriesByGiftId: Map<Long, GiftDelivery>
|
||||||
|
): GiftListItemResult {
|
||||||
val direction = if (senderMemberId == memberId) GiftDirection.SENT else GiftDirection.RECEIVED
|
val direction = if (senderMemberId == memberId) GiftDirection.SENT else GiftDirection.RECEIVED
|
||||||
val counterpartId = if (direction == GiftDirection.SENT) recipientMemberId else senderMemberId
|
val counterpartId = if (direction == GiftDirection.SENT) recipientMemberId else senderMemberId
|
||||||
val counterpart = membersById[counterpartId] ?: throw invalidRequest()
|
val counterpart = membersById[counterpartId] ?: throw invalidRequest()
|
||||||
|
val delivery = deliveriesByGiftId[id] ?: throw invalidRequest()
|
||||||
|
val recipientAddressRegistered = delivery.hasCompleteRecipientInformation()
|
||||||
|
|
||||||
return GiftListItemResult(
|
return GiftListItemResult(
|
||||||
applicationNo = applicationNo,
|
applicationNo = applicationNo,
|
||||||
@@ -219,6 +231,9 @@ class GiftQueryService(
|
|||||||
counterpartMemberId = counterpartId,
|
counterpartMemberId = counterpartId,
|
||||||
counterpartNickname = counterpart.nickname,
|
counterpartNickname = counterpart.nickname,
|
||||||
counterpartProfileImageUrl = counterpart.profileImageUrl(),
|
counterpartProfileImageUrl = counterpart.profileImageUrl(),
|
||||||
|
recipientAddressRegistered = recipientAddressRegistered,
|
||||||
|
recipientAddressDeadlineAt = delivery.recipientAddressDeadlineAt.takeUnless { recipientAddressRegistered },
|
||||||
|
canceledAt = delivery.canceledAt,
|
||||||
createdAt = createdAt
|
createdAt = createdAt
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
+26
@@ -100,7 +100,18 @@ class GiftControllerTest @Autowired constructor(
|
|||||||
senderMemberId = member.id!!,
|
senderMemberId = member.id!!,
|
||||||
recipientMemberId = counterpart.id!!,
|
recipientMemberId = counterpart.id!!,
|
||||||
status = GiftStatus.RECEIVED
|
status = GiftStatus.RECEIVED
|
||||||
|
).also { gift ->
|
||||||
|
giftDeliveryRepository.save(
|
||||||
|
GiftDelivery(
|
||||||
|
giftId = gift.id!!,
|
||||||
|
senderName = "홍길동",
|
||||||
|
senderPhoneNumber = "01012345678",
|
||||||
|
senderZipCode = "06234",
|
||||||
|
senderAddress = "서울시 강남구",
|
||||||
|
trackingDeadlineAt = LocalDateTime.of(2026, 10, 2, 12, 0)
|
||||||
)
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
saveGift(
|
saveGift(
|
||||||
"G-CONTROLLER-HIDDEN",
|
"G-CONTROLLER-HIDDEN",
|
||||||
senderMemberId = counterpart.id!!,
|
senderMemberId = counterpart.id!!,
|
||||||
@@ -112,7 +123,19 @@ class GiftControllerTest @Autowired constructor(
|
|||||||
senderMemberId = counterpart.id!!,
|
senderMemberId = counterpart.id!!,
|
||||||
recipientMemberId = member.id!!,
|
recipientMemberId = member.id!!,
|
||||||
status = GiftStatus.TRACKING_REGISTERED
|
status = GiftStatus.TRACKING_REGISTERED
|
||||||
|
).also { gift ->
|
||||||
|
giftDeliveryRepository.save(
|
||||||
|
GiftDelivery(
|
||||||
|
giftId = gift.id!!,
|
||||||
|
senderName = "홍길동",
|
||||||
|
senderPhoneNumber = "01012345678",
|
||||||
|
senderZipCode = "06234",
|
||||||
|
senderAddress = "서울시 강남구",
|
||||||
|
trackingDeadlineAt = LocalDateTime.of(2026, 10, 2, 12, 0),
|
||||||
|
recipientAddressDeadlineAt = LocalDateTime.of(2026, 10, 7, 12, 0)
|
||||||
)
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
mockMvc.perform(
|
mockMvc.perform(
|
||||||
get("/api/v2/gifts")
|
get("/api/v2/gifts")
|
||||||
@@ -138,6 +161,9 @@ class GiftControllerTest @Autowired constructor(
|
|||||||
.andExpect(jsonPath("$.data.items[0].counterpartMemberId").value(counterpart.id!!))
|
.andExpect(jsonPath("$.data.items[0].counterpartMemberId").value(counterpart.id!!))
|
||||||
.andExpect(jsonPath("$.data.items[0].counterpartNickname").value("counterpart"))
|
.andExpect(jsonPath("$.data.items[0].counterpartNickname").value("counterpart"))
|
||||||
.andExpect(jsonPath("$.data.items[0].counterpartProfileImageUrl").value("https://cdn.test/profile/counterpart.png"))
|
.andExpect(jsonPath("$.data.items[0].counterpartProfileImageUrl").value("https://cdn.test/profile/counterpart.png"))
|
||||||
|
.andExpect(jsonPath("$.data.items[0].recipientAddressRegistered").value(false))
|
||||||
|
.andExpect(jsonPath("$.data.items[0].recipientAddressDeadlineAt").value("2026-10-07T12:00:00Z"))
|
||||||
|
.andExpect(jsonPath("$.data.items[0].canceledAt").isEmpty)
|
||||||
.andExpect(jsonPath("$.data.items[0].createdAt").value(endsWith("Z")))
|
.andExpect(jsonPath("$.data.items[0].createdAt").value(endsWith("Z")))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+72
-1
@@ -66,12 +66,14 @@ class GiftQueryServiceTest @Autowired constructor(
|
|||||||
fun shouldListReceivedGiftForSender() {
|
fun shouldListReceivedGiftForSender() {
|
||||||
val sender = saveMember("fan")
|
val sender = saveMember("fan")
|
||||||
val recipient = saveMember("creator", "profile/creator.png")
|
val recipient = saveMember("creator", "profile/creator.png")
|
||||||
|
saveDelivery(
|
||||||
saveGift(
|
saveGift(
|
||||||
"G-SENT-RECEIVED",
|
"G-SENT-RECEIVED",
|
||||||
senderMemberId = sender.id!!,
|
senderMemberId = sender.id!!,
|
||||||
recipientMemberId = recipient.id!!,
|
recipientMemberId = recipient.id!!,
|
||||||
status = GiftStatus.RECEIVED
|
status = GiftStatus.RECEIVED
|
||||||
)
|
)
|
||||||
|
)
|
||||||
|
|
||||||
val result = queryService().getGifts(
|
val result = queryService().getGifts(
|
||||||
memberId = sender.id!!,
|
memberId = sender.id!!,
|
||||||
@@ -107,12 +109,14 @@ class GiftQueryServiceTest @Autowired constructor(
|
|||||||
recipientMemberId = recipient.id!!,
|
recipientMemberId = recipient.id!!,
|
||||||
status = GiftStatus.RECEIVED
|
status = GiftStatus.RECEIVED
|
||||||
)
|
)
|
||||||
|
saveDelivery(
|
||||||
saveGift(
|
saveGift(
|
||||||
"G-RECEIVED-VISIBLE",
|
"G-RECEIVED-VISIBLE",
|
||||||
senderMemberId = visibleSender.id!!,
|
senderMemberId = visibleSender.id!!,
|
||||||
recipientMemberId = recipient.id!!,
|
recipientMemberId = recipient.id!!,
|
||||||
status = GiftStatus.TRACKING_REGISTERED
|
status = GiftStatus.TRACKING_REGISTERED
|
||||||
)
|
)
|
||||||
|
)
|
||||||
|
|
||||||
val result = queryService().getGifts(
|
val result = queryService().getGifts(
|
||||||
memberId = recipient.id!!,
|
memberId = recipient.id!!,
|
||||||
@@ -136,13 +140,22 @@ class GiftQueryServiceTest @Autowired constructor(
|
|||||||
val creator = saveMember("creator")
|
val creator = saveMember("creator")
|
||||||
val fan = saveMember("fan")
|
val fan = saveMember("fan")
|
||||||
val hiddenFan = saveMember("hidden-fan")
|
val hiddenFan = saveMember("hidden-fan")
|
||||||
saveGift("G-ALL-SENT", senderMemberId = member.id!!, recipientMemberId = creator.id!!, status = GiftStatus.CANCELED)
|
saveDelivery(
|
||||||
|
saveGift(
|
||||||
|
"G-ALL-SENT",
|
||||||
|
senderMemberId = member.id!!,
|
||||||
|
recipientMemberId = creator.id!!,
|
||||||
|
status = GiftStatus.CANCELED
|
||||||
|
)
|
||||||
|
)
|
||||||
|
saveDelivery(
|
||||||
saveGift(
|
saveGift(
|
||||||
"G-ALL-RECEIVED",
|
"G-ALL-RECEIVED",
|
||||||
senderMemberId = fan.id!!,
|
senderMemberId = fan.id!!,
|
||||||
recipientMemberId = member.id!!,
|
recipientMemberId = member.id!!,
|
||||||
status = GiftStatus.ARRIVED_AT_MAILBOX
|
status = GiftStatus.ARRIVED_AT_MAILBOX
|
||||||
)
|
)
|
||||||
|
)
|
||||||
saveGift("G-ALL-HIDDEN", senderMemberId = hiddenFan.id!!, recipientMemberId = member.id!!, status = GiftStatus.RECEIVED)
|
saveGift("G-ALL-HIDDEN", senderMemberId = hiddenFan.id!!, recipientMemberId = member.id!!, status = GiftStatus.RECEIVED)
|
||||||
|
|
||||||
val result = queryService().getGifts(
|
val result = queryService().getGifts(
|
||||||
@@ -163,12 +176,70 @@ class GiftQueryServiceTest @Autowired constructor(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("선물함 목록은 배송지 입력 여부와 기한, 취소일을 반환한다")
|
||||||
|
fun shouldListRecipientAddressAndCancellationFields() {
|
||||||
|
val member = saveMember("creator")
|
||||||
|
val pendingSender = saveMember("pending-fan")
|
||||||
|
val completedSender = saveMember("completed-fan")
|
||||||
|
val canceledRecipient = saveMember("canceled-creator")
|
||||||
|
val pendingGift = saveGift(
|
||||||
|
"G-LIST-PENDING",
|
||||||
|
senderMemberId = pendingSender.id!!,
|
||||||
|
recipientMemberId = member.id!!,
|
||||||
|
status = GiftStatus.TRACKING_REGISTERED
|
||||||
|
)
|
||||||
|
saveDelivery(pendingGift).apply {
|
||||||
|
trackingRegisteredAt = NOW.plusHours(1)
|
||||||
|
recipientAddressDeadlineAt = NOW.plusDays(7)
|
||||||
|
}
|
||||||
|
val completedGift = saveGift(
|
||||||
|
"G-LIST-COMPLETE",
|
||||||
|
senderMemberId = completedSender.id!!,
|
||||||
|
recipientMemberId = member.id!!,
|
||||||
|
status = GiftStatus.ARRIVED_AT_MAILBOX
|
||||||
|
)
|
||||||
|
saveDelivery(completedGift).apply {
|
||||||
|
trackingRegisteredAt = NOW.plusHours(1)
|
||||||
|
arrivedAtMailboxAt = NOW.plusHours(2)
|
||||||
|
recipientAddressDeadlineAt = NOW.plusDays(7)
|
||||||
|
recipientName = "수신자"
|
||||||
|
recipientPhoneNumber = "01098765432"
|
||||||
|
recipientZipCode = "04524"
|
||||||
|
recipientAddress = "서울시 중구"
|
||||||
|
}
|
||||||
|
val canceledGift = saveGift(
|
||||||
|
"G-LIST-CANCELED",
|
||||||
|
senderMemberId = member.id!!,
|
||||||
|
recipientMemberId = canceledRecipient.id!!,
|
||||||
|
status = GiftStatus.CANCELED
|
||||||
|
)
|
||||||
|
saveDelivery(canceledGift).canceledAt = NOW.plusHours(3)
|
||||||
|
|
||||||
|
val result = queryService().getGifts(
|
||||||
|
memberId = member.id!!,
|
||||||
|
type = GiftListType.ALL,
|
||||||
|
page = 0,
|
||||||
|
size = 20
|
||||||
|
)
|
||||||
|
|
||||||
|
val items = result.items.associateBy { it.applicationNo }
|
||||||
|
assertEquals(false, items.getValue("G-LIST-PENDING").recipientAddressRegistered)
|
||||||
|
assertEquals(NOW.plusDays(7), items.getValue("G-LIST-PENDING").recipientAddressDeadlineAt)
|
||||||
|
assertNull(items.getValue("G-LIST-PENDING").canceledAt)
|
||||||
|
assertEquals(true, items.getValue("G-LIST-COMPLETE").recipientAddressRegistered)
|
||||||
|
assertNull(items.getValue("G-LIST-COMPLETE").recipientAddressDeadlineAt)
|
||||||
|
assertEquals(NOW.plusHours(3), items.getValue("G-LIST-CANCELED").canceledAt)
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("선물함 page와 size는 음수 또는 0이면 기본값으로 보정된다")
|
@DisplayName("선물함 page와 size는 음수 또는 0이면 기본값으로 보정된다")
|
||||||
fun shouldNormalizeGiftListPageAndSize() {
|
fun shouldNormalizeGiftListPageAndSize() {
|
||||||
val sender = saveMember("fan")
|
val sender = saveMember("fan")
|
||||||
val recipient = saveMember("creator")
|
val recipient = saveMember("creator")
|
||||||
|
saveDelivery(
|
||||||
saveGift("G-PAGING", senderMemberId = sender.id!!, recipientMemberId = recipient.id!!, status = GiftStatus.RECEIVED)
|
saveGift("G-PAGING", senderMemberId = sender.id!!, recipientMemberId = recipient.id!!, status = GiftStatus.RECEIVED)
|
||||||
|
)
|
||||||
|
|
||||||
val result = queryService().getGifts(
|
val result = queryService().getGifts(
|
||||||
memberId = sender.id!!,
|
memberId = sender.id!!,
|
||||||
|
|||||||
Reference in New Issue
Block a user