...
This commit is contained in:
@@ -16,34 +16,96 @@ import java.io.File
|
||||
import java.util.zip.ZipInputStream
|
||||
import javax.xml.parsers.DocumentBuilderFactory
|
||||
|
||||
import kotlinx.serialization.encodeToString // 추가 필요
|
||||
@Serializable
|
||||
data class StockItem(
|
||||
val code: String,
|
||||
val name: String
|
||||
){}
|
||||
|
||||
object StockUniverseLoader {
|
||||
private val json = Json { ignoreUnknownKeys = true }
|
||||
|
||||
fun loadUniverse(filePath: String = "stocks_universe.json"): List<Pair<String, String>> {
|
||||
object StockUniverseLoader {
|
||||
// prettyPrint = true 를 주면 JSON 파일이 한 줄로 안 뭉치고 예쁘게 저장됩니다.
|
||||
private val json = Json { ignoreUnknownKeys = true; prettyPrint = true }
|
||||
private const val DEFAULT_FILE_PATH = "stocks_universe.json"
|
||||
|
||||
fun loadUniverse(filePath: String = DEFAULT_FILE_PATH): List<Pair<String, String>> {
|
||||
return try {
|
||||
val file = File(filePath)
|
||||
if (!file.exists()) {
|
||||
println("⚠️ 파일을 찾을 수 없습니다: ${file.absolutePath}")
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
// 1. JSON 파일 읽기 및 역직렬화
|
||||
val rawJson = file.readText()
|
||||
val stockItems = json.decodeFromString<List<StockItem>>(rawJson)
|
||||
|
||||
// 2. Pair(코드, 이름) 튜플 리스트로 변환
|
||||
stockItems.map { it.code to it.name }
|
||||
} catch (e: Exception) {
|
||||
println("❌ 유니버스 로드 실패: ${e.message}")
|
||||
emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
// 💡 [신규] JSON 파일로 덮어쓰기 저장
|
||||
fun saveUniverse(items: List<Pair<String, String>>, filePath: String = DEFAULT_FILE_PATH) {
|
||||
try {
|
||||
val stockItems = items.map { StockItem(it.first, it.second) }
|
||||
val jsonString = json.encodeToString(stockItems)
|
||||
File(filePath).writeText(jsonString)
|
||||
println("💾 [System] 유니버스 영구 저장 완료: 총 ${items.size}종목")
|
||||
} catch (e: Exception) {
|
||||
println("❌ 유니버스 저장 실패: ${e.message}")
|
||||
}
|
||||
}
|
||||
|
||||
// 💡 [신규] CSV 파일을 받아 파싱, 중복 제거, 저장까지 원스톱으로 처리
|
||||
fun parseAndMergeCsv(file: File, targetJsonPath: String = DEFAULT_FILE_PATH): List<Pair<String, String>> {
|
||||
val newItems = mutableListOf<Pair<String, String>>()
|
||||
try {
|
||||
val lines = file.readLines()
|
||||
if (lines.isEmpty()) return loadUniverse(targetJsonPath)
|
||||
|
||||
// 헤더 자동 추적
|
||||
val headers = lines[0].split(",").map { it.replace("\"", "").trim() }
|
||||
val codeIndex = headers.indexOfFirst { it.contains("종목코드") || it.contains("코드") }
|
||||
val nameIndex = headers.indexOfFirst { it.contains("종목명") || it.contains("이름") }
|
||||
|
||||
val finalCodeIdx = if (codeIndex != -1) codeIndex else 0
|
||||
val finalNameIdx = if (nameIndex != -1) nameIndex else 1
|
||||
|
||||
for (i in 1 until lines.size) {
|
||||
val line = lines[i]
|
||||
if (line.isBlank()) continue
|
||||
|
||||
val parts = line.split(",").map { it.replace("\"", "").trim() }
|
||||
if (parts.size > maxOf(finalCodeIdx, finalNameIdx)) {
|
||||
// 엑셀이 날려먹은 앞자리 '0' 복원 (6자리 맞춤)
|
||||
val rawCode = parts[finalCodeIdx].replace(Regex("[^0-9]"), "")
|
||||
val code = rawCode.padStart(6, '0')
|
||||
val name = parts[finalNameIdx]
|
||||
|
||||
if (code.length == 6) {
|
||||
newItems.add(code to name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 1. 기존 데이터 불러오기
|
||||
val existing = loadUniverse(targetJsonPath)
|
||||
|
||||
// 2. 종목코드(it.first) 기준으로 완벽하게 중복 제거 병합
|
||||
val mergedList = (existing + newItems).distinctBy { it.first }
|
||||
|
||||
// 3. 파일에 덮어쓰기 (영구 저장)
|
||||
saveUniverse(mergedList, targetJsonPath)
|
||||
|
||||
println("✅ CSV 병합 성공! (신규 ${mergedList.size - existing.size}건 추가됨)")
|
||||
return mergedList
|
||||
|
||||
} catch (e: Exception) {
|
||||
println("❌ CSV 파싱 및 병합 실패: ${e.message}")
|
||||
return loadUniverse(targetJsonPath) // 실패 시 기존 데이터라도 반환
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user