1111import store .lastdance .dto .gemini .GeminiResponseDTO ;
1212import store .lastdance .exception .CustomException ;
1313import store .lastdance .exception .ErrorCode ;
14+ import store .lastdance .service .prompt .PromptService ; // Import PromptService
1415
1516import java .util .List ;
1617import java .util .regex .Matcher ;
2930@ Slf4j
3031public class ExpenseAnalyzerImpl implements ExpenseAnalyzer {
3132
33+ // Regex Pattern for JSON block parsing (kept as static final for now)
34+ private static final Pattern JSON_BLOCK_PATTERN = Pattern .compile ("```json\\ s*([\\ s\\ S]+?)\\ s*```|```\\ s*([\\ s\\ S]+?)\\ s*```" , Pattern .CASE_INSENSITIVE );
35+
3236 private final ObjectMapper objectMapper ;
3337 private final WebClient webClient ;
38+ private final PromptService promptService ; // Inject PromptService
3439
35- public ExpenseAnalyzerImpl (ObjectMapper objectMapper , @ Value ("${GOOGLE_GEMINI_KEY}" ) String apiKey
40+ public ExpenseAnalyzerImpl (ObjectMapper objectMapper , @ Value ("${GOOGLE_GEMINI_KEY}" ) String apiKey , PromptService promptService
3641 ) {
3742 this .objectMapper = objectMapper ;
3843 this .webClient = WebClient .builder ()
3944 .baseUrl ("https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key=" + apiKey )
4045 .defaultHeader ("Content-Type" , "application/json" )
4146 .build ();
47+ this .promptService = promptService ; // Assign injected service
4248 }
4349
4450 @ Override
@@ -56,47 +62,8 @@ public AnalyzeExpenseResponseDTO.Suggestion analyzerExpenseData(String expenseJs
5662 }
5763
5864 private String createPrompt (String expenseJson ) {
59- String systemInstruction = """
60- 당신은 재정관리 전문가입니다.
61- 사용자의 지출 데이터를 기반으로, 불필요한 지출을 줄일 수 있는 가장 효과적인 절약 팁 하나를 제안해주세요.
62- 답변은 오직 *JSON 형식*에 맞춰 개선 제안 하나만 포함해야 합니다.
63- """ ;
64- String userPrompt = "나의 지출 내역은 다음과 같다: ```json\n " + expenseJson + "\n ```\n \n " +
65- "이 데이터를 바탕으로 가장 효과적인 개선 제안 하나를 다음 형식에 맞춰 제공해주세요." ;
66- String formatInstruction = """
67- ***Format***
68- 반드시 첫 줄부터 아래 포맷만 출력하고, 안내 문구나 예시 등은 출력하지 마세요.
69- - "title" : [개선 제안의 제목]
70- - "description" : [구체적인 설명, Markdown 형식으로 작성]
71- - "effect" : [예상되는 효과]
72- - "difficulty" : [쉬움/보통/어려움 중 하나]
73-
74- ***Format Example***
75- {
76- "title" : "자동 저축 설정"
77- "description" : **Markdown 형식으로 작성**
78- "
79- 지출 내역을 분석한 결과, '그룹' 유형의 지출이 상당히 많습니다. 특히 '식비', '유흥', '쇼핑' 카테고리
80- 에서 그룹 지출이 빈번하게 발생하고 있습니다.
81- ### 제안:
82-
83- - 지출 항목 분석: 그룹 내에서 어떤 항목에 가장 많은 지출이 발생하는지 파악합니다. (예: 식비, 엔터테인먼트, 쇼핑 등)
84- - 예산 설정: 각 항목별로 합리적인 월별 예산을 설정합니다. 예산을 초과하지 않도록 그룹 구성원들과 함께 노력합니다.
85- - 대안 모색: 더 저렴한 대안을 찾아봅니다. (예: 외식 대신 직접 요리, 저렴한 엔터테인먼트 활동 찾기)
86- - 정기적인 검토: 매달 예산 대비 실제 지출을 검토하고, 필요에 따라 예산을 조정합니다.
87- "
88- "effect" : "연간 목표 달성률 40% 향상"
89- "difficulty" : "쉬움"
90- }
91- ************
92- 위 형식을 반드시 준수하여 JSON 객체만 응답하세요.
93- """ ;
94-
95- String finalPrompt = systemInstruction + "\n \n " +
96- userPrompt + "\n \n " +
97- formatInstruction ;
98-
99- return finalPrompt ;
65+ String combinedPromptTemplate = promptService .getPromptContent ("LLM_EXPENSE_ANALYSIS_PROMPT" );
66+ return String .format (combinedPromptTemplate , expenseJson );
10067 }
10168 private GeminiRequestDTO createRequestJson (String prompt ) {
10269 GeminiRequestDTO dto = new GeminiRequestDTO (List .of (new GeminiRequestDTO .Content (List .of (new GeminiRequestDTO .Part (prompt )))));
@@ -114,8 +81,7 @@ private AnalyzeExpenseResponseDTO.Suggestion parseSuggestionResponse(GeminiRespo
11481 log .info ("LLM이 생성한 텍스트 : {}" , rawText );
11582
11683 String jsonText = null ;
117- Pattern p = Pattern .compile ("```json\\ s*([\\ s\\ S]+?)\\ s*```|```\\ s*([\\ s\\ S]+?)\\ s*```" , Pattern .CASE_INSENSITIVE );
118- Matcher m = p .matcher (rawText );
84+ Matcher m = JSON_BLOCK_PATTERN .matcher (rawText ); // Use the static final Pattern
11985 if (m .find ()) {
12086 jsonText = m .group (1 ) != null ? m .group (1 ) : m .group (2 );
12187 } else if (rawText .startsWith ("{" ) && rawText .endsWith ("}" )) {
0 commit comments