release: 2026-04-04 (31건 커밋) #223

병합
htlee develop 에서 main 로 23 commits 를 머지했습니다 2026-04-04 10:53:05 +09:00
17개의 변경된 파일30개의 추가작업 그리고 34개의 파일을 삭제
Showing only changes of commit 23828c742e - Show all commits

파일 보기

@ -17,7 +17,7 @@ import lombok.NoArgsConstructor;
import java.time.LocalDateTime;
@Entity
@Table(name = "login_history", schema = "kcg")
@Table(name = "login_history")
@Getter
@Builder
@NoArgsConstructor

파일 보기

@ -15,7 +15,7 @@ import lombok.Setter;
import java.time.LocalDateTime;
@Entity
@Table(name = "users", schema = "kcg")
@Table(name = "users")
@Getter
@Setter
@Builder

파일 보기

@ -14,7 +14,7 @@ import java.util.List;
@Configuration
public class WebConfig {
@Value("${app.cors.allowed-origins:http://localhost:5173}")
@Value("${app.cors.allowed-origins:http://localhost:5174,http://localhost:5173}")
private List<String> allowedOrigins;
@Bean

파일 보기

@ -7,7 +7,7 @@ import org.locationtech.jts.geom.Point;
import java.time.Instant;
@Entity
@Table(name = "aircraft_positions", schema = "kcg")
@Table(name = "aircraft_positions")
@Getter
@Setter
@NoArgsConstructor

파일 보기

@ -9,7 +9,7 @@ import java.time.Instant;
import java.util.Map;
@Entity
@Table(name = "vessel_analysis_results", schema = "kcg")
@Table(name = "vessel_analysis_results")
@Getter
@Setter
@NoArgsConstructor

파일 보기

@ -9,7 +9,7 @@ import java.time.Instant;
import java.util.Map;
@Entity
@Table(name = "events", schema = "kcg")
@Table(name = "events")
@Getter
@Setter
@NoArgsConstructor

파일 보기

@ -9,7 +9,6 @@ import java.time.Instant;
@Entity
@Table(
name = "osint_feeds",
schema = "kcg",
uniqueConstraints = @UniqueConstraint(columnNames = {"source", "source_url"})
)
@Getter

파일 보기

@ -6,7 +6,7 @@ import lombok.*;
import java.time.Instant;
@Entity
@Table(name = "satellite_tle", schema = "kcg")
@Table(name = "satellite_tle")
@Getter
@Setter
@NoArgsConstructor

파일 보기

@ -8,7 +8,6 @@ import java.time.Instant;
@Entity
@Table(
name = "pressure_readings",
schema = "kcg",
uniqueConstraints = @UniqueConstraint(columnNames = {"station", "reading_time"})
)
@Getter

파일 보기

@ -6,7 +6,7 @@ import lombok.*;
import java.time.Instant;
@Entity
@Table(name = "seismic_events", schema = "kcg")
@Table(name = "seismic_events")
@Getter
@Setter
@NoArgsConstructor

파일 보기

@ -1,16 +1,19 @@
spring:
datasource:
url: jdbc:postgresql://localhost:5432/kcgdb?currentSchema=kcg
username: kcg_user
password: kcg_pass
url: ${DB_URL:jdbc:postgresql://localhost:5432/kcgdb?currentSchema=kcg,public}
username: ${DB_USERNAME:kcg_user}
password: ${DB_PASSWORD:kcg_pass}
app:
jwt:
secret: local-dev-secret-key-32chars-minimum!!
expiration-ms: 86400000
secret: ${JWT_SECRET:local-dev-secret-key-32chars-minimum!!}
expiration-ms: ${JWT_EXPIRATION_MS:86400000}
google:
client-id: YOUR_GOOGLE_CLIENT_ID
client-id: ${GOOGLE_CLIENT_ID:YOUR_GOOGLE_CLIENT_ID}
auth:
allowed-domain: gcsc.co.kr
allowed-domain: ${AUTH_ALLOWED_DOMAIN:gcsc.co.kr}
collector:
open-sky-client-id: YOUR_OPENSKY_CLIENT_ID
open-sky-client-secret: YOUR_OPENSKY_CLIENT_SECRET
open-sky-client-id: ${OPENSKY_CLIENT_ID:YOUR_OPENSKY_CLIENT_ID}
open-sky-client-secret: ${OPENSKY_CLIENT_SECRET:YOUR_OPENSKY_CLIENT_SECRET}
prediction-base-url: ${PREDICTION_BASE_URL:http://localhost:8001}
cors:
allowed-origins: ${APP_CORS_ALLOWED_ORIGINS:http://localhost:5174,http://localhost:5173}

파일 보기

@ -16,4 +16,4 @@ app:
open-sky-client-secret: ${OPENSKY_CLIENT_SECRET:}
prediction-base-url: ${PREDICTION_BASE_URL:http://192.168.1.18:8001}
cors:
allowed-origins: http://localhost:5173,https://kcg.gc-si.dev
allowed-origins: ${APP_CORS_ALLOWED_ORIGINS:http://localhost:5174,http://localhost:5173,https://kcg.gc-si.dev}

파일 보기

@ -6,6 +6,6 @@ spring:
ddl-auto: none
properties:
hibernate:
default_schema: kcg
default_schema: ${DB_SCHEMA:kcg}
server:
port: 8080
port: ${SERVER_PORT:8080}

파일 보기

@ -10,6 +10,7 @@
/* Bundler mode */
"moduleResolution": "bundler",
"resolveJsonModule": true,
"allowImportingTsExtensions": true,
"verbatimModuleSyntax": true,
"moduleDetection": "force",

파일 보기

@ -7,6 +7,7 @@ import numpy as np
_KST = ZoneInfo('Asia/Seoul')
import pandas as pd
from time_bucket import compute_initial_window_start, compute_safe_bucket
logger = logging.getLogger(__name__)
@ -192,8 +193,6 @@ class VesselStore:
"""Remove track points older than N hours and evict empty MMSI entries."""
import datetime as _dt
from time_bucket import compute_initial_window_start, compute_safe_bucket
safe_bucket = compute_safe_bucket()
cutoff_bucket = compute_initial_window_start(hours, safe_bucket)
now = datetime.now(timezone.utc)
@ -210,6 +209,7 @@ class VesselStore:
mask = bucket_col >= pd.Timestamp(cutoff_bucket)
else:
ts_col = df['timestamp']
# Handle tz-aware and tz-naive timestamps uniformly
if hasattr(ts_col.dtype, 'tz') and ts_col.dtype.tz is not None:
mask = ts_col >= pd.Timestamp(cutoff_aware)
else:

파일 보기

@ -62,7 +62,6 @@ def fetch_all_tracks(hours: int = 24) -> pd.DataFrame:
LineStringM 지오메트리에서 개별 포인트를 추출하며,
한국 해역(122-132E, 31-39N) 최근 N시간 데이터를 반환한다.
safe_bucket(현재 - 12)까지만 조회하여 미완성 버킷을 제외한다.
"""
safe_bucket = compute_safe_bucket()
window_start = compute_initial_window_start(hours, safe_bucket)
@ -106,8 +105,8 @@ def fetch_all_tracks(hours: int = 24) -> pd.DataFrame:
def fetch_incremental(last_bucket: datetime) -> pd.DataFrame:
"""last_bucket 이후의 신규 궤적 포인트를 조회한다.
safe_bucket(현재 - 12)까지만 조회하여 미완성 버킷을 제외하고,
from_bucket(last_bucket - 15)부터 재수집하여 지연 INSERT를 보상한.
스케줄러 증분 업데이트에 사용되며, time_bucket > last_bucket 조건으로
이미 처리한 버킷을 건너뛴.
"""
safe_bucket = compute_safe_bucket()
from_bucket = compute_incremental_window_start(last_bucket)

파일 보기

@ -121,6 +121,7 @@ def run_analysis_cycle():
# 4.7 어구 연관성 분석 (멀티모델 패턴 추적)
try:
from algorithms.gear_correlation import run_gear_correlation
from algorithms.gear_parent_inference import run_gear_parent_inference
corr_result = run_gear_correlation(
vessel_store=vessel_store,
@ -132,12 +133,6 @@ def run_analysis_cycle():
corr_result['updated'], corr_result['raw_inserted'],
corr_result['models'],
)
except Exception as e:
logger.warning('gear correlation failed: %s', e)
# 4.8 어구 모선 추론 (episode continuity + 다층 점수 모델)
try:
from algorithms.gear_parent_inference import run_gear_parent_inference
inference_result = run_gear_parent_inference(
vessel_store=vessel_store,
@ -154,7 +149,7 @@ def run_analysis_cycle():
inference_result['skipped'],
)
except Exception as e:
logger.warning('gear parent inference failed: %s', e)
logger.warning('gear correlation failed: %s', e)
# 5. 선박별 추가 알고리즘 → AnalysisResult 생성
results = []