/** * gear_identity_collisions 조회 + 분류 액션 API 서비스. * 백엔드 /api/analysis/gear-collisions 연동. */ import type { AnalysisPageResponse } from './analysisApi'; const API_BASE = import.meta.env.VITE_API_URL ?? '/api'; // ─── DTO (백엔드 GearCollisionResponse 1:1 매핑) ───────────── export interface GearCollision { id: number; name: string; mmsiLo: string; mmsiHi: string; parentName: string | null; parentVesselId: number | null; firstSeenAt: string; lastSeenAt: string; coexistenceCount: number; swapCount: number; maxDistanceKm: number | null; lastLatLo: number | null; lastLonLo: number | null; lastLatHi: number | null; lastLonHi: number | null; severity: 'CRITICAL' | 'HIGH' | 'MEDIUM' | 'LOW' | string; status: 'OPEN' | 'REVIEWED' | 'CONFIRMED_ILLEGAL' | 'FALSE_POSITIVE' | string; resolutionNote: string | null; evidence: Array> | null; updatedAt: string; } export interface GearCollisionStats { total: number; byStatus: Record; bySeverity: Record; hours: number; } export type GearCollisionResolveAction = | 'REVIEWED' | 'CONFIRMED_ILLEGAL' | 'FALSE_POSITIVE' | 'REOPEN'; export interface GearCollisionResolveRequest { action: GearCollisionResolveAction; note?: string; } // ─── 내부 헬퍼 ───────────── function buildQuery(params: Record): string { const qs = new URLSearchParams(); for (const [k, v] of Object.entries(params)) { if (v === undefined || v === null || v === '') continue; qs.set(k, String(v)); } const s = qs.toString(); return s ? `?${s}` : ''; } async function apiGet(path: string, params: Record = {}): Promise { const res = await fetch(`${API_BASE}${path}${buildQuery(params)}`, { credentials: 'include' }); if (!res.ok) throw new Error(`API error: ${res.status}`); return res.json(); } async function apiPost(path: string, body: unknown): Promise { const res = await fetch(`${API_BASE}${path}`, { method: 'POST', credentials: 'include', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body), }); if (!res.ok) throw new Error(`API error: ${res.status}`); return res.json(); } // ─── 공개 함수 ───────────── /** 어구 정체성 충돌 목록 조회 (필터 + 페이징). */ export function listGearCollisions(params?: { status?: string; severity?: string; name?: string; hours?: number; page?: number; size?: number; }): Promise> { return apiGet('/analysis/gear-collisions', { hours: 48, page: 0, size: 50, ...params, }); } /** status/severity 집계 */ export function getGearCollisionStats(hours = 48): Promise { return apiGet('/analysis/gear-collisions/stats', { hours }); } /** 단건 상세 조회 */ export function getGearCollision(id: number): Promise { return apiGet(`/analysis/gear-collisions/${id}`); } /** 운영자 분류 액션 */ export function resolveGearCollision( id: number, body: GearCollisionResolveRequest, ): Promise { return apiPost(`/analysis/gear-collisions/${id}/resolve`, body); }