This commit is contained in:
박세원 2025-10-30 20:12:14 +09:00
parent cf379407e8
commit d14a7349bc
4 changed files with 208 additions and 0 deletions

View File

@ -10,7 +10,9 @@ import com.kt.event.eventservice.domain.entity.*;
import com.kt.event.eventservice.domain.enums.EventStatus;
import com.kt.event.eventservice.domain.repository.EventRepository;
import com.kt.event.eventservice.domain.repository.JobRepository;
import com.kt.event.eventservice.infrastructure.client.AIServiceClient;
import com.kt.event.eventservice.infrastructure.client.ContentServiceClient;
import com.kt.event.eventservice.infrastructure.client.dto.AIRecommendationResponse;
import com.kt.event.eventservice.infrastructure.client.dto.ContentImageGenerationRequest;
import com.kt.event.eventservice.infrastructure.client.dto.ContentJobResponse;
import com.kt.event.eventservice.infrastructure.kafka.AIJobKafkaProducer;
@ -43,6 +45,7 @@ public class EventService {
private final EventRepository eventRepository;
private final JobRepository jobRepository;
private final AIServiceClient aiServiceClient;
private final ContentServiceClient contentServiceClient;
private final AIJobKafkaProducer aiJobKafkaProducer;
private final ImageJobKafkaProducer imageJobKafkaProducer;
@ -611,4 +614,30 @@ public class EventService {
.updatedAt(event.getUpdatedAt())
.build();
}
/**
* AI 추천안 조회 (AI Service에서 직접 조회)
*
* @param userId 사용자 ID
* @param eventId 이벤트 ID
* @return AI 추천 결과
*/
public AIRecommendationResponse getAiRecommendations(String userId, String eventId) {
log.info("AI 추천안 조회 - userId: {}, eventId: {}", userId, eventId);
// 이벤트 권한 확인
Event event = eventRepository.findByEventIdAndUserId(eventId, userId)
.orElseThrow(() -> new BusinessException(ErrorCode.EVENT_001));
// AI Service에서 추천안 조회
try {
AIRecommendationResponse response = aiServiceClient.getRecommendation(eventId);
log.info("AI 추천안 조회 성공 - eventId: {}, 추천안 수: {}",
eventId, response.getRecommendations() != null ? response.getRecommendations().size() : 0);
return response;
} catch (Exception e) {
log.error("AI 추천안 조회 실패 - eventId: {}", eventId, e);
throw new BusinessException(ErrorCode.AI_004);
}
}
}

View File

@ -0,0 +1,31 @@
package com.kt.event.eventservice.infrastructure.client;
import com.kt.event.eventservice.infrastructure.client.dto.AIRecommendationResponse;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
/**
* AI Service Feign Client
*
* AI Service의 추천안 조회 API를 호출합니다.
*
* @author Event Service Team
* @version 1.0.0
* @since 2025-10-30
*/
@FeignClient(
name = "ai-service",
url = "${feign.ai-service.url:http://localhost:8083}"
)
public interface AIServiceClient {
/**
* AI 추천 결과 조회
*
* @param eventId 이벤트 ID
* @return AI 추천 결과
*/
@GetMapping("/recommendations/{eventId}")
AIRecommendationResponse getRecommendation(@PathVariable("eventId") String eventId);
}

View File

@ -0,0 +1,123 @@
package com.kt.event.eventservice.infrastructure.client.dto;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
/**
* AI Service 추천안 응답 DTO
*
* @author Event Service Team
* @version 1.0.0
* @since 2025-10-30
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class AIRecommendationResponse {
private String eventId;
private TrendAnalysis trendAnalysis;
private List<EventRecommendation> recommendations;
private LocalDateTime generatedAt;
private LocalDateTime expiresAt;
private String aiProvider;
/**
* 트렌드 분석
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public static class TrendAnalysis {
private List<TrendKeyword> industryTrends;
private List<TrendKeyword> regionalTrends;
private List<TrendKeyword> seasonalTrends;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public static class TrendKeyword {
private String keyword;
private Double relevance;
private String description;
}
}
/**
* 이벤트 추천안
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public static class EventRecommendation {
private Integer optionNumber;
private String concept;
private String title;
private String description;
private String targetAudience;
private Duration duration;
private Mechanics mechanics;
private List<String> promotionChannels;
private EstimatedCost estimatedCost;
private ExpectedMetrics expectedMetrics;
private String differentiator;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public static class Duration {
private Integer recommendedDays;
private String recommendedPeriod;
}
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public static class Mechanics {
private String type;
private String details;
}
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public static class EstimatedCost {
private Integer min;
private Integer max;
private Map<String, Integer> breakdown;
}
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public static class ExpectedMetrics {
private Range newCustomers;
private Range revenueIncrease;
private Range roi;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public static class Range {
private Double min;
private Double max;
}
}
}
}

View File

@ -6,6 +6,7 @@ import com.kt.event.common.security.UserPrincipal;
import com.kt.event.eventservice.application.dto.request.*;
import com.kt.event.eventservice.application.dto.response.*;
import com.kt.event.eventservice.application.service.EventService;
import com.kt.event.eventservice.infrastructure.client.dto.AIRecommendationResponse;
import com.kt.event.eventservice.domain.enums.EventStatus;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
@ -287,6 +288,30 @@ public class EventController {
.body(ApiResponse.success(response));
}
/**
* AI 추천안 조회 (Step 2-1)
*
* @param eventId 이벤트 ID
* @param userPrincipal 인증된 사용자 정보
* @return AI 추천 결과
*/
@GetMapping("/{eventId}/ai-recommendations")
@Operation(summary = "AI 추천안 조회", description = "AI Service에서 생성된 추천안을 조회합니다.")
public ResponseEntity<ApiResponse<AIRecommendationResponse>> getAiRecommendations(
@PathVariable String eventId,
@AuthenticationPrincipal UserPrincipal userPrincipal) {
log.info("AI 추천안 조회 API 호출 - userId: {}, eventId: {}",
userPrincipal.getUserId(), eventId);
AIRecommendationResponse response = eventService.getAiRecommendations(
userPrincipal.getUserId(),
eventId
);
return ResponseEntity.ok(ApiResponse.success(response));
}
/**
* AI 추천 선택 (Step 2-2)
*