From 128e9d6076fb0ddbaccc0c0c15b37ab896722071 Mon Sep 17 00:00:00 2001 From: daeun084 <030804jk@naver.com> Date: Sat, 9 Nov 2024 02:07:20 +0900 Subject: [PATCH 1/2] =?UTF-8?q?add:=20cloud=20natural=20language=20api=20?= =?UTF-8?q?=EC=82=AC=EC=9A=A9=EC=9D=84=20=EC=9C=84=ED=95=9C=20config=20?= =?UTF-8?q?=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 3 ++ .../LearnMate/dev/common/ErrorStatus.java | 3 +- .../dev/common/config/AsyncConfig.java | 33 +++++++++++++++++++ .../config/GoogleNaturalLanguageConfig.java | 28 ++++++++++++++++ .../common/{ => exception}/ApiException.java | 3 +- .../CustomAsyncUncaughtExceptionHandler.java | 17 ++++++++++ .../{ => exception}/ExceptionAdvice.java | 4 ++- .../dev/model/converter/DiaryConverter.java | 2 +- .../dto/response/DiaryAnalysisResponse.java | 2 +- .../LearnMate/dev/service/UserService.java | 2 +- 10 files changed, 91 insertions(+), 6 deletions(-) create mode 100644 src/main/java/LearnMate/dev/common/config/AsyncConfig.java create mode 100644 src/main/java/LearnMate/dev/common/config/GoogleNaturalLanguageConfig.java rename src/main/java/LearnMate/dev/common/{ => exception}/ApiException.java (86%) create mode 100644 src/main/java/LearnMate/dev/common/exception/CustomAsyncUncaughtExceptionHandler.java rename src/main/java/LearnMate/dev/common/{ => exception}/ExceptionAdvice.java (97%) diff --git a/build.gradle b/build.gradle index 79c1172..a2cfa9a 100644 --- a/build.gradle +++ b/build.gradle @@ -56,6 +56,9 @@ dependencies { // json implementation 'org.json:json:20230227' + + // google natural language + implementation 'com.google.cloud:google-cloud-language:2.53.0' } jib { diff --git a/src/main/java/LearnMate/dev/common/ErrorStatus.java b/src/main/java/LearnMate/dev/common/ErrorStatus.java index 7e5de16..f60be5f 100644 --- a/src/main/java/LearnMate/dev/common/ErrorStatus.java +++ b/src/main/java/LearnMate/dev/common/ErrorStatus.java @@ -43,7 +43,8 @@ public enum ErrorStatus implements BaseErrorCode { // Emotion _INVALID_EMOTION_SCORE(HttpStatus.BAD_REQUEST, "EMOTION400", "감정 점수는 -1과 1 사이여야 합니다."), - _INVALID_EMOTION_SPECTRUN(HttpStatus.BAD_REQUEST, "EMOTION400", "유효하지 않은 감정 지표입니다.") + _INVALID_EMOTION_SPECTRUN(HttpStatus.BAD_REQUEST, "EMOTION400", "유효하지 않은 감정 지표입니다."), + _ANALYZE_EMOTION_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "EMOTION500", "감정 분석 API 호출 중 오류가 발생했습니다.") ; diff --git a/src/main/java/LearnMate/dev/common/config/AsyncConfig.java b/src/main/java/LearnMate/dev/common/config/AsyncConfig.java new file mode 100644 index 0000000..1c8656b --- /dev/null +++ b/src/main/java/LearnMate/dev/common/config/AsyncConfig.java @@ -0,0 +1,33 @@ +package LearnMate.dev.common.config; + +import LearnMate.dev.common.exception.CustomAsyncUncaughtExceptionHandler; +import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.annotation.AsyncConfigurer; +import org.springframework.scheduling.annotation.EnableAsync; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; + +import java.util.concurrent.Executor; + +@EnableAsync +@Configuration +public class AsyncConfig implements AsyncConfigurer { + + @Override + public Executor getAsyncExecutor() { + ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); + executor.setCorePoolSize(5); + executor.setMaxPoolSize(5); + executor.setQueueCapacity(5); + executor.setKeepAliveSeconds(30); + executor.setThreadNamePrefix("async-executor-"); + executor.initialize(); + return executor; + } + + @Override + public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { + return new CustomAsyncUncaughtExceptionHandler(); + } + +} diff --git a/src/main/java/LearnMate/dev/common/config/GoogleNaturalLanguageConfig.java b/src/main/java/LearnMate/dev/common/config/GoogleNaturalLanguageConfig.java new file mode 100644 index 0000000..6509540 --- /dev/null +++ b/src/main/java/LearnMate/dev/common/config/GoogleNaturalLanguageConfig.java @@ -0,0 +1,28 @@ +package LearnMate.dev.common.config; + +import com.google.auth.oauth2.GoogleCredentials; +import com.google.cloud.language.v1.LanguageServiceSettings; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.io.Resource; + +import java.io.IOException; + +@Configuration +public class GoogleNaturalLanguageConfig { + @Value("classpath:nlp.json") + Resource gcsCredentials; + + @Bean + public LanguageServiceSettings languageServiceSettings() { + try { + return LanguageServiceSettings.newBuilder() + .setCredentialsProvider(() -> + GoogleCredentials.fromStream(gcsCredentials.getInputStream())) + .build(); + } catch (IOException e) { + throw new RuntimeException("Failed to initialize LanguageServiceSettings", e); + } + } +} diff --git a/src/main/java/LearnMate/dev/common/ApiException.java b/src/main/java/LearnMate/dev/common/exception/ApiException.java similarity index 86% rename from src/main/java/LearnMate/dev/common/ApiException.java rename to src/main/java/LearnMate/dev/common/exception/ApiException.java index 8ffa661..ae50845 100644 --- a/src/main/java/LearnMate/dev/common/ApiException.java +++ b/src/main/java/LearnMate/dev/common/exception/ApiException.java @@ -1,5 +1,6 @@ -package LearnMate.dev.common; +package LearnMate.dev.common.exception; +import LearnMate.dev.common.ErrorStatus; import LearnMate.dev.model.dto.ErrorReasonDto; import lombok.Getter; diff --git a/src/main/java/LearnMate/dev/common/exception/CustomAsyncUncaughtExceptionHandler.java b/src/main/java/LearnMate/dev/common/exception/CustomAsyncUncaughtExceptionHandler.java new file mode 100644 index 0000000..cf2d1dd --- /dev/null +++ b/src/main/java/LearnMate/dev/common/exception/CustomAsyncUncaughtExceptionHandler.java @@ -0,0 +1,17 @@ +package LearnMate.dev.common.exception; + +import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler; + +import java.lang.reflect.Method; + +public class CustomAsyncUncaughtExceptionHandler implements AsyncUncaughtExceptionHandler { + @Override + public void handleUncaughtException(Throwable ex, Method method, Object... params) { + System.out.println("Exception message - " + ex.getMessage()); + System.out.println("Method name - " + method.getName()); + + for (Object param : params) { + System.out.println("Parameter value - " + param); + } + } +} diff --git a/src/main/java/LearnMate/dev/common/ExceptionAdvice.java b/src/main/java/LearnMate/dev/common/exception/ExceptionAdvice.java similarity index 97% rename from src/main/java/LearnMate/dev/common/ExceptionAdvice.java rename to src/main/java/LearnMate/dev/common/exception/ExceptionAdvice.java index 8591e17..a3f1bba 100644 --- a/src/main/java/LearnMate/dev/common/ExceptionAdvice.java +++ b/src/main/java/LearnMate/dev/common/exception/ExceptionAdvice.java @@ -1,5 +1,7 @@ -package LearnMate.dev.common; +package LearnMate.dev.common.exception; +import LearnMate.dev.common.ApiResponse; +import LearnMate.dev.common.ErrorStatus; import LearnMate.dev.model.dto.ErrorReasonDto; import jakarta.servlet.http.HttpServletRequest; import jakarta.validation.ConstraintViolation; diff --git a/src/main/java/LearnMate/dev/model/converter/DiaryConverter.java b/src/main/java/LearnMate/dev/model/converter/DiaryConverter.java index eadde58..c437442 100644 --- a/src/main/java/LearnMate/dev/model/converter/DiaryConverter.java +++ b/src/main/java/LearnMate/dev/model/converter/DiaryConverter.java @@ -28,7 +28,7 @@ public static DiaryDetailResponse toDiaryDetailResponse(Diary diary) { .build(); } - public static DiaryAnalysisResponse toDiaryAnalysisResponse(Double score, String actionTip) { + public static DiaryAnalysisResponse toDiaryAnalysisResponse(Float score, String actionTip) { return DiaryAnalysisResponse.builder() .emotionScore(score) .actionTip(actionTip) diff --git a/src/main/java/LearnMate/dev/model/dto/response/DiaryAnalysisResponse.java b/src/main/java/LearnMate/dev/model/dto/response/DiaryAnalysisResponse.java index 57a6563..4fe8788 100644 --- a/src/main/java/LearnMate/dev/model/dto/response/DiaryAnalysisResponse.java +++ b/src/main/java/LearnMate/dev/model/dto/response/DiaryAnalysisResponse.java @@ -11,7 +11,7 @@ @NoArgsConstructor public class DiaryAnalysisResponse { - private Double emotionScore; + private Float emotionScore; private String actionTip; } diff --git a/src/main/java/LearnMate/dev/service/UserService.java b/src/main/java/LearnMate/dev/service/UserService.java index 41045b1..aa280b3 100644 --- a/src/main/java/LearnMate/dev/service/UserService.java +++ b/src/main/java/LearnMate/dev/service/UserService.java @@ -1,6 +1,6 @@ package LearnMate.dev.service; -import LearnMate.dev.common.ApiException; +import LearnMate.dev.common.exception.ApiException; import LearnMate.dev.common.ErrorStatus; import LearnMate.dev.model.dto.request.UserSignInRequest; import LearnMate.dev.model.dto.request.UserSignUpRequest; From a92264ac443bc3f49b38e997bf18b1fedcab27c4 Mon Sep 17 00:00:00 2001 From: daeun084 <030804jk@naver.com> Date: Sat, 9 Nov 2024 02:08:02 +0900 Subject: [PATCH 2/2] =?UTF-8?q?feat:=20=EA=B0=90=EC=A0=95=20=EB=B6=84?= =?UTF-8?q?=EC=84=9D=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dev/service/ActionTipService.java | 17 +++++++++ .../LearnMate/dev/service/DiaryService.java | 12 ++++-- .../dev/service/NaturalLanguageService.java | 37 +++++++++++++++++++ 3 files changed, 63 insertions(+), 3 deletions(-) create mode 100644 src/main/java/LearnMate/dev/service/ActionTipService.java create mode 100644 src/main/java/LearnMate/dev/service/NaturalLanguageService.java diff --git a/src/main/java/LearnMate/dev/service/ActionTipService.java b/src/main/java/LearnMate/dev/service/ActionTipService.java new file mode 100644 index 0000000..fab0914 --- /dev/null +++ b/src/main/java/LearnMate/dev/service/ActionTipService.java @@ -0,0 +1,17 @@ +package LearnMate.dev.service; + +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Service; + +import java.util.concurrent.CompletableFuture; + +@Service +public class ActionTipService { + + @Async + public CompletableFuture getActionTip(String content) { + String text = "Action Tip"; + + return CompletableFuture.completedFuture(text); + } +} diff --git a/src/main/java/LearnMate/dev/service/DiaryService.java b/src/main/java/LearnMate/dev/service/DiaryService.java index 3b27c72..ba441bb 100644 --- a/src/main/java/LearnMate/dev/service/DiaryService.java +++ b/src/main/java/LearnMate/dev/service/DiaryService.java @@ -1,6 +1,6 @@ package LearnMate.dev.service; -import LearnMate.dev.common.ApiException; +import LearnMate.dev.common.exception.ApiException; import LearnMate.dev.common.ErrorStatus; import LearnMate.dev.model.converter.ActionTipConverter; import LearnMate.dev.model.converter.DiaryConverter; @@ -22,6 +22,7 @@ import org.springframework.transaction.annotation.Transactional; import java.time.LocalDate; +import java.util.concurrent.CompletableFuture; @Service @Transactional(readOnly = true) @@ -29,6 +30,8 @@ public class DiaryService { private final UserRepository userRepository; private final DiaryRepository diaryRepository; + private final NaturalLanguageService naturalLanguageService; + private final ActionTipService actionTipService; /* * 유저의 일기 내용을 기반으로 감정을 분석하고 행동 요령을 제안함 @@ -44,11 +47,14 @@ public DiaryAnalysisResponse analyzeDiary(Long userId, DiaryAnalysisRequest requ String content = request.getContent(); validContentLength(content); - // TODO: 감정 분석 API 호출 + // 감정 분석 후 감정 점수 반환 + CompletableFuture scoreFuture = naturalLanguageService.analyzeEmotion(content); // TODO: 행동 요령 제안 API 호출 + CompletableFuture actionTipFuture = actionTipService.getActionTip(content); - return DiaryConverter.toDiaryAnalysisResponse(1.0, "ActionTip"); + // 두 CompletableFuture 조합 + return scoreFuture.thenCombine(actionTipFuture, DiaryConverter::toDiaryAnalysisResponse).join(); } /* diff --git a/src/main/java/LearnMate/dev/service/NaturalLanguageService.java b/src/main/java/LearnMate/dev/service/NaturalLanguageService.java new file mode 100644 index 0000000..af623f5 --- /dev/null +++ b/src/main/java/LearnMate/dev/service/NaturalLanguageService.java @@ -0,0 +1,37 @@ +package LearnMate.dev.service; + +import LearnMate.dev.common.exception.ApiException; +import LearnMate.dev.common.ErrorStatus; +import com.google.cloud.language.v1.Document; +import com.google.cloud.language.v1.LanguageServiceClient; +import com.google.cloud.language.v1.Sentiment; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Service; + +import java.io.IOException; +import java.util.concurrent.CompletableFuture; + +@Service +public class NaturalLanguageService { + + /* + * cloud Natural Language API를 호출해 텍스트에 대한 감정을 분석 + * @param text + * @return + */ + @Async + public CompletableFuture analyzeEmotion(String text) { + try (LanguageServiceClient language = LanguageServiceClient.create()) { + + Document doc = Document.newBuilder().setContent(text).setType(Document.Type.PLAIN_TEXT).build(); + Sentiment sentiment = language.analyzeSentiment(doc).getDocumentSentiment(); + + System.out.printf("Text: %s%n", text); + System.out.printf("Sentiment: %s, %s%n", sentiment.getScore(), sentiment.getMagnitude()); + + return CompletableFuture.completedFuture(sentiment.getScore()); + } catch (IOException e) { + throw new ApiException(ErrorStatus._ANALYZE_EMOTION_ERROR); + } + } +}