refactor: codex 이식 완료 — 환경변수 동적화 + @Table schema 제거 + import 정리
- Backend: @Table(schema="kcg") 하드코딩 제거 → application.yml default_schema 활용
- Backend: application.yml/prod.yml 환경변수 ${} 패턴 전환
- Backend: WebConfig CORS 5174 포트 추가
- Frontend: tsconfig resolveJsonModule 추가
- Prediction: scheduler/snpdb/vessel_store import 위치 + 주석 codex 동기화
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
부모
5432e1f282
커밋
23828c742e
@ -17,7 +17,7 @@ import lombok.NoArgsConstructor;
|
|||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "login_history", schema = "kcg")
|
@Table(name = "login_history")
|
||||||
@Getter
|
@Getter
|
||||||
@Builder
|
@Builder
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
|
|||||||
@ -15,7 +15,7 @@ import lombok.Setter;
|
|||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "users", schema = "kcg")
|
@Table(name = "users")
|
||||||
@Getter
|
@Getter
|
||||||
@Setter
|
@Setter
|
||||||
@Builder
|
@Builder
|
||||||
|
|||||||
@ -14,7 +14,7 @@ import java.util.List;
|
|||||||
@Configuration
|
@Configuration
|
||||||
public class WebConfig {
|
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;
|
private List<String> allowedOrigins;
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
|
|||||||
@ -7,7 +7,7 @@ import org.locationtech.jts.geom.Point;
|
|||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "aircraft_positions", schema = "kcg")
|
@Table(name = "aircraft_positions")
|
||||||
@Getter
|
@Getter
|
||||||
@Setter
|
@Setter
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
|
|||||||
@ -9,7 +9,7 @@ import java.time.Instant;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "vessel_analysis_results", schema = "kcg")
|
@Table(name = "vessel_analysis_results")
|
||||||
@Getter
|
@Getter
|
||||||
@Setter
|
@Setter
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
|
|||||||
@ -9,7 +9,7 @@ import java.time.Instant;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "events", schema = "kcg")
|
@Table(name = "events")
|
||||||
@Getter
|
@Getter
|
||||||
@Setter
|
@Setter
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
|
|||||||
@ -9,7 +9,6 @@ import java.time.Instant;
|
|||||||
@Entity
|
@Entity
|
||||||
@Table(
|
@Table(
|
||||||
name = "osint_feeds",
|
name = "osint_feeds",
|
||||||
schema = "kcg",
|
|
||||||
uniqueConstraints = @UniqueConstraint(columnNames = {"source", "source_url"})
|
uniqueConstraints = @UniqueConstraint(columnNames = {"source", "source_url"})
|
||||||
)
|
)
|
||||||
@Getter
|
@Getter
|
||||||
|
|||||||
@ -6,7 +6,7 @@ import lombok.*;
|
|||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "satellite_tle", schema = "kcg")
|
@Table(name = "satellite_tle")
|
||||||
@Getter
|
@Getter
|
||||||
@Setter
|
@Setter
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
|
|||||||
@ -8,7 +8,6 @@ import java.time.Instant;
|
|||||||
@Entity
|
@Entity
|
||||||
@Table(
|
@Table(
|
||||||
name = "pressure_readings",
|
name = "pressure_readings",
|
||||||
schema = "kcg",
|
|
||||||
uniqueConstraints = @UniqueConstraint(columnNames = {"station", "reading_time"})
|
uniqueConstraints = @UniqueConstraint(columnNames = {"station", "reading_time"})
|
||||||
)
|
)
|
||||||
@Getter
|
@Getter
|
||||||
|
|||||||
@ -6,7 +6,7 @@ import lombok.*;
|
|||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "seismic_events", schema = "kcg")
|
@Table(name = "seismic_events")
|
||||||
@Getter
|
@Getter
|
||||||
@Setter
|
@Setter
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
|
|||||||
@ -1,16 +1,19 @@
|
|||||||
spring:
|
spring:
|
||||||
datasource:
|
datasource:
|
||||||
url: jdbc:postgresql://localhost:5432/kcgdb?currentSchema=kcg
|
url: ${DB_URL:jdbc:postgresql://localhost:5432/kcgdb?currentSchema=kcg,public}
|
||||||
username: kcg_user
|
username: ${DB_USERNAME:kcg_user}
|
||||||
password: kcg_pass
|
password: ${DB_PASSWORD:kcg_pass}
|
||||||
app:
|
app:
|
||||||
jwt:
|
jwt:
|
||||||
secret: local-dev-secret-key-32chars-minimum!!
|
secret: ${JWT_SECRET:local-dev-secret-key-32chars-minimum!!}
|
||||||
expiration-ms: 86400000
|
expiration-ms: ${JWT_EXPIRATION_MS:86400000}
|
||||||
google:
|
google:
|
||||||
client-id: YOUR_GOOGLE_CLIENT_ID
|
client-id: ${GOOGLE_CLIENT_ID:YOUR_GOOGLE_CLIENT_ID}
|
||||||
auth:
|
auth:
|
||||||
allowed-domain: gcsc.co.kr
|
allowed-domain: ${AUTH_ALLOWED_DOMAIN:gcsc.co.kr}
|
||||||
collector:
|
collector:
|
||||||
open-sky-client-id: YOUR_OPENSKY_CLIENT_ID
|
open-sky-client-id: ${OPENSKY_CLIENT_ID:YOUR_OPENSKY_CLIENT_ID}
|
||||||
open-sky-client-secret: YOUR_OPENSKY_CLIENT_SECRET
|
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:}
|
open-sky-client-secret: ${OPENSKY_CLIENT_SECRET:}
|
||||||
prediction-base-url: ${PREDICTION_BASE_URL:http://192.168.1.18:8001}
|
prediction-base-url: ${PREDICTION_BASE_URL:http://192.168.1.18:8001}
|
||||||
cors:
|
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
|
ddl-auto: none
|
||||||
properties:
|
properties:
|
||||||
hibernate:
|
hibernate:
|
||||||
default_schema: kcg
|
default_schema: ${DB_SCHEMA:kcg}
|
||||||
server:
|
server:
|
||||||
port: 8080
|
port: ${SERVER_PORT:8080}
|
||||||
|
|||||||
@ -10,6 +10,7 @@
|
|||||||
|
|
||||||
/* Bundler mode */
|
/* Bundler mode */
|
||||||
"moduleResolution": "bundler",
|
"moduleResolution": "bundler",
|
||||||
|
"resolveJsonModule": true,
|
||||||
"allowImportingTsExtensions": true,
|
"allowImportingTsExtensions": true,
|
||||||
"verbatimModuleSyntax": true,
|
"verbatimModuleSyntax": true,
|
||||||
"moduleDetection": "force",
|
"moduleDetection": "force",
|
||||||
|
|||||||
4
prediction/cache/vessel_store.py
vendored
4
prediction/cache/vessel_store.py
vendored
@ -7,6 +7,7 @@ import numpy as np
|
|||||||
|
|
||||||
_KST = ZoneInfo('Asia/Seoul')
|
_KST = ZoneInfo('Asia/Seoul')
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
|
from time_bucket import compute_initial_window_start, compute_safe_bucket
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -192,8 +193,6 @@ class VesselStore:
|
|||||||
"""Remove track points older than N hours and evict empty MMSI entries."""
|
"""Remove track points older than N hours and evict empty MMSI entries."""
|
||||||
import datetime as _dt
|
import datetime as _dt
|
||||||
|
|
||||||
from time_bucket import compute_initial_window_start, compute_safe_bucket
|
|
||||||
|
|
||||||
safe_bucket = compute_safe_bucket()
|
safe_bucket = compute_safe_bucket()
|
||||||
cutoff_bucket = compute_initial_window_start(hours, safe_bucket)
|
cutoff_bucket = compute_initial_window_start(hours, safe_bucket)
|
||||||
now = datetime.now(timezone.utc)
|
now = datetime.now(timezone.utc)
|
||||||
@ -210,6 +209,7 @@ class VesselStore:
|
|||||||
mask = bucket_col >= pd.Timestamp(cutoff_bucket)
|
mask = bucket_col >= pd.Timestamp(cutoff_bucket)
|
||||||
else:
|
else:
|
||||||
ts_col = df['timestamp']
|
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:
|
if hasattr(ts_col.dtype, 'tz') and ts_col.dtype.tz is not None:
|
||||||
mask = ts_col >= pd.Timestamp(cutoff_aware)
|
mask = ts_col >= pd.Timestamp(cutoff_aware)
|
||||||
else:
|
else:
|
||||||
|
|||||||
@ -62,7 +62,6 @@ def fetch_all_tracks(hours: int = 24) -> pd.DataFrame:
|
|||||||
|
|
||||||
LineStringM 지오메트리에서 개별 포인트를 추출하며,
|
LineStringM 지오메트리에서 개별 포인트를 추출하며,
|
||||||
한국 해역(122-132E, 31-39N) 내 최근 N시간 데이터를 반환한다.
|
한국 해역(122-132E, 31-39N) 내 최근 N시간 데이터를 반환한다.
|
||||||
safe_bucket(현재 - 12분)까지만 조회하여 미완성 버킷을 제외한다.
|
|
||||||
"""
|
"""
|
||||||
safe_bucket = compute_safe_bucket()
|
safe_bucket = compute_safe_bucket()
|
||||||
window_start = compute_initial_window_start(hours, 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:
|
def fetch_incremental(last_bucket: datetime) -> pd.DataFrame:
|
||||||
"""last_bucket 이후의 신규 궤적 포인트를 조회한다.
|
"""last_bucket 이후의 신규 궤적 포인트를 조회한다.
|
||||||
|
|
||||||
safe_bucket(현재 - 12분)까지만 조회하여 미완성 버킷을 제외하고,
|
스케줄러 증분 업데이트에 사용되며, time_bucket > last_bucket 조건으로
|
||||||
from_bucket(last_bucket - 15분)부터 재수집하여 지연 INSERT를 보상한다.
|
이미 처리한 버킷을 건너뛴다.
|
||||||
"""
|
"""
|
||||||
safe_bucket = compute_safe_bucket()
|
safe_bucket = compute_safe_bucket()
|
||||||
from_bucket = compute_incremental_window_start(last_bucket)
|
from_bucket = compute_incremental_window_start(last_bucket)
|
||||||
|
|||||||
@ -121,6 +121,7 @@ def run_analysis_cycle():
|
|||||||
# 4.7 어구 연관성 분석 (멀티모델 패턴 추적)
|
# 4.7 어구 연관성 분석 (멀티모델 패턴 추적)
|
||||||
try:
|
try:
|
||||||
from algorithms.gear_correlation import run_gear_correlation
|
from algorithms.gear_correlation import run_gear_correlation
|
||||||
|
from algorithms.gear_parent_inference import run_gear_parent_inference
|
||||||
|
|
||||||
corr_result = run_gear_correlation(
|
corr_result = run_gear_correlation(
|
||||||
vessel_store=vessel_store,
|
vessel_store=vessel_store,
|
||||||
@ -132,12 +133,6 @@ def run_analysis_cycle():
|
|||||||
corr_result['updated'], corr_result['raw_inserted'],
|
corr_result['updated'], corr_result['raw_inserted'],
|
||||||
corr_result['models'],
|
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(
|
inference_result = run_gear_parent_inference(
|
||||||
vessel_store=vessel_store,
|
vessel_store=vessel_store,
|
||||||
@ -154,7 +149,7 @@ def run_analysis_cycle():
|
|||||||
inference_result['skipped'],
|
inference_result['skipped'],
|
||||||
)
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.warning('gear parent inference failed: %s', e)
|
logger.warning('gear correlation failed: %s', e)
|
||||||
|
|
||||||
# 5. 선박별 추가 알고리즘 → AnalysisResult 생성
|
# 5. 선박별 추가 알고리즘 → AnalysisResult 생성
|
||||||
results = []
|
results = []
|
||||||
|
|||||||
불러오는 중...
Reference in New Issue
Block a user