# Database Migrations

> **실제 SQL 파일 위치**: [`backend/src/main/resources/db/migration/`](../../backend/src/main/resources/db/migration/)
>
> Spring Boot Flyway 표준 위치를 따르므로 SQL 파일은 백엔드 모듈 안에 있습니다.
> Spring Boot 기동 시 Flyway가 자동으로 적용합니다.

## DB 정보
- **DB Name**: `kcgaidb`
- **User**: `kcg-app`
- **Schema**: `kcg`
- **Host**: `211.208.115.83:5432`
- **현재 버전**: v022 (2026-04-09)

---

## 마이그레이션 히스토리 (V001~V022)

Flyway 마이그레이션은 **증분 방식** — 각 파일은 이전 버전에 대한 변경(ALTER/INSERT/CREATE)만 포함합니다.
V001이 처음 테이블을 만들고, 이후 파일들이 컬럼 추가·시드 INSERT·신규 테이블 생성 등을 수행합니다.

### 인증/권한/감사 (V001~V007)

| 파일 | 내용 |
|---|---|
| `V001__auth_init.sql` | auth_user, auth_org, auth_role, auth_user_role, auth_login_hist, auth_setting |
| `V002__perm_tree.sql` | auth_perm_tree (권한 트리) + auth_perm (권한 매트릭스) |
| `V003__perm_seed.sql` | 역할 5종 시드 + 트리 노드 47개 + 역할별 권한 매트릭스 |
| `V004__access_logs.sql` | auth_audit_log + auth_access_log |
| `V005__parent_workflow.sql` | gear_group_parent_resolution, review_log, exclusions, label_sessions |
| `V006__demo_accounts.sql` | 데모 계정 5종 (admin/operator/analyst/field/viewer) |
| `V007__perm_tree_label_align.sql` | 트리 노드 명칭 일치 조정 |

### 마스터 데이터 (V008~V011)

| 파일 | 내용 |
|---|---|
| `V008__code_master.sql` | code_master (계층형 72코드: 위반유형/이벤트/단속 등) |
| `V009__gear_type_master.sql` | gear_type_master 6종 (어구 분류 룰 + 합법성 기준) |
| `V010__zone_polygon_master.sql` | zone_polygon_master (PostGIS, 8개 해역 시드) |
| `V011__vessel_permit_patrol.sql` | vessel_permit_master(9척) + patrol_ship_master(6척) + fleet_companies(2개) |

### Prediction 분석 (V012~V015)

| 파일 | 내용 |
|---|---|
| `V012__prediction_events_stats.sql` | vessel_analysis_results(파티션) + prediction_events + alerts + stats(시/일/월) + KPI + risk_grid + label_input |
| `V013__enforcement_operations.sql` | enforcement_records + plans + patrol_assignments + ai_model_versions + metrics |
| `V014__fleet_prediction_tables.sql` | fleet_vessels/tracking_snapshot + gear_identity_log + correlation_scores/raw_metrics + correlation_param_models + group_polygon_snapshots + gear_group_episodes/episode_snapshots + parent_candidate_snapshots + label_tracking_cycles + system_config |
| `V015__fix_numeric_precision.sql` | NUMERIC 정밀도 확대 (점수/비율 컬럼) |

### 모선 워크플로우 확장 + 기능 추가 (V016~V019)

| 파일 | 내용 |
|---|---|
| `V016__parent_workflow_columns.sql` | gear_group_parent_resolution 확장 (confidence, decision_source, episode_id 등) |
| `V017__role_color_hex.sql` | auth_role.color_hex 컬럼 추가 |
| `V018__prediction_event_features.sql` | prediction_events.features JSONB 컬럼 추가 |
| `V019__llm_ops_perm.sql` | ai-operations:llm-ops 권한 트리 노드 + ADMIN 권한 |

### 메뉴 DB SSOT (V020~V022)

| 파일 | 내용 |
|---|---|
| `V020__menu_config.sql` | menu_config 테이블 생성 + 시드 (V021에서 통합 후 폐기) |
| `V021__menu_into_perm_tree.sql` | auth_perm_tree에 메뉴 컬럼 추가 (url_path, label_key, component_key, nav_group, nav_sub_group, nav_sort) + 공유 리소스 분리 (statistics:reports, admin:data-hub, admin:notices) + menu_config DROP |
| `V022__perm_tree_i18n_labels.sql` | auth_perm_tree.labels JSONB 추가 — DB가 i18n SSOT (`{"ko":"...", "en":"..."}`) |

