From 124a615a85e44824fa9e57bfdee23b4836611d68 Mon Sep 17 00:00:00 2001 From: Klaus Date: Fri, 2 Oct 2026 20:08:05 +0900 Subject: [PATCH] =?UTF-8?q?fix(gift):=20=EB=B0=B0=EC=86=A1=EC=A7=80=20?= =?UTF-8?q?=EB=AF=B8=EC=9E=85=EB=A0=A5=20=EB=A7=8C=EB=A3=8C=20=EC=83=81?= =?UTF-8?q?=ED=83=9C=20=EB=B2=94=EC=9C=84=EB=A5=BC=20=ED=99=95=EC=9E=A5?= =?UTF-8?q?=ED=95=9C=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../adapter/out/persistence/GiftRepository.kt | 8 ++- .../adapter/out/scheduler/GiftScheduler.kt | 2 +- .../out/scheduler/GiftSchedulerTest.kt | 68 ++++++++++++++++++- 3 files changed, 73 insertions(+), 5 deletions(-) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/v2/gift/adapter/out/persistence/GiftRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/v2/gift/adapter/out/persistence/GiftRepository.kt index d689862b..376fafde 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/v2/gift/adapter/out/persistence/GiftRepository.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/v2/gift/adapter/out/persistence/GiftRepository.kt @@ -80,7 +80,11 @@ interface GiftRepository : JpaRepository { """ select g from Gift g - where g.status = kr.co.vividnext.sodalive.v2.gift.domain.GiftStatus.TRACKING_REGISTERED + where g.status in ( + kr.co.vividnext.sodalive.v2.gift.domain.GiftStatus.TRACKING_REGISTERED, + kr.co.vividnext.sodalive.v2.gift.domain.GiftStatus.ARRIVED_AT_MAILBOX, + kr.co.vividnext.sodalive.v2.gift.domain.GiftStatus.INSPECTION_COMPLETED + ) and exists ( select d.id from GiftDelivery d @@ -90,7 +94,7 @@ interface GiftRepository : JpaRepository { ) """ ) - fun findTrackingRegisteredGiftsWithRecipientAddressDeadlineBefore( + fun findGiftsWithRecipientAddressDeadlineBefore( @Param("now") now: LocalDateTime ): List diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/v2/gift/adapter/out/scheduler/GiftScheduler.kt b/src/main/kotlin/kr/co/vividnext/sodalive/v2/gift/adapter/out/scheduler/GiftScheduler.kt index 5ede6cbf..3d7b57c5 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/v2/gift/adapter/out/scheduler/GiftScheduler.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/v2/gift/adapter/out/scheduler/GiftScheduler.kt @@ -51,7 +51,7 @@ class GiftScheduler( @Transactional fun markExpiredRecipientAddressGiftsUndeliverable(now: LocalDateTime): Int { - val expiredGifts = giftRepository.findTrackingRegisteredGiftsWithRecipientAddressDeadlineBefore(now) + val expiredGifts = giftRepository.findGiftsWithRecipientAddressDeadlineBefore(now) expiredGifts.forEach { gift -> gift.status = GiftStatus.UNDELIVERABLE val delivery = requireNotNull(giftDeliveryRepository.findByGiftId(requireNotNull(gift.id))) diff --git a/src/test/kotlin/kr/co/vividnext/sodalive/v2/gift/adapter/out/scheduler/GiftSchedulerTest.kt b/src/test/kotlin/kr/co/vividnext/sodalive/v2/gift/adapter/out/scheduler/GiftSchedulerTest.kt index 8cef5474..72cf16ad 100644 --- a/src/test/kotlin/kr/co/vividnext/sodalive/v2/gift/adapter/out/scheduler/GiftSchedulerTest.kt +++ b/src/test/kotlin/kr/co/vividnext/sodalive/v2/gift/adapter/out/scheduler/GiftSchedulerTest.kt @@ -117,6 +117,29 @@ class GiftSchedulerTest @Autowired constructor( assertEquals("배송지 미입력 기한 초과", delivery.undeliverableReason) } + @Test + @DisplayName("배송지 입력 기한이 지난 진행 중 선물은 현재 상태와 무관하게 전달 불가 처리한다") + fun shouldMarkOverdueInProgressGiftsWithoutRecipientAddressUndeliverable() { + val now = LocalDateTime.of(2026, 10, 9, 12, 0) + val gifts = listOf(GiftStatus.ARRIVED_AT_MAILBOX, GiftStatus.INSPECTION_COMPLETED).mapIndexed { index, status -> + saveGift( + status = status, + trackingDeadlineAt = now.minusDays(7), + recipientAddressDeadlineAt = now.minusSeconds(1), + applicationNo = "$APPLICATION_NO-$index" + ) + } + + val updatedCount = scheduler.markExpiredRecipientAddressGiftsUndeliverable(now) + + assertEquals(2, updatedCount) + gifts.forEach { (gift, delivery) -> + assertEquals(GiftStatus.UNDELIVERABLE, gift.status) + assertEquals(now, delivery.undeliverableAt) + assertEquals("배송지 미입력 기한 초과", delivery.undeliverableReason) + } + } + @Test @DisplayName("배송지 입력 기한과 같은 발송 확인 선물은 전달 불가 처리하지 않는다") fun shouldIgnoreTrackingRegisteredGiftWhoseRecipientAddressDeadlineIsNotPast() { @@ -154,6 +177,46 @@ class GiftSchedulerTest @Autowired constructor( assertEquals(null, delivery.undeliverableReason) } + @Test + @DisplayName("미래 또는 미설정 기한과 대상 외 상태는 배송지 미입력 전달 불가 처리하지 않는다") + fun shouldIgnoreUnexpiredAndOutOfScopeGiftsWithoutRecipientAddress() { + val now = LocalDateTime.of(2026, 10, 9, 12, 0) + val unexpiredGifts = listOf( + saveGift( + GiftStatus.ARRIVED_AT_MAILBOX, + now.minusDays(7), + now.plusSeconds(1), + applicationNo = "A-1002609302001" + ), + saveGift( + GiftStatus.INSPECTION_COMPLETED, + now.minusDays(7), + applicationNo = "A-1002609302002" + ) + ) + val terminalGifts = listOf( + GiftStatus.RECEIVED, + GiftStatus.DELIVERED, + GiftStatus.UNDELIVERABLE, + GiftStatus.CANCELED + ).mapIndexed { index, status -> + saveGift( + status, + now.minusDays(7), + now.minusSeconds(1), + applicationNo = "A-100260930201$index" + ) + } + + val updatedCount = scheduler.markExpiredRecipientAddressGiftsUndeliverable(now) + + assertEquals(0, updatedCount) + (unexpiredGifts + terminalGifts).forEach { (_, delivery) -> + assertEquals(null, delivery.undeliverableAt) + assertEquals(null, delivery.undeliverableReason) + } + } + @Test @DisplayName("배송지 미입력 전달 불가를 다시 실행해도 이미 전달 불가인 선물은 처리하지 않는다") fun shouldNotUpdateAlreadyUndeliverableGiftOnSecondRun() { @@ -240,11 +303,12 @@ class GiftSchedulerTest @Autowired constructor( status: GiftStatus, trackingDeadlineAt: LocalDateTime, recipientAddressDeadlineAt: LocalDateTime? = null, - recipientAddress: String? = null + recipientAddress: String? = null, + applicationNo: String = APPLICATION_NO ): Pair { val gift = giftRepository.save( Gift( - applicationNo = APPLICATION_NO, + applicationNo = applicationNo, senderMemberId = SENDER_MEMBER_ID, recipientMemberId = RECIPIENT_MEMBER_ID, status = status,