/** * 불법 조업 이벤트 전용 서비스 — 기존 /api/events 를 category 다중 조회로 래핑. * * category 는 event_generator 의 rule 에서 오는 단일 값이지만, UI 관점에서 "불법 조업" * 은 여러 카테고리의 합집합이다: * - GEAR_ILLEGAL : G-01 수역-어구 / G-05 고정어구 drift / G-06 쌍끌이 * - EEZ_INTRUSION : 영해 침범 / 접속수역 + 고위험 * - ZONE_DEPARTURE : 특정수역 진입 (관심구역 모니터링) * * backend 변경 없이 클라이언트에서 병렬 조회 후 머지한다. */ import { getEvents, type EventPageResponse, type PredictionEvent } from './event'; export const ILLEGAL_FISHING_CATEGORIES = [ 'GEAR_ILLEGAL', 'EEZ_INTRUSION', 'ZONE_DEPARTURE', ] as const; export type IllegalFishingCategory = (typeof ILLEGAL_FISHING_CATEGORIES)[number]; export interface ListParams { /** 단일 카테고리를 지정하면 해당 카테고리만, '' 이면 3개 모두 병합 조회 */ category?: IllegalFishingCategory | ''; level?: string; status?: string; vesselMmsi?: string; size?: number; } export interface IllegalFishingPatternPage { content: PredictionEvent[]; totalElements: number; byCategory: Record; byLevel: Record; } /** * 병합 조회 — category 미지정 시 3개 병렬 조회 후 occurredAt desc 정렬로 머지. * 각 카테고리 최대 size 건씩 수집하므로, 기본 200 * 3 = 600 건이 상한. */ export async function listIllegalFishingEvents(params?: ListParams): Promise { const size = params?.size ?? 200; const targetCategories: IllegalFishingCategory[] = params?.category ? [params.category] : [...ILLEGAL_FISHING_CATEGORIES]; const pages: EventPageResponse[] = await Promise.all( targetCategories.map((category) => getEvents({ category, level: params?.level, status: params?.status, vesselMmsi: params?.vesselMmsi, page: 0, size, }), ), ); const allEvents: PredictionEvent[] = pages.flatMap((p) => p.content); allEvents.sort((a, b) => b.occurredAt.localeCompare(a.occurredAt)); const byCategory: Record = {}; const byLevel: Record = {}; for (const e of allEvents) { byCategory[e.category] = (byCategory[e.category] ?? 0) + 1; byLevel[e.level] = (byLevel[e.level] ?? 0) + 1; } return { content: allEvents, totalElements: pages.reduce((acc, p) => acc + p.totalElements, 0), byCategory, byLevel, }; }