diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/v2/gift/application/GiftAdminService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/v2/gift/application/GiftAdminService.kt index 64e7eaa9..f25d3f11 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/v2/gift/application/GiftAdminService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/v2/gift/application/GiftAdminService.kt @@ -16,6 +16,7 @@ import org.springframework.data.repository.findByIdOrNull import org.springframework.stereotype.Service import org.springframework.transaction.annotation.Transactional import java.time.LocalDateTime +import java.time.temporal.ChronoUnit data class GiftCategoryCommand( val classificationNumber: String, @@ -157,12 +158,13 @@ class GiftAdminService( val gift = giftRepository.findByApplicationNoForUpdate(applicationNo) ?: throw invalidRequest() if (gift.status != GiftStatus.RECEIVED) throw invalidRequest() val delivery = giftDeliveryRepository.findByGiftId(requireNotNull(gift.id)) ?: throw invalidRequest() + val canceledAt = now.truncatedTo(ChronoUnit.SECONDS) gift.status = GiftStatus.CANCELED - delivery.canceledAt = now + delivery.canceledAt = canceledAt canPaymentService.refundGiftCan(gift.senderMemberId, gift.canUsageId) - return GiftOperationStatusResult(gift.applicationNo, gift.status, gift.status.displayName, now) + return GiftOperationStatusResult(gift.applicationNo, gift.status, gift.status.displayName, canceledAt) } @Transactional diff --git a/src/test/kotlin/kr/co/vividnext/sodalive/v2/gift/application/GiftAdminServiceTest.kt b/src/test/kotlin/kr/co/vividnext/sodalive/v2/gift/application/GiftAdminServiceTest.kt index 447c2631..b548f8a7 100644 --- a/src/test/kotlin/kr/co/vividnext/sodalive/v2/gift/application/GiftAdminServiceTest.kt +++ b/src/test/kotlin/kr/co/vividnext/sodalive/v2/gift/application/GiftAdminServiceTest.kt @@ -25,6 +25,7 @@ import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest import org.springframework.context.annotation.Import import java.time.LocalDateTime +import javax.persistence.EntityManager @DataJpaTest( properties = [ @@ -39,7 +40,8 @@ class GiftAdminServiceTest @Autowired constructor( private val sizePriceRepository: GiftSizePriceRepository, private val giftRepository: GiftRepository, private val giftDeliveryRepository: GiftDeliveryRepository, - private val giftMailboxRepository: GiftMailboxRepository + private val giftMailboxRepository: GiftMailboxRepository, + private val entityManager: EntityManager ) { private val giftPushService = Mockito.mock(GiftPushService::class.java) private val canPaymentService = Mockito.mock(CanPaymentService::class.java) @@ -332,6 +334,24 @@ class GiftAdminServiceTest @Autowired constructor( Mockito.verifyNoInteractions(giftPushService) } + @ParameterizedTest + @ValueSource(ints = [0, 123456789, 999999999]) + @DisplayName("관리자 취소 시각은 나노초 입력에도 초 단위 저장값과 응답이 일치한다") + fun shouldPersistAndReturnSameSecondPrecisionCancellationTime(nanos: Int) { + val (gift, _) = saveGift(GiftStatus.RECEIVED) + val giftId = requireNotNull(gift.id) + val now = LocalDateTime.of(2026, 10, 22, 23, 59, 59, nanos) + + val result = service.cancelGift(APPLICATION_NO, now) + entityManager.flush() + entityManager.clear() + + val persisted = requireNotNull(giftDeliveryRepository.findByGiftId(giftId)?.canceledAt) + assertEquals(persisted, result.occurredAt) + assertEquals(LocalDateTime.of(2026, 10, 22, 23, 59, 59), persisted) + assertEquals(GiftStatus.CANCELED, giftRepository.findById(giftId).orElseThrow().status) + } + @ParameterizedTest @EnumSource(value = GiftStatus::class, names = ["RECEIVED"], mode = EnumSource.Mode.EXCLUDE) @DisplayName("운영자는 접수가 아닌 모든 상태의 선물을 취소할 수 없다")