---

## 테이블 목록 (49개, flyway_schema_history 포함)

### 인증/권한 (8 테이블)

| 테이블 | PK | 설명 | 주요 컬럼 |
|---|---|---|---|
| `auth_user` | user_id (UUID) | 사용자 | user_acnt(UQ), pswd_hash, user_nm, rnkp_nm, email, org_sn(FK→auth_org), user_stts_cd, fail_cnt, auth_provider |
| `auth_org` | org_sn (BIGSERIAL) | 조직 | org_nm, org_abbr_nm, org_tp_cd, upper_org_sn(FK 자기참조) |
| `auth_role` | role_sn (BIGSERIAL) | 역할 | role_cd(UQ), role_nm, role_dc, dflt_yn, builtin_yn, color_hex |
| `auth_user_role` | (user_id, role_sn) | 사용자-역할 매핑 | granted_at, granted_by |
| `auth_perm_tree` | rsrc_cd (VARCHAR 100) | 권한 트리 + **메뉴 SSOT** | parent_cd(FK 자기참조), rsrc_nm, icon, rsrc_level, sort_ord, **url_path, label_key, component_key, nav_group, nav_sub_group, nav_sort, labels(JSONB)** |
| `auth_perm` | perm_sn (BIGSERIAL) | 권한 매트릭스 | role_sn(FK→auth_role), rsrc_cd(FK→auth_perm_tree), oper_cd, grant_yn, UQ(role_sn,rsrc_cd,oper_cd) |
| `auth_setting` | setting_key (VARCHAR 50) | 시스템 설정 | setting_val(JSONB) |
| `auth_login_hist` | hist_sn (BIGSERIAL) | 로그인 이력 | user_id, user_acnt, login_dtm, login_ip, result, fail_reason, auth_provider |

### 감사 (2 테이블)

| 테이블 | PK | 설명 | 주요 컬럼 |
|---|---|---|---|
| `auth_audit_log` | audit_sn (BIGSERIAL) | 감사 로그 | user_id, action_cd, resource_type, resource_id, detail(JSONB), ip_address, result |
| `auth_access_log` | access_sn (BIGSERIAL) | API 접근 이력 | user_id, http_method, request_path, status_code, duration_ms, ip_address |

### 모선 워크플로우 (7 테이블)

| 테이블 | PK | 설명 |
|---|---|---|
| `gear_group_parent_resolution` | id (BIGSERIAL), UQ(group_key, sub_cluster_id) | 모선 확정/거부 결과 (status, selected_parent_mmsi, confidence, decision_source, scores, episode_id) |
| `gear_group_parent_review_log` | id (BIGSERIAL) | 운영자 리뷰 이력 (action, actor, comment) |
| `gear_parent_candidate_exclusions` | id (BIGSERIAL) | 후보 제외 관리 (scope_type, excluded_mmsi, reason, active_from/until) |
| `gear_parent_label_sessions` | id (BIGSERIAL) | 학습 세션 (label_parent_mmsi, status, duration_days, anchor_snapshot) |
| `gear_parent_label_tracking_cycles` | (label_session_id, observed_at) | 학습 추적 사이클 (top_candidate, labeled_candidate 비교) |
| `gear_group_episodes` | episode_id (VARCHAR 50) | 어구 그룹 에피소드 (lineage_key, status, member_mmsis, center_point) |
| `gear_group_episode_snapshots` | (episode_id, observed_at) | 에피소드 스냅샷 |

### 마스터 데이터 (5 테이블)

| 테이블 | PK | 설명 | 시드 |
|---|---|---|---|
| `code_master` | code_id (VARCHAR 100) | 계층형 코드 | 12그룹, 72코드 |
| `gear_type_master` | gear_code (VARCHAR 20) | 어구 유형 | 6종 |
| `zone_polygon_master` | zone_code (VARCHAR 30) | 해역 폴리곤 (PostGIS GEOMETRY 4326) | 8해역 |
| `vessel_permit_master` | mmsi (VARCHAR 20) | 어선 허가 | 9척 |
| `patrol_ship_master` | ship_id (BIGSERIAL), UQ(ship_code) | 함정 | 6척 |

