fix(gift): 수령확인 시각을 초 단위로 정규화한다
This commit is contained in:
@@ -18,6 +18,7 @@ import org.springframework.data.repository.findByIdOrNull
|
|||||||
import org.springframework.stereotype.Service
|
import org.springframework.stereotype.Service
|
||||||
import org.springframework.transaction.annotation.Transactional
|
import org.springframework.transaction.annotation.Transactional
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
|
import java.time.temporal.ChronoUnit
|
||||||
|
|
||||||
data class GiftApplicationCommand(
|
data class GiftApplicationCommand(
|
||||||
val recipientMemberId: Long,
|
val recipientMemberId: Long,
|
||||||
@@ -297,7 +298,7 @@ class GiftCommandService(
|
|||||||
throw invalidRequest()
|
throw invalidRequest()
|
||||||
}
|
}
|
||||||
val delivery = deliveryRepository.findByGiftIdForUpdate(gift.id ?: throw invalidRequest()) ?: throw invalidRequest()
|
val delivery = deliveryRepository.findByGiftIdForUpdate(gift.id ?: throw invalidRequest()) ?: throw invalidRequest()
|
||||||
val deliveredAt = now()
|
val deliveredAt = now().truncatedTo(ChronoUnit.SECONDS)
|
||||||
|
|
||||||
gift.status = GiftStatus.DELIVERED
|
gift.status = GiftStatus.DELIVERED
|
||||||
delivery.deliveredAt = deliveredAt
|
delivery.deliveredAt = deliveredAt
|
||||||
|
|||||||
+53
@@ -22,6 +22,7 @@ import kr.co.vividnext.sodalive.member.MemberAdapter
|
|||||||
import kr.co.vividnext.sodalive.member.MemberRepository
|
import kr.co.vividnext.sodalive.member.MemberRepository
|
||||||
import kr.co.vividnext.sodalive.member.MemberRole
|
import kr.co.vividnext.sodalive.member.MemberRole
|
||||||
import kr.co.vividnext.sodalive.support.EmbeddedRedisInitializer
|
import kr.co.vividnext.sodalive.support.EmbeddedRedisInitializer
|
||||||
|
import kr.co.vividnext.sodalive.v2.api.gift.dto.GiftDeliveryConfirmationResponse
|
||||||
import kr.co.vividnext.sodalive.v2.gift.adapter.out.persistence.Gift
|
import kr.co.vividnext.sodalive.v2.gift.adapter.out.persistence.Gift
|
||||||
import kr.co.vividnext.sodalive.v2.gift.adapter.out.persistence.GiftCategory
|
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
|
||||||
@@ -29,11 +30,13 @@ 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.GiftRepository
|
import kr.co.vividnext.sodalive.v2.gift.adapter.out.persistence.GiftRepository
|
||||||
import kr.co.vividnext.sodalive.v2.gift.adapter.out.persistence.GiftReview
|
import kr.co.vividnext.sodalive.v2.gift.adapter.out.persistence.GiftReview
|
||||||
|
import kr.co.vividnext.sodalive.v2.gift.adapter.out.persistence.GiftReviewRepository
|
||||||
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
|
||||||
import kr.co.vividnext.sodalive.v2.gift.adapter.out.scheduler.GiftScheduler
|
import kr.co.vividnext.sodalive.v2.gift.adapter.out.scheduler.GiftScheduler
|
||||||
import kr.co.vividnext.sodalive.v2.gift.application.GiftAdminService
|
import kr.co.vividnext.sodalive.v2.gift.application.GiftAdminService
|
||||||
import kr.co.vividnext.sodalive.v2.gift.application.GiftApplicationCommand
|
import kr.co.vividnext.sodalive.v2.gift.application.GiftApplicationCommand
|
||||||
|
import kr.co.vividnext.sodalive.v2.gift.application.GiftApplicationNoGenerator
|
||||||
import kr.co.vividnext.sodalive.v2.gift.application.GiftCommandService
|
import kr.co.vividnext.sodalive.v2.gift.application.GiftCommandService
|
||||||
import kr.co.vividnext.sodalive.v2.gift.application.GiftPushService
|
import kr.co.vividnext.sodalive.v2.gift.application.GiftPushService
|
||||||
import kr.co.vividnext.sodalive.v2.gift.application.GiftRecipientAddressRegistrationCommand
|
import kr.co.vividnext.sodalive.v2.gift.application.GiftRecipientAddressRegistrationCommand
|
||||||
@@ -55,6 +58,7 @@ import org.junit.jupiter.api.Test
|
|||||||
import org.junit.jupiter.params.ParameterizedTest
|
import org.junit.jupiter.params.ParameterizedTest
|
||||||
import org.junit.jupiter.params.provider.EnumSource
|
import org.junit.jupiter.params.provider.EnumSource
|
||||||
import org.junit.jupiter.params.provider.MethodSource
|
import org.junit.jupiter.params.provider.MethodSource
|
||||||
|
import org.junit.jupiter.params.provider.ValueSource
|
||||||
import org.mockito.Mockito
|
import org.mockito.Mockito
|
||||||
import org.springframework.aop.framework.Advised
|
import org.springframework.aop.framework.Advised
|
||||||
import org.springframework.aop.support.AopUtils
|
import org.springframework.aop.support.AopUtils
|
||||||
@@ -121,6 +125,8 @@ class AdminGiftControllerTest @Autowired constructor(
|
|||||||
private val sizePriceRepository: GiftSizePriceRepository,
|
private val sizePriceRepository: GiftSizePriceRepository,
|
||||||
private val giftRepository: GiftRepository,
|
private val giftRepository: GiftRepository,
|
||||||
private val giftDeliveryRepository: GiftDeliveryRepository,
|
private val giftDeliveryRepository: GiftDeliveryRepository,
|
||||||
|
private val giftReviewRepository: GiftReviewRepository,
|
||||||
|
private val applicationNoGenerator: GiftApplicationNoGenerator,
|
||||||
private val memberRepository: MemberRepository,
|
private val memberRepository: MemberRepository,
|
||||||
private val chargeRepository: ChargeRepository,
|
private val chargeRepository: ChargeRepository,
|
||||||
private val useCanRepository: UseCanRepository,
|
private val useCanRepository: UseCanRepository,
|
||||||
@@ -592,6 +598,53 @@ class AdminGiftControllerTest @Autowired constructor(
|
|||||||
println("P16_CONFIRM_HTTP status=200 success=true rejected=true repeated=false senderPush=1 recipientPush=0")
|
println("P16_CONFIRM_HTTP status=200 success=true rejected=true repeated=false senderPush=1 recipientPush=0")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@ValueSource(ints = [0, 123456789, 999999999])
|
||||||
|
@DisplayName("수령 확인 시각은 나노초 입력에도 초 단위 DB 저장값과 응답이 일치한다")
|
||||||
|
fun shouldPersistAndReturnSameSecondPrecisionDeliveryTime(nanos: Int) {
|
||||||
|
val (gift, _) = saveAdminGift(
|
||||||
|
applicationNo = APPLICATION_NO,
|
||||||
|
status = GiftStatus.INSPECTION_COMPLETED,
|
||||||
|
senderNickname = "precision-sender",
|
||||||
|
recipientNickname = "precision-recipient",
|
||||||
|
withTracking = true
|
||||||
|
)
|
||||||
|
val giftId = requireNotNull(gift.id)
|
||||||
|
val fixedNow = LocalDateTime.of(2026, 10, 22, 23, 59, 59, nanos)
|
||||||
|
val service = GiftCommandService(
|
||||||
|
giftRepository = giftRepository,
|
||||||
|
deliveryRepository = giftDeliveryRepository,
|
||||||
|
reviewRepository = giftReviewRepository,
|
||||||
|
categoryRepository = categoryRepository,
|
||||||
|
sizePriceRepository = sizePriceRepository,
|
||||||
|
memberRepository = memberRepository,
|
||||||
|
applicationNoGenerator = applicationNoGenerator,
|
||||||
|
canPaymentService = canPaymentService,
|
||||||
|
giftPushService = giftPushService,
|
||||||
|
now = { fixedNow }
|
||||||
|
)
|
||||||
|
|
||||||
|
val result = requireNotNull(
|
||||||
|
TransactionTemplate(transactionManager).execute {
|
||||||
|
val recipient = memberRepository.findById(gift.recipientMemberId).orElseThrow()
|
||||||
|
service.confirmDelivery(recipient, gift.applicationNo).also {
|
||||||
|
entityManager.flush()
|
||||||
|
entityManager.clear()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
val persisted = requireNotNull(giftDeliveryRepository.findByGiftId(giftId)?.deliveredAt)
|
||||||
|
println(
|
||||||
|
"P16_CONFIRM_PRECISION nanos=$nanos input=$fixedNow returned=${result.deliveredAt} " +
|
||||||
|
"reloaded=$persisted expected=2026-10-22T23:59:59"
|
||||||
|
)
|
||||||
|
assertEquals(persisted, result.deliveredAt)
|
||||||
|
assertEquals(LocalDateTime.of(2026, 10, 22, 23, 59, 59), persisted)
|
||||||
|
assertEquals(0, persisted.nano)
|
||||||
|
assertEquals("2026-10-22T23:59:59Z", GiftDeliveryConfirmationResponse.from(result).deliveredAt)
|
||||||
|
assertEquals(GiftStatus.DELIVERED, giftRepository.findById(giftId).orElseThrow().status)
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("실제 transactional 스케줄러 배송지 만료는 발신자만 한 번 알리고 재실행은 추가 알림을 보내지 않는다")
|
@DisplayName("실제 transactional 스케줄러 배송지 만료는 발신자만 한 번 알리고 재실행은 추가 알림을 보내지 않는다")
|
||||||
fun shouldExpireRecipientAddressThroughTransactionalSchedulerAndNotifyOnlySenderOnce() {
|
fun shouldExpireRecipientAddressThroughTransactionalSchedulerAndNotifyOnlySenderOnce() {
|
||||||
|
|||||||
Reference in New Issue
Block a user