Merge pull request 'fix: ST_AsText WKT 공백 불일치로 인한 daily merge 전량 필터 수정' (#80) from develop into main
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 4m12s

This commit is contained in:
htlee 2026-02-21 01:17:02 +09:00
커밋 8c1cfdd6b5
8개의 변경된 파일34개의 추가작업 그리고 12개의 파일을 삭제

파일 보기

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

파일 보기

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

파일 보기

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

파일 보기

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

파일 보기

@ -146,10 +146,15 @@ public class CacheBasedDailyTrackReader implements ItemReader<List<VesselTrack>>
|| (endPos == null && rs.getString("end_position") != null)) { || (endPos == null && rs.getString("end_position") != null)) {
parseFailCount[0]++; 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() VesselTrack track = VesselTrack.builder()
.mmsi(mmsi) .mmsi(mmsi)
.timeBucket(rs.getTimestamp("time_bucket").toLocalDateTime()) .timeBucket(rs.getTimestamp("time_bucket").toLocalDateTime())
.trackGeom(rs.getString("geom_text")) .trackGeom(geomWkt)
.distanceNm(rs.getBigDecimal("distance_nm")) .distanceNm(rs.getBigDecimal("distance_nm"))
.avgSpeed(rs.getBigDecimal("avg_speed")) .avgSpeed(rs.getBigDecimal("avg_speed"))
.maxSpeed(rs.getBigDecimal("max_speed")) .maxSpeed(rs.getBigDecimal("max_speed"))

파일 보기

@ -146,10 +146,15 @@ public class CacheBasedHourlyTrackReader implements ItemReader<List<VesselTrack>
|| (endPos == null && rs.getString("end_position") != null)) { || (endPos == null && rs.getString("end_position") != null)) {
parseFailCount[0]++; 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() VesselTrack track = VesselTrack.builder()
.mmsi(mmsi) .mmsi(mmsi)
.timeBucket(rs.getTimestamp("time_bucket").toLocalDateTime()) .timeBucket(rs.getTimestamp("time_bucket").toLocalDateTime())
.trackGeom(rs.getString("geom_text")) .trackGeom(geomWkt)
.distanceNm(rs.getBigDecimal("distance_nm")) .distanceNm(rs.getBigDecimal("distance_nm"))
.avgSpeed(rs.getBigDecimal("avg_speed")) .avgSpeed(rs.getBigDecimal("avg_speed"))
.maxSpeed(rs.getBigDecimal("max_speed")) .maxSpeed(rs.getBigDecimal("max_speed"))

파일 보기

@ -169,10 +169,15 @@ public class CacheWarmupService {
try (ResultSet rs = ps.executeQuery()) { try (ResultSet rs = ps.executeQuery()) {
while (rs.next()) { 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() VesselTrack track = VesselTrack.builder()
.mmsi(rs.getString("mmsi")) .mmsi(rs.getString("mmsi"))
.timeBucket(rs.getTimestamp("time_bucket").toLocalDateTime()) .timeBucket(rs.getTimestamp("time_bucket").toLocalDateTime())
.trackGeom(rs.getString("track_geom")) .trackGeom(geomWkt)
.distanceNm(BigDecimal.valueOf(rs.getDouble("distance_nm"))) .distanceNm(BigDecimal.valueOf(rs.getDouble("distance_nm")))
.avgSpeed(BigDecimal.valueOf(rs.getDouble("avg_speed"))) .avgSpeed(BigDecimal.valueOf(rs.getDouble("avg_speed")))
.maxSpeed(BigDecimal.valueOf(rs.getDouble("max_speed"))) .maxSpeed(BigDecimal.valueOf(rs.getDouble("max_speed")))

파일 보기

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