mirror of
https://github.com/ktds-dg0501/kt-event-marketing.git
synced 2025-12-06 10:46:23 +00:00
Merge branch 'develop' of https://github.com/ktds-dg0501/kt-event-marketing into feature/distribution
This commit is contained in:
commit
06ea838547
@ -32,7 +32,7 @@ public class HealthController {
|
|||||||
* 서비스 헬스체크
|
* 서비스 헬스체크
|
||||||
*/
|
*/
|
||||||
@Operation(summary = "서비스 헬스체크", description = "AI Service 상태 및 외부 연동 확인")
|
@Operation(summary = "서비스 헬스체크", description = "AI Service 상태 및 외부 연동 확인")
|
||||||
@GetMapping("/api/v1/ai-service/health")
|
@GetMapping("/health")
|
||||||
public ResponseEntity<HealthCheckResponse> healthCheck() {
|
public ResponseEntity<HealthCheckResponse> healthCheck() {
|
||||||
// Redis 상태 확인
|
// Redis 상태 확인
|
||||||
ServiceStatus redisStatus = checkRedis();
|
ServiceStatus redisStatus = checkRedis();
|
||||||
|
|||||||
@ -39,7 +39,7 @@ spring:
|
|||||||
server:
|
server:
|
||||||
port: ${SERVER_PORT:8083}
|
port: ${SERVER_PORT:8083}
|
||||||
servlet:
|
servlet:
|
||||||
context-path: /api/v1/ai-service
|
context-path: /api/v1/ai
|
||||||
encoding:
|
encoding:
|
||||||
charset: UTF-8
|
charset: UTF-8
|
||||||
enabled: true
|
enabled: true
|
||||||
|
|||||||
@ -225,7 +225,7 @@ public class SampleDataLoader implements ApplicationRunner {
|
|||||||
private void publishEventCreatedEvents() throws Exception {
|
private void publishEventCreatedEvents() throws Exception {
|
||||||
// 이벤트 1: 신년맞이 할인 이벤트 (진행중, 높은 성과 - ROI 200%)
|
// 이벤트 1: 신년맞이 할인 이벤트 (진행중, 높은 성과 - ROI 200%)
|
||||||
EventCreatedEvent event1 = EventCreatedEvent.builder()
|
EventCreatedEvent event1 = EventCreatedEvent.builder()
|
||||||
.eventId("1")
|
.eventId("evt_2025012301")
|
||||||
.eventTitle("신년맞이 20% 할인 이벤트")
|
.eventTitle("신년맞이 20% 할인 이벤트")
|
||||||
.storeId("store_001")
|
.storeId("store_001")
|
||||||
.totalInvestment(new BigDecimal("5000000"))
|
.totalInvestment(new BigDecimal("5000000"))
|
||||||
@ -238,7 +238,7 @@ public class SampleDataLoader implements ApplicationRunner {
|
|||||||
|
|
||||||
// 이벤트 2: 설날 특가 이벤트 (진행중, 중간 성과 - ROI 100%)
|
// 이벤트 2: 설날 특가 이벤트 (진행중, 중간 성과 - ROI 100%)
|
||||||
EventCreatedEvent event2 = EventCreatedEvent.builder()
|
EventCreatedEvent event2 = EventCreatedEvent.builder()
|
||||||
.eventId("2")
|
.eventId("evt_2025012302")
|
||||||
.eventTitle("설날 특가 선물세트 이벤트")
|
.eventTitle("설날 특가 선물세트 이벤트")
|
||||||
.storeId("store_001")
|
.storeId("store_001")
|
||||||
.totalInvestment(new BigDecimal("3500000"))
|
.totalInvestment(new BigDecimal("3500000"))
|
||||||
@ -251,7 +251,7 @@ public class SampleDataLoader implements ApplicationRunner {
|
|||||||
|
|
||||||
// 이벤트 3: 겨울 신메뉴 런칭 이벤트 (종료, 저조한 성과 - ROI 50%)
|
// 이벤트 3: 겨울 신메뉴 런칭 이벤트 (종료, 저조한 성과 - ROI 50%)
|
||||||
EventCreatedEvent event3 = EventCreatedEvent.builder()
|
EventCreatedEvent event3 = EventCreatedEvent.builder()
|
||||||
.eventId("3")
|
.eventId("evt_2025012303")
|
||||||
.eventTitle("겨울 신메뉴 런칭 이벤트")
|
.eventTitle("겨울 신메뉴 런칭 이벤트")
|
||||||
.storeId("store_001")
|
.storeId("store_001")
|
||||||
.totalInvestment(new BigDecimal("2000000"))
|
.totalInvestment(new BigDecimal("2000000"))
|
||||||
@ -269,7 +269,7 @@ public class SampleDataLoader implements ApplicationRunner {
|
|||||||
* DistributionCompleted 이벤트 발행 (설계서 기준 - 이벤트당 1번 발행, 여러 채널 배열)
|
* DistributionCompleted 이벤트 발행 (설계서 기준 - 이벤트당 1번 발행, 여러 채널 배열)
|
||||||
*/
|
*/
|
||||||
private void publishDistributionCompletedEvents() throws Exception {
|
private void publishDistributionCompletedEvents() throws Exception {
|
||||||
String[] eventIds = {"1", "2", "3"};
|
String[] eventIds = {"evt_2025012301", "evt_2025012302", "evt_2025012303"};
|
||||||
int[][] expectedViews = {
|
int[][] expectedViews = {
|
||||||
{5000, 10000, 3000, 2000}, // 이벤트1: 우리동네TV, 지니TV, 링고비즈, SNS
|
{5000, 10000, 3000, 2000}, // 이벤트1: 우리동네TV, 지니TV, 링고비즈, SNS
|
||||||
{3500, 7000, 2000, 1500}, // 이벤트2
|
{3500, 7000, 2000, 1500}, // 이벤트2
|
||||||
@ -359,7 +359,7 @@ public class SampleDataLoader implements ApplicationRunner {
|
|||||||
* - 이벤트3: 30명 (user071~user100) → 30명이 이전 이벤트들과 중복
|
* - 이벤트3: 30명 (user071~user100) → 30명이 이전 이벤트들과 중복
|
||||||
*/
|
*/
|
||||||
private void publishParticipantRegisteredEvents() throws Exception {
|
private void publishParticipantRegisteredEvents() throws Exception {
|
||||||
String[] eventIds = {"1", "2", "3"};
|
String[] eventIds = {"evt_2025012301", "evt_2025012302", "evt_2025012303"};
|
||||||
String[] channels = {"우리동네TV", "지니TV", "링고비즈", "SNS"};
|
String[] channels = {"우리동네TV", "지니TV", "링고비즈", "SNS"};
|
||||||
|
|
||||||
// 이벤트별 참여자 범위 (중복 참여 반영)
|
// 이벤트별 참여자 범위 (중복 참여 반영)
|
||||||
|
|||||||
@ -3,7 +3,6 @@ package com.kt.event.analytics.config;
|
|||||||
import com.kt.event.common.security.JwtAuthenticationFilter;
|
import com.kt.event.common.security.JwtAuthenticationFilter;
|
||||||
import com.kt.event.common.security.JwtTokenProvider;
|
import com.kt.event.common.security.JwtTokenProvider;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
|
||||||
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.config.annotation.web.builders.HttpSecurity;
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||||
@ -12,15 +11,12 @@ import org.springframework.security.config.annotation.web.configurers.AbstractHt
|
|||||||
import org.springframework.security.config.http.SessionCreationPolicy;
|
import org.springframework.security.config.http.SessionCreationPolicy;
|
||||||
import org.springframework.security.web.SecurityFilterChain;
|
import org.springframework.security.web.SecurityFilterChain;
|
||||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||||
import org.springframework.web.cors.CorsConfiguration;
|
|
||||||
import org.springframework.web.cors.CorsConfigurationSource;
|
|
||||||
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Spring Security 설정
|
* Spring Security 설정
|
||||||
* JWT 기반 인증 및 API 보안 설정
|
* JWT 기반 인증 및 API 보안 설정
|
||||||
|
*
|
||||||
|
* ⚠️ CORS 설정은 WebConfig에서 관리합니다.
|
||||||
*/
|
*/
|
||||||
@Configuration
|
@Configuration
|
||||||
@EnableWebSecurity
|
@EnableWebSecurity
|
||||||
@ -29,14 +25,11 @@ public class SecurityConfig {
|
|||||||
|
|
||||||
private final JwtTokenProvider jwtTokenProvider;
|
private final JwtTokenProvider jwtTokenProvider;
|
||||||
|
|
||||||
@Value("${cors.allowed-origins:http://localhost:*}")
|
|
||||||
private String allowedOrigins;
|
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||||
return http
|
return http
|
||||||
.csrf(AbstractHttpConfigurer::disable)
|
.csrf(AbstractHttpConfigurer::disable)
|
||||||
.cors(cors -> cors.configurationSource(corsConfigurationSource()))
|
.cors(AbstractHttpConfigurer::disable) // CORS는 WebConfig에서 관리
|
||||||
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
|
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
|
||||||
.authorizeHttpRequests(auth -> auth
|
.authorizeHttpRequests(auth -> auth
|
||||||
.anyRequest().permitAll()
|
.anyRequest().permitAll()
|
||||||
@ -46,25 +39,5 @@ public class SecurityConfig {
|
|||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
// CORS 설정은 WebConfig에서 관리 (모든 origin 허용)
|
||||||
public CorsConfigurationSource corsConfigurationSource() {
|
|
||||||
CorsConfiguration configuration = new CorsConfiguration();
|
|
||||||
|
|
||||||
String[] origins = allowedOrigins.split(",");
|
|
||||||
configuration.setAllowedOriginPatterns(Arrays.asList(origins));
|
|
||||||
|
|
||||||
configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS"));
|
|
||||||
|
|
||||||
configuration.setAllowedHeaders(Arrays.asList(
|
|
||||||
"Authorization", "Content-Type", "X-Requested-With", "Accept",
|
|
||||||
"Origin", "Access-Control-Request-Method", "Access-Control-Request-Headers"
|
|
||||||
));
|
|
||||||
|
|
||||||
configuration.setAllowCredentials(true);
|
|
||||||
configuration.setMaxAge(3600L);
|
|
||||||
|
|
||||||
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
|
|
||||||
source.registerCorsConfiguration("/**", configuration);
|
|
||||||
return source;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -19,7 +19,7 @@ spec:
|
|||||||
- name: kt-event-marketing
|
- name: kt-event-marketing
|
||||||
containers:
|
containers:
|
||||||
- name: ai-service
|
- name: ai-service
|
||||||
image: acrdigitalgarage01.azurecr.io/kt-event-marketing/ai-service:latest
|
image: acrdigitalgarage01.azurecr.io/kt-event-marketing/ai-service:dev
|
||||||
imagePullPolicy: Always
|
imagePullPolicy: Always
|
||||||
ports:
|
ports:
|
||||||
- containerPort: 8083
|
- containerPort: 8083
|
||||||
@ -42,21 +42,21 @@ spec:
|
|||||||
memory: "1024Mi"
|
memory: "1024Mi"
|
||||||
startupProbe:
|
startupProbe:
|
||||||
httpGet:
|
httpGet:
|
||||||
path: /api/v1/ai-service/actuator/health
|
path: /api/v1/ai/actuator/health
|
||||||
port: 8083
|
port: 8083
|
||||||
initialDelaySeconds: 30
|
initialDelaySeconds: 30
|
||||||
periodSeconds: 10
|
periodSeconds: 10
|
||||||
failureThreshold: 30
|
failureThreshold: 30
|
||||||
readinessProbe:
|
readinessProbe:
|
||||||
httpGet:
|
httpGet:
|
||||||
path: /api/v1/ai-service/actuator/health/readiness
|
path: /api/v1/ai/actuator/health/readiness
|
||||||
port: 8083
|
port: 8083
|
||||||
initialDelaySeconds: 10
|
initialDelaySeconds: 10
|
||||||
periodSeconds: 5
|
periodSeconds: 5
|
||||||
failureThreshold: 3
|
failureThreshold: 3
|
||||||
livenessProbe:
|
livenessProbe:
|
||||||
httpGet:
|
httpGet:
|
||||||
path: /api/v1/ai-service/actuator/health/liveness
|
path: /api/v1/ai/actuator/health/liveness
|
||||||
port: 8083
|
port: 8083
|
||||||
initialDelaySeconds: 30
|
initialDelaySeconds: 30
|
||||||
periodSeconds: 10
|
periodSeconds: 10
|
||||||
|
|||||||
@ -56,7 +56,7 @@ spec:
|
|||||||
number: 80
|
number: 80
|
||||||
|
|
||||||
# AI Service
|
# AI Service
|
||||||
- path: /api/v1/ai-service
|
- path: /api/v1/ai
|
||||||
pathType: Prefix
|
pathType: Prefix
|
||||||
backend:
|
backend:
|
||||||
service:
|
service:
|
||||||
|
|||||||
@ -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.enums.EventStatus;
|
||||||
import com.kt.event.eventservice.domain.repository.EventRepository;
|
import com.kt.event.eventservice.domain.repository.EventRepository;
|
||||||
import com.kt.event.eventservice.domain.repository.JobRepository;
|
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.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.ContentImageGenerationRequest;
|
||||||
import com.kt.event.eventservice.infrastructure.client.dto.ContentJobResponse;
|
import com.kt.event.eventservice.infrastructure.client.dto.ContentJobResponse;
|
||||||
import com.kt.event.eventservice.infrastructure.kafka.AIJobKafkaProducer;
|
import com.kt.event.eventservice.infrastructure.kafka.AIJobKafkaProducer;
|
||||||
@ -43,6 +45,7 @@ public class EventService {
|
|||||||
|
|
||||||
private final EventRepository eventRepository;
|
private final EventRepository eventRepository;
|
||||||
private final JobRepository jobRepository;
|
private final JobRepository jobRepository;
|
||||||
|
private final AIServiceClient aiServiceClient;
|
||||||
private final ContentServiceClient contentServiceClient;
|
private final ContentServiceClient contentServiceClient;
|
||||||
private final AIJobKafkaProducer aiJobKafkaProducer;
|
private final AIJobKafkaProducer aiJobKafkaProducer;
|
||||||
private final ImageJobKafkaProducer imageJobKafkaProducer;
|
private final ImageJobKafkaProducer imageJobKafkaProducer;
|
||||||
@ -611,4 +614,30 @@ public class EventService {
|
|||||||
.updatedAt(event.getUpdatedAt())
|
.updatedAt(event.getUpdatedAt())
|
||||||
.build();
|
.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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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);
|
||||||
|
}
|
||||||
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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.request.*;
|
||||||
import com.kt.event.eventservice.application.dto.response.*;
|
import com.kt.event.eventservice.application.dto.response.*;
|
||||||
import com.kt.event.eventservice.application.service.EventService;
|
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 com.kt.event.eventservice.domain.enums.EventStatus;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
@ -287,6 +288,30 @@ public class EventController {
|
|||||||
.body(ApiResponse.success(response));
|
.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)
|
* AI 추천 선택 (Step 2-2)
|
||||||
*
|
*
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user