2026-01-10 18:16:50 +09:00
|
|
|
package ui
|
|
|
|
|
|
|
|
|
|
import androidx.compose.foundation.border
|
|
|
|
|
import androidx.compose.foundation.layout.*
|
|
|
|
|
import androidx.compose.foundation.lazy.LazyColumn
|
|
|
|
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
|
|
|
|
import androidx.compose.material.*
|
|
|
|
|
import androidx.compose.runtime.*
|
|
|
|
|
import androidx.compose.ui.Alignment
|
|
|
|
|
import androidx.compose.ui.DragData
|
|
|
|
|
import androidx.compose.ui.ExperimentalComposeUiApi
|
|
|
|
|
import androidx.compose.ui.Modifier
|
|
|
|
|
import androidx.compose.ui.graphics.Color
|
|
|
|
|
import androidx.compose.ui.onExternalDrag
|
2026-01-13 16:04:25 +09:00
|
|
|
import androidx.compose.ui.text.font.FontWeight
|
2026-01-10 18:16:50 +09:00
|
|
|
import androidx.compose.ui.text.input.PasswordVisualTransformation
|
|
|
|
|
import androidx.compose.ui.unit.dp
|
|
|
|
|
import androidx.compose.ui.unit.sp
|
|
|
|
|
import kotlinx.coroutines.launch
|
|
|
|
|
import model.AppConfig
|
2026-01-13 16:04:25 +09:00
|
|
|
import model.KisSession
|
2026-01-10 18:16:50 +09:00
|
|
|
import network.KisAuthService
|
2026-01-13 16:04:25 +09:00
|
|
|
import network.KisTradeService
|
2026-01-10 18:16:50 +09:00
|
|
|
import org.jetbrains.exposed.sql.deleteAll
|
|
|
|
|
import org.jetbrains.exposed.sql.insert
|
|
|
|
|
import org.jetbrains.exposed.sql.transactions.transaction
|
|
|
|
|
|
|
|
|
|
|
2026-01-13 16:04:25 +09:00
|
|
|
// src/main/kotlin/ui/SettingsScreen.kt
|
2026-01-10 18:16:50 +09:00
|
|
|
@OptIn(ExperimentalComposeUiApi::class)
|
|
|
|
|
@Composable
|
2026-01-13 16:04:25 +09:00
|
|
|
fun SettingsScreen(onAuthSuccess: () -> Unit) {
|
2026-01-10 18:16:50 +09:00
|
|
|
val scope = rememberCoroutineScope()
|
2026-01-13 16:04:25 +09:00
|
|
|
var config by remember { mutableStateOf(KisSession.config) }
|
|
|
|
|
var statusMessage by remember { mutableStateOf("정보를 입력하세요.") }
|
2026-01-10 18:16:50 +09:00
|
|
|
|
2026-01-13 16:04:25 +09:00
|
|
|
// 계좌번호 입력 시 데이터 자동 로드 함수
|
|
|
|
|
fun checkAndLoadConfig(accountNo: String, isReal: Boolean) {
|
|
|
|
|
val loaded = DatabaseFactory.findConfigByAccount(accountNo)
|
|
|
|
|
if (loaded != null) {
|
|
|
|
|
config = loaded
|
|
|
|
|
statusMessage = "✅ 기존 데이터를 불러왔습니다."
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-01-10 18:16:50 +09:00
|
|
|
|
|
|
|
|
LazyColumn(modifier = Modifier.fillMaxSize().padding(24.dp)) {
|
|
|
|
|
item {
|
2026-01-13 16:04:25 +09:00
|
|
|
Text("거래 방식 선택", style = MaterialTheme.typography.h6)
|
2026-01-10 18:16:50 +09:00
|
|
|
Row(verticalAlignment = Alignment.CenterVertically) {
|
2026-01-13 16:04:25 +09:00
|
|
|
RadioButton(selected = !config.isSimulation, onClick = { config = config.copy(isSimulation = false) })
|
|
|
|
|
Text("실전투자")
|
|
|
|
|
Spacer(Modifier.width(16.dp))
|
|
|
|
|
RadioButton(selected = config.isSimulation, onClick = { config = config.copy(isSimulation = true) })
|
|
|
|
|
Text("모의투자")
|
2026-01-10 18:16:50 +09:00
|
|
|
}
|
2026-01-13 16:04:25 +09:00
|
|
|
Divider(Modifier.padding(vertical = 12.dp))
|
2026-01-10 18:16:50 +09:00
|
|
|
|
2026-01-13 16:04:25 +09:00
|
|
|
// 실전 3종 입력
|
|
|
|
|
Text("실전투자 정보 (시세 조회 필수)", fontWeight = FontWeight.Bold)
|
|
|
|
|
OutlinedTextField(value = config.realAccountNo, onValueChange = {
|
|
|
|
|
config = config.copy(realAccountNo = it)
|
|
|
|
|
if(it.length >= 8) checkAndLoadConfig(it, true)
|
|
|
|
|
}, label = { Text("실전 계좌번호") }, modifier = Modifier.fillMaxWidth())
|
|
|
|
|
OutlinedTextField(value = config.realAppKey, onValueChange = { config = config.copy(realAppKey = it) }, label = { Text("실전 App Key") }, modifier = Modifier.fillMaxWidth())
|
|
|
|
|
OutlinedTextField(value = config.realSecretKey, onValueChange = { config = config.copy(realSecretKey = it) }, label = { Text("실전 Secret Key") }, modifier = Modifier.fillMaxWidth(), visualTransformation = PasswordVisualTransformation())
|
2026-01-10 18:16:50 +09:00
|
|
|
|
2026-01-13 16:04:25 +09:00
|
|
|
Spacer(Modifier.height(16.dp))
|
2026-01-10 18:16:50 +09:00
|
|
|
|
2026-01-13 16:04:25 +09:00
|
|
|
// 모의 3종 입력
|
|
|
|
|
Text("모의투자 정보", fontWeight = FontWeight.Bold)
|
|
|
|
|
OutlinedTextField(value = config.vtsAccountNo, onValueChange = {
|
|
|
|
|
config = config.copy(vtsAccountNo = it)
|
|
|
|
|
if(it.length >= 8) checkAndLoadConfig(it, false)
|
|
|
|
|
}, label = { Text("모의 계좌번호") }, modifier = Modifier.fillMaxWidth())
|
|
|
|
|
OutlinedTextField(value = config.vtsAppKey, onValueChange = { config = config.copy(vtsAppKey = it) }, label = { Text("모의 App Key") }, modifier = Modifier.fillMaxWidth())
|
|
|
|
|
OutlinedTextField(value = config.vtsSecretKey, onValueChange = { config = config.copy(vtsSecretKey = it) }, label = { Text("모의 Secret Key") }, modifier = Modifier.fillMaxWidth(), visualTransformation = PasswordVisualTransformation())
|
|
|
|
|
|
|
|
|
|
Divider(Modifier.padding(vertical = 16.dp))
|
2026-01-10 18:16:50 +09:00
|
|
|
|
2026-01-13 16:04:25 +09:00
|
|
|
// AI 모델 경로 및 드래그 앤 드롭
|
|
|
|
|
Text("AI 모델 설정", fontWeight = FontWeight.Bold)
|
2026-01-10 18:16:50 +09:00
|
|
|
Box(
|
2026-01-13 16:04:25 +09:00
|
|
|
modifier = Modifier.fillMaxWidth().height(100.dp).border(1.dp, Color.Gray, RoundedCornerShape(8.dp))
|
2026-01-10 18:16:50 +09:00
|
|
|
.onExternalDrag(onDrop = { state ->
|
|
|
|
|
val data = state.dragData
|
|
|
|
|
if (data is DragData.FilesList) {
|
|
|
|
|
val path = data.readFiles().firstOrNull()?.removePrefix("file:")
|
2026-01-13 16:04:25 +09:00
|
|
|
if (path?.endsWith(".gguf") == true) config = config.copy(modelPath = path)
|
2026-01-10 18:16:50 +09:00
|
|
|
}
|
|
|
|
|
}),
|
|
|
|
|
contentAlignment = Alignment.Center
|
|
|
|
|
) {
|
2026-01-13 16:04:25 +09:00
|
|
|
Text(if(config.modelPath.isEmpty()) "GGUF 모델 파일을 여기로 드래그하세요" else config.modelPath, fontSize = 12.sp)
|
2026-01-10 18:16:50 +09:00
|
|
|
}
|
|
|
|
|
|
2026-01-13 16:04:25 +09:00
|
|
|
Spacer(Modifier.height(24.dp))
|
2026-01-10 18:16:50 +09:00
|
|
|
|
|
|
|
|
Button(
|
|
|
|
|
modifier = Modifier.fillMaxWidth().height(50.dp),
|
|
|
|
|
onClick = {
|
|
|
|
|
scope.launch {
|
2026-01-13 16:04:25 +09:00
|
|
|
// isLoading = true
|
|
|
|
|
// 1. KisSession.config 업데이트 및 DB 저장
|
|
|
|
|
KisSession.config = config
|
|
|
|
|
DatabaseFactory.saveConfig(config)
|
|
|
|
|
val authService = KisAuthService()
|
|
|
|
|
val tradeService = KisTradeService()
|
|
|
|
|
val authSuccess = authService.refreshAllTokens()
|
|
|
|
|
val wsKeySuccess = tradeService.refreshWebsocketKey()
|
2026-01-10 18:16:50 +09:00
|
|
|
|
2026-01-13 16:04:25 +09:00
|
|
|
if (authSuccess && wsKeySuccess) {
|
|
|
|
|
statusMessage = "✅ 인증 성공! LLM 시작 중..."
|
|
|
|
|
onAuthSuccess()
|
|
|
|
|
} else {
|
|
|
|
|
statusMessage = "❌ 인증 실패. 키 정보를 확인하세요."
|
2026-01-10 18:16:50 +09:00
|
|
|
}
|
2026-01-13 16:04:25 +09:00
|
|
|
// isLoading = false
|
2026-01-10 18:16:50 +09:00
|
|
|
}
|
|
|
|
|
}
|
2026-01-13 16:04:25 +09:00
|
|
|
) { Text("설정 저장 및 실행") }
|
|
|
|
|
Text(statusMessage, color = Color.Gray, modifier = Modifier.padding(top = 8.dp))
|
2026-01-10 18:16:50 +09:00
|
|
|
}
|
|
|
|
|
}
|
2026-01-13 16:04:25 +09:00
|
|
|
}
|