feat(gift): 선물 카테고리 운영 필드를 추가한다

This commit is contained in:
2026-09-30 17:37:23 +09:00
parent dda5de4d61
commit 13a842a9a5
4 changed files with 98 additions and 4 deletions
@@ -4,14 +4,20 @@ import kr.co.vividnext.sodalive.v2.gift.application.GiftCategoryCommand
import kr.co.vividnext.sodalive.v2.gift.application.GiftSizePriceCommand import kr.co.vividnext.sodalive.v2.gift.application.GiftSizePriceCommand
data class AdminGiftCategoryRequest( data class AdminGiftCategoryRequest(
val classificationNumber: String,
val categoryCode: String, val categoryCode: String,
val name: String, val name: String,
val receiptCode: String,
val representativeItem: String,
val requiresDamageWaiver: Boolean, val requiresDamageWaiver: Boolean,
val isActive: Boolean val isActive: Boolean
) { ) {
fun toCommand() = GiftCategoryCommand( fun toCommand() = GiftCategoryCommand(
classificationNumber = classificationNumber,
categoryCode = categoryCode, categoryCode = categoryCode,
name = name, name = name,
receiptCode = receiptCode,
representativeItem = representativeItem,
requiresDamageWaiver = requiresDamageWaiver, requiresDamageWaiver = requiresDamageWaiver,
isActive = isActive isActive = isActive
) )
@@ -9,15 +9,28 @@ import javax.persistence.UniqueConstraint
@Entity @Entity
@Table( @Table(
name = "gift_category", name = "gift_category",
uniqueConstraints = [UniqueConstraint(name = "uk_gift_category_code", columnNames = ["category_code"])] uniqueConstraints = [
UniqueConstraint(name = "uk_gift_category_classification_number", columnNames = ["classification_number"]),
UniqueConstraint(name = "uk_gift_category_code", columnNames = ["category_code"]),
UniqueConstraint(name = "uk_gift_category_receipt_code", columnNames = ["receipt_code"])
]
) )
class GiftCategory( class GiftCategory(
@Column(name = "classification_number", nullable = false, length = 3)
var classificationNumber: String,
@Column(name = "category_code", nullable = false, length = 50) @Column(name = "category_code", nullable = false, length = 50)
val categoryCode: String, val categoryCode: String,
@Column(name = "name", nullable = false, length = 50) @Column(name = "name", nullable = false, length = 50)
var name: String, var name: String,
@Column(name = "receipt_code", nullable = false, length = 9)
var receiptCode: String,
@Column(name = "representative_item", nullable = false, length = 100)
var representativeItem: String,
@Column(name = "requires_damage_waiver", nullable = false) @Column(name = "requires_damage_waiver", nullable = false)
var requiresDamageWaiver: Boolean = false, var requiresDamageWaiver: Boolean = false,
@@ -15,16 +15,22 @@ import org.springframework.transaction.annotation.Transactional
import java.time.LocalDateTime import java.time.LocalDateTime
data class GiftCategoryCommand( data class GiftCategoryCommand(
val classificationNumber: String,
val categoryCode: String, val categoryCode: String,
val name: String, val name: String,
val receiptCode: String,
val representativeItem: String,
val requiresDamageWaiver: Boolean, val requiresDamageWaiver: Boolean,
val isActive: Boolean val isActive: Boolean
) )
data class GiftCategoryResult( data class GiftCategoryResult(
val categoryId: Long, val categoryId: Long,
val classificationNumber: String,
val categoryCode: String, val categoryCode: String,
val name: String, val name: String,
val receiptCode: String,
val representativeItem: String,
val requiresDamageWaiver: Boolean, val requiresDamageWaiver: Boolean,
val isActive: Boolean val isActive: Boolean
) )
@@ -66,8 +72,11 @@ class GiftAdminService(
validateCategory(command) validateCategory(command)
return categoryRepository.save( return categoryRepository.save(
GiftCategory( GiftCategory(
classificationNumber = command.classificationNumber,
categoryCode = command.categoryCode, categoryCode = command.categoryCode,
name = command.name, name = command.name,
receiptCode = command.receiptCode,
representativeItem = command.representativeItem,
requiresDamageWaiver = command.requiresDamageWaiver, requiresDamageWaiver = command.requiresDamageWaiver,
isActive = command.isActive isActive = command.isActive
) )
@@ -78,7 +87,10 @@ class GiftAdminService(
fun updateCategory(categoryId: Long, command: GiftCategoryCommand): GiftCategoryResult { fun updateCategory(categoryId: Long, command: GiftCategoryCommand): GiftCategoryResult {
validateCategory(command) validateCategory(command)
val category = categoryRepository.findByIdOrNull(categoryId) ?: throw invalidRequest() val category = categoryRepository.findByIdOrNull(categoryId) ?: throw invalidRequest()
category.classificationNumber = command.classificationNumber
category.name = command.name category.name = command.name
category.receiptCode = command.receiptCode
category.representativeItem = command.representativeItem
category.requiresDamageWaiver = command.requiresDamageWaiver category.requiresDamageWaiver = command.requiresDamageWaiver
category.isActive = command.isActive category.isActive = command.isActive
return category.toResult() return category.toResult()
@@ -172,7 +184,11 @@ class GiftAdminService(
command.categoryCode.isBlank() || command.categoryCode.isBlank() ||
command.categoryCode.length > 50 || command.categoryCode.length > 50 ||
command.name.isBlank() || command.name.isBlank() ||
command.name.length > 50 command.name.length > 50 ||
!CLASSIFICATION_NUMBER_REGEX.matches(command.classificationNumber) ||
!RECEIPT_CODE_REGEX.matches(command.receiptCode) ||
command.representativeItem.isBlank() ||
command.representativeItem.length > 100
) { ) {
throw invalidRequest() throw invalidRequest()
} }
@@ -184,8 +200,11 @@ class GiftAdminService(
private fun GiftCategory.toResult() = GiftCategoryResult( private fun GiftCategory.toResult() = GiftCategoryResult(
categoryId = id ?: throw invalidRequest(), categoryId = id ?: throw invalidRequest(),
classificationNumber = classificationNumber,
categoryCode = categoryCode, categoryCode = categoryCode,
name = name, name = name,
receiptCode = receiptCode,
representativeItem = representativeItem,
requiresDamageWaiver = requiresDamageWaiver, requiresDamageWaiver = requiresDamageWaiver,
isActive = isActive isActive = isActive
) )
@@ -206,5 +225,7 @@ class GiftAdminService(
GiftStatus.ARRIVED_AT_MAILBOX, GiftStatus.ARRIVED_AT_MAILBOX,
GiftStatus.INSPECTION_COMPLETED GiftStatus.INSPECTION_COMPLETED
) )
val CLASSIFICATION_NUMBER_REGEX = Regex("\\d{3}")
val RECEIPT_CODE_REGEX = Regex("[A-Z]{1,9}")
} }
} }
@@ -48,20 +48,58 @@ class GiftAdminServiceTest @Autowired constructor(
@DisplayName("카테고리를 등록, 수정, 논리삭제한다") @DisplayName("카테고리를 등록, 수정, 논리삭제한다")
fun shouldCreateUpdateAndDeactivateCategory() { fun shouldCreateUpdateAndDeactivateCategory() {
val created = service.createCategory( val created = service.createCategory(
GiftCategoryCommand(categoryCode = "DOLL", name = "인형", requiresDamageWaiver = true, isActive = true) GiftCategoryCommand(
classificationNumber = "100",
categoryCode = "DOLL",
name = "인형",
receiptCode = "A",
representativeItem = "피규어",
requiresDamageWaiver = true,
isActive = true
)
) )
val updated = service.updateCategory( val updated = service.updateCategory(
created.categoryId, created.categoryId,
GiftCategoryCommand(categoryCode = "DOLL", name = "큰 인형", requiresDamageWaiver = false, isActive = true) GiftCategoryCommand(
classificationNumber = "101",
categoryCode = "DOLL",
name = "큰 인형",
receiptCode = "B",
representativeItem = "대형 피규어",
requiresDamageWaiver = false,
isActive = true
)
) )
service.deleteCategory(created.categoryId) service.deleteCategory(created.categoryId)
val saved = categoryRepository.findById(created.categoryId).orElseThrow() val saved = categoryRepository.findById(created.categoryId).orElseThrow()
assertEquals("101", updated.classificationNumber)
assertEquals("DOLL", updated.categoryCode)
assertEquals("큰 인형", updated.name) assertEquals("큰 인형", updated.name)
assertEquals("B", updated.receiptCode)
assertEquals("대형 피규어", updated.representativeItem)
assertFalse(updated.requiresDamageWaiver) assertFalse(updated.requiresDamageWaiver)
assertFalse(saved.isActive) assertFalse(saved.isActive)
} }
@Test
@DisplayName("카테고리 분류번호와 접수코드 형식을 검증한다")
fun shouldValidateCategoryClassificationNumberAndReceiptCode() {
val invalidClassificationNumber = assertThrows(SodaException::class.java) {
service.createCategory(categoryCommand(classificationNumber = "10A"))
}
val invalidReceiptCode = assertThrows(SodaException::class.java) {
service.createCategory(categoryCommand(receiptCode = "A1"))
}
val blankRepresentativeItem = assertThrows(SodaException::class.java) {
service.createCategory(categoryCommand(representativeItem = " "))
}
assertEquals("common.error.invalid_request", invalidClassificationNumber.messageKey)
assertEquals("common.error.invalid_request", invalidReceiptCode.messageKey)
assertEquals("common.error.invalid_request", blankRepresentativeItem.messageKey)
}
@Test @Test
@DisplayName("사이즈 가격을 수정하고 salePriceCan이 basePriceCan보다 크면 거부한다") @DisplayName("사이즈 가격을 수정하고 salePriceCan이 basePriceCan보다 크면 거부한다")
fun shouldUpdateSizePriceAndRejectInvalidSalePrice() { fun shouldUpdateSizePriceAndRejectInvalidSalePrice() {
@@ -213,6 +251,22 @@ class GiftAdminServiceTest @Autowired constructor(
return gift to delivery return gift to delivery
} }
private fun categoryCommand(
classificationNumber: String = "100",
categoryCode: String = "DOLL",
name: String = "인형",
receiptCode: String = "A",
representativeItem: String = "피규어"
) = GiftCategoryCommand(
classificationNumber = classificationNumber,
categoryCode = categoryCode,
name = name,
receiptCode = receiptCode,
representativeItem = representativeItem,
requiresDamageWaiver = true,
isActive = true
)
private companion object { private companion object {
const val APPLICATION_NO = "G20260929000001" const val APPLICATION_NO = "G20260929000001"
const val SENDER_MEMBER_ID = 1L const val SENDER_MEMBER_ID = 1L