diff --git a/src/main/kotlin/com/back/koreaTravelGuide/domain/ai/aiChat/controller/ChatController.kt b/src/main/kotlin/com/back/koreaTravelGuide/domain/ai/aiChat/controller/ChatController.kt index 0562b86..3788bcd 100644 --- a/src/main/kotlin/com/back/koreaTravelGuide/domain/ai/aiChat/controller/ChatController.kt +++ b/src/main/kotlin/com/back/koreaTravelGuide/domain/ai/aiChat/controller/ChatController.kt @@ -2,7 +2,7 @@ package com.back.koreaTravelGuide.domain.ai.aiChat.controller // TODO: 채팅 컨트롤러 - AI 채팅 API 및 SSE 스트리밍 엔드포인트 제공 import com.back.koreaTravelGuide.domain.ai.aiChat.tool.WeatherTool -import com.back.koreaTravelGuide.domain.ai.weather.dto.remove.WeatherResponse +import com.back.koreaTravelGuide.domain.ai.weather.dto.MidForecastDto import org.springframework.ai.chat.client.ChatClient import org.springframework.http.MediaType import org.springframework.web.bind.annotation.GetMapping @@ -117,40 +117,36 @@ class ChatController( return emitter } - // 날씨 API 직접 테스트용 엔드포인트 +// 날씨 API 직접 테스트용 엔드포인트 @GetMapping("/weather/test") fun testWeather( - @RequestParam(required = false) location: String?, - @RequestParam(required = false) regionCode: String?, @RequestParam(required = false) baseTime: String?, - ): WeatherResponse { - return weatherTool.getWeatherForecast( - location = location, - regionCode = regionCode, + ): List? { + return weatherTool.queryMidTermNarrative( baseTime = baseTime, ) } // 지역별 날씨 간단 조회 - @GetMapping("/weather/simple") - fun simpleWeather( - @RequestParam(defaultValue = "서울") location: String, - ): String { - val response = - weatherTool.getWeatherForecast( - location = location, - regionCode = null, - baseTime = null, - ) - - return """ - |지역: ${response.region} - |지역코드: ${response.regionCode} - |발표시각: ${response.baseTime} - | - |${response.forecast} - """.trimMargin() - } +// @GetMapping("/weather/simple") +// fun simpleWeather( +// @RequestParam(defaultValue = "서울") location: String, +// ): String { +// val response = +// weatherTool.getWeatherForecast( +// location = location, +// regionCode = null, +// baseTime = null, +// ) +// +// return """ +// |지역: ${response.region} +// |지역코드: ${response.regionCode} +// |발표시각: ${response.baseTime} +// | +// |${response.forecast} +// """.trimMargin() +// } // 현재 서버 시간 확인용 엔드포인트 @GetMapping("/time/current") @@ -164,37 +160,37 @@ class ChatController( } // 원시 XML 응답 확인용 엔드포인트 - @GetMapping("/weather/debug") - fun debugWeatherApi( - @RequestParam(defaultValue = "서울") location: String, - @RequestParam(required = false) regionCode: String?, - @RequestParam(required = false) baseTime: String?, - ): Map { - return try { - println("🚀 디버그 API 호출 시작 - location: $location") - val response = - weatherTool.getWeatherForecast( - location = location, - regionCode = regionCode, - baseTime = baseTime, - ) - - mapOf( - "success" to true, - "location" to location, - "regionCode" to (regionCode ?: "자동변환"), - "baseTime" to (baseTime ?: "자동계산"), - "response" to response, - "hasData" to (response.details.day4 != null || response.details.day5 != null), - "message" to "디버그 정보가 콘솔에 출력되었습니다.", - ) - } catch (e: Exception) { - mapOf( - "success" to false, - "error" to (e.message ?: "알 수 없는 오류"), - "location" to location, - "message" to "오류 발생: ${e.message ?: "알 수 없는 오류"}", - ) - } - } +// @GetMapping("/weather/debug") +// fun debugWeatherApi( +// @RequestParam(defaultValue = "서울") location: String, +// @RequestParam(required = false) regionCode: String?, +// @RequestParam(required = false) baseTime: String?, +// ): Map { +// return try { +// println("🚀 디버그 API 호출 시작 - location: $location") +// val response = +// weatherTool.getWeatherForecast( +// location = location, +// regionCode = regionCode, +// baseTime = baseTime, +// ) +// +// mapOf( +// "success" to true, +// "location" to location, +// "regionCode" to (regionCode ?: "자동변환"), +// "baseTime" to (baseTime ?: "자동계산"), +// "response" to response, +// "hasData" to (response.details.day4 != null || response.details.day5 != null), +// "message" to "디버그 정보가 콘솔에 출력되었습니다.", +// ) +// } catch (e: Exception) { +// mapOf( +// "success" to false, +// "error" to (e.message ?: "알 수 없는 오류"), +// "location" to location, +// "message" to "오류 발생: ${e.message ?: "알 수 없는 오류"}", +// ) +// } +// } } diff --git a/src/main/kotlin/com/back/koreaTravelGuide/domain/ai/aiChat/tool/WeatherTool.kt b/src/main/kotlin/com/back/koreaTravelGuide/domain/ai/aiChat/tool/WeatherTool.kt index 1d9ef58..0828d64 100644 --- a/src/main/kotlin/com/back/koreaTravelGuide/domain/ai/aiChat/tool/WeatherTool.kt +++ b/src/main/kotlin/com/back/koreaTravelGuide/domain/ai/aiChat/tool/WeatherTool.kt @@ -1,10 +1,9 @@ package com.back.koreaTravelGuide.domain.ai.aiChat.tool // TODO: AI 날씨 도구 - Spring AI @Tool 어노테이션으로 AI가 호출할 수 있는 날씨 기능 -import com.back.koreaTravelGuide.domain.ai.weather.dto.remove.MidTermMetricsResult -import com.back.koreaTravelGuide.domain.ai.weather.dto.remove.MidTermNarrativeResult -import com.back.koreaTravelGuide.domain.ai.weather.dto.remove.WeatherResponse -import com.back.koreaTravelGuide.domain.ai.weather.service.WeatherService +import com.back.koreaTravelGuide.domain.ai.weather.dto.MidForecastDto +import com.back.koreaTravelGuide.domain.ai.weather.dto.TemperatureAndLandForecastDto +import com.back.koreaTravelGuide.domain.ai.weather.service.WeatherServiceCore import org.springframework.ai.tool.annotation.Tool import org.springframework.ai.tool.annotation.ToolParam import org.springframework.stereotype.Service @@ -14,7 +13,7 @@ import java.time.format.DateTimeFormatter @Service class WeatherTool( - private val weatherService: WeatherService, + private val weatherServiceCore: WeatherServiceCore, ) { @Tool(description = "현재 한국 시간을 조회합니다.") fun getCurrentTime(): String { @@ -22,45 +21,43 @@ class WeatherTool( return "현재 한국 표준시(KST): ${now.format(DateTimeFormatter.ofPattern("yyyy년 MM월 dd일 HH시 mm분"))}" } - @Tool(description = "중기전망 텍스트를 조회해 여행하기 좋은 지역 후보를 파악합니다. 먼저 호출하여 비교할 지역 코드를 추려 주세요.") + @Tool(description = "전국 중기전망 텍스트를 조회해 여행하기 좋은 지역 후보를 파악합니다. 먼저 호출하여 비교할 지역 코드를 추려 주세요.") fun queryMidTermNarrative( - @ToolParam(description = "권역 또는 대표 지역 이름 (예: 전국, 서울, 부산). 비워 두면 서울 기준.", required = false) regionGroup: String?, - @ToolParam(description = "중기예보 regId (예: 11B10101). regionGroup 대신 직접 지정 가능.", required = false) regionCode: String?, @ToolParam(description = "발표 시각 (YYYYMMDDHHMM). 미지정 시 최근 발표시각 사용.", required = false) baseTime: String?, - ): MidTermNarrativeResult { - return weatherService.getMidTermNarrative( - regionGroup = regionGroup, - regionCode = regionCode, + ): List? { + return weatherServiceCore.getWeatherForecast( baseTime = baseTime, ) } @Tool(description = "중기 기온과 강수 확률 지표를 지역별로 조회합니다. 첫 번째 툴에서 제안한 지역 코드로 비교 분석에 사용하세요.") - fun queryMidTermMetrics( - @ToolParam(description = "중기예보 regId 배열 (예: [\"11B10101\", \"11H20301\"]).", required = true) regionCodes: List, + fun queryMidTermAndLandForecastMetrics( + @ToolParam(description = "지역 이름 (예: 서울, 인천)", required = true) location: String?, + @ToolParam(description = "중기예보 regId (예: [\"11B10101\", \"11H20301\"]).", required = true) regionCode: String?, +// @ToolParam(description = "중기예보 regId 배열 (예: [\"11B10101\", \"11H20301\"]).", required = true) regionCodes: List, @ToolParam(description = "발표 시각 (YYYYMMDDHHMM). 미지정 시 최근 발표시각 사용.", required = false) baseTime: String?, - @ToolParam(description = "확인할 일 수 offset 목록 (4~10). 비워 두면 4~10일 모두 반환.", required = false) days: List?, - ): MidTermMetricsResult { - return weatherService.getMidTermMetrics( - regionCodes = regionCodes, - baseTime = baseTime, - days = days, - ) - } - - @Deprecated( - message = "AI 툴 분리 이후에는 queryMidTermNarrative/queryMidTermMetrics를 사용하세요.", - level = DeprecationLevel.WARNING, - ) - fun getWeatherForecast( - location: String?, - regionCode: String?, - baseTime: String?, - ): WeatherResponse { - return weatherService.getWeatherForecast( +// @ToolParam(description = "확인할 일 수 offset 목록 (4~10). 비워 두면 4~10일 모두 반환.", required = false) days: List?, + ): TemperatureAndLandForecastDto? { + return weatherServiceCore.getTemperatureAndLandForecast( location = location, regionCode = regionCode, baseTime = baseTime, ) } + +// @Deprecated( +// message = "AI 툴 분리 이후에는 queryMidTermNarrative/queryMidTermMetrics를 사용하세요.", +// level = DeprecationLevel.WARNING, +// ) +// fun getWeatherForecast( +// location: String?, +// regionCode: String?, +// baseTime: String?, +// ): WeatherResponse { +// return weatherService.getWeatherForecast( +// location = location, +// regionCode = regionCode, +// baseTime = baseTime, +// ) +// } } diff --git a/src/main/kotlin/com/back/koreaTravelGuide/domain/ai/weather/client/WeatherApiClient.kt b/src/main/kotlin/com/back/koreaTravelGuide/domain/ai/weather/client/WeatherApiClient.kt index c180c4e..d33a687 100644 --- a/src/main/kotlin/com/back/koreaTravelGuide/domain/ai/weather/client/WeatherApiClient.kt +++ b/src/main/kotlin/com/back/koreaTravelGuide/domain/ai/weather/client/WeatherApiClient.kt @@ -3,6 +3,7 @@ package com.back.koreaTravelGuide.domain.ai.weather.client // TODO: 기상청 API 클라이언트 - HTTP 요청으로 날씨 데이터 조회 및 JSON 파싱 import com.back.koreaTravelGuide.domain.ai.weather.client.parser.DataParser import com.back.koreaTravelGuide.domain.ai.weather.client.tools.Tools +import com.back.koreaTravelGuide.domain.ai.weather.dto.LandForecastData import com.back.koreaTravelGuide.domain.ai.weather.dto.TemperatureData import org.springframework.beans.factory.annotation.Value import org.springframework.stereotype.Component diff --git a/src/main/kotlin/com/back/koreaTravelGuide/domain/ai/weather/client/tools/Tools.kt b/src/main/kotlin/com/back/koreaTravelGuide/domain/ai/weather/client/tools/Tools.kt index 6b60fbf..b233e85 100644 --- a/src/main/kotlin/com/back/koreaTravelGuide/domain/ai/weather/client/tools/Tools.kt +++ b/src/main/kotlin/com/back/koreaTravelGuide/domain/ai/weather/client/tools/Tools.kt @@ -2,7 +2,7 @@ package com.back.koreaTravelGuide.domain.ai.weather.client.tools import org.springframework.stereotype.Component -@Component +@Component("clientTools") class Tools { fun getStnIdFromRegionCode(regionCode: String): String { return when { diff --git a/src/main/kotlin/com/back/koreaTravelGuide/domain/ai/weather/dto/TemperatureAndLandForecastDto.kt b/src/main/kotlin/com/back/koreaTravelGuide/domain/ai/weather/dto/TemperatureAndLandForecastDto.kt index 1a36ae0..307d08b 100644 --- a/src/main/kotlin/com/back/koreaTravelGuide/domain/ai/weather/dto/TemperatureAndLandForecastDto.kt +++ b/src/main/kotlin/com/back/koreaTravelGuide/domain/ai/weather/dto/TemperatureAndLandForecastDto.kt @@ -1,3 +1,5 @@ package com.back.koreaTravelGuide.domain.ai.weather.dto -data class TemperatureAndLandForecastDto() +data class TemperatureAndLandForecastDto( + val a: String, +) diff --git a/src/main/kotlin/com/back/koreaTravelGuide/domain/ai/weather/dto/parser/DtoParser.kt b/src/main/kotlin/com/back/koreaTravelGuide/domain/ai/weather/dto/parser/DtoParser.kt index ad8ab15..744d66a 100644 --- a/src/main/kotlin/com/back/koreaTravelGuide/domain/ai/weather/dto/parser/DtoParser.kt +++ b/src/main/kotlin/com/back/koreaTravelGuide/domain/ai/weather/dto/parser/DtoParser.kt @@ -59,6 +59,8 @@ class DtoParser { temperatureData: TemperatureData, landForecastData: LandForecastData, ): TemperatureAndLandForecastDto { - return TemperatureAndLandForecastDto() + return TemperatureAndLandForecastDto( + "a", + ) } } diff --git a/src/main/kotlin/com/back/koreaTravelGuide/domain/ai/weather/service/WeatherService.kt b/src/main/kotlin/com/back/koreaTravelGuide/domain/ai/weather/service/WeatherService.kt index 4f69796..355ed7d 100644 --- a/src/main/kotlin/com/back/koreaTravelGuide/domain/ai/weather/service/WeatherService.kt +++ b/src/main/kotlin/com/back/koreaTravelGuide/domain/ai/weather/service/WeatherService.kt @@ -15,7 +15,7 @@ class WeatherService( private val weatherApiClient: WeatherApiClient, private val parser: DtoParser, ) { - @Cacheable("weatherMidFore", key = "#actualRegionCode + '_' + #actualBaseTime") + @Cacheable("weatherMidFore", key = "'전국_' + #actualBaseTime") fun fetchMidForecast(actualBaseTime: String): List? { val prefixes = listOf("11B", "11D1", "11D2", "11C2", "11C1", "11F2", "11F1", "11H1", "11H2", "11G") val midForecastList = mutableListOf() diff --git a/src/main/kotlin/com/back/koreaTravelGuide/domain/ai/weather/service/tools/Tools.kt b/src/main/kotlin/com/back/koreaTravelGuide/domain/ai/weather/service/tools/Tools.kt index 58ba3e8..a692eb6 100644 --- a/src/main/kotlin/com/back/koreaTravelGuide/domain/ai/weather/service/tools/Tools.kt +++ b/src/main/kotlin/com/back/koreaTravelGuide/domain/ai/weather/service/tools/Tools.kt @@ -5,7 +5,7 @@ import java.time.ZoneId import java.time.ZonedDateTime import java.time.format.DateTimeFormatter -@Component +@Component("serviceTools") open class Tools { fun getRegionCodeFromLocation(location: String): String { return REGION_MAP[location] ?: "11B10101" diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index bc589f4..1d36c69 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -76,7 +76,7 @@ springdoc: # Weather API 설정 weather: api: - key: ${WEATHER_API_KEY:your-weather-api-key-here} + key: ${WEATHER_API_KEY:6f5c5689b976ae29611f2b2e1a024430f0fd31383ecb502104a250954a2b916e} base-url: http://apis.data.go.kr/1360000/MidFcstInfoService