feat : 분석 api 수정
This commit is contained in:
parent
52c7e7decc
commit
23c1d9d244
@ -4,6 +4,7 @@ import com.ktds.hi.analytics.biz.domain.ActionPlan;
|
|||||||
import com.ktds.hi.analytics.biz.domain.Analytics;
|
import com.ktds.hi.analytics.biz.domain.Analytics;
|
||||||
import com.ktds.hi.analytics.biz.domain.AiFeedback;
|
import com.ktds.hi.analytics.biz.domain.AiFeedback;
|
||||||
import com.ktds.hi.analytics.biz.domain.PlanStatus;
|
import com.ktds.hi.analytics.biz.domain.PlanStatus;
|
||||||
|
import com.ktds.hi.analytics.biz.domain.SentimentType;
|
||||||
import com.ktds.hi.analytics.biz.usecase.in.AnalyticsUseCase;
|
import com.ktds.hi.analytics.biz.usecase.in.AnalyticsUseCase;
|
||||||
import com.ktds.hi.analytics.biz.usecase.out.*;
|
import com.ktds.hi.analytics.biz.usecase.out.*;
|
||||||
import com.ktds.hi.analytics.infra.dto.*;
|
import com.ktds.hi.analytics.infra.dto.*;
|
||||||
@ -13,10 +14,14 @@ import org.springframework.cache.annotation.Cacheable;
|
|||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 분석 서비스 구현 클래스 (수정버전)
|
* 분석 서비스 구현 클래스 (수정버전)
|
||||||
@ -35,6 +40,7 @@ public class AnalyticsService implements AnalyticsUseCase {
|
|||||||
private final CachePort cachePort;
|
private final CachePort cachePort;
|
||||||
private final EventPort eventPort;
|
private final EventPort eventPort;
|
||||||
private final ActionPlanPort actionPlanPort; // 추가된 의존성
|
private final ActionPlanPort actionPlanPort; // 추가된 의존성
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
// @Cacheable(value = "storeAnalytics", key = "#storeId")
|
// @Cacheable(value = "storeAnalytics", key = "#storeId")
|
||||||
@ -280,8 +286,10 @@ public class AnalyticsService implements AnalyticsUseCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 3. 응답 생성
|
// 3. 응답 생성
|
||||||
int positiveCount = countPositiveReviews(recentReviews);
|
ReviewSentimentCount sentimentCount = analyzeReviewSentiments(recentReviews);
|
||||||
int negativeCount = countNegativeReviews(recentReviews);
|
int positiveCount = sentimentCount.getPositiveCount();
|
||||||
|
int negativeCount = sentimentCount.getNegativeCount();
|
||||||
|
int neutralCount = sentimentCount.getNeutralCount();
|
||||||
int totalCount = recentReviews.size();
|
int totalCount = recentReviews.size();
|
||||||
|
|
||||||
ReviewAnalysisResponse response = ReviewAnalysisResponse.builder()
|
ReviewAnalysisResponse response = ReviewAnalysisResponse.builder()
|
||||||
@ -289,8 +297,10 @@ public class AnalyticsService implements AnalyticsUseCase {
|
|||||||
.totalReviews(totalCount)
|
.totalReviews(totalCount)
|
||||||
.positiveReviewCount(positiveCount)
|
.positiveReviewCount(positiveCount)
|
||||||
.negativeReviewCount(negativeCount)
|
.negativeReviewCount(negativeCount)
|
||||||
.positiveRate(Math.floor((double) positiveCount / totalCount * 100) / 10.0)
|
.neutralReviewCount(neutralCount)
|
||||||
.negativeRate(Math.floor((double) negativeCount / totalCount * 100) / 10.0)
|
.positiveRate(Math.floor((double) positiveCount / totalCount * 1000) / 10.0)
|
||||||
|
.negativeRate(Math.floor((double) negativeCount / totalCount * 1000) / 10.0)
|
||||||
|
.neutralRate(Math.floor((double) neutralCount / totalCount * 1000) / 10.0)
|
||||||
.analysisDate(LocalDate.now())
|
.analysisDate(LocalDate.now())
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
@ -305,7 +315,99 @@ public class AnalyticsService implements AnalyticsUseCase {
|
|||||||
throw new RuntimeException("리뷰 분석에 실패했습니다.", e);
|
throw new RuntimeException("리뷰 분석에 실패했습니다.", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* LLM 기반 리뷰 감정 분석 - 한 번의 분석으로 긍정/부정/중립 수 모두 반환
|
||||||
|
*
|
||||||
|
* @param reviews 분석할 리뷰 목록
|
||||||
|
* @return ReviewSentimentCount 감정별 리뷰 수
|
||||||
|
*/
|
||||||
|
private ReviewSentimentCount analyzeReviewSentiments(List<String> reviews) {
|
||||||
|
log.info("LLM 기반 리뷰 감정 분석 시작: 총 리뷰 수={}", reviews.size());
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (reviews.isEmpty()) {
|
||||||
|
return new ReviewSentimentCount(0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 유효한 리뷰만 필터링
|
||||||
|
List<String> validReviews = reviews.stream()
|
||||||
|
.filter(review -> review != null && !review.trim().isEmpty())
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
if (validReviews.isEmpty()) {
|
||||||
|
return new ReviewSentimentCount(0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int positiveCount = 0;
|
||||||
|
int negativeCount = 0;
|
||||||
|
int neutralCount = 0;
|
||||||
|
|
||||||
|
// 각 리뷰를 AI로 감정 분석
|
||||||
|
for (String review : validReviews) {
|
||||||
|
try {
|
||||||
|
SentimentType sentiment = aiServicePort.analyzeSentiment(review);
|
||||||
|
|
||||||
|
switch (sentiment) {
|
||||||
|
case POSITIVE:
|
||||||
|
positiveCount++;
|
||||||
|
break;
|
||||||
|
case NEGATIVE:
|
||||||
|
negativeCount++;
|
||||||
|
break;
|
||||||
|
case NEUTRAL:
|
||||||
|
default:
|
||||||
|
neutralCount++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warn("개별 리뷰 감정 분석 실패, 중립으로 처리: {}",
|
||||||
|
review.substring(0, Math.min(30, review.length())), e);
|
||||||
|
neutralCount++; // 분석 실패 시 중립으로 처리
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ReviewSentimentCount result = new ReviewSentimentCount(positiveCount, negativeCount, neutralCount);
|
||||||
|
|
||||||
|
log.info("리뷰 감정 분석 완료: 긍정={}, 부정={}, 중립={}, 전체={}",
|
||||||
|
positiveCount, negativeCount, neutralCount, validReviews.size());
|
||||||
|
|
||||||
|
return result;
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("리뷰 감정 분석 중 전체 오류 발생, fallback 사용", e);
|
||||||
|
// 오류 시 기존 가정값 사용
|
||||||
|
int total = reviews.size();
|
||||||
|
return new ReviewSentimentCount(
|
||||||
|
(int) (total * 0.6), // 60% 긍정
|
||||||
|
(int) (total * 0.2), // 20% 부정
|
||||||
|
total - (int) (total * 0.6) - (int) (total * 0.2) // 나머지 중립
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 리뷰 감정 분석 결과를 담는 내부 클래스
|
||||||
|
*/
|
||||||
|
public static class ReviewSentimentCount {
|
||||||
|
private final int positiveCount;
|
||||||
|
private final int negativeCount;
|
||||||
|
private final int neutralCount;
|
||||||
|
|
||||||
|
public ReviewSentimentCount(int positiveCount, int negativeCount, int neutralCount) {
|
||||||
|
this.positiveCount = positiveCount;
|
||||||
|
this.negativeCount = negativeCount;
|
||||||
|
this.neutralCount = neutralCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getPositiveCount() { return positiveCount; }
|
||||||
|
public int getNegativeCount() { return negativeCount; }
|
||||||
|
public int getNeutralCount() { return neutralCount; }
|
||||||
|
public int getTotalCount() { return positiveCount + negativeCount + neutralCount; }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// private 메서드들
|
// private 메서드들
|
||||||
@Transactional
|
@Transactional
|
||||||
public Analytics generateNewAnalytics(Long storeId) {
|
public Analytics generateNewAnalytics(Long storeId) {
|
||||||
@ -429,15 +531,6 @@ public class AnalyticsService implements AnalyticsUseCase {
|
|||||||
return "추천사항이 없습니다.";
|
return "추천사항이 없습니다.";
|
||||||
}
|
}
|
||||||
|
|
||||||
private int countPositiveReviews(List<String> reviews) {
|
|
||||||
// 실제로는 AI 서비스를 통한 감정 분석 필요
|
|
||||||
return (int) (reviews.size() * 0.6); // 60% 가정
|
|
||||||
}
|
|
||||||
|
|
||||||
private int countNegativeReviews(List<String> reviews) {
|
|
||||||
// 실제로는 AI 서비스를 통한 감정 분석 필요
|
|
||||||
return (int) (reviews.size() * 0.2); // 20% 가정
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional
|
@Transactional
|
||||||
|
|||||||
@ -20,7 +20,9 @@ public class ReviewAnalysisResponse {
|
|||||||
private Integer totalReviews;
|
private Integer totalReviews;
|
||||||
private Integer positiveReviewCount;
|
private Integer positiveReviewCount;
|
||||||
private Integer negativeReviewCount;
|
private Integer negativeReviewCount;
|
||||||
|
private Integer neutralReviewCount;
|
||||||
private Double positiveRate;
|
private Double positiveRate;
|
||||||
private Double negativeRate;
|
private Double negativeRate;
|
||||||
|
private Double neutralRate;
|
||||||
private LocalDate analysisDate;
|
private LocalDate analysisDate;
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user