From 8f784de358552e160ddfea088dcc34c87b5814ba Mon Sep 17 00:00:00 2001 From: htlee Date: Fri, 27 Mar 2026 07:56:16 +0900 Subject: [PATCH 1/2] =?UTF-8?q?feat(batch):=20=EB=B9=84=EC=A0=95=EC=83=81?= =?UTF-8?q?=20=EA=B6=A4=EC=A0=81=20=ED=8F=AC=ED=95=A8=20=EC=A0=80=EC=9E=A5?= =?UTF-8?q?=20=ED=94=8C=EB=9E=98=EA=B7=B8=20=EC=B6=94=EA=B0=80=20=E2=80=94?= =?UTF-8?q?=20=EA=B0=95=ED=99=94=ED=95=99=EC=8A=B5=20=EB=8D=B0=EC=9D=B4?= =?UTF-8?q?=ED=84=B0=20=EC=88=98=EC=A7=91=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GPS 스푸핑 등 비정상 운항 패턴의 강화학습 분류기 고도화를 위해, 비정상 궤적을 정상 테이블(5min/hourly/daily)과 캐시(L1/L2)에도 포함 저장하는 설정 플래그 추가. - vessel.batch.track.include-abnormal-in-tracks 플래그 (기본 false) - 5min: isAbnormal 시에도 filteredTracks에 포함 (플래그 true) - Hourly/Daily: correctedTrack null 시에도 originalTrack 포함 (플래그 true) - 비정상 검출 + t_abnormal_tracks 기록은 플래그와 무관하게 항상 유지 - prod 환경 true 설정 (강화학습 데이터 수집) Co-Authored-By: Claude Opus 4.6 (1M context) --- .../batch/job/DailyAggregationStepConfig.java | 7 +++++- .../job/HourlyAggregationStepConfig.java | 6 ++++- .../batch/job/VesselTrackStepConfig.java | 24 ++++++++++++------- .../batch/writer/CompositeTrackWriter.java | 11 ++++++--- src/main/resources/application-prod.yml | 1 + src/main/resources/application.yml | 2 ++ 6 files changed, 37 insertions(+), 14 deletions(-) diff --git a/src/main/java/gc/mda/signal_batch/batch/job/DailyAggregationStepConfig.java b/src/main/java/gc/mda/signal_batch/batch/job/DailyAggregationStepConfig.java index 00ffb8e..e6915f2 100644 --- a/src/main/java/gc/mda/signal_batch/batch/job/DailyAggregationStepConfig.java +++ b/src/main/java/gc/mda/signal_batch/batch/job/DailyAggregationStepConfig.java @@ -62,6 +62,9 @@ public class DailyAggregationStepConfig { @Value("${vessel.batch.chunk-size:5000}") private int chunkSize; + @Value("${vessel.batch.track.include-abnormal-in-tracks:false}") + private boolean includeAbnormalInTracks; + @Bean public Step mergeDailyTracksStep() { log.info("Building mergeDailyTracksStep with cache-based in-memory merge"); @@ -110,7 +113,9 @@ public class DailyAggregationStepConfig { return new CompositeTrackWriter( vesselTrackBulkWriter, abnormalTrackWriter, - "daily" + "daily", + null, + includeAbnormalInTracks ); } diff --git a/src/main/java/gc/mda/signal_batch/batch/job/HourlyAggregationStepConfig.java b/src/main/java/gc/mda/signal_batch/batch/job/HourlyAggregationStepConfig.java index 7718d1a..b473611 100644 --- a/src/main/java/gc/mda/signal_batch/batch/job/HourlyAggregationStepConfig.java +++ b/src/main/java/gc/mda/signal_batch/batch/job/HourlyAggregationStepConfig.java @@ -69,6 +69,9 @@ public class HourlyAggregationStepConfig { @Value("${vessel.batch.chunk-size:5000}") private int chunkSize; + @Value("${vessel.batch.track.include-abnormal-in-tracks:false}") + private boolean includeAbnormalInTracks; + // ────────────────────────────────────────────── // Step 1: 5분 → 시간 병합 (인메모리 캐시 기반) // ────────────────────────────────────────────── @@ -122,7 +125,8 @@ public class HourlyAggregationStepConfig { vesselTrackBulkWriter, abnormalTrackWriter, "hourly", - hourlyTrackCache + hourlyTrackCache, + includeAbnormalInTracks ); } diff --git a/src/main/java/gc/mda/signal_batch/batch/job/VesselTrackStepConfig.java b/src/main/java/gc/mda/signal_batch/batch/job/VesselTrackStepConfig.java index 14e33eb..d43832d 100644 --- a/src/main/java/gc/mda/signal_batch/batch/job/VesselTrackStepConfig.java +++ b/src/main/java/gc/mda/signal_batch/batch/job/VesselTrackStepConfig.java @@ -96,6 +96,9 @@ public class VesselTrackStepConfig { @Value("${vessel.batch.chunk-size:1000}") private int chunkSize; + @Value("${vessel.batch.track.include-abnormal-in-tracks:false}") + private boolean includeAbnormalInTracks; + @PostConstruct public void init() { // 5분 Job의 이름을 명시적으로 설정 @@ -203,18 +206,21 @@ public class VesselTrackStepConfig { log.warn("비정상 궤적 감지 [{}]: vessel={}, avg_speed={}, distance={}", abnormalReason, track.getVesselKey(), track.getAvgSpeed(), track.getDistanceNm()); saveAbnormalTrack(track, abnormalReason); + if (includeAbnormalInTracks) { + filteredTracks.add(track); // 플래그 true → 정상 테이블+캐시에도 포함 + } } else { filteredTracks.add(track); + } - // 정상 궤적의 종료 위치 저장 (캐시 업데이트용) - if (track.getEndPosition() != null) { - currentBucketEndPositions.put(track.getMmsi(), VesselBucketPositionDto.builder() - .mmsi(track.getMmsi()) - .endLon(track.getEndPosition().getLon()) - .endLat(track.getEndPosition().getLat()) - .endTime(track.getEndPosition().getTime()) - .build()); - } + // 궤적의 종료 위치 저장 (캐시 업데이트용) — 비정상 포함 시에도 위치 추적 + if (filteredTracks.contains(track) && track.getEndPosition() != null) { + currentBucketEndPositions.put(track.getMmsi(), VesselBucketPositionDto.builder() + .mmsi(track.getMmsi()) + .endLon(track.getEndPosition().getLon()) + .endLat(track.getEndPosition().getLat()) + .endTime(track.getEndPosition().getTime()) + .build()); } } diff --git a/src/main/java/gc/mda/signal_batch/batch/writer/CompositeTrackWriter.java b/src/main/java/gc/mda/signal_batch/batch/writer/CompositeTrackWriter.java index 1efeca5..74562d8 100644 --- a/src/main/java/gc/mda/signal_batch/batch/writer/CompositeTrackWriter.java +++ b/src/main/java/gc/mda/signal_batch/batch/writer/CompositeTrackWriter.java @@ -25,21 +25,24 @@ public class CompositeTrackWriter implements ItemWriter private final AbnormalTrackWriter abnormalTrackWriter; private final String targetTable; private final HourlyTrackCache hourlyTrackCache; // nullable (daily writer는 미사용) + private final boolean includeAbnormalInTracks; public CompositeTrackWriter(VesselTrackBulkWriter vesselTrackBulkWriter, AbnormalTrackWriter abnormalTrackWriter, String targetTable, - HourlyTrackCache hourlyTrackCache) { + HourlyTrackCache hourlyTrackCache, + boolean includeAbnormalInTracks) { this.vesselTrackBulkWriter = vesselTrackBulkWriter; this.abnormalTrackWriter = abnormalTrackWriter; this.targetTable = targetTable; this.hourlyTrackCache = hourlyTrackCache; + this.includeAbnormalInTracks = includeAbnormalInTracks; } public CompositeTrackWriter(VesselTrackBulkWriter vesselTrackBulkWriter, AbnormalTrackWriter abnormalTrackWriter, String targetTable) { - this(vesselTrackBulkWriter, abnormalTrackWriter, targetTable, null); + this(vesselTrackBulkWriter, abnormalTrackWriter, targetTable, null, false); } @BeforeStep @@ -66,9 +69,11 @@ public class CompositeTrackWriter implements ItemWriter abnormalResults.add(result); // 정정된 궤적이 있으면 정상 궤적으로 저장 - // null이면 전체 궤적이 비정상이므로 제외 + // null이면 전체 궤적이 비정상이므로 제외 (플래그 true면 원본 포함) if (result.getCorrectedTrack() != null) { normalTracks.add(result.getCorrectedTrack()); + } else if (includeAbnormalInTracks) { + normalTracks.add(result.getOriginalTrack()); } else { log.debug("비정상 궤적 전체 제외: vessel={}", result.getOriginalTrack().getVesselKey()); diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index 6c00a95..2552570 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -173,6 +173,7 @@ vessel: # spring 하위가 아닌 최상위 레벨 # 궤적 비정상 검출 설정 track: + include-abnormal-in-tracks: true # 비정상 궤적도 정상 테이블+캐시에 포함 (강화학습 데이터 수집용) abnormal-detection: large-gap-threshold-hours: 4 # 이 시간 이상 gap은 연결 안함 extreme-speed-threshold: 1000 # 이 속도 이상은 무조건 비정상 (knots) diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 682c7e8..159eb74 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -159,6 +159,8 @@ vessel: page-size: ${BATCH_PAGE_SIZE:10000} partition-size: ${BATCH_PARTITION_SIZE:24} skip-limit: 100 + track: + include-abnormal-in-tracks: false # true: 비정상 궤적도 정상 테이블+캐시에 포함 (강화학습 데이터 수집용) retry-limit: 3 # Reader 설정 use-cursor-reader: true # Cursor Reader 사용 여부 From 44cd532d525602154f0eb6754220f957ec62d5ad Mon Sep 17 00:00:00 2001 From: htlee Date: Fri, 27 Mar 2026 08:06:53 +0900 Subject: [PATCH 2/2] =?UTF-8?q?docs:=20=EB=A6=B4=EB=A6=AC=EC=A6=88=20?= =?UTF-8?q?=EB=85=B8=ED=8A=B8=20=EC=97=85=EB=8D=B0=EC=9D=B4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/RELEASE-NOTES.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/RELEASE-NOTES.md b/docs/RELEASE-NOTES.md index f9d303e..83ce331 100644 --- a/docs/RELEASE-NOTES.md +++ b/docs/RELEASE-NOTES.md @@ -4,6 +4,9 @@ ## [Unreleased] +### 추가 +- 비정상 궤적 포함 저장 플래그 (`include-abnormal-in-tracks`) — 강화학습 데이터 수집용 + ### 수정 - REST API 경로 client_id 수집 누락 수정 — JWT 쿠키 파싱 공용 메서드 추출