mirror of
https://github.com/ktds-dg0501/kt-event-marketing-fe.git
synced 2025-12-06 07:36:23 +00:00
- AI 서비스 API 클라이언트 추가 (aiApi.ts) - Event 서비스 API 클라이언트 추가 (eventApi.ts) - RecommendationStep에서 실제 API 호출로 변경 - Job 폴링 메커니즘 구현 (5초 간격) - ContentPreviewStep의 Mock 데이터 제거 - Props를 통한 eventId 전달 구조 개선 - ApprovalStep의 타입 오류 수정 - 모든 Mock/Static 데이터 제거 완료 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
179 lines
4.1 KiB
TypeScript
179 lines
4.1 KiB
TypeScript
import axios, { AxiosInstance } from 'axios';
|
|
|
|
// AI Service API 클라이언트
|
|
const AI_API_BASE_URL = process.env.NEXT_PUBLIC_AI_HOST || 'http://localhost:8083';
|
|
|
|
export const aiApiClient: AxiosInstance = axios.create({
|
|
baseURL: AI_API_BASE_URL,
|
|
timeout: 300000, // AI 생성은 최대 5분
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
});
|
|
|
|
// Request interceptor
|
|
aiApiClient.interceptors.request.use(
|
|
(config) => {
|
|
console.log('🤖 AI API Request:', {
|
|
method: config.method?.toUpperCase(),
|
|
url: config.url,
|
|
baseURL: config.baseURL,
|
|
data: config.data,
|
|
});
|
|
|
|
const token = localStorage.getItem('accessToken');
|
|
if (token && config.headers) {
|
|
config.headers.Authorization = `Bearer ${token}`;
|
|
}
|
|
return config;
|
|
},
|
|
(error) => {
|
|
console.error('❌ AI API Request Error:', error);
|
|
return Promise.reject(error);
|
|
}
|
|
);
|
|
|
|
// Response interceptor
|
|
aiApiClient.interceptors.response.use(
|
|
(response) => {
|
|
console.log('✅ AI API Response:', {
|
|
status: response.status,
|
|
url: response.config.url,
|
|
data: response.data,
|
|
});
|
|
return response;
|
|
},
|
|
(error) => {
|
|
console.error('❌ AI API Error:', {
|
|
message: error.message,
|
|
status: error.response?.status,
|
|
url: error.config?.url,
|
|
data: error.response?.data,
|
|
});
|
|
return Promise.reject(error);
|
|
}
|
|
);
|
|
|
|
// Types
|
|
export interface TrendKeyword {
|
|
keyword: string;
|
|
relevance: number;
|
|
description: string;
|
|
}
|
|
|
|
export interface TrendAnalysis {
|
|
industryTrends: TrendKeyword[];
|
|
regionalTrends: TrendKeyword[];
|
|
seasonalTrends: TrendKeyword[];
|
|
}
|
|
|
|
export interface ExpectedMetrics {
|
|
newCustomers: {
|
|
min: number;
|
|
max: number;
|
|
};
|
|
repeatVisits?: {
|
|
min: number;
|
|
max: number;
|
|
};
|
|
revenueIncrease: {
|
|
min: number;
|
|
max: number;
|
|
};
|
|
roi: {
|
|
min: number;
|
|
max: number;
|
|
};
|
|
socialEngagement?: {
|
|
estimatedPosts: number;
|
|
estimatedReach: number;
|
|
};
|
|
}
|
|
|
|
export interface EventRecommendation {
|
|
optionNumber: number;
|
|
concept: string;
|
|
title: string;
|
|
description: string;
|
|
targetAudience: string;
|
|
duration: {
|
|
recommendedDays: number;
|
|
recommendedPeriod?: string;
|
|
};
|
|
mechanics: {
|
|
type: 'DISCOUNT' | 'GIFT' | 'STAMP' | 'EXPERIENCE' | 'LOTTERY' | 'COMBO';
|
|
details: string;
|
|
};
|
|
promotionChannels: string[];
|
|
estimatedCost: {
|
|
min: number;
|
|
max: number;
|
|
breakdown?: {
|
|
material?: number;
|
|
promotion?: number;
|
|
discount?: number;
|
|
};
|
|
};
|
|
expectedMetrics: ExpectedMetrics;
|
|
differentiator: string;
|
|
}
|
|
|
|
export interface AIRecommendationResult {
|
|
eventId: string;
|
|
trendAnalysis: TrendAnalysis;
|
|
recommendations: EventRecommendation[];
|
|
generatedAt: string;
|
|
expiresAt: string;
|
|
aiProvider: 'CLAUDE' | 'GPT4';
|
|
}
|
|
|
|
export interface JobStatusResponse {
|
|
jobId: string;
|
|
status: 'PENDING' | 'PROCESSING' | 'COMPLETED' | 'FAILED';
|
|
progress: number;
|
|
message: string;
|
|
eventId?: string;
|
|
createdAt: string;
|
|
startedAt?: string;
|
|
completedAt?: string;
|
|
failedAt?: string;
|
|
errorMessage?: string;
|
|
retryCount?: number;
|
|
processingTimeMs?: number;
|
|
}
|
|
|
|
export interface HealthCheckResponse {
|
|
status: 'UP' | 'DOWN' | 'DEGRADED';
|
|
timestamp: string;
|
|
services: {
|
|
kafka: 'UP' | 'DOWN';
|
|
redis: 'UP' | 'DOWN';
|
|
claude_api: 'UP' | 'DOWN' | 'CIRCUIT_OPEN';
|
|
gpt4_api?: 'UP' | 'DOWN' | 'CIRCUIT_OPEN';
|
|
circuit_breaker: 'CLOSED' | 'OPEN' | 'HALF_OPEN';
|
|
};
|
|
}
|
|
|
|
// API Functions
|
|
export const aiApi = {
|
|
// 헬스체크
|
|
healthCheck: async (): Promise<HealthCheckResponse> => {
|
|
const response = await aiApiClient.get<HealthCheckResponse>('/health');
|
|
return response.data;
|
|
},
|
|
|
|
// Job 상태 조회 (Internal API)
|
|
getJobStatus: async (jobId: string): Promise<JobStatusResponse> => {
|
|
const response = await aiApiClient.get<JobStatusResponse>(`/internal/jobs/${jobId}/status`);
|
|
return response.data;
|
|
},
|
|
|
|
// AI 추천 결과 조회 (Internal API)
|
|
getRecommendations: async (eventId: string): Promise<AIRecommendationResult> => {
|
|
const response = await aiApiClient.get<AIRecommendationResult>(`/internal/recommendations/${eventId}`);
|
|
return response.data;
|
|
},
|
|
};
|
|
|
|
export default aiApi;
|