Merge branch 'main' of https://github.com/dg04-hi/hi-backend into mn
# Conflicts: # build.gradle
This commit is contained in:
commit
de5ba56c95
@ -31,13 +31,13 @@ import java.util.Map;
|
|||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class RedisConfig {
|
public class RedisConfig {
|
||||||
|
|
||||||
@Value("${spring.redis.host}")
|
@Value("${spring.data.redis.host}")
|
||||||
private String redisHost;
|
private String redisHost;
|
||||||
|
|
||||||
@Value("${spring.redis.port}")
|
@Value("${spring.data.redis.port}")
|
||||||
private int redisPort;
|
private int redisPort;
|
||||||
|
|
||||||
@Value("${spring.redis.password:}")
|
@Value("${spring.data.redis.password:}")
|
||||||
private String redisPassword;
|
private String redisPassword;
|
||||||
|
|
||||||
private final ObjectMapper objectMapper;
|
private final ObjectMapper objectMapper;
|
||||||
|
|||||||
@ -8,4 +8,8 @@ public class ActionPlanNotFoundException extends AnalyticsException {
|
|||||||
public ActionPlanNotFoundException(Long planId) {
|
public ActionPlanNotFoundException(Long planId) {
|
||||||
super("ACTION_PLAN_NOT_FOUND", "실행 계획을 찾을 수 없습니다: " + planId);
|
super("ACTION_PLAN_NOT_FOUND", "실행 계획을 찾을 수 없습니다: " + planId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ActionPlanNotFoundException(String message) {
|
||||||
|
super("ACTION_PLAN_NOT_FOUND", message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,9 @@
|
|||||||
package com.ktds.hi.analytics.infra.exception;
|
package com.ktds.hi.analytics.infra.exception;
|
||||||
|
|
||||||
import com.ktds.hi.common.dto.ErrorResponse;
|
import com.ktds.hi.common.dto.ErrorResponse;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import jakarta.validation.ConstraintViolation;
|
||||||
|
import jakarta.validation.ConstraintViolationException;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
@ -11,14 +14,13 @@ import org.springframework.web.bind.annotation.ExceptionHandler;
|
|||||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||||
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
|
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
|
||||||
|
|
||||||
import jakarta.validation.*;
|
import java.util.List;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 글로벌 예외 처리 핸들러
|
* 글로벌 예외 처리 핸들러 (수정 완료)
|
||||||
* 모든 컨트롤러에서 발생하는 예외를 중앙에서 처리
|
* 모든 컨트롤러에서 발생하는 예외를 중앙에서 처리
|
||||||
|
* 새로운 ErrorResponse 필드 구조에 맞게 수정
|
||||||
*/
|
*/
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@RestControllerAdvice
|
@RestControllerAdvice
|
||||||
@ -28,16 +30,15 @@ public class GlobalExceptionHandler {
|
|||||||
* 분석 서비스 커스텀 예외 처리
|
* 분석 서비스 커스텀 예외 처리
|
||||||
*/
|
*/
|
||||||
@ExceptionHandler(AnalyticsException.class)
|
@ExceptionHandler(AnalyticsException.class)
|
||||||
public ResponseEntity<ErrorResponse> handleAnalyticsException(AnalyticsException ex) {
|
public ResponseEntity<ErrorResponse<Void>> handleAnalyticsException(
|
||||||
|
AnalyticsException ex, HttpServletRequest request) {
|
||||||
log.error("Analytics Exception: {}", ex.getMessage(), ex);
|
log.error("Analytics Exception: {}", ex.getMessage(), ex);
|
||||||
|
|
||||||
ErrorResponse errorResponse = ErrorResponse.builder()
|
ErrorResponse<Void> errorResponse = ErrorResponse.of(
|
||||||
.timestamp(LocalDateTime.now())
|
ex.getErrorCode(),
|
||||||
.status(HttpStatus.BAD_REQUEST.value())
|
ex.getMessage(),
|
||||||
.error("Analytics Error")
|
request.getRequestURI()
|
||||||
.message(ex.getMessage())
|
);
|
||||||
.path("/api/analytics")
|
|
||||||
.build();
|
|
||||||
|
|
||||||
return ResponseEntity.badRequest().body(errorResponse);
|
return ResponseEntity.badRequest().body(errorResponse);
|
||||||
}
|
}
|
||||||
@ -46,16 +47,15 @@ public class GlobalExceptionHandler {
|
|||||||
* 매장 정보 없음 예외 처리
|
* 매장 정보 없음 예외 처리
|
||||||
*/
|
*/
|
||||||
@ExceptionHandler(StoreNotFoundException.class)
|
@ExceptionHandler(StoreNotFoundException.class)
|
||||||
public ResponseEntity<ErrorResponse> handleStoreNotFoundException(StoreNotFoundException ex) {
|
public ResponseEntity<ErrorResponse<Void>> handleStoreNotFoundException(
|
||||||
|
StoreNotFoundException ex, HttpServletRequest request) {
|
||||||
log.error("Store Not Found: {}", ex.getMessage());
|
log.error("Store Not Found: {}", ex.getMessage());
|
||||||
|
|
||||||
ErrorResponse errorResponse = ErrorResponse.builder()
|
ErrorResponse<Void> errorResponse = ErrorResponse.of(
|
||||||
.timestamp(LocalDateTime.now())
|
"STORE_NOT_FOUND",
|
||||||
.status(HttpStatus.NOT_FOUND.value())
|
ex.getMessage(),
|
||||||
.error("Store Not Found")
|
request.getRequestURI()
|
||||||
.message(ex.getMessage())
|
);
|
||||||
.path("/api/analytics")
|
|
||||||
.build();
|
|
||||||
|
|
||||||
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(errorResponse);
|
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(errorResponse);
|
||||||
}
|
}
|
||||||
@ -64,16 +64,15 @@ public class GlobalExceptionHandler {
|
|||||||
* 실행 계획 없음 예외 처리
|
* 실행 계획 없음 예외 처리
|
||||||
*/
|
*/
|
||||||
@ExceptionHandler(ActionPlanNotFoundException.class)
|
@ExceptionHandler(ActionPlanNotFoundException.class)
|
||||||
public ResponseEntity<ErrorResponse> handleActionPlanNotFoundException(ActionPlanNotFoundException ex) {
|
public ResponseEntity<ErrorResponse<Void>> handleActionPlanNotFoundException(
|
||||||
|
ActionPlanNotFoundException ex, HttpServletRequest request) {
|
||||||
log.error("Action Plan Not Found: {}", ex.getMessage());
|
log.error("Action Plan Not Found: {}", ex.getMessage());
|
||||||
|
|
||||||
ErrorResponse errorResponse = ErrorResponse.builder()
|
ErrorResponse<Void> errorResponse = ErrorResponse.of(
|
||||||
.timestamp(LocalDateTime.now())
|
"ACTION_PLAN_NOT_FOUND",
|
||||||
.status(HttpStatus.NOT_FOUND.value())
|
ex.getMessage(),
|
||||||
.error("Action Plan Not Found")
|
request.getRequestURI()
|
||||||
.message(ex.getMessage())
|
);
|
||||||
.path("/api/action-plans")
|
|
||||||
.build();
|
|
||||||
|
|
||||||
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(errorResponse);
|
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(errorResponse);
|
||||||
}
|
}
|
||||||
@ -82,16 +81,15 @@ public class GlobalExceptionHandler {
|
|||||||
* AI 서비스 예외 처리
|
* AI 서비스 예외 처리
|
||||||
*/
|
*/
|
||||||
@ExceptionHandler(AIServiceException.class)
|
@ExceptionHandler(AIServiceException.class)
|
||||||
public ResponseEntity<ErrorResponse> handleAIServiceException(AIServiceException ex) {
|
public ResponseEntity<ErrorResponse<Void>> handleAIServiceException(
|
||||||
|
AIServiceException ex, HttpServletRequest request) {
|
||||||
log.error("AI Service Exception: {}", ex.getMessage(), ex);
|
log.error("AI Service Exception: {}", ex.getMessage(), ex);
|
||||||
|
|
||||||
ErrorResponse errorResponse = ErrorResponse.builder()
|
ErrorResponse<Void> errorResponse = ErrorResponse.of(
|
||||||
.timestamp(LocalDateTime.now())
|
"AI_SERVICE_ERROR",
|
||||||
.status(HttpStatus.SERVICE_UNAVAILABLE.value())
|
"AI 서비스 연동 중 오류가 발생했습니다.",
|
||||||
.error("AI Service Error")
|
request.getRequestURI()
|
||||||
.message("AI 서비스 연동 중 오류가 발생했습니다.")
|
);
|
||||||
.path("/api/analytics")
|
|
||||||
.build();
|
|
||||||
|
|
||||||
return ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE).body(errorResponse);
|
return ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE).body(errorResponse);
|
||||||
}
|
}
|
||||||
@ -100,16 +98,15 @@ public class GlobalExceptionHandler {
|
|||||||
* 외부 서비스 예외 처리
|
* 외부 서비스 예외 처리
|
||||||
*/
|
*/
|
||||||
@ExceptionHandler(ExternalServiceException.class)
|
@ExceptionHandler(ExternalServiceException.class)
|
||||||
public ResponseEntity<ErrorResponse> handleExternalServiceException(ExternalServiceException ex) {
|
public ResponseEntity<ErrorResponse<Void>> handleExternalServiceException(
|
||||||
|
ExternalServiceException ex, HttpServletRequest request) {
|
||||||
log.error("External Service Exception: {}", ex.getMessage(), ex);
|
log.error("External Service Exception: {}", ex.getMessage(), ex);
|
||||||
|
|
||||||
ErrorResponse errorResponse = ErrorResponse.builder()
|
ErrorResponse<Void> errorResponse = ErrorResponse.of(
|
||||||
.timestamp(LocalDateTime.now())
|
ex.getErrorCode(),
|
||||||
.status(HttpStatus.SERVICE_UNAVAILABLE.value())
|
"외부 서비스 연동 중 오류가 발생했습니다.",
|
||||||
.error("External Service Error")
|
request.getRequestURI()
|
||||||
.message("외부 서비스 연동 중 오류가 발생했습니다.")
|
);
|
||||||
.path("/api/analytics")
|
|
||||||
.build();
|
|
||||||
|
|
||||||
return ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE).body(errorResponse);
|
return ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE).body(errorResponse);
|
||||||
}
|
}
|
||||||
@ -118,20 +115,25 @@ public class GlobalExceptionHandler {
|
|||||||
* 입력 값 검증 예외 처리
|
* 입력 값 검증 예외 처리
|
||||||
*/
|
*/
|
||||||
@ExceptionHandler(MethodArgumentNotValidException.class)
|
@ExceptionHandler(MethodArgumentNotValidException.class)
|
||||||
public ResponseEntity<ErrorResponse> handleValidationException(MethodArgumentNotValidException ex) {
|
public ResponseEntity<ErrorResponse<Void>> handleValidationException(
|
||||||
|
MethodArgumentNotValidException ex, HttpServletRequest request) {
|
||||||
log.error("Validation Exception: {}", ex.getMessage());
|
log.error("Validation Exception: {}", ex.getMessage());
|
||||||
|
|
||||||
String errorMessage = ex.getBindingResult().getFieldErrors().stream()
|
List<ErrorResponse.ValidationError> validationErrors = ex.getBindingResult()
|
||||||
.map(FieldError::getDefaultMessage)
|
.getFieldErrors()
|
||||||
.collect(Collectors.joining(", "));
|
.stream()
|
||||||
|
.map(error -> ErrorResponse.ValidationError.builder()
|
||||||
|
.field(error.getField())
|
||||||
|
.rejectedValue(error.getRejectedValue())
|
||||||
|
.message(error.getDefaultMessage())
|
||||||
|
.build())
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
ErrorResponse errorResponse = ErrorResponse.builder()
|
ErrorResponse<Void> errorResponse = ErrorResponse.ofValidation(
|
||||||
.timestamp(LocalDateTime.now())
|
"입력값 검증 실패",
|
||||||
.status(HttpStatus.BAD_REQUEST.value())
|
request.getRequestURI(),
|
||||||
.error("Validation Error")
|
validationErrors
|
||||||
.message(errorMessage)
|
);
|
||||||
.path("/api/analytics")
|
|
||||||
.build();
|
|
||||||
|
|
||||||
return ResponseEntity.badRequest().body(errorResponse);
|
return ResponseEntity.badRequest().body(errorResponse);
|
||||||
}
|
}
|
||||||
@ -140,20 +142,24 @@ public class GlobalExceptionHandler {
|
|||||||
* 바인딩 예외 처리
|
* 바인딩 예외 처리
|
||||||
*/
|
*/
|
||||||
@ExceptionHandler(BindException.class)
|
@ExceptionHandler(BindException.class)
|
||||||
public ResponseEntity<ErrorResponse> handleBindException(BindException ex) {
|
public ResponseEntity<ErrorResponse<Void>> handleBindException(
|
||||||
|
BindException ex, HttpServletRequest request) {
|
||||||
log.error("Bind Exception: {}", ex.getMessage());
|
log.error("Bind Exception: {}", ex.getMessage());
|
||||||
|
|
||||||
String errorMessage = ex.getFieldErrors().stream()
|
List<ErrorResponse.ValidationError> validationErrors = ex.getFieldErrors()
|
||||||
.map(FieldError::getDefaultMessage)
|
.stream()
|
||||||
.collect(Collectors.joining(", "));
|
.map(error -> ErrorResponse.ValidationError.builder()
|
||||||
|
.field(error.getField())
|
||||||
|
.rejectedValue(error.getRejectedValue())
|
||||||
|
.message(error.getDefaultMessage())
|
||||||
|
.build())
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
ErrorResponse errorResponse = ErrorResponse.builder()
|
ErrorResponse<Void> errorResponse = ErrorResponse.ofValidation(
|
||||||
.timestamp(LocalDateTime.now())
|
"바인딩 실패",
|
||||||
.status(HttpStatus.BAD_REQUEST.value())
|
request.getRequestURI(),
|
||||||
.error("Binding Error")
|
validationErrors
|
||||||
.message(errorMessage)
|
);
|
||||||
.path("/api/analytics")
|
|
||||||
.build();
|
|
||||||
|
|
||||||
return ResponseEntity.badRequest().body(errorResponse);
|
return ResponseEntity.badRequest().body(errorResponse);
|
||||||
}
|
}
|
||||||
@ -162,20 +168,19 @@ public class GlobalExceptionHandler {
|
|||||||
* 제약 조건 위반 예외 처리
|
* 제약 조건 위반 예외 처리
|
||||||
*/
|
*/
|
||||||
@ExceptionHandler(ConstraintViolationException.class)
|
@ExceptionHandler(ConstraintViolationException.class)
|
||||||
public ResponseEntity<ErrorResponse> handleConstraintViolationException(ConstraintViolationException ex) {
|
public ResponseEntity<ErrorResponse<Void>> handleConstraintViolationException(
|
||||||
|
ConstraintViolationException ex, HttpServletRequest request) {
|
||||||
log.error("Constraint Violation Exception: {}", ex.getMessage());
|
log.error("Constraint Violation Exception: {}", ex.getMessage());
|
||||||
|
|
||||||
String errorMessage = ex.getConstraintViolations().stream()
|
String errorMessage = ex.getConstraintViolations().stream()
|
||||||
.map(ConstraintViolation::getMessage)
|
.map(ConstraintViolation::getMessage)
|
||||||
.collect(Collectors.joining(", "));
|
.collect(Collectors.joining(", "));
|
||||||
|
|
||||||
ErrorResponse errorResponse = ErrorResponse.builder()
|
ErrorResponse<Void> errorResponse = ErrorResponse.of(
|
||||||
.timestamp(LocalDateTime.now())
|
"CONSTRAINT_VIOLATION",
|
||||||
.status(HttpStatus.BAD_REQUEST.value())
|
errorMessage,
|
||||||
.error("Constraint Violation")
|
request.getRequestURI()
|
||||||
.message(errorMessage)
|
);
|
||||||
.path("/api/analytics")
|
|
||||||
.build();
|
|
||||||
|
|
||||||
return ResponseEntity.badRequest().body(errorResponse);
|
return ResponseEntity.badRequest().body(errorResponse);
|
||||||
}
|
}
|
||||||
@ -184,16 +189,15 @@ public class GlobalExceptionHandler {
|
|||||||
* 타입 불일치 예외 처리
|
* 타입 불일치 예외 처리
|
||||||
*/
|
*/
|
||||||
@ExceptionHandler(MethodArgumentTypeMismatchException.class)
|
@ExceptionHandler(MethodArgumentTypeMismatchException.class)
|
||||||
public ResponseEntity<ErrorResponse> handleTypeMismatchException(MethodArgumentTypeMismatchException ex) {
|
public ResponseEntity<ErrorResponse<Void>> handleTypeMismatchException(
|
||||||
|
MethodArgumentTypeMismatchException ex, HttpServletRequest request) {
|
||||||
log.error("Type Mismatch Exception: {}", ex.getMessage());
|
log.error("Type Mismatch Exception: {}", ex.getMessage());
|
||||||
|
|
||||||
ErrorResponse errorResponse = ErrorResponse.builder()
|
ErrorResponse<Void> errorResponse = ErrorResponse.of(
|
||||||
.timestamp(LocalDateTime.now())
|
"TYPE_MISMATCH",
|
||||||
.status(HttpStatus.BAD_REQUEST.value())
|
"잘못된 파라미터 타입입니다: " + ex.getName(),
|
||||||
.error("Type Mismatch")
|
request.getRequestURI()
|
||||||
.message("잘못된 파라미터 타입입니다: " + ex.getName())
|
);
|
||||||
.path("/api/analytics")
|
|
||||||
.build();
|
|
||||||
|
|
||||||
return ResponseEntity.badRequest().body(errorResponse);
|
return ResponseEntity.badRequest().body(errorResponse);
|
||||||
}
|
}
|
||||||
@ -202,16 +206,15 @@ public class GlobalExceptionHandler {
|
|||||||
* 일반적인 RuntimeException 처리
|
* 일반적인 RuntimeException 처리
|
||||||
*/
|
*/
|
||||||
@ExceptionHandler(RuntimeException.class)
|
@ExceptionHandler(RuntimeException.class)
|
||||||
public ResponseEntity<ErrorResponse> handleRuntimeException(RuntimeException ex) {
|
public ResponseEntity<ErrorResponse<Void>> handleRuntimeException(
|
||||||
|
RuntimeException ex, HttpServletRequest request) {
|
||||||
log.error("Runtime Exception: {}", ex.getMessage(), ex);
|
log.error("Runtime Exception: {}", ex.getMessage(), ex);
|
||||||
|
|
||||||
ErrorResponse errorResponse = ErrorResponse.builder()
|
ErrorResponse<Void> errorResponse = ErrorResponse.of(
|
||||||
.timestamp(LocalDateTime.now())
|
"RUNTIME_ERROR",
|
||||||
.status(HttpStatus.INTERNAL_SERVER_ERROR.value())
|
"내부 서버 오류가 발생했습니다.",
|
||||||
.error("Internal Server Error")
|
request.getRequestURI()
|
||||||
.message("내부 서버 오류가 발생했습니다.")
|
);
|
||||||
.path("/api/analytics")
|
|
||||||
.build();
|
|
||||||
|
|
||||||
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(errorResponse);
|
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(errorResponse);
|
||||||
}
|
}
|
||||||
@ -220,16 +223,13 @@ public class GlobalExceptionHandler {
|
|||||||
* 모든 예외의 최종 처리
|
* 모든 예외의 최종 처리
|
||||||
*/
|
*/
|
||||||
@ExceptionHandler(Exception.class)
|
@ExceptionHandler(Exception.class)
|
||||||
public ResponseEntity<ErrorResponse> handleAllExceptions(Exception ex) {
|
public ResponseEntity<ErrorResponse<Void>> handleAllExceptions(
|
||||||
|
Exception ex, HttpServletRequest request) {
|
||||||
log.error("Unexpected Exception: {}", ex.getMessage(), ex);
|
log.error("Unexpected Exception: {}", ex.getMessage(), ex);
|
||||||
|
|
||||||
ErrorResponse errorResponse = ErrorResponse.builder()
|
ErrorResponse<Void> errorResponse = ErrorResponse.ofInternalError(
|
||||||
.timestamp(LocalDateTime.now())
|
"예상치 못한 오류가 발생했습니다."
|
||||||
.status(HttpStatus.INTERNAL_SERVER_ERROR.value())
|
);
|
||||||
.error("Unexpected Error")
|
|
||||||
.message("예상치 못한 오류가 발생했습니다.")
|
|
||||||
.path("/api/analytics")
|
|
||||||
.build();
|
|
||||||
|
|
||||||
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(errorResponse);
|
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(errorResponse);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,4 +8,8 @@ public class StoreNotFoundException extends AnalyticsException {
|
|||||||
public StoreNotFoundException(Long storeId) {
|
public StoreNotFoundException(Long storeId) {
|
||||||
super("STORE_NOT_FOUND", "매장을 찾을 수 없습니다: " + storeId);
|
super("STORE_NOT_FOUND", "매장을 찾을 수 없습니다: " + storeId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public StoreNotFoundException(String message) {
|
||||||
|
super("STORE_NOT_FOUND", message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,9 +1,12 @@
|
|||||||
package com.ktds.hi.analytics.infra.gateway;
|
package com.ktds.hi.analytics.infra.gateway;
|
||||||
|
|
||||||
|
import static com.azure.ai.textanalytics.models.TextSentiment.*;
|
||||||
|
|
||||||
import com.azure.ai.textanalytics.TextAnalyticsClient;
|
import com.azure.ai.textanalytics.TextAnalyticsClient;
|
||||||
import com.azure.ai.textanalytics.TextAnalyticsClientBuilder;
|
import com.azure.ai.textanalytics.TextAnalyticsClientBuilder;
|
||||||
import com.azure.ai.textanalytics.models.AnalyzeSentimentResult;
|
import com.azure.ai.textanalytics.models.AnalyzeSentimentResult;
|
||||||
import com.azure.ai.textanalytics.models.DocumentSentiment;
|
import com.azure.ai.textanalytics.models.DocumentSentiment;
|
||||||
|
import com.azure.ai.textanalytics.models.TextSentiment;
|
||||||
import com.azure.core.credential.AzureKeyCredential;
|
import com.azure.core.credential.AzureKeyCredential;
|
||||||
import com.ktds.hi.analytics.biz.domain.AiFeedback;
|
import com.ktds.hi.analytics.biz.domain.AiFeedback;
|
||||||
import com.ktds.hi.analytics.biz.domain.SentimentType;
|
import com.ktds.hi.analytics.biz.domain.SentimentType;
|
||||||
@ -97,15 +100,18 @@ public class AIServiceAdapter implements AIServicePort {
|
|||||||
@Override
|
@Override
|
||||||
public SentimentType analyzeSentiment(String content) {
|
public SentimentType analyzeSentiment(String content) {
|
||||||
try {
|
try {
|
||||||
AnalyzeSentimentResult result = textAnalyticsClient.analyzeSentiment(content);
|
DocumentSentiment documentSentiment = textAnalyticsClient.analyzeSentiment(content);
|
||||||
DocumentSentiment sentiment = result.getDocumentSentiment();
|
TextSentiment sentiment = documentSentiment.getSentiment();
|
||||||
|
|
||||||
switch (sentiment) {
|
if (sentiment == TextSentiment.POSITIVE) {
|
||||||
case POSITIVE:
|
|
||||||
return SentimentType.POSITIVE;
|
return SentimentType.POSITIVE;
|
||||||
case NEGATIVE:
|
} else if (sentiment == TextSentiment.NEGATIVE) {
|
||||||
return SentimentType.NEGATIVE;
|
return SentimentType.NEGATIVE;
|
||||||
default:
|
} else if (sentiment == TextSentiment.NEUTRAL) {
|
||||||
|
return SentimentType.NEUTRAL;
|
||||||
|
} else if (sentiment == TextSentiment.MIXED) {
|
||||||
|
return SentimentType.NEUTRAL; // MIXED는 NEUTRAL로 처리
|
||||||
|
} else {
|
||||||
return SentimentType.NEUTRAL;
|
return SentimentType.NEUTRAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -31,7 +31,7 @@ import java.util.concurrent.Executors;
|
|||||||
@Slf4j
|
@Slf4j
|
||||||
@Component
|
@Component
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class EventHubAdapter implements EventPort {
|
public class EventHubAdapter {
|
||||||
|
|
||||||
@Qualifier("reviewEventConsumer")
|
@Qualifier("reviewEventConsumer")
|
||||||
private final EventHubConsumerClient reviewEventConsumer;
|
private final EventHubConsumerClient reviewEventConsumer;
|
||||||
@ -61,7 +61,6 @@ public class EventHubAdapter implements EventPort {
|
|||||||
aiAnalysisEventProducer.close();
|
aiAnalysisEventProducer.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void publishAnalysisCompletedEvent(Long storeId, AnalysisType analysisType) {
|
public void publishAnalysisCompletedEvent(Long storeId, AnalysisType analysisType) {
|
||||||
try {
|
try {
|
||||||
Map<String, Object> eventData = new HashMap<>();
|
Map<String, Object> eventData = new HashMap<>();
|
||||||
@ -84,7 +83,7 @@ public class EventHubAdapter implements EventPort {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void publishActionPlanCreatedEvent(ActionPlan actionPlan) {
|
public void publishActionPlanCreatedEvent(ActionPlan actionPlan) {
|
||||||
try {
|
try {
|
||||||
Map<String, Object> eventData = new HashMap<>();
|
Map<String, Object> eventData = new HashMap<>();
|
||||||
|
|||||||
@ -26,7 +26,13 @@ import java.time.LocalDateTime;
|
|||||||
* 점주의 개선 실행 계획을 저장
|
* 점주의 개선 실행 계획을 저장
|
||||||
*/
|
*/
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "action_plan")
|
@Table(name = "action_plan",
|
||||||
|
indexes = {
|
||||||
|
@Index(name = "idx_action_plan_store_id", columnList = "store_id"),
|
||||||
|
@Index(name = "idx_action_plan_user_id", columnList = "user_id"),
|
||||||
|
@Index(name = "idx_action_plan_status", columnList = "status"),
|
||||||
|
@Index(name = "idx_action_plan_created_at", columnList = "created_at")
|
||||||
|
})
|
||||||
@Getter
|
@Getter
|
||||||
@Builder
|
@Builder
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
@ -73,10 +79,4 @@ public class ActionPlanEntity {
|
|||||||
@LastModifiedDate
|
@LastModifiedDate
|
||||||
@Column(name = "updated_at")
|
@Column(name = "updated_at")
|
||||||
private LocalDateTime updatedAt;
|
private LocalDateTime updatedAt;
|
||||||
|
|
||||||
@Index(name = "idx_action_plan_store_id", columnList = "store_id")
|
|
||||||
@Index(name = "idx_action_plan_user_id", columnList = "user_id")
|
|
||||||
@Index(name = "idx_action_plan_status", columnList = "status")
|
|
||||||
public static class Indexes {
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,7 +16,13 @@ import java.time.LocalDateTime;
|
|||||||
* AI가 생성한 피드백 정보를 저장
|
* AI가 생성한 피드백 정보를 저장
|
||||||
*/
|
*/
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "ai_feedback")
|
@Table(name = "ai_feedback",
|
||||||
|
indexes = {
|
||||||
|
@Index(name = "idx_ai_feedback_store_id", columnList = "store_id"),
|
||||||
|
@Index(name = "idx_ai_feedback_generated_at", columnList = "generated_at"),
|
||||||
|
@Index(name = "idx_ai_feedback_created_at", columnList = "created_at"),
|
||||||
|
@Index(name = "idx_ai_feedback_confidence_score", columnList = "confidence_score")
|
||||||
|
})
|
||||||
@Getter
|
@Getter
|
||||||
@Builder
|
@Builder
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
@ -60,7 +66,4 @@ public class AiFeedbackEntity {
|
|||||||
@Column(name = "updated_at")
|
@Column(name = "updated_at")
|
||||||
private LocalDateTime updatedAt;
|
private LocalDateTime updatedAt;
|
||||||
|
|
||||||
@Index(name = "idx_ai_feedback_store_id", columnList = "store_id")
|
|
||||||
public static class Indexes {
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,7 +16,13 @@ import java.time.LocalDateTime;
|
|||||||
* 매장의 분석 정보를 저장
|
* 매장의 분석 정보를 저장
|
||||||
*/
|
*/
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "analytics")
|
@Table(name = "analytics",
|
||||||
|
indexes = {
|
||||||
|
@Index(name = "idx_analytics_store_id", columnList = "store_id"),
|
||||||
|
@Index(name = "idx_analytics_last_analysis_date", columnList = "last_analysis_date"),
|
||||||
|
@Index(name = "idx_analytics_created_at", columnList = "created_at"),
|
||||||
|
@Index(name = "idx_analytics_average_rating", columnList = "average_rating")
|
||||||
|
})
|
||||||
@Getter
|
@Getter
|
||||||
@Builder
|
@Builder
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
@ -57,7 +63,4 @@ public class AnalyticsEntity {
|
|||||||
@Column(name = "updated_at")
|
@Column(name = "updated_at")
|
||||||
private LocalDateTime updatedAt;
|
private LocalDateTime updatedAt;
|
||||||
|
|
||||||
@Index(name = "idx_analytics_store_id", columnList = "store_id")
|
|
||||||
public static class Indexes {
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,7 +13,7 @@ spring:
|
|||||||
|
|
||||||
jpa:
|
jpa:
|
||||||
hibernate:
|
hibernate:
|
||||||
ddl-auto: ${JPA_DDL_AUTO:validate}
|
ddl-auto: ${JPA_DDL_AUTO:create}
|
||||||
show-sql: ${JPA_SHOW_SQL:false}
|
show-sql: ${JPA_SHOW_SQL:false}
|
||||||
properties:
|
properties:
|
||||||
hibernate:
|
hibernate:
|
||||||
@ -44,6 +44,13 @@ ai-api:
|
|||||||
# api-key: ${CLAUDE_API_KEY:}
|
# api-key: ${CLAUDE_API_KEY:}
|
||||||
# base-url: https://api.anthropic.com
|
# base-url: https://api.anthropic.com
|
||||||
|
|
||||||
|
# 외부 서비스 설정
|
||||||
|
external:
|
||||||
|
services:
|
||||||
|
review: ${EXTERNAL_SERVICES_REVIEW:http://localhost:8082}
|
||||||
|
store: ${EXTERNAL_SERVICES_STORE:http://localhost:8081}
|
||||||
|
member: ${EXTERNAL_SERVICES_MEMBER:http://localhost:8080}
|
||||||
|
|
||||||
springdoc:
|
springdoc:
|
||||||
api-docs:
|
api-docs:
|
||||||
path: /api-docs
|
path: /api-docs
|
||||||
@ -55,3 +62,24 @@ management:
|
|||||||
web:
|
web:
|
||||||
exposure:
|
exposure:
|
||||||
include: health,info,metrics
|
include: health,info,metrics
|
||||||
|
|
||||||
|
# AI 서비스 설정
|
||||||
|
ai:
|
||||||
|
azure:
|
||||||
|
cognitive:
|
||||||
|
endpoint: ${AI_AZURE_COGNITIVE_ENDPOINT:https://your-cognitive-service.cognitiveservices.azure.com}
|
||||||
|
key: ${AI_AZURE_COGNITIVE_KEY:your-cognitive-service-key}
|
||||||
|
openai:
|
||||||
|
api-key: ${AI_OPENAI_API_KEY:your-openai-api-key}
|
||||||
|
|
||||||
|
# Azure Event Hub 설정
|
||||||
|
azure:
|
||||||
|
eventhub:
|
||||||
|
connection-string: ${AZURE_EVENTHUB_CONNECTION_STRING:Endpoint=sb://your-eventhub.servicebus.windows.net/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=your-key}
|
||||||
|
consumer-group: ${AZURE_EVENTHUB_CONSUMER_GROUP:analytics-consumer}
|
||||||
|
event-hubs:
|
||||||
|
review-events: ${AZURE_EVENTHUB_REVIEW_EVENTS:review-events}
|
||||||
|
ai-analysis-events: ${AZURE_EVENTHUB_AI_ANALYSIS_EVENTS:ai-analysis-events}
|
||||||
|
storage:
|
||||||
|
connection-string: ${AZURE_STORAGE_CONNECTION_STRING:DefaultEndpointsProtocol=https;AccountName=yourstorageaccount;AccountKey=your-storage-key;EndpointSuffix=core.windows.net}
|
||||||
|
container-name: ${AZURE_STORAGE_CONTAINER_NAME:eventhub-checkpoints}
|
||||||
51
build.gradle
51
build.gradle
@ -1,7 +1,7 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id 'java'
|
id 'java'
|
||||||
id 'org.springframework.boot' version '3.4.0' apply false
|
id 'org.springframework.boot' version '3.4.0' apply false
|
||||||
id 'io.spring.dependency-management' version '1.1.6' apply false
|
id 'io.spring.dependency-management' version '1.1.4' apply false
|
||||||
}
|
}
|
||||||
|
|
||||||
allprojects {
|
allprojects {
|
||||||
@ -19,35 +19,48 @@ subprojects {
|
|||||||
apply plugin: 'io.spring.dependency-management'
|
apply plugin: 'io.spring.dependency-management'
|
||||||
|
|
||||||
java {
|
java {
|
||||||
sourceCompatibility = '21'
|
toolchain {
|
||||||
}
|
languageVersion = JavaLanguageVersion.of(21)
|
||||||
|
|
||||||
configurations {
|
|
||||||
compileOnly {
|
|
||||||
extendsFrom annotationProcessor
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 공통 의존성
|
|
||||||
dependencies {
|
dependencies {
|
||||||
// Lombok (모든 서브 프로젝트에서 사용)
|
// 공통 의존성
|
||||||
|
implementation 'org.springframework.boot:spring-boot-starter-web'
|
||||||
|
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
|
||||||
|
implementation 'org.springframework.boot:spring-boot-starter-validation'
|
||||||
|
implementation 'org.springframework.boot:spring-boot-starter-web'
|
||||||
|
implementation 'org.springframework.boot:spring-boot-starter-security'
|
||||||
|
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
|
||||||
|
implementation 'org.springframework.boot:spring-boot-starter-actuator'
|
||||||
|
|
||||||
|
// Swagger
|
||||||
|
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.2.0'
|
||||||
|
|
||||||
|
// JWT
|
||||||
|
implementation 'io.jsonwebtoken:jjwt-api:0.12.3'
|
||||||
|
runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.12.3'
|
||||||
|
runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.12.3'
|
||||||
|
|
||||||
|
// Database
|
||||||
|
runtimeOnly 'org.postgresql:postgresql'
|
||||||
|
|
||||||
|
// Lombok
|
||||||
compileOnly 'org.projectlombok:lombok'
|
compileOnly 'org.projectlombok:lombok'
|
||||||
annotationProcessor 'org.projectlombok:lombok'
|
annotationProcessor 'org.projectlombok:lombok'
|
||||||
|
|
||||||
// 테스트 (모든 서브 프로젝트에서 사용)
|
// MapStruct
|
||||||
|
implementation 'org.mapstruct:mapstruct:1.5.5.Final'
|
||||||
|
annotationProcessor 'org.mapstruct:mapstruct-processor:1.5.5.Final'
|
||||||
|
|
||||||
|
// Test
|
||||||
testImplementation 'org.springframework.boot:spring-boot-starter-test'
|
testImplementation 'org.springframework.boot:spring-boot-starter-test'
|
||||||
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
|
testImplementation 'org.springframework.security:spring-security-test'
|
||||||
|
testImplementation 'org.testcontainers:junit-jupiter'
|
||||||
|
testImplementation 'org.testcontainers:postgresql'
|
||||||
}
|
}
|
||||||
|
|
||||||
// 컴파일 옵션
|
|
||||||
tasks.withType(JavaCompile) {
|
|
||||||
options.encoding = 'UTF-8'
|
|
||||||
options.compilerArgs += ['-parameters']
|
|
||||||
}
|
|
||||||
|
|
||||||
// 테스트 설정
|
|
||||||
tasks.named('test') {
|
tasks.named('test') {
|
||||||
useJUnitPlatform()
|
useJUnitPlatform()
|
||||||
systemProperty 'spring.profiles.active', 'test'
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -5,11 +5,14 @@ plugins {
|
|||||||
description = 'Common utilities and shared components'
|
description = 'Common utilities and shared components'
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
|
||||||
|
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
|
||||||
// Spring Boot Starters
|
// Spring Boot Starters
|
||||||
api 'org.springframework.boot:spring-boot-starter-web'
|
api 'org.springframework.boot:spring-boot-starter-web'
|
||||||
api 'org.springframework.boot:spring-boot-starter-data-jpa'
|
api 'org.springframework.boot:spring-boot-starter-data-jpa'
|
||||||
api 'org.springframework.boot:spring-boot-starter-validation'
|
api 'org.springframework.boot:spring-boot-starter-validation'
|
||||||
api 'org.springframework.boot:spring-boot-starter-security'
|
api 'org.springframework.boot:spring-boot-starter-security'
|
||||||
|
api 'org.springframework.data:spring-data-commons'
|
||||||
|
|
||||||
// AOP (AspectJ) - 명시적 의존성 추가
|
// AOP (AspectJ) - 명시적 의존성 추가
|
||||||
api 'org.springframework.boot:spring-boot-starter-aop'
|
api 'org.springframework.boot:spring-boot-starter-aop'
|
||||||
@ -36,6 +39,10 @@ dependencies {
|
|||||||
compileOnly 'org.projectlombok:lombok'
|
compileOnly 'org.projectlombok:lombok'
|
||||||
annotationProcessor 'org.projectlombok:lombok'
|
annotationProcessor 'org.projectlombok:lombok'
|
||||||
|
|
||||||
|
// Database
|
||||||
|
runtimeOnly 'org.postgresql:postgresql'
|
||||||
|
testRuntimeOnly 'com.h2database:h2'
|
||||||
|
|
||||||
// 테스트
|
// 테스트
|
||||||
testImplementation 'org.springframework.boot:spring-boot-starter-test'
|
testImplementation 'org.springframework.boot:spring-boot-starter-test'
|
||||||
testImplementation 'org.springframework.security:spring-security-test'
|
testImplementation 'org.springframework.security:spring-security-test'
|
||||||
|
|||||||
@ -11,7 +11,10 @@ import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
|
|||||||
*/
|
*/
|
||||||
@Configuration
|
@Configuration
|
||||||
@ComponentScan(basePackages = "com.ktds.hi.common")
|
@ComponentScan(basePackages = "com.ktds.hi.common")
|
||||||
@EntityScan(basePackages = "com.ktds.hi.common.entity")
|
@EntityScan(basePackages = {
|
||||||
|
"com.ktds.hi.common.entity",
|
||||||
|
"com.ktds.hi.common.audit"
|
||||||
|
})
|
||||||
@EnableJpaRepositories(basePackages = "com.ktds.hi.common.repository")
|
@EnableJpaRepositories(basePackages = "com.ktds.hi.common.repository")
|
||||||
public class CommonModuleConfiguration {
|
public class CommonModuleConfiguration {
|
||||||
// 설정 클래스는 어노테이션만으로도 충분
|
// 설정 클래스는 어노테이션만으로도 충분
|
||||||
|
|||||||
@ -1,5 +1,8 @@
|
|||||||
package com.ktds.hi.common.audit;
|
package com.ktds.hi.common.audit;
|
||||||
|
|
||||||
|
import jakarta.persistence.Entity;
|
||||||
|
import jakarta.persistence.EntityListeners;
|
||||||
|
import jakarta.persistence.Id;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Builder;
|
import lombok.Builder;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
@ -7,15 +10,20 @@ import lombok.NoArgsConstructor;
|
|||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 감사 로그 엔티티
|
* 감사 로그 엔티티
|
||||||
*/
|
*/
|
||||||
@Getter
|
@Getter
|
||||||
|
@Entity
|
||||||
@Builder
|
@Builder
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
|
@EntityListeners(AuditingEntityListener.class)
|
||||||
public class AuditLog {
|
public class AuditLog {
|
||||||
|
|
||||||
|
@Id
|
||||||
private Long id;
|
private Long id;
|
||||||
private String entityType;
|
private String entityType;
|
||||||
private String entityId;
|
private String entityId;
|
||||||
@ -26,4 +34,8 @@ public class AuditLog {
|
|||||||
private String userAgent;
|
private String userAgent;
|
||||||
private String ipAddress;
|
private String ipAddress;
|
||||||
private LocalDateTime timestamp;
|
private LocalDateTime timestamp;
|
||||||
|
private LocalDateTime createdAt;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -1,11 +0,0 @@
|
|||||||
package com.ktds.hi.common.audit;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 감사 로그 리포지토리 인터페이스
|
|
||||||
*/
|
|
||||||
public interface AuditLogRepository {
|
|
||||||
|
|
||||||
void save(AuditLog auditLog);
|
|
||||||
|
|
||||||
AuditLog findById(Long id);
|
|
||||||
}
|
|
||||||
@ -1,5 +1,6 @@
|
|||||||
package com.ktds.hi.common.audit;
|
package com.ktds.hi.common.audit;
|
||||||
|
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||||
import org.springframework.data.domain.AuditorAware;
|
import org.springframework.data.domain.AuditorAware;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.core.context.SecurityContextHolder;
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
@ -11,6 +12,7 @@ import java.util.Optional;
|
|||||||
* JPA Auditing을 위한 사용자 정보 제공자
|
* JPA Auditing을 위한 사용자 정보 제공자
|
||||||
*/
|
*/
|
||||||
@Component
|
@Component
|
||||||
|
@ConditionalOnClass(AuditorAware.class) // 👈 이 어노테이션 추가
|
||||||
public class CustomAuditorAware implements AuditorAware<String> {
|
public class CustomAuditorAware implements AuditorAware<String> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@ -3,6 +3,7 @@ package com.ktds.hi.member.config;
|
|||||||
import com.ktds.hi.member.service.JwtTokenProvider;
|
import com.ktds.hi.member.service.JwtTokenProvider;
|
||||||
import com.ktds.hi.member.service.AuthService;
|
import com.ktds.hi.member.service.AuthService;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.beans.factory.annotation.Qualifier;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.security.authentication.AuthenticationManager;
|
import org.springframework.security.authentication.AuthenticationManager;
|
||||||
@ -24,6 +25,7 @@ import org.springframework.security.web.authentication.UsernamePasswordAuthentic
|
|||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class SecurityConfig {
|
public class SecurityConfig {
|
||||||
|
|
||||||
|
@Qualifier("memberJwtTokenProvider")
|
||||||
private final JwtTokenProvider jwtTokenProvider;
|
private final JwtTokenProvider jwtTokenProvider;
|
||||||
private final AuthService authService;
|
private final AuthService authService;
|
||||||
|
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import com.ktds.hi.member.repository.jpa.MemberRepository;
|
|||||||
import com.ktds.hi.common.exception.BusinessException;
|
import com.ktds.hi.common.exception.BusinessException;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.beans.factory.annotation.Qualifier;
|
||||||
import org.springframework.data.redis.core.RedisTemplate;
|
import org.springframework.data.redis.core.RedisTemplate;
|
||||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
@ -23,6 +24,7 @@ public class AuthServiceImpl implements AuthService {
|
|||||||
|
|
||||||
private final MemberRepository memberRepository;
|
private final MemberRepository memberRepository;
|
||||||
private final PasswordEncoder passwordEncoder;
|
private final PasswordEncoder passwordEncoder;
|
||||||
|
@Qualifier("memberJwtTokenProvider")
|
||||||
private final JwtTokenProvider jwtTokenProvider;
|
private final JwtTokenProvider jwtTokenProvider;
|
||||||
private final SmsService smsService;
|
private final SmsService smsService;
|
||||||
private final RedisTemplate<String, String> redisTemplate;
|
private final RedisTemplate<String, String> redisTemplate;
|
||||||
|
|||||||
@ -17,7 +17,7 @@ import java.util.Date;
|
|||||||
* JWT 토큰 프로바이더 클래스
|
* JWT 토큰 프로바이더 클래스
|
||||||
* JWT 토큰 생성, 검증, 파싱 기능을 제공
|
* JWT 토큰 생성, 검증, 파싱 기능을 제공
|
||||||
*/
|
*/
|
||||||
@Component
|
@Component("memberJwtTokenProvider") // 기존: @Component
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class JwtTokenProvider {
|
public class JwtTokenProvider {
|
||||||
|
|
||||||
|
|||||||
@ -2,5 +2,7 @@ dependencies {
|
|||||||
implementation project(':common')
|
implementation project(':common')
|
||||||
|
|
||||||
// External API Integration
|
// External API Integration
|
||||||
|
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
|
||||||
implementation 'org.springframework.boot:spring-boot-starter-webflux'
|
implementation 'org.springframework.boot:spring-boot-starter-webflux'
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
33
store/src/main/java/com/ktds/hi/store/StoreApplication.java
Normal file
33
store/src/main/java/com/ktds/hi/store/StoreApplication.java
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
package com.ktds.hi.store;
|
||||||
|
|
||||||
|
import org.springframework.boot.SpringApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
|
||||||
|
import org.springframework.web.client.RestTemplate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 추천 서비스 메인 애플리케이션 클래스
|
||||||
|
* 가게 추천, 취향 분석 기능을 제공
|
||||||
|
*
|
||||||
|
* @author 하이오더 개발팀
|
||||||
|
* @version 1.0.0
|
||||||
|
*/
|
||||||
|
@SpringBootApplication(scanBasePackages = {
|
||||||
|
"com.ktds.hi.store",
|
||||||
|
"com.ktds.hi.common"
|
||||||
|
})
|
||||||
|
|
||||||
|
@EnableJpaAuditing
|
||||||
|
public class StoreApplication {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
SpringApplication.run(StoreApplication.class, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 👈 이 부분만 추가
|
||||||
|
@Bean
|
||||||
|
public RestTemplate restTemplate() {
|
||||||
|
return new RestTemplate();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -13,7 +13,7 @@ spring:
|
|||||||
|
|
||||||
jpa:
|
jpa:
|
||||||
hibernate:
|
hibernate:
|
||||||
ddl-auto: ${JPA_DDL_AUTO:validate}
|
ddl-auto: ${JPA_DDL_AUTO:create}
|
||||||
show-sql: ${JPA_SHOW_SQL:false}
|
show-sql: ${JPA_SHOW_SQL:false}
|
||||||
properties:
|
properties:
|
||||||
hibernate:
|
hibernate:
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user