From f3f46c413c5fc2f8546aa1254dd57a6ec2a8adfb Mon Sep 17 00:00:00 2001 From: Klaus Date: Wed, 30 Sep 2026 17:37:56 +0900 Subject: [PATCH] =?UTF-8?q?feat(gift):=20=EC=8B=A0=EC=B2=AD=EB=B2=88?= =?UTF-8?q?=ED=98=B8=20=EC=B1=84=EB=B2=88=20=EC=A0=80=EC=9E=A5=20=EA=B8=B0?= =?UTF-8?q?=EC=A4=80=EC=9D=84=20=EB=B6=84=EB=A6=AC=ED=95=9C=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../persistence/GiftApplicationNoSequence.kt | 12 +++++-- .../GiftApplicationNoSequenceRepository.kt | 16 ++++++++-- .../out/persistence/GiftEntityMappingTest.kt | 31 +++++++++++++++---- 3 files changed, 48 insertions(+), 11 deletions(-) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/v2/gift/adapter/out/persistence/GiftApplicationNoSequence.kt b/src/main/kotlin/kr/co/vividnext/sodalive/v2/gift/adapter/out/persistence/GiftApplicationNoSequence.kt index 988d1cd7..73307433 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/v2/gift/adapter/out/persistence/GiftApplicationNoSequence.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/v2/gift/adapter/out/persistence/GiftApplicationNoSequence.kt @@ -9,10 +9,18 @@ import javax.persistence.UniqueConstraint @Entity @Table( name = "gift_application_no_sequence", - uniqueConstraints = [UniqueConstraint(name = "uk_gift_application_no_sequence_date", columnNames = ["sequence_date"])] + uniqueConstraints = [ + UniqueConstraint( + name = "uk_gift_application_no_sequence_category_date", + columnNames = ["category_id", "sequence_date"] + ) + ] ) class GiftApplicationNoSequence( - @Column(name = "sequence_date", nullable = false, length = 8) + @Column(name = "category_id", nullable = false) + val categoryId: Long, + + @Column(name = "sequence_date", nullable = false, length = 6) val sequenceDate: String, @Column(name = "last_sequence", nullable = false) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/v2/gift/adapter/out/persistence/GiftApplicationNoSequenceRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/v2/gift/adapter/out/persistence/GiftApplicationNoSequenceRepository.kt index b557d321..5a938dd4 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/v2/gift/adapter/out/persistence/GiftApplicationNoSequenceRepository.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/v2/gift/adapter/out/persistence/GiftApplicationNoSequenceRepository.kt @@ -7,9 +7,19 @@ import org.springframework.data.repository.query.Param import javax.persistence.LockModeType interface GiftApplicationNoSequenceRepository : JpaRepository { - fun findBySequenceDate(sequenceDate: String): GiftApplicationNoSequence? + fun findByCategoryIdAndSequenceDate(categoryId: Long, sequenceDate: String): GiftApplicationNoSequence? @Lock(LockModeType.PESSIMISTIC_WRITE) - @Query("select s from GiftApplicationNoSequence s where s.sequenceDate = :sequenceDate") - fun findBySequenceDateForUpdate(@Param("sequenceDate") sequenceDate: String): GiftApplicationNoSequence? + @Query( + """ + select s + from GiftApplicationNoSequence s + where s.categoryId = :categoryId + and s.sequenceDate = :sequenceDate + """ + ) + fun findByCategoryIdAndSequenceDateForUpdate( + @Param("categoryId") categoryId: Long, + @Param("sequenceDate") sequenceDate: String + ): GiftApplicationNoSequence? } diff --git a/src/test/kotlin/kr/co/vividnext/sodalive/v2/gift/adapter/out/persistence/GiftEntityMappingTest.kt b/src/test/kotlin/kr/co/vividnext/sodalive/v2/gift/adapter/out/persistence/GiftEntityMappingTest.kt index 9a758734..cf7e4057 100644 --- a/src/test/kotlin/kr/co/vividnext/sodalive/v2/gift/adapter/out/persistence/GiftEntityMappingTest.kt +++ b/src/test/kotlin/kr/co/vividnext/sodalive/v2/gift/adapter/out/persistence/GiftEntityMappingTest.kt @@ -35,8 +35,11 @@ class GiftEntityMappingTest @Autowired constructor( fun shouldSaveAndFindGiftWithDelivery() { val category = giftCategoryRepository.save( GiftCategory( + classificationNumber = "100", categoryCode = "DOLL", name = "인형", + receiptCode = "A", + representativeItem = "피규어", requiresDamageWaiver = true ) ) @@ -89,9 +92,9 @@ class GiftEntityMappingTest @Autowired constructor( } @Test - @DisplayName("신청번호와 카테고리 코드와 리뷰 선물 ID는 중복 저장할 수 없다") + @DisplayName("신청번호와 카테고리 unique 값과 리뷰 선물 ID는 중복 저장할 수 없다") fun shouldRejectDuplicateUniqueValues() { - val category = giftCategoryRepository.save(GiftCategory(categoryCode = "DOLL", name = "인형")) + val category = giftCategoryRepository.save(giftCategory("100", "DOLL", "A")) val gift = giftRepository.save(gift(category.id!!, "G20260929000001")) giftRepository.saveAndFlush(gift(category.id!!, "G20260929000002")) giftReviewRepository.saveAndFlush(GiftReview(giftId = gift.id!!, senderMemberId = 1L, rating = 5)) @@ -100,7 +103,13 @@ class GiftEntityMappingTest @Autowired constructor( giftRepository.saveAndFlush(gift(category.id!!, "G20260929000001")) } assertThrows(DataIntegrityViolationException::class.java) { - giftCategoryRepository.saveAndFlush(GiftCategory(categoryCode = "DOLL", name = "중복")) + giftCategoryRepository.saveAndFlush(giftCategory("101", "DOLL", "B")) + } + assertThrows(DataIntegrityViolationException::class.java) { + giftCategoryRepository.saveAndFlush(giftCategory("100", "TOY", "B")) + } + assertThrows(DataIntegrityViolationException::class.java) { + giftCategoryRepository.saveAndFlush(giftCategory("101", "TOY", "A")) } assertThrows(DataIntegrityViolationException::class.java) { giftReviewRepository.saveAndFlush(GiftReview(giftId = gift.id!!, senderMemberId = 1L, rating = 4)) @@ -108,13 +117,15 @@ class GiftEntityMappingTest @Autowired constructor( } @Test - @DisplayName("날짜별 신청번호 채번 sequence를 저장한다") + @DisplayName("카테고리와 날짜별 신청번호 채번 sequence를 저장한다") fun shouldSaveApplicationNoSequence() { + val category = giftCategoryRepository.save(giftCategory("100", "DOLL", "A")) val sequence = giftApplicationNoSequenceRepository.save( - GiftApplicationNoSequence(sequenceDate = "20260929", lastSequence = 1) + GiftApplicationNoSequence(categoryId = category.id!!, sequenceDate = "260929", lastSequence = 1) ) - assertEquals("20260929", sequence.sequenceDate) + assertEquals(category.id, sequence.categoryId) + assertEquals("260929", sequence.sequenceDate) assertEquals(1, sequence.lastSequence) } @@ -134,4 +145,12 @@ class GiftEntityMappingTest @Autowired constructor( senderPrivacyAgreedAt = LocalDateTime.of(2026, 9, 29, 12, 0), damageWaiverAgreed = true ) + + private fun giftCategory(classificationNumber: String, categoryCode: String, receiptCode: String) = GiftCategory( + classificationNumber = classificationNumber, + categoryCode = categoryCode, + name = "인형", + receiptCode = receiptCode, + representativeItem = "피규어" + ) }