diff --git a/frontend/src/api/types.ts b/frontend/src/api/types.ts
index 3521754..cd6a57a 100644
--- a/frontend/src/api/types.ts
+++ b/frontend/src/api/types.ts
@@ -75,7 +75,7 @@ export interface ProcessingDelay {
aisLatestTime: string
queryLatestTime: string
recentAisCount: number
- processedTiles: number
+ processedVessels: number
}
export interface MetricsSummary {
@@ -159,10 +159,9 @@ export interface CacheDetails {
export interface HaeguStat {
haegu_no: number
haegu_name: string
- active_tiles: number
current_vessels: number
+ avg_speed: number
avg_density: number
- max_tile_vessels: number
last_update: string
center_lon: number | null
center_lat: number | null
@@ -187,7 +186,7 @@ export interface ThroughputMetrics {
export interface DataQuality {
duplicateRecords: number
- missingTiles: number
+ stalePositions: number
qualityScore: 'GOOD' | 'NEEDS_ATTENTION' | 'ERROR'
checkedAt: string
}
diff --git a/frontend/src/i18n/en.ts b/frontend/src/i18n/en.ts
index 99d5ccd..70c9d86 100644
--- a/frontend/src/i18n/en.ts
+++ b/frontend/src/i18n/en.ts
@@ -37,7 +37,7 @@ const en = {
'dashboard.aisLatest': 'AIS Latest',
'dashboard.processLatest': 'Process Latest',
'dashboard.aisReceived': 'AIS Received',
- 'dashboard.tileProcessed': 'Tiles Processed',
+ 'dashboard.vesselsProcessed': 'Vessels Processed',
'dashboard.systemMetrics': 'System Metrics',
'dashboard.memory': 'Memory',
'dashboard.threads': 'Threads',
@@ -105,18 +105,17 @@ const en = {
'area.haeguStats': 'Area Status',
'area.haeguNo': 'Area No.',
'area.haeguName': 'Area Name',
- 'area.activeTiles': 'Active Tiles',
'area.currentVessels': 'Vessels',
+ 'area.avgSpeed': 'Avg Speed',
'area.avgDensityCol': 'Avg Density',
- 'area.maxTileVessels': 'Max Tile Vessels',
'area.lastUpdate': 'Last Update',
'area.throughput': 'Throughput',
'area.vesselsPerMin': 'vessels/min',
'area.vesselsPerHour': 'vessels/hour',
- 'area.partitions': 'Partition Sizes',
+ 'area.tableSizes': 'Table Sizes',
'area.dataQualityTitle': 'Data Quality Check',
- 'area.duplicates': 'Duplicates',
- 'area.missingTiles': 'Missing Tiles',
+ 'area.duplicates': 'Duplicate Tracks',
+ 'area.stalePositions': 'Stale Positions',
'area.checkedAt': 'Checked at',
// Time Range
diff --git a/frontend/src/i18n/ko.ts b/frontend/src/i18n/ko.ts
index 2eb6d7c..66f4e87 100644
--- a/frontend/src/i18n/ko.ts
+++ b/frontend/src/i18n/ko.ts
@@ -37,7 +37,7 @@ const ko = {
'dashboard.aisLatest': 'AIS 최신',
'dashboard.processLatest': '처리 최신',
'dashboard.aisReceived': 'AIS 수신',
- 'dashboard.tileProcessed': '타일 처리',
+ 'dashboard.vesselsProcessed': '선박 집계',
'dashboard.systemMetrics': '시스템 메트릭',
'dashboard.memory': '메모리',
'dashboard.threads': '스레드',
@@ -105,18 +105,17 @@ const ko = {
'area.haeguStats': '대해구별 현황',
'area.haeguNo': '해구번호',
'area.haeguName': '해구명',
- 'area.activeTiles': '활성 타일',
'area.currentVessels': '현재 선박',
+ 'area.avgSpeed': '평균 속력',
'area.avgDensityCol': '평균 밀도',
- 'area.maxTileVessels': '최대 타일 선박',
'area.lastUpdate': '최종 갱신',
'area.throughput': '처리량',
'area.vesselsPerMin': '선박/분',
'area.vesselsPerHour': '선박/시간',
- 'area.partitions': '파티션 크기',
+ 'area.tableSizes': '테이블 크기',
'area.dataQualityTitle': '데이터 품질 검증',
- 'area.duplicates': '중복 레코드',
- 'area.missingTiles': '누락 타일',
+ 'area.duplicates': '중복 항적',
+ 'area.stalePositions': '갱신 지연 위치',
'area.checkedAt': '검증 시각',
// Time Range
diff --git a/frontend/src/pages/AreaStats.tsx b/frontend/src/pages/AreaStats.tsx
index c2b3dfa..bc64e4f 100644
--- a/frontend/src/pages/AreaStats.tsx
+++ b/frontend/src/pages/AreaStats.tsx
@@ -28,7 +28,6 @@ export default function AreaStats() {
return (
- {/* Header */}
{t('area.title')}
{/* Summary Cards */}
@@ -67,10 +66,9 @@ export default function AreaStats() {
| {t('area.haeguNo')} |
{t('area.haeguName')} |
- {t('area.activeTiles')} |
{t('area.currentVessels')} |
+ {t('area.avgSpeed')} |
{t('area.avgDensityCol')} |
- {t('area.maxTileVessels')} |
{t('area.lastUpdate')} |
@@ -79,10 +77,9 @@ export default function AreaStats() {
| {h.haegu_no} |
{h.haegu_name} |
- {formatNumber(h.active_tiles)} |
{formatNumber(h.current_vessels)} |
- {(h.avg_density ?? 0).toFixed(2)} |
- {formatNumber(h.max_tile_vessels)} |
+ {(h.avg_speed ?? 0).toFixed(1)} kn |
+ {(h.avg_density ?? 0).toFixed(4)} |
{formatDateTime(h.last_update)} |
))}
@@ -111,10 +108,9 @@ export default function AreaStats() {
)}
- {/* Partition Sizes */}
{throughput.partitionSizes && throughput.partitionSizes.length > 0 && (
-
{t('area.partitions')}
+
{t('area.tableSizes')}
{throughput.partitionSizes.map((p, i) => (
@@ -149,8 +145,8 @@ export default function AreaStats() {
{formatNumber(quality.duplicateRecords)}
-
{t('area.missingTiles')}
-
{formatNumber(quality.missingTiles)}
+
{t('area.stalePositions')}
+
{formatNumber(quality.stalePositions)}
diff --git a/frontend/src/pages/Dashboard.tsx b/frontend/src/pages/Dashboard.tsx
index f8b353a..21a1b03 100644
--- a/frontend/src/pages/Dashboard.tsx
+++ b/frontend/src/pages/Dashboard.tsx
@@ -125,8 +125,8 @@ export default function Dashboard() {
{formatNumber(delay.recentAisCount)}{t('common.items')}
-
{t('dashboard.tileProcessed')}
-
{formatNumber(delay.processedTiles)}{t('common.items')}
+
{t('dashboard.vesselsProcessed')}
+
{formatNumber(delay.processedVessels)}{t('common.items')}
diff --git a/src/main/java/gc/mda/signal_batch/monitoring/controller/MonitoringController.java b/src/main/java/gc/mda/signal_batch/monitoring/controller/MonitoringController.java
index 6493d77..cdf2bc6 100644
--- a/src/main/java/gc/mda/signal_batch/monitoring/controller/MonitoringController.java
+++ b/src/main/java/gc/mda/signal_batch/monitoring/controller/MonitoringController.java
@@ -22,15 +22,14 @@ public class MonitoringController {
private final JdbcTemplate queryJdbcTemplate;
/**
- * 데이터 처리 지연 상태 확인
+ * 데이터 처리 지연 상태 확인 (AIS 수집 vs 5분 집계 간 지연)
*/
@GetMapping("/delay")
- @Operation(summary = "데이터 처리 지연 상태", description = "수집DB와 조회DB 간의 데이터 처리 지연 시간을 확인합니다")
+ @Operation(summary = "데이터 처리 지연 상태", description = "AIS 수집 시점과 5분 집계 시점 간의 처리 지연을 확인합니다")
public Map getProcessingDelay() {
Map result = new HashMap<>();
-
+
try {
- // AIS 최신 위치 데이터 (캐시 스냅샷)
Map aisLatest = queryJdbcTemplate.queryForMap(
"""
SELECT
@@ -41,18 +40,23 @@ public class MonitoringController {
"""
);
- // 집계 데이터의 최신 처리 시간
Map queryLatest = queryJdbcTemplate.queryForMap(
"""
SELECT
MAX(time_bucket) as latest_processed_time,
- COUNT(DISTINCT tile_id) as processed_tiles
- FROM signal.t_tile_summary
+ COUNT(DISTINCT mmsi) as processed_vessels
+ FROM signal.t_vessel_tracks_5min
WHERE time_bucket > NOW() - INTERVAL '10 minutes'
"""
);
- LocalDateTime aisTime = (LocalDateTime) aisLatest.get("latest_update_time");
+ Object aisRaw = aisLatest.get("latest_update_time");
+ LocalDateTime aisTime = null;
+ if (aisRaw instanceof java.time.OffsetDateTime odt) {
+ aisTime = odt.toLocalDateTime();
+ } else if (aisRaw instanceof LocalDateTime ldt) {
+ aisTime = ldt;
+ }
LocalDateTime queryTime = (LocalDateTime) queryLatest.get("latest_processed_time");
long delayMinutes = 0;
@@ -62,7 +66,7 @@ public class MonitoringController {
delayMinutes = java.time.Duration.between(queryTime, aisTime).toMinutes();
delayStatus = delayMinutes < 10 ? "NORMAL" : delayMinutes < 30 ? "WARNING" : "CRITICAL";
} else if (aisTime == null && queryTime == null) {
- delayStatus = "NORMAL"; // 데이터 없음 (수집 전 정상 상태)
+ delayStatus = "NORMAL";
} else {
delayStatus = "WARNING";
}
@@ -72,57 +76,48 @@ public class MonitoringController {
result.put("aisLatestTime", aisTime);
result.put("queryLatestTime", queryTime);
result.put("recentAisCount", aisLatest.get("recent_count"));
- result.put("processedTiles", queryLatest.get("processed_tiles"));
-
+ result.put("processedVessels", queryLatest.get("processed_vessels"));
+
} catch (Exception e) {
log.error("Failed to get processing delay", e);
result.put("error", e.getMessage());
result.put("status", "ERROR");
}
-
+
return result;
}
/**
- * 대해구별 실시간 처리 현황
+ * 대해구별 실시간 선박 현황 (t_ais_position + t_haegu_definitions 공간 조인)
*/
@GetMapping("/haegu/realtime")
- @Operation(summary = "대해구별 실시간 현황", description = "최신 타일 데이터 기준 대해구별 선박 통계를 조회합니다")
+ @Operation(summary = "대해구별 실시간 현황", description = "최신 AIS 위치 데이터 기준 대해구별 선박 통계를 조회합니다")
public List