### Prediction 이벤트/통계 (8 테이블)

| 테이블 | PK | 설명 |
|---|---|---|
| `vessel_analysis_results` | (id, analyzed_at) 파티션 | 선박 분석 결과 (35컬럼: mmsi, risk_score, is_dark, transship_suspect, features JSONB 등) |
| `vessel_analysis_results_default` | — | 기본 파티션 |
| `prediction_events` | id (BIGSERIAL), UQ(event_uid) | 탐지 이벤트 (level, category, vessel_mmsi, status, features JSONB) |
| `prediction_alerts` | id (BIGSERIAL) | 경보 발송 (event_id FK, channel, delivery_status) |
| `event_workflow` | id (BIGSERIAL) | 이벤트 상태 변경 이력 (prev/new_status, actor) |
| `prediction_stats_hourly` | stat_hour (TIMESTAMPTZ) | 시간별 통계 (by_category/by_zone JSONB) |
| `prediction_stats_daily` | stat_date (DATE) | 일별 통계 |
| `prediction_stats_monthly` | stat_month (DATE) | 월별 통계 |

### Prediction 보조 (7 테이블)

| 테이블 | PK | 설명 |
|---|---|---|
| `prediction_kpi_realtime` | kpi_key (VARCHAR 50) | 실시간 KPI (value, trend, delta_pct) |
| `prediction_risk_grid` | (cell_id, stat_hour) | 위험도 격자 |
| `prediction_label_input` | id (BIGSERIAL) | 학습 피드백 입력 |
| `gear_correlation_scores` | (model_id, group_key, sub_cluster_id, target_mmsi) | 어구-선박 상관 점수 |
| `gear_correlation_raw_metrics` | id (BIGSERIAL) | 상관 원시 지표 |
| `correlation_param_models` | id (BIGSERIAL) | 상관 모델 파라미터 |
| `group_polygon_snapshots` | id (BIGSERIAL) | 그룹 폴리곤 스냅샷 (PostGIS) |

### Prediction 후보 (1 테이블)

| 테이블 | PK | 설명 |
|---|---|---|
| `gear_group_parent_candidate_snapshots` | id (BIGSERIAL) | 모선 후보 스냅샷 (25컬럼: 점수 분해, evidence JSONB) |

### 단속/작전 (3 테이블)

| 테이블 | PK | 설명 |
|---|---|---|
| `enforcement_records` | id (BIGSERIAL), UQ(enf_uid) | 단속 이력 (event_id FK, vessel_mmsi, action, result) |
| `enforcement_plans` | id (BIGSERIAL), UQ(plan_uid) | 단속 계획 (planned_date, risk_level, status) |
| `patrol_assignments` | id (BIGSERIAL) | 함정 배치 (ship_id FK, plan_id FK, waypoints JSONB) |

### AI 모델 (2 테이블)

| 테이블 | PK | 설명 |
|---|---|---|
| `ai_model_versions` | id (BIGSERIAL) | AI 모델 버전 (accuracy, status, train_config JSONB) |
| `ai_model_metrics` | id (BIGSERIAL) | 모델 메트릭 (model_id FK, metric_name, metric_value) |

### Fleet (3 테이블)

| 테이블 | PK | 설명 |
|---|---|---|
| `fleet_companies` | id (BIGSERIAL) | 선단 업체 (name_cn/en/ko, country) |
| `fleet_vessels` | id (BIGSERIAL) | 선단 선박 (company_id FK, mmsi, gear_code, fleet_role) |
| `fleet_tracking_snapshot` | id (BIGSERIAL) | 선단 추적 스냅샷 (company_id FK) |

### 기타 (2 테이블)

| 테이블 | PK | 설명 |
|---|---|---|
| `gear_identity_log` | id (BIGSERIAL) | 어구 식별 로그 (mmsi, name, parent_mmsi, match_method) |
| `system_config` | key (VARCHAR 100) | 시스템 설정 (value JSONB) |

---

## 인덱스 현황 (149개)

주요 패턴:
- **시계열 DESC**: `(occurred_at DESC)`, `(created_at DESC)`, `(analyzed_at DESC)` — 최신 데이터 우선 조회
- **복합 키**: `(group_key, sub_cluster_id, observed_at DESC)` — 어구 그룹 시계열
- **GiST 공간**: `polygon`, `polygon_geom` — PostGIS 공간 검색
- **GIN 배열**: `violation_categories` — 위반 카테고리 배열 검색
- **부분 인덱스**: `(released_at) WHERE released_at IS NULL` — 활성 제외만, `(is_dark) WHERE is_dark = true` — dark vessel만

## FK 관계 (21개)

```
auth_user ─→ auth_org (org_sn)
auth_user_role ─→ auth_user (user_id), auth_role (role_sn)
auth_perm ─→ auth_role (role_sn), auth_perm_tree (rsrc_cd)
auth_perm_tree ─→ auth_perm_tree (parent_cd, 자기참조)
code_master ─→ code_master (parent_id, 자기참조)
zone_polygon_master ─→ zone_polygon_master (parent_zone_code, 자기참조)
auth_org ─→ auth_org (upper_org_sn, 자기참조)
enforcement_records ─→ prediction_events (event_id), patrol_ship_master (patrol_ship_id)
event_workflow ─→ prediction_events (event_id)
prediction_alerts ─→ prediction_events (event_id)
patrol_assignments ─→ patrol_ship_master (ship_id), enforcement_plans (plan_id)
ai_model_metrics ─→ ai_model_versions (model_id)
gear_correlation_scores ─→ correlation_param_models (model_id)
gear_parent_label_tracking_cycles ─→ gear_parent_label_sessions (label_session_id)
fleet_tracking_snapshot ─→ fleet_companies (company_id)
fleet_vessels ─→ fleet_companies (company_id)
vessel_permit_master ─→ fleet_companies (company_id)
```

---

## 실행 방법

### 최초 1회 - DB/사용자 생성 (관리자 권한 필요)
```sql
psql -h 211.208.115.83 -U snp -d postgres

CREATE DATABASE kcgaidb;
CREATE USER "kcg-app" WITH PASSWORD 'Kcg2026ai';
GRANT ALL PRIVILEGES ON DATABASE kcgaidb TO "kcg-app";

\c kcgaidb
CREATE SCHEMA IF NOT EXISTS kcg AUTHORIZATION "kcg-app";
GRANT ALL ON SCHEMA kcg TO "kcg-app";
ALTER DATABASE kcgaidb OWNER TO "kcg-app";
```

### 마이그레이션 실행 (자동)
백엔드 기동 시 Flyway가 자동 적용:
```bash
cd backend && ./mvnw spring-boot:run
```

### 수동 적용
```bash
cd backend && ./mvnw flyway:migrate \
  -Dflyway.url=jdbc:postgresql://211.208.115.83:5432/kcgaidb \
  -Dflyway.user=kcg-app \
  -Dflyway.password=Kcg2026ai \
  -Dflyway.schemas=kcg
```

### Checksum 불일치 시 (마이그레이션 파일 수정 후)
```bash
cd backend && ./mvnw flyway:repair -Dflyway.url=... (위와 동일)
```

## 신규 마이그레이션 추가
[`backend/src/main/resources/db/migration/`](../../backend/src/main/resources/db/migration/)에 `V0NN__설명.sql` 형식으로 추가하면 다음 기동 시 자동 적용됩니다.

### 메뉴 추가 시 필수 포함 사항
auth_perm_tree에 INSERT 시 메뉴 SSOT 컬럼도 함께 지정:
```sql
INSERT INTO kcg.auth_perm_tree(
    rsrc_cd, parent_cd, rsrc_nm, rsrc_level, sort_ord, icon,
    url_path, label_key, component_key, nav_group, nav_sort,
    labels
) VALUES (
    'new-feature:sub', 'new-feature', '새 기능', 1, 10, 'Sparkles',
    '/new-feature/sub', 'nav.newFeatureSub', 'features/new-feature/SubPage', NULL, 1400,
    '{"ko":"새 기능 서브","en":"New Feature Sub"}'
);
```
