From 993c593944e8920b6c8b841f762a11884d5b5e2f Mon Sep 17 00:00:00 2001 From: pricelees Date: Sun, 14 Sep 2025 16:21:48 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20store=20=ED=85=8C=EC=9D=B4=EB=B8=94=20?= =?UTF-8?q?=EC=B4=88=EA=B8=B0=20=EB=8D=B0=EC=9D=B4=ED=84=B0=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=20=EC=BD=94=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../roomescape/data/StoreDataInitializer.kt | 127 ++++++++++++++++++ 1 file changed, 127 insertions(+) create mode 100644 src/test/kotlin/roomescape/data/StoreDataInitializer.kt diff --git a/src/test/kotlin/roomescape/data/StoreDataInitializer.kt b/src/test/kotlin/roomescape/data/StoreDataInitializer.kt new file mode 100644 index 00000000..fbcf64da --- /dev/null +++ b/src/test/kotlin/roomescape/data/StoreDataInitializer.kt @@ -0,0 +1,127 @@ +package roomescape.data + +import io.kotest.core.spec.style.StringSpec +import roomescape.common.config.next +import roomescape.supports.tsidFactory +import java.io.File +import java.time.LocalDateTime +import java.time.ZoneId +import java.time.format.DateTimeFormatter +import kotlin.random.Random + +/** + * DataParser에서 전처리된 지역 + 인구 정보를 이용하여 Store 초기 데이터 생성 + */ +class StoreDataInitializer : StringSpec({ + + val positiveWords = listOf( + "사랑", "행복", "희망", "감사", "기쁨", "미소", "축복", "선물", "평화", + "열정", "미래", "자유", "도전", "지혜", "행운" + ) + + "초기 매장 데이터를 준비한다." { + val regions = initializeRegionWithStoreCount() + val usedStoreName = mutableListOf() + val usedBusinessRegNums = mutableListOf() + val storeSqlRows = mutableListOf() + val storeDataRows = mutableListOf() + val storeIds = mutableListOf() + + regions.forEachIndexed { idx, region -> + for (i in 0..region.storeCount) { + var address: String + var storeName: String + do { + val randomPositiveWord = positiveWords.random() + storeName = "${parseSigunguName(region.sigunguName)}${randomPositiveWord}점" + address = + "${region.sidoName} ${region.sigunguName} ${randomPositiveWord}${Random.nextInt(1, 10)}길 ${Random.nextInt(1, 100)}" + } while (usedStoreName.contains(storeName)) + usedStoreName.add(storeName) + + var businessRegNum: String + do { + businessRegNum = generateBusinessRegNum() + } while (usedBusinessRegNums.contains(businessRegNum)) + usedBusinessRegNums.add(businessRegNum) + + val createdAt = randomLocalDateTime() + val updatedAt = createdAt + + val id: Long = tsidFactory.next().also { storeIds.add(it) } + + storeSqlRows.add( + "(${id}, '$storeName', '$address', '$businessRegNum', '${region.regionCode}', '$createdAt', '$updatedAt')" + ) + storeDataRows.add( + "$id, $storeName, $address, $businessRegNum, ${region.regionCode}, $createdAt, $updatedAt" + ) + } + } + + StringBuilder("INSERT INTO store (id, name, address, business_reg_num, region_code, created_at, updated_at) VALUES ") + .append(storeSqlRows.joinToString(",\n")) + .append(";") + .toString() + .also { File("$BASE_DIR/store_data.sql").writeText(it) } + + File("$BASE_DIR/store_data.txt").writeText( + storeDataRows.joinToString("\n") + ) + } +}) + +private fun parseSigunguName(sigunguName: String): String { + if (sigunguName.length == 2) { + return sigunguName + } + return sigunguName.substring(0, sigunguName.length - 1) +} + +private fun randomLocalDateTime(): String { + val year = Random.nextInt(2020, 2024) + val month = Random.nextInt(1, 13) + val day = when (month) { + 2 -> Random.nextInt(1, 29) + else -> Random.nextInt(1, 31) + } + val hour = Random.nextInt(9, 19) + val minute = Random.nextInt(0, 60) + val second = Random.nextInt(0, 60) + + return LocalDateTime.of(year, month, day, hour, minute, second) + .atZone(ZoneId.systemDefault()) + .toOffsetDateTime() + .format(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssXXX")) +} + +private fun generateBusinessRegNum(): String { + val part1 = Random.nextInt(100, 1000) + val part2 = Random.nextInt(10, 100) + val part3 = Random.nextInt(10000, 100000) + return "$part1-$part2-$part3" +} + +private fun initializeRegionWithStoreCount(): List { + return File(PARSED_REGION_POPULATION_FILE).readText().lines().map { + val parts = it.split(", ") + val regionCode = parts[0] + val sidoName = parts[3] + val sigunguName = parts[4] + val storeCount: Int = (parts[5].toInt() / MIN_POPULATION_FOR_PER_STORE) + + RegionWithStoreCount( + regionCode = regionCode, + sidoName = sidoName, + sigunguName = sigunguName, + storeCount = storeCount + ) + } +} + +data class RegionWithStoreCount( + val regionCode: String, + val sidoName: String, + val sigunguName: String, + var storeCount: Int +)