fix: ST_AsText WKT 공백 불일치로 인한 daily merge 전량 필터 수정 #80

병합
htlee develop 에서 main 로 2 commits 를 머지했습니다 2026-02-21 01:17:02 +09:00
8개의 변경된 파일34개의 추가작업 그리고 12개의 파일을 삭제

파일 보기

@ -83,7 +83,7 @@ const en = {
'pipeline.aisLatest': 'AIS Latest',
'pipeline.processLatest': 'Process Latest',
'pipeline.cacheOverview': 'Cache Overview',
'pipeline.cachedDays': 'days cached',
'pipeline.cachedDays': ' days cached',
'pipeline.totalHitRate': 'Total Hit Rate',
'pipeline.dailyThroughput': 'Daily Throughput Trend',
'pipeline.totalProcessed': 'Total Processed',

파일 보기

@ -157,8 +157,8 @@ export default function DataPipeline() {
</div>
<div>
<div className="text-xs text-muted">L3 (Daily)</div>
<div className="text-lg font-bold">{cacheDetails.l3_daily?.cachedDays ?? 0}</div>
<div className="text-xs text-muted">{t('pipeline.cachedDays')}</div>
<div className="text-lg font-bold">{formatNumber(cacheDetails.l3_daily?.totalVessels)}</div>
<div className="text-xs text-muted">{cacheDetails.l3_daily?.cachedDays ?? 0}{t('pipeline.cachedDays')}</div>
</div>
</div>
{cache && (

파일 보기

@ -35,7 +35,7 @@ import java.util.regex.Pattern;
public class DailyTrackMergeProcessor
implements ItemProcessor<List<VesselTrack>, AbnormalDetectionResult>, StepExecutionListener {
private static final Pattern WKT_COORDS_PATTERN = Pattern.compile("LINESTRING M\\((.+)\\)");
private static final Pattern WKT_COORDS_PATTERN = Pattern.compile("LINESTRING M\\s*\\((.+)\\)");
private static final DateTimeFormatter TIMESTAMP_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
private final AbnormalTrackDetector abnormalTrackDetector;
@ -217,7 +217,9 @@ public class DailyTrackMergeProcessor
private int countWktPoints(String wkt) {
if (wkt == null || !wkt.startsWith("LINESTRING M")) return 0;
try {
String coords = wkt.substring("LINESTRING M(".length(), wkt.length() - 1);
int parenIdx = wkt.indexOf('(');
if (parenIdx < 0) return 0;
String coords = wkt.substring(parenIdx + 1, wkt.length() - 1);
return coords.split(",").length;
} catch (Exception e) {
return 0;

파일 보기

@ -35,7 +35,7 @@ import java.util.regex.Pattern;
public class HourlyTrackMergeProcessor
implements ItemProcessor<List<VesselTrack>, AbnormalDetectionResult>, StepExecutionListener {
private static final Pattern WKT_COORDS_PATTERN = Pattern.compile("LINESTRING M\\((.+)\\)");
private static final Pattern WKT_COORDS_PATTERN = Pattern.compile("LINESTRING M\\s*\\((.+)\\)");
private static final DateTimeFormatter TIMESTAMP_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
private final AbnormalTrackDetector abnormalTrackDetector;
@ -212,7 +212,9 @@ public class HourlyTrackMergeProcessor
private int countWktPoints(String wkt) {
if (wkt == null || !wkt.startsWith("LINESTRING M")) return 0;
try {
String coords = wkt.substring("LINESTRING M(".length(), wkt.length() - 1);
int parenIdx = wkt.indexOf('(');
if (parenIdx < 0) return 0;
String coords = wkt.substring(parenIdx + 1, wkt.length() - 1);
return coords.split(",").length;
} catch (Exception e) {
return 0;

파일 보기

@ -146,10 +146,15 @@ public class CacheBasedDailyTrackReader implements ItemReader<List<VesselTrack>>
|| (endPos == null && rs.getString("end_position") != null)) {
parseFailCount[0]++;
}
// ST_AsText() "LINESTRING M (...)" 공백 포함 반환 Java 내부 형식으로 정규화
String geomWkt = rs.getString("geom_text");
if (geomWkt != null) {
geomWkt = geomWkt.replace("LINESTRING M (", "LINESTRING M(");
}
VesselTrack track = VesselTrack.builder()
.mmsi(mmsi)
.timeBucket(rs.getTimestamp("time_bucket").toLocalDateTime())
.trackGeom(rs.getString("geom_text"))
.trackGeom(geomWkt)
.distanceNm(rs.getBigDecimal("distance_nm"))
.avgSpeed(rs.getBigDecimal("avg_speed"))
.maxSpeed(rs.getBigDecimal("max_speed"))

파일 보기

@ -146,10 +146,15 @@ public class CacheBasedHourlyTrackReader implements ItemReader<List<VesselTrack>
|| (endPos == null && rs.getString("end_position") != null)) {
parseFailCount[0]++;
}
// ST_AsText() "LINESTRING M (...)" 공백 포함 반환 정규화
String geomWkt = rs.getString("geom_text");
if (geomWkt != null) {
geomWkt = geomWkt.replace("LINESTRING M (", "LINESTRING M(");
}
VesselTrack track = VesselTrack.builder()
.mmsi(mmsi)
.timeBucket(rs.getTimestamp("time_bucket").toLocalDateTime())
.trackGeom(rs.getString("geom_text"))
.trackGeom(geomWkt)
.distanceNm(rs.getBigDecimal("distance_nm"))
.avgSpeed(rs.getBigDecimal("avg_speed"))
.maxSpeed(rs.getBigDecimal("max_speed"))

파일 보기

@ -169,10 +169,15 @@ public class CacheWarmupService {
try (ResultSet rs = ps.executeQuery()) {
while (rs.next()) {
// ST_AsText() "LINESTRING M (...)" 공백 포함 반환 Java 내부 형식으로 정규화
String geomWkt = rs.getString("track_geom");
if (geomWkt != null) {
geomWkt = geomWkt.replace("LINESTRING M (", "LINESTRING M(");
}
VesselTrack track = VesselTrack.builder()
.mmsi(rs.getString("mmsi"))
.timeBucket(rs.getTimestamp("time_bucket").toLocalDateTime())
.trackGeom(rs.getString("track_geom"))
.trackGeom(geomWkt)
.distanceNm(BigDecimal.valueOf(rs.getDouble("distance_nm")))
.avgSpeed(BigDecimal.valueOf(rs.getDouble("avg_speed")))
.maxSpeed(BigDecimal.valueOf(rs.getDouble("max_speed")))

파일 보기

@ -87,8 +87,11 @@ public class BatchAdminController {
.addString("endTime", end.withNano(0).toString())
.addLong("executionTime", System.currentTimeMillis());
// vesselTrackAggregationJob의 경우 timeBucket 파라미터 추가
if ("vesselTrackAggregationJob".equals(jobName)) {
// timeBucket 파라미터 추가 (vesselTrack: 5분 단위, daily: 시작)
if ("dailyAggregationJob".equals(jobName)) {
LocalDateTime timeBucket = start.toLocalDate().atStartOfDay();
paramsBuilder.addString("timeBucket", timeBucket.toString());
} else {
LocalDateTime timeBucket = start.withSecond(0).withNano(0)
.minusMinutes(start.getMinute() % 5);
paramsBuilder.addString("timeBucket", timeBucket.toString());