From c7c8e9ddcfc1d6c49d4d2fa6ac6059a079e8b7c7 Mon Sep 17 00:00:00 2001 From: pricelees Date: Thu, 18 Sep 2025 20:25:55 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=EB=A7=A4=EC=9E=A5=20CRUD=20=EA=B4=80?= =?UTF-8?q?=EB=A0=A8=20Validation=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../roomescape/store/business/StoreService.kt | 5 ++ .../store/business/StoreValidator.kt | 59 +++++++++++++++++++ .../store/exception/StoreException.kt | 6 +- 3 files changed, 69 insertions(+), 1 deletion(-) create mode 100644 src/main/kotlin/roomescape/store/business/StoreValidator.kt diff --git a/src/main/kotlin/roomescape/store/business/StoreService.kt b/src/main/kotlin/roomescape/store/business/StoreService.kt index bc534d6e..8cbaebc4 100644 --- a/src/main/kotlin/roomescape/store/business/StoreService.kt +++ b/src/main/kotlin/roomescape/store/business/StoreService.kt @@ -21,6 +21,7 @@ private val log: KLogger = KotlinLogging.logger {} @Service class StoreService( private val storeRepository: StoreRepository, + private val storeValidator: StoreValidator, private val adminService: AdminService, private val regionService: RegionService, private val tsidFactory: TsidFactory, @@ -41,6 +42,8 @@ class StoreService( fun register(request: StoreRegisterRequest): StoreRegisterResponse { log.info { "[StoreService.register] 매장 등록 시작: name=${request.name}" } + storeValidator.validateCanRegister(request) + val store = StoreEntity( id = tsidFactory.next(), name = request.name, @@ -62,6 +65,8 @@ class StoreService( fun update(id: Long, request: StoreUpdateRequest) { log.info { "[StoreService.update] 매장 수정 시작: id=${id}, request=${request}" } + storeValidator.validateCanUpdate(request) + findOrThrow(id).apply { this.modifyIfNotNull(request.name, request.address, request.contact) }.also { diff --git a/src/main/kotlin/roomescape/store/business/StoreValidator.kt b/src/main/kotlin/roomescape/store/business/StoreValidator.kt new file mode 100644 index 00000000..01ce5471 --- /dev/null +++ b/src/main/kotlin/roomescape/store/business/StoreValidator.kt @@ -0,0 +1,59 @@ +package roomescape.store.business + +import io.github.oshai.kotlinlogging.KLogger +import io.github.oshai.kotlinlogging.KotlinLogging +import org.springframework.stereotype.Component +import roomescape.store.exception.StoreErrorCode +import roomescape.store.exception.StoreException +import roomescape.store.infrastructure.persistence.StoreRepository +import roomescape.store.web.StoreRegisterRequest +import roomescape.store.web.StoreUpdateRequest + +private val log: KLogger = KotlinLogging.logger {} + +@Component +class StoreValidator( + private val storeRepository: StoreRepository +) { + + fun validateCanRegister(request: StoreRegisterRequest) { + validateDuplicateNameExist(request.name) + validateDuplicateContactExist(request.contact) + validateDuplicateAddressExist(request.address) + validateDuplicateBusinessRegNumExist(request.businessRegNum) + } + + fun validateCanUpdate(request: StoreUpdateRequest) { + request.name?.let { validateDuplicateNameExist(it) } + request.contact?.let { validateDuplicateContactExist(it) } + request.address?.let { validateDuplicateAddressExist(it) } + } + + private fun validateDuplicateNameExist(name: String) { + if (storeRepository.existsByName(name)) { + log.info { "[StoreValidator.validateDuplicateNameExist] 이름 중복: name=${name}" } + throw StoreException(StoreErrorCode.STORE_NAME_DUPLICATED) + } + } + + private fun validateDuplicateContactExist(contact: String) { + if (storeRepository.existsByContact(contact)) { + log.info { "[StoreValidator.validateDuplicateContact] 연락처 중복: contact=${contact}" } + throw StoreException(StoreErrorCode.STORE_CONTACT_DUPLICATED) + } + } + + private fun validateDuplicateAddressExist(address: String) { + if (storeRepository.existsByAddress(address)) { + log.info { "[StoreValidator.validateDuplicateAddress] 주소 중복: address=${address}" } + throw StoreException(StoreErrorCode.STORE_ADDRESS_DUPLICATED) + } + } + + private fun validateDuplicateBusinessRegNumExist(businessRegNum: String) { + if (storeRepository.existsByBusinessRegNum(businessRegNum)) { + log.info { "[StoreValidator.validateDuplicateBusinessRegNum] 사업자번호 중복: businessRegNum=${businessRegNum}" } + throw StoreException(StoreErrorCode.STORE_BUSINESS_REG_NUM_DUPLICATED) + } + } +} diff --git a/src/main/kotlin/roomescape/store/exception/StoreException.kt b/src/main/kotlin/roomescape/store/exception/StoreException.kt index 71fe9781..0416e6cc 100644 --- a/src/main/kotlin/roomescape/store/exception/StoreException.kt +++ b/src/main/kotlin/roomescape/store/exception/StoreException.kt @@ -15,5 +15,9 @@ enum class StoreErrorCode( override val message: String ) : ErrorCode { STORE_NOT_FOUND(HttpStatus.NOT_FOUND, "ST001", "매장을 찾을 수 없어요."), - SIDO_CODE_REQUIRED(HttpStatus.BAD_REQUEST, "ST002", "시/도 정보를 찾을 수 없어요. 다시 시도해주세요.") + SIDO_CODE_REQUIRED(HttpStatus.BAD_REQUEST, "ST002", "시/도 정보를 찾을 수 없어요. 다시 시도해주세요."), + STORE_NAME_DUPLICATED(HttpStatus.CONFLICT, "ST003", "이름이 같은 매장이 있어요."), + STORE_CONTACT_DUPLICATED(HttpStatus.CONFLICT, "ST004", "연락처가 같은 매장이 있어요."), + STORE_ADDRESS_DUPLICATED(HttpStatus.CONFLICT, "ST005", "주소가 같은 매장이 있어요."), + STORE_BUSINESS_REG_NUM_DUPLICATED(HttpStatus.CONFLICT, "ST006", "사업자번호가 같은 매장이 있어요."), }