docs/prediction-analysis.md §7 P1 권고의 "UI 미노출 탐지" 해소 중 두 번째.
prediction algorithms/transshipment.py 5단계 필터 파이프라인 결과를 전체 목록·
집계·상세 수준으로 조회하는 READ 전용 대시보드.
### 배경
기존 features/vessel/TransferDetection.tsx 는 선박 상세 수준(특정 MMSI 의 환적
이력)이고, 환적 의심 선박 전체 목록을 보려면 ChinaFishing 의 탭 중 하나를 거쳐야
했다. /api/analysis/transship 엔드포인트는 이미 존재하나 전용 페이지가 없었음.
### 변경
- frontend/src/features/detection/TransshipmentDetection.tsx 신설 (405 라인)
- PageContainer + PageHeader(ArrowLeftRight) + KPI 5장
(Total / Transship tier CRITICAL/HIGH/MEDIUM / Risk CRITICAL)
- DataTable 8컬럼 (analyzedAt / mmsi / pairMmsi / duration / tier / risk / zone)
- features.transship_tier 읽어 Badge 로 심각도 표시
- 필터: hours(1/6/12/24/48) / riskLevel / mmsi 검색
- 상세 패널: 분석 피처 JSON 원본 + 좌표 + transship_score
- 기존 analysisApi.getTransshipSuspects 재사용 — backend 변경 없음
- index.ts + componentRegistry.ts 등록
- detection.json (ko/en) transshipment.* 네임스페이스 추가 (각 44키)
- common.json (ko/en) nav.transshipment 추가
- V033__menu_transshipment_detection.sql
- auth_perm_tree(detection:transshipment, nav_sort=910)
- ADMIN 5 ops + OPERATOR/ANALYST/FIELD/VIEWER READ
### 권한 주의
/api/analysis/transship 의 @RequirePermission 은 현재 detection:dark-vessel.
이 메뉴 READ 만으로는 API 호출 불가. 현행 운영자 역할(OPERATOR/ANALYST/FIELD)
은 dark-vessel READ 도 보유하므로 실용 동작.
향후 VesselAnalysisController.listTransshipSuspects 의 @RequirePermission 을
detection:transshipment 로 교체하는 권한 일관화는 별도 MR (후속).
### 검증
- npx tsc --noEmit 통과
- pre-commit tsc + ESLint 통과 예정
- Flyway V033 자동 적용 (백엔드 재배포 필요)
195 lines
6.1 KiB
JSON
195 lines
6.1 KiB
JSON
{
|
|
"darkVessel": {
|
|
"title": "Dark Vessel 탐지",
|
|
"desc": "SFR-09 | AIS 조작·위장·Dark Vessel 등 불법 패턴 탐지·분류·라벨링"
|
|
},
|
|
"gearDetection": {
|
|
"title": "어망·어구 탐지",
|
|
"desc": "SFR-10 | 불법 어구 위치·허가 현황·위험도 모니터링"
|
|
},
|
|
"chinaFishing": {
|
|
"title": "중국어선 분석",
|
|
"desc": "SFR-09/10 | 통합센서 기반 중국어선 조업 패턴·환적·어구 분석"
|
|
},
|
|
"gearId": {
|
|
"title": "어구 식별 분석",
|
|
"desc": "SFR-10 | AI 기반 어구 원산지·유형 자동 식별 및 판정"
|
|
},
|
|
"transshipment": {
|
|
"title": "환적 의심 탐지",
|
|
"desc": "prediction 5단계 필터 파이프라인(이종 쌍 → 감시영역 → RENDEZVOUS 90분+ → 점수 50+ → 밀집 방폭) 통과 선박 목록. features.transship_tier 기반 심각도 분류",
|
|
"refresh": "새로고침",
|
|
"stats": {
|
|
"title": "현황 요약",
|
|
"total": "전체",
|
|
"tierCritical": "환적 CRITICAL",
|
|
"tierHigh": "환적 HIGH",
|
|
"tierMedium": "환적 MEDIUM",
|
|
"riskCritical": "종합위험 CRITICAL"
|
|
},
|
|
"list": {
|
|
"title": "환적 의심 선박",
|
|
"empty": "최근 {{hours}}시간 내 환적 의심 선박이 없습니다."
|
|
},
|
|
"columns": {
|
|
"analyzedAt": "분석 시각",
|
|
"mmsi": "MMSI",
|
|
"pairMmsi": "상대 MMSI",
|
|
"durationMin": "지속(분)",
|
|
"tier": "환적 tier",
|
|
"riskScore": "위험도",
|
|
"riskLevel": "종합 위험",
|
|
"zone": "수역"
|
|
},
|
|
"filters": {
|
|
"hours": "조회 기간",
|
|
"level": "위험도",
|
|
"mmsi": "MMSI 검색",
|
|
"hoursValue": "최근 {{h}}시간",
|
|
"allLevel": "전체 위험도"
|
|
},
|
|
"detail": {
|
|
"title": "환적 의심 상세",
|
|
"location": "좌표",
|
|
"features": "분석 피처 원본",
|
|
"transshipScore": "환적 점수",
|
|
"close": "닫기"
|
|
},
|
|
"error": {
|
|
"loadFailed": "환적 의심 목록을 불러오지 못했습니다."
|
|
}
|
|
},
|
|
"illegalPattern": {
|
|
"title": "불법 조업 이벤트",
|
|
"desc": "수역-어구 위반 / 영해 침범 / 특정수역 진입 등 불법 조업 의심 이벤트 통합 조회 (READ 전용 — 처리 액션은 이벤트 목록에서)",
|
|
"refresh": "새로고침",
|
|
"stats": {
|
|
"title": "심각도 분포",
|
|
"total": "전체"
|
|
},
|
|
"byCategory": {
|
|
"title": "카테고리별 건수"
|
|
},
|
|
"category": {
|
|
"GEAR_ILLEGAL": "어구 위반",
|
|
"EEZ_INTRUSION": "영해/접속수역 침범",
|
|
"ZONE_DEPARTURE": "특정수역 진입"
|
|
},
|
|
"categoryDesc": {
|
|
"GEAR_ILLEGAL": "G-01/G-05/G-06 수역·어구 불일치, 고정어구 drift, 쌍끌이 공조",
|
|
"EEZ_INTRUSION": "영해(CRITICAL) / 접속수역 + 고위험 위반",
|
|
"ZONE_DEPARTURE": "관심 수역(ZONE_I~IV) 진입 + 위험도 40+"
|
|
},
|
|
"list": {
|
|
"title": "이벤트 목록",
|
|
"empty": "조건에 맞는 불법 조업 이벤트가 없습니다."
|
|
},
|
|
"columns": {
|
|
"occurredAt": "발생 시각",
|
|
"level": "심각도",
|
|
"category": "카테고리",
|
|
"title": "제목",
|
|
"mmsi": "MMSI",
|
|
"zone": "수역",
|
|
"status": "상태"
|
|
},
|
|
"filters": {
|
|
"category": "카테고리",
|
|
"level": "심각도",
|
|
"mmsi": "MMSI 검색",
|
|
"limit": "최대",
|
|
"allCategory": "전체 카테고리",
|
|
"allLevel": "전체 심각도"
|
|
},
|
|
"status": {
|
|
"NEW": "신규",
|
|
"ACKED": "확인",
|
|
"RESOLVED": "처리완료",
|
|
"FALSE_ALARM": "오탐"
|
|
},
|
|
"detail": {
|
|
"title": "이벤트 상세",
|
|
"vesselName": "선박명",
|
|
"location": "좌표",
|
|
"features": "추가 정보",
|
|
"close": "닫기",
|
|
"openEventList": "이벤트 목록에서 열기"
|
|
},
|
|
"error": {
|
|
"loadFailed": "이벤트를 불러오지 못했습니다."
|
|
}
|
|
},
|
|
"gearCollision": {
|
|
"title": "어구 정체성 충돌 탐지",
|
|
"desc": "동일 어구 이름이 서로 다른 MMSI 로 같은 사이클에 동시 송출되는 공존 패턴 — 어구 복제/스푸핑 의심",
|
|
"stats": {
|
|
"title": "현황 요약",
|
|
"total": "전체",
|
|
"open": "미검토",
|
|
"reviewed": "검토됨",
|
|
"confirmed": "불법 확정",
|
|
"falsePositive": "오탐"
|
|
},
|
|
"list": {
|
|
"title": "충돌 이력",
|
|
"empty": "최근 {{hours}}시간 내 감지된 충돌이 없습니다.",
|
|
"refresh": "새로고침"
|
|
},
|
|
"columns": {
|
|
"name": "어구명",
|
|
"mmsiPair": "MMSI 쌍",
|
|
"parentName": "추정 모선",
|
|
"coexistenceCount": "공존 횟수",
|
|
"maxDistance": "최대 거리(km)",
|
|
"severity": "심각도",
|
|
"status": "상태",
|
|
"lastSeen": "마지막 감지",
|
|
"actions": "액션"
|
|
},
|
|
"filters": {
|
|
"status": "상태",
|
|
"severity": "심각도",
|
|
"name": "어구명 검색",
|
|
"hours": "조회 기간(시간)",
|
|
"allStatus": "전체 상태",
|
|
"allSeverity": "전체 심각도"
|
|
},
|
|
"detail": {
|
|
"title": "공존 상세",
|
|
"evidence": "관측 이력",
|
|
"trajectoryCompare": "궤적 비교",
|
|
"firstSeenAt": "최초 감지",
|
|
"lastSeenAt": "마지막 감지",
|
|
"swapCount": "교체 누적"
|
|
},
|
|
"resolve": {
|
|
"title": "운영자 분류",
|
|
"reviewed": "검토 완료",
|
|
"confirmedIllegal": "불법으로 확정",
|
|
"falsePositive": "오탐으로 분류",
|
|
"reopen": "재오픈",
|
|
"note": "판정 메모",
|
|
"notePlaceholder": "분류 사유·추가 증거 등을 기록하세요",
|
|
"submit": "저장",
|
|
"cancel": "취소",
|
|
"confirmPrompt": "선택한 분류로 상태를 갱신합니다. 계속할까요?"
|
|
},
|
|
"status": {
|
|
"open": "미검토",
|
|
"reviewed": "검토됨",
|
|
"confirmedIllegal": "불법 확정",
|
|
"falsePositive": "오탐"
|
|
},
|
|
"severity": {
|
|
"CRITICAL": "매우 심각",
|
|
"HIGH": "심각",
|
|
"MEDIUM": "주의",
|
|
"LOW": "경미"
|
|
},
|
|
"error": {
|
|
"loadFailed": "충돌 목록을 불러오지 못했습니다",
|
|
"resolveFailed": "분류 저장에 실패했습니다"
|
|
}
|
|
}
|
|
}
|