kcg-ai-monitoring/frontend/src/services/chatApi.ts
htlee 95ca1018b5 feat: Phase 6-8 - iran 백엔드 실연결 + 시스템 상태 + AI 채팅 기반
Phase 6: iran 백엔드 실연결 + 화면 연동
- application.yml: app.iran-backend.base-url=https://kcg.gc-si.dev
- IranBackendClient: RestClient 확장 (Accept JSON header, getAs<T>)
- VesselAnalysisProxyController: HYBRID 합성 로직 추가
  - GET /api/vessel-analysis: stats + 7423건 분석 결과 통과
  - GET /api/vessel-analysis/groups: 476건 그룹 + 자체 DB resolution 합성
  - GET /api/vessel-analysis/groups/{key}/detail
  - GET /api/vessel-analysis/groups/{key}/correlations
  - 권한: detection / detection:gear-detection (READ)
- 프론트 services/vesselAnalysisApi.ts: 타입 + 필터 헬퍼
  (filterDarkVessels, filterSpoofingVessels, filterTransshipSuspects)
- features/detection/RealGearGroups.tsx: 어구/선단 그룹 실시간 표시
  (FLEET/GEAR_IN_ZONE/GEAR_OUT_ZONE 필터, 통계 5종, 운영자 결정 합성 표시)
- features/detection/RealVesselAnalysis.tsx: 분석 결과 모드별 렌더
  - mode='dark' / 'spoofing' / 'transship' / 'all'
  - 위험도순 정렬 + 6개 통계 카드 + 해역/Dark/Spoofing/전재 표시
- 화면 연동:
  - GearDetection → RealGearGroups 추가
  - DarkVesselDetection → RealDarkVessels + RealSpoofingVessels
  - ChinaFishing(dashboard) → RealAllVessels
  - TransferDetection → RealTransshipSuspects

Phase 7: 시스템 상태 대시보드
- features/monitoring/SystemStatusPanel.tsx
  - 3개 서비스 카드: KCG Backend / iran 백엔드 / Prediction
  - 위험도 분포 (CRITICAL/HIGH/MEDIUM/LOW) 4개 박스
  - 30초 자동 폴링
- MonitoringDashboard 최상단에 SystemStatusPanel 추가

Phase 8: AI 채팅 기반 (SSE는 Phase 9 인증 후)
- 프론트 services/chatApi.ts: sendChatMessage (graceful fallback)
- 백엔드 PredictionProxyController.chat 추가
  - POST /api/prediction/chat
  - 권한: ai-operations:ai-assistant (READ)
  - 현재 stub 응답 (iran chat 인증 토큰 필요)
- AIAssistant 페이지에 백엔드 호출 통합
  (handleSend → sendChatMessage → 응답 표시 + graceful 메시지)

검증:
- 백엔드 컴파일/기동 성공 (Started in 5.2s)
- iran 프록시: 471개 그룹, 7423건 분석 결과 정상 통과
- 프론트 빌드 통과 (502ms)
- E2E 시나리오:
  - admin 로그인 → /api/vessel-analysis/groups → 476건 + serviceAvailable=true
  - /api/prediction/chat → stub 응답 (Phase 9 안내)

설계 원칙:
- iran 백엔드 미연결 시 graceful degradation (serviceAvailable=false + 빈 데이터)
- HYBRID 합성: prediction 후보 + 자체 DB의 운영자 결정을 백엔드에서 조합
- 향후 iran 인증 토큰 통과 후 SSE 채팅 활성화

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 10:22:04 +09:00

68 lines
2.0 KiB
TypeScript

/**
* AI 채팅 API.
* - 백엔드 prediction 프록시 호출 (/api/prediction/chat)
* - SSE 스트리밍 (현재 stub) - 향후 prediction 인증 통과 시 활성화
*
* 향후 SSE 구현 시:
* const ctrl = new AbortController();
* const res = await fetch(`${API_BASE}/prediction/chat`, {
* method: 'POST',
* credentials: 'include',
* headers: { 'Content-Type': 'application/json' },
* body: JSON.stringify({ message, stream: true }),
* signal: ctrl.signal,
* });
* const reader = res.body!.getReader();
* const decoder = new TextDecoder();
* while (true) {
* const { value, done } = await reader.read();
* if (done) break;
* const chunk = decoder.decode(value);
* // 'data: {...}\n\n' 파싱
* }
*/
const API_BASE = import.meta.env.VITE_API_URL ?? '/api';
export interface ChatMessage {
role: 'user' | 'assistant' | 'system';
content: string;
}
export interface ChatResponse {
ok: boolean;
reply?: string;
message?: string;
serviceAvailable?: boolean;
}
/**
* 비동기 채팅 (SSE 미사용 버전).
* Phase 8에서는 미연결 시 graceful 응답.
*/
export async function sendChatMessage(message: string): Promise<ChatResponse> {
try {
const res = await fetch(`${API_BASE}/prediction/chat`, {
method: 'POST',
credentials: 'include',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ message, stream: false }),
});
if (!res.ok) {
return {
ok: false,
serviceAvailable: false,
message: `Prediction 채팅 서비스 미연결 (HTTP ${res.status}). 향후 인증 연동 후 활성화 예정.`,
};
}
const data = await res.json();
return { ok: true, reply: data.reply || data.content || JSON.stringify(data) };
} catch (e: unknown) {
return {
ok: false,
serviceAvailable: false,
message: 'Prediction 채팅 서비스 연결 실패: ' + (e instanceof Error ? e.message : 'unknown'),
};
}
}