[#44] 매장 기능 도입 #45

Merged
pricelees merged 116 commits from feat/#44 into main 2025-09-20 03:15:06 +00:00
10 changed files with 276 additions and 3078 deletions
Showing only changes of commit 116dd24e26 - Show all commits

View File

@ -46,29 +46,14 @@ class RegionService(
}
@Transactional(readOnly = true)
fun findDongBySidoAndSigungu(sidoCode: String, sigunguCode: String): DongListResponse {
log.info { "[RegionService.findDongBySidoAndSigungu] 행정동 조회 시작: sidoCode=${sidoCode} / sigunguCode=${sigunguCode}" }
val result: List<Pair<String, String>> = regionRepository.findAllDongBySidoAndSigungu(sidoCode, sigunguCode)
fun findRegionCode(sidoCode: String, sigunguCode: String): RegionCodeResponse {
log.info { "[RegionService.findRegionCode] 지역 코드 조회 시작: sidoCode=${sidoCode} / sigunguCode=${sigunguCode}" }
if (result.isEmpty()) {
log.warn { "[RegionService.findDongBySidoAndSigungu] 행정동 조회 실패: sidoCode=${sidoCode} / sigunguCode=${sigunguCode}" }
throw RegionException(RegionErrorCode.DONG_CODE_NOT_FOUND)
}
return DongListResponse(result.map { DongResponse(code = it.first, name = it.second) }).also {
log.info { "[RegionService.findDongBySidoAndSigungu] sidoCode=${sidoCode}, sigunguCode=${sigunguCode}${it.dongList.size}개의 행정동 조회 완료" }
}
}
@Transactional(readOnly = true)
fun findRegionCode(sidoCode: String, sigunguCode: String, dongCode: String): RegionCodeResponse {
log.info { "[RegionService.findRegionCode] 지역 코드 조회 시작: sidoCode=${sidoCode} / sigunguCode=${sigunguCode} / dongCode=${dongCode}" }
return regionRepository.findRegionCode(sidoCode, sigunguCode, dongCode)?.let {
log.info { "[RegionService.findRegionCode] 지역 코드 조회 완료: code=${it} sidoCode=${sidoCode} / sigunguCode=${sigunguCode} / dongCode=${dongCode}" }
return regionRepository.findRegionCode(sidoCode, sigunguCode)?.let {
log.info { "[RegionService.findRegionCode] 지역 코드 조회 완료: code=${it} sidoCode=${sidoCode} / sigunguCode=${sigunguCode}" }
RegionCodeResponse(it)
} ?: run {
log.warn { "[RegionService.findRegionCode] 지역 코드 조회 실패: sidoCode=${sidoCode} / sigunguCode=${sigunguCode} / dongCode=${dongCode}" }
log.warn { "[RegionService.findRegionCode] 지역 코드 조회 실패: sidoCode=${sidoCode} / sigunguCode=${sigunguCode}" }
throw RegionException(RegionErrorCode.REGION_CODE_NOT_FOUND)
}
}

View File

@ -20,7 +20,6 @@ interface RegionAPI {
fun findRegionCode(
@RequestParam(name = "sidoCode", required = true) sidoCode: String,
@RequestParam(name = "sigunguCode", required = true) sigunguCode: String,
@RequestParam(name = "dongCode", required = true) dongCode: String,
): ResponseEntity<CommonApiResponse<RegionCodeResponse>>
@Public
@ -34,12 +33,4 @@ interface RegionAPI {
fun findAllSigunguBySido(
@RequestParam(required = true) sidoCode: String
): ResponseEntity<CommonApiResponse<SigunguListResponse>>
@Public
@Operation(summary = "모든 행정동 목록 조회")
@ApiResponses(ApiResponse(responseCode = "200", description = "성공", useReturnTypeSchema = true))
fun findAllDongBySigungu(
@RequestParam(name = "sidoCode", required = true) sidoCode: String,
@RequestParam(name = "sigunguCode", required = true) sigunguCode: String
): ResponseEntity<CommonApiResponse<DongListResponse>>
}

View File

@ -3,16 +3,15 @@ package roomescape.region.infrastructure.persistence
import jakarta.persistence.Entity
import jakarta.persistence.Id
import jakarta.persistence.Table
import jakarta.persistence.UniqueConstraint
@Entity
@Table(name = "region")
@Table(name = "region", uniqueConstraints = [UniqueConstraint(columnNames = ["sidoCode", "sigunguCode"])])
class RegionEntity(
@Id
val code: String,
val sidoCode: String,
val sigunguCode: String,
val dongCode: String,
val sidoName: String,
val sigunguName: String,
val dongName: String,
)

View File

@ -34,22 +34,6 @@ interface RegionRepository : JpaRepository<RegionEntity, String> {
@Param("sidoCode") sidoCode: String
): List<Pair<String, String>>
@Query("""
SELECT
new kotlin.Pair(r.dongCode, r.dongName)
FROM
RegionEntity r
WHERE
r.sidoCode = :sidoCode
AND r.sigunguCode = :sigunguCode
ORDER BY
r.dongName
""")
fun findAllDongBySidoAndSigungu(
@Param("sidoCode") sidoCode: String,
@Param("sigunguCode") sigunguCode: String
): List<Pair<String, String>>
@Query("""
SELECT
r.code
@ -58,11 +42,9 @@ interface RegionRepository : JpaRepository<RegionEntity, String> {
WHERE
r.sidoCode = :sidoCode
AND r.sigunguCode = :sigunguCode
AND r.dongCode = :dongCode
""")
fun findRegionCode(
@Param("sidoCode") sidoCode: String,
@Param("sigunguCode") sigunguCode: String,
@Param("dongCode") dongCode: String,
): String?
}

View File

@ -18,9 +18,8 @@ class RegionController(
override fun findRegionCode(
@RequestParam(name = "sidoCode", required = true) sidoCode: String,
@RequestParam(name = "sigunguCode", required = true) sigunguCode: String,
@RequestParam(name = "dongCode", required = true) dongCode: String,
): ResponseEntity<CommonApiResponse<RegionCodeResponse>> {
val response = regionService.findRegionCode(sidoCode, sigunguCode, dongCode)
val response = regionService.findRegionCode(sidoCode, sigunguCode)
return ResponseEntity.ok(CommonApiResponse(response))
}
@ -40,14 +39,4 @@ class RegionController(
return ResponseEntity.ok(CommonApiResponse(response))
}
@GetMapping("/dong")
override fun findAllDongBySigungu(
@RequestParam(name = "sidoCode", required = true) sidoCode: String,
@RequestParam(name = "sigunguCode", required = true) sigunguCode: String
): ResponseEntity<CommonApiResponse<DongListResponse>> {
val response = regionService.findDongBySidoAndSigungu(sidoCode, sigunguCode)
return ResponseEntity.ok(CommonApiResponse(response))
}
}

File diff suppressed because it is too large Load Diff

View File

@ -2,10 +2,39 @@ create table if not exists region (
code varchar(10) primary key,
sido_code varchar(2) not null,
sigungu_code varchar(3) not null,
dong_code varchar(5) not null ,
sido_name varchar(20) not null,
sigungu_name varchar(20) not null,
dong_name varchar(20) not null
constraint uk_region__sido_sigungu_code unique (sido_code, sigungu_code)
);
create table if not exists store(
id bigint primary key,
name varchar(20) not null,
address varchar(100) not null,
business_reg_num varchar(10) not null,
region_code varchar(10) not null,
created_at timestamp not null,
updated_at timestamp not null,
constraint uk_store__business_reg_num unique (business_reg_num),
constraint fk_store__sido_code foreign key (region_code) references region (code)
);
create table if not exists room(
id bigint primary key ,
name varchar(20) not null,
store_id bigint not null,
max_capacity smallint not null,
status varchar(20) not null,
created_at timestamp not null,
created_by bigint not null,
updated_at timestamp not null,
updated_by bigint not null,
constraint fk_room__store_id foreign key (store_id) references store (id)
);
create table if not exists users(

View File

@ -37,26 +37,14 @@ class RegionApiFailTest(
)
}
test("행정동") {
every {
regionRepository.findAllDongBySidoAndSigungu(any(), any())
} returns emptyList()
runExceptionTest(
method = HttpMethod.GET,
endpoint = "/regions/dong?sidoCode=11&sigunguCode=110",
expectedErrorCode = RegionErrorCode.DONG_CODE_NOT_FOUND,
)
}
test("지역 코드") {
every {
regionRepository.findRegionCode(any(), any(), any())
regionRepository.findRegionCode(any(), any())
} returns null
runExceptionTest(
method = HttpMethod.GET,
endpoint = "/regions/code?sidoCode=11&sigunguCode=110&dongCode=10100",
endpoint = "/regions/code?sidoCode=11&sigunguCode=110",
expectedErrorCode = RegionErrorCode.REGION_CODE_NOT_FOUND,
)
}

View File

@ -13,7 +13,7 @@ import roomescape.supports.runTest
class RegionApiSuccessTest: FunSpecSpringbootTest() {
init {
context("시/도 -> 시/군/구 -> 행정동 -> 지역 코드 순으로 조회한다.") {
context("시/도 -> 시/군/구 -> 지역 코드 순으로 조회한다.") {
test("정상 응답") {
val sidoCode: String = runTest(
on = {
@ -33,25 +33,16 @@ class RegionApiSuccessTest: FunSpecSpringbootTest() {
}
).extract().path("data.sigunguList[0].code")
val dongCode: String = runTest(
on = {
get("/regions/dong?sidoCode=$sidoCode&sigunguCode=$sigunguCode")
},
expect = {
statusCode(HttpStatus.OK.value())
}
).extract().path("data.dongList[0].code")
val regionCode: String = runTest(
on = {
get("/regions/code?sidoCode=$sidoCode&sigunguCode=$sigunguCode&dongCode=${dongCode}")
get("/regions/code?sidoCode=$sidoCode&sigunguCode=$sigunguCode")
},
expect = {
statusCode(HttpStatus.OK.value())
}
).extract().path("data.code")
regionCode shouldBe "$sidoCode$sigunguCode$dongCode"
regionCode shouldBe "$sidoCode$sigunguCode"
}
}
}

View File

@ -52,7 +52,7 @@ object UserFixture {
email: String = "sample@example.com",
password: String = "a".repeat(MIN_PASSWORD_LENGTH),
phone: String = "01012345678",
regionCode: String = "1111010100",
regionCode: String = "1111000000",
status: UserStatus = UserStatus.ACTIVE
): UserEntity = UserEntity(
id = id,
@ -69,7 +69,7 @@ object UserFixture {
email = "sample@example.com",
password = "a".repeat(MIN_PASSWORD_LENGTH),
phone = "01012345678",
regionCode = "1111010100"
regionCode = "1111000000"
)
}