feat(gift): 신청번호 채번 저장 기준을 분리한다

This commit is contained in:
2026-09-30 17:37:56 +09:00
parent a6ba46e017
commit f3f46c413c
3 changed files with 48 additions and 11 deletions
@@ -9,10 +9,18 @@ import javax.persistence.UniqueConstraint
@Entity @Entity
@Table( @Table(
name = "gift_application_no_sequence", 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( 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, val sequenceDate: String,
@Column(name = "last_sequence", nullable = false) @Column(name = "last_sequence", nullable = false)
@@ -7,9 +7,19 @@ import org.springframework.data.repository.query.Param
import javax.persistence.LockModeType import javax.persistence.LockModeType
interface GiftApplicationNoSequenceRepository : JpaRepository<GiftApplicationNoSequence, Long> { interface GiftApplicationNoSequenceRepository : JpaRepository<GiftApplicationNoSequence, Long> {
fun findBySequenceDate(sequenceDate: String): GiftApplicationNoSequence? fun findByCategoryIdAndSequenceDate(categoryId: Long, sequenceDate: String): GiftApplicationNoSequence?
@Lock(LockModeType.PESSIMISTIC_WRITE) @Lock(LockModeType.PESSIMISTIC_WRITE)
@Query("select s from GiftApplicationNoSequence s where s.sequenceDate = :sequenceDate") @Query(
fun findBySequenceDateForUpdate(@Param("sequenceDate") sequenceDate: String): GiftApplicationNoSequence? """
select s
from GiftApplicationNoSequence s
where s.categoryId = :categoryId
and s.sequenceDate = :sequenceDate
"""
)
fun findByCategoryIdAndSequenceDateForUpdate(
@Param("categoryId") categoryId: Long,
@Param("sequenceDate") sequenceDate: String
): GiftApplicationNoSequence?
} }
@@ -35,8 +35,11 @@ class GiftEntityMappingTest @Autowired constructor(
fun shouldSaveAndFindGiftWithDelivery() { fun shouldSaveAndFindGiftWithDelivery() {
val category = giftCategoryRepository.save( val category = giftCategoryRepository.save(
GiftCategory( GiftCategory(
classificationNumber = "100",
categoryCode = "DOLL", categoryCode = "DOLL",
name = "인형", name = "인형",
receiptCode = "A",
representativeItem = "피규어",
requiresDamageWaiver = true requiresDamageWaiver = true
) )
) )
@@ -89,9 +92,9 @@ class GiftEntityMappingTest @Autowired constructor(
} }
@Test @Test
@DisplayName("신청번호와 카테고리 코드와 리뷰 선물 ID는 중복 저장할 수 없다") @DisplayName("신청번호와 카테고리 unique 값과 리뷰 선물 ID는 중복 저장할 수 없다")
fun shouldRejectDuplicateUniqueValues() { 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")) val gift = giftRepository.save(gift(category.id!!, "G20260929000001"))
giftRepository.saveAndFlush(gift(category.id!!, "G20260929000002")) giftRepository.saveAndFlush(gift(category.id!!, "G20260929000002"))
giftReviewRepository.saveAndFlush(GiftReview(giftId = gift.id!!, senderMemberId = 1L, rating = 5)) giftReviewRepository.saveAndFlush(GiftReview(giftId = gift.id!!, senderMemberId = 1L, rating = 5))
@@ -100,7 +103,13 @@ class GiftEntityMappingTest @Autowired constructor(
giftRepository.saveAndFlush(gift(category.id!!, "G20260929000001")) giftRepository.saveAndFlush(gift(category.id!!, "G20260929000001"))
} }
assertThrows(DataIntegrityViolationException::class.java) { 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) { assertThrows(DataIntegrityViolationException::class.java) {
giftReviewRepository.saveAndFlush(GiftReview(giftId = gift.id!!, senderMemberId = 1L, rating = 4)) giftReviewRepository.saveAndFlush(GiftReview(giftId = gift.id!!, senderMemberId = 1L, rating = 4))
@@ -108,13 +117,15 @@ class GiftEntityMappingTest @Autowired constructor(
} }
@Test @Test
@DisplayName("날짜별 신청번호 채번 sequence를 저장한다") @DisplayName("카테고리와 날짜별 신청번호 채번 sequence를 저장한다")
fun shouldSaveApplicationNoSequence() { fun shouldSaveApplicationNoSequence() {
val category = giftCategoryRepository.save(giftCategory("100", "DOLL", "A"))
val sequence = giftApplicationNoSequenceRepository.save( 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) assertEquals(1, sequence.lastSequence)
} }
@@ -134,4 +145,12 @@ class GiftEntityMappingTest @Autowired constructor(
senderPrivacyAgreedAt = LocalDateTime.of(2026, 9, 29, 12, 0), senderPrivacyAgreedAt = LocalDateTime.of(2026, 9, 29, 12, 0),
damageWaiverAgreed = true damageWaiverAgreed = true
) )
private fun giftCategory(classificationNumber: String, categoryCode: String, receiptCode: String) = GiftCategory(
classificationNumber = classificationNumber,
categoryCode = categoryCode,
name = "인형",
receiptCode = receiptCode,
representativeItem = "피규어"
)
} }