From 13d6ca69e2d269c5cdae76587a18feec441c3e97 Mon Sep 17 00:00:00 2001 From: htlee Date: Sun, 1 Mar 2026 15:16:38 +0900 Subject: [PATCH] =?UTF-8?q?refactor(db):=20DDL=20=EC=8A=A4=ED=81=AC?= =?UTF-8?q?=EB=A6=BD=ED=8A=B8=20=ED=98=84=ED=96=89=ED=99=94=20+=20wing=5Fa?= =?UTF-8?q?uth=E2=86=92auth=20=EC=8A=A4=ED=82=A4=EB=A7=88=20=EB=AC=B8?= =?UTF-8?q?=EC=84=9C=20=EC=A0=84=EB=A9=B4=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - database/schema/ 14개 DDL 파일 신규 생성 (운영 DB pg_dump 기반) - database/seed/ 14개 초기 데이터 파일 분리 - database/_deprecated/로 구 init.sql, auth_init.sql 이동 - database/README.md 신규 작성 (DB 아키텍처, 설치 절차) - docs/ 6개 가이드 문서 wing_auth→auth 스키마 구조로 수정 - README.md, CLAUDE.md wing 단일 DB 구조 반영 Co-Authored-By: Claude Opus 4.6 --- CLAUDE.md | 10 +- README.md | 20 +- database/README.md | 106 ++ database/{ => _deprecated}/auth_init.sql | 0 database/{ => _deprecated}/init.sql | 0 database/schema/00_extensions.sql | 21 + database/schema/01_auth_tables.sql | 252 +++++ database/schema/02_common_tables.sql | 88 ++ database/schema/03_org_user_tables.sql | 83 ++ database/schema/04_accident_tables.sql | 202 ++++ database/schema/05_hns_tables.sql | 106 ++ database/schema/06_scat_tables.sql | 188 ++++ database/schema/07_boom_backtrack_tables.sql | 128 +++ database/schema/08_asset_tables.sql | 130 +++ database/schema/09_board_manual_tables.sql | 109 ++ database/schema/10_report_tables.sql | 235 +++++ database/schema/11_aerial_rescue_tables.sql | 217 ++++ database/schema/12_incidents_tables.sql | 190 ++++ database/schema/13_indexes.sql | 135 +++ database/seed/01_auth_roles.sql | 11 + database/seed/02_auth_perms.sql | 147 +++ database/seed/03_auth_perm_tree.sql | 96 ++ database/seed/04_auth_orgs.sql | 16 + database/seed/05_auth_settings.sql | 20 + database/seed/06_auth_admin.sql | 19 + database/seed/07_common_codes.sql | 192 ++++ database/seed/08_organizations.sql | 16 + database/seed/09_layers.sql | 4 + database/seed/10_hns_substances.sql | 4 + database/seed/11_sample_accident.sql | 29 + database/seed/12_assets.sql | 999 +++++++++++++++++++ database/seed/13_reports_templates.sql | 137 +++ database/seed/14_cctv_cameras.sql | 18 + database/seed/README.md | 53 + docs/COMMON-GUIDE.md | 14 +- docs/CRUD-API-GUIDE.md | 10 +- docs/DEVELOPMENT-GUIDE.md | 13 +- docs/INSTALL_GUIDE.md | 70 +- docs/MENU-TAB-GUIDE.md | 12 +- docs/README.md | 36 +- 40 files changed, 4048 insertions(+), 88 deletions(-) create mode 100644 database/README.md rename database/{ => _deprecated}/auth_init.sql (100%) rename database/{ => _deprecated}/init.sql (100%) create mode 100644 database/schema/00_extensions.sql create mode 100644 database/schema/01_auth_tables.sql create mode 100644 database/schema/02_common_tables.sql create mode 100644 database/schema/03_org_user_tables.sql create mode 100644 database/schema/04_accident_tables.sql create mode 100644 database/schema/05_hns_tables.sql create mode 100644 database/schema/06_scat_tables.sql create mode 100644 database/schema/07_boom_backtrack_tables.sql create mode 100644 database/schema/08_asset_tables.sql create mode 100644 database/schema/09_board_manual_tables.sql create mode 100644 database/schema/10_report_tables.sql create mode 100644 database/schema/11_aerial_rescue_tables.sql create mode 100644 database/schema/12_incidents_tables.sql create mode 100644 database/schema/13_indexes.sql create mode 100644 database/seed/01_auth_roles.sql create mode 100644 database/seed/02_auth_perms.sql create mode 100644 database/seed/03_auth_perm_tree.sql create mode 100644 database/seed/04_auth_orgs.sql create mode 100644 database/seed/05_auth_settings.sql create mode 100644 database/seed/06_auth_admin.sql create mode 100644 database/seed/07_common_codes.sql create mode 100644 database/seed/08_organizations.sql create mode 100644 database/seed/09_layers.sql create mode 100644 database/seed/10_hns_substances.sql create mode 100644 database/seed/11_sample_accident.sql create mode 100644 database/seed/12_assets.sql create mode 100644 database/seed/13_reports_templates.sql create mode 100644 database/seed/14_cctv_cameras.sql create mode 100644 database/seed/README.md diff --git a/CLAUDE.md b/CLAUDE.md index 991cbe0..5d1c9ef 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -5,7 +5,7 @@ - **타입**: react-ts 모노레포 (frontend + backend) - **Frontend**: React 19 + Vite 7 + TypeScript 5.9 + Tailwind CSS 3 - **Backend**: Express 4 + PostgreSQL (pg) + TypeScript -- **DB**: PostgreSQL 16 + PostGIS (wing 운영DB + wing_auth 인증DB) +- **DB**: PostgreSQL 16 + PostGIS (wing 단일 DB: public, auth, wing 3개 스키마) - **상태관리**: Zustand (클라이언트), TanStack Query (서버) - **지도**: MapLibre GL JS 5.x + deck.gl 9.x (Leaflet 제거됨) - **실시간**: Socket.IO @@ -86,8 +86,12 @@ wing/ │ ├── hns/ HNS 물질 검색 API │ ├── routes/ 레이어, 시뮬레이션 │ ├── middleware/ 보안 (입력 살균, rate-limit, Helmet CORP cross-origin) -│ └── db/ DB 연결 (wingDb, authDb), seed -├── database/ SQL 스크립트 + 마이그레이션 (001~016) +│ └── db/ DB 연결 (wingDb), seed +├── database/ DB 스크립트 +│ ├── schema/ ERD용 순수 DDL (14파일) +│ ├── seed/ 초기 데이터 (14파일) +│ ├── migration/ 증분 마이그레이션 (001~016) +│ └── _deprecated/ 구 스크립트 (통합 이전) ├── docs/ 개발 문서 ├── .claude/ 팀 워크플로우 (rules, skills, scripts, agents) └── .githooks/ Git hooks (pre-commit, commit-msg) diff --git a/README.md b/README.md index c709935..829c57b 100644 --- a/README.md +++ b/README.md @@ -120,8 +120,12 @@ wing/ │ ├── hns/ HNS 물질 검색 API │ ├── routes/ 레이어, 시뮬레이션 │ ├── middleware/ 보안 (입력 살균, rate-limit) -│ └── db/ DB 연결 (wingDb, authDb), seed -├── database/ SQL 스크립트 + 마이그레이션 (001~016) +│ └── db/ DB 연결 (wingDb), seed +├── database/ DB 스크립트 +│ ├── schema/ ERD용 순수 DDL (14파일) +│ ├── seed/ 초기 데이터 (14파일) +│ ├── migration/ 증분 마이그레이션 (001~016) +│ └── _deprecated/ 구 스크립트 (통합 이전) ├── docs/ 개발 문서 ├── .claude/ 팀 워크플로우 (rules, skills, scripts, agents) └── .githooks/ Git hooks (pre-commit, commit-msg) @@ -142,7 +146,7 @@ wing/ | Backend | Express 4, TypeScript, pg (PostgreSQL) | | 보안 | Helmet, CORS, Rate-limit, Input sanitization | | 인증 | JWT (HttpOnly Cookie `WING_SESSION`), bcrypt, Google OAuth | -| DB | PostgreSQL 16 + PostGIS (wing 운영DB + wing_auth 인증DB) | +| DB | PostgreSQL 16 + PostGIS (wing 단일 DB: public, auth, wing 3개 스키마) | | CI/CD | Gitea Actions (.gitea/workflows/deploy.yml) | --- @@ -188,11 +192,11 @@ VITE_GOOGLE_CLIENT_ID=your-google-client-id PORT=3001 NODE_ENV=development JWT_SECRET=your-jwt-secret -AUTH_DB_HOST=localhost -AUTH_DB_PORT=5432 -AUTH_DB_NAME=wing_auth -AUTH_DB_USER=wing_auth -AUTH_DB_PASSWORD=<비밀번호> +DB_HOST=211.208.115.83 +DB_PORT=5432 +DB_NAME=wing +DB_USER=wing +DB_PASSWORD=<비밀번호> GOOGLE_CLIENT_ID=your-google-client-id ``` diff --git a/database/README.md b/database/README.md new file mode 100644 index 0000000..fd0f743 --- /dev/null +++ b/database/README.md @@ -0,0 +1,106 @@ +# WING-OPS Database + +## 아키텍처 + +wing 단일 DB에 3개 스키마로 구성. `search_path = wing, auth, public` 설정으로 스키마 접두사 없이 쿼리 가능. + +``` +wing (database) +├── public PostGIS 확장 (geometry, geography, spatial_ref_sys) +├── auth 인증/권한 (9 테이블) +└── wing 운영 데이터 (35+ 테이블) +``` + +| 스키마 | 역할 | 주요 테이블 | +|--------|------|-------------| +| public | PostGIS 공간 함수, pg_trgm, uuid-ossp | (확장 전용) | +| auth | 사용자 인증, 역할, 권한, 감사로그 | auth_user, auth_role, auth_perm, auth_perm_tree, auth_audit_log | +| wing | 사고, 확산예측, HNS, SCAT, 자산, 게시판, 보고서 | acdnt, pred_exec, hns_substance, cst_sect, asset_org, board_post, report | + +## 초기 설치 + +```bash +# 1. DB 생성 + 사용자 +createdb -U postgres wing +psql -U postgres -c "CREATE USER wing WITH PASSWORD 'Wing2026';" +psql -U postgres -c "GRANT ALL ON DATABASE wing TO wing;" + +# 2. 스키마 DDL 실행 (순서대로) +for f in database/schema/0*.sql database/schema/1*.sql; do + psql -U wing -d wing -f "$f" +done + +# 3. Seed 데이터 (SQL) +for f in database/seed/0[1-8]*.sql database/seed/1[1-4]*.sql; do + psql -U wing -d wing -f "$f" +done + +# 4. Seed 데이터 (Node.js — layer 160건, HNS 1,316건) +cd backend && npm run db:seed +``` + +## 디렉토리 구조 + +``` +database/ +├── schema/ ERD용 순수 DDL (현행 운영 DB 기준) +│ ├── 00_extensions.sql 확장 + 스키마 생성 +│ ├── 01_auth_tables.sql auth 스키마 (9 테이블) +│ ├── 02_common_tables.sql 공통코드, 레이어 +│ ├── 03_org_user_tables.sql 조직, 사용자 +│ ├── 04_accident_tables.sql 사고, 유출, 예측, 기상, 미디어 +│ ├── 05_hns_tables.sql HNS 물질, 분석 +│ ├── 06_scat_tables.sql 해안조사 구역, 구간 +│ ├── 07_boom_backtrack_tables.sql 오일붐, 역추적, 선박 +│ ├── 08_asset_tables.sql 자산 기관, 장비, 연락처 +│ ├── 09_board_manual_tables.sql 게시판, 매뉴얼 +│ ├── 10_report_tables.sql 보고서, 템플릿, 분석카테고리 +│ ├── 11_aerial_rescue_tables.sql 항공, CCTV, 위성, 구조 +│ ├── 12_incidents_tables.sql 사건사고 +│ └── 13_indexes.sql 인덱스 (GiST, GIN, btree) +├── seed/ 초기 데이터 (INSERT) +│ ├── 01~06 auth 스키마 seed +│ ├── 07~08 공통코드, 조직 +│ ├── 09~10 참조 안내 (Node.js seed) +│ ├── 11~14 샘플 데이터 +│ └── README.md 실행 순서 안내 +├── migration/ 증분 마이그레이션 (001~016) +└── _deprecated/ 구 스크립트 (통합 이전 버전) + ├── init.sql 구 wing DB 초기 스키마 + └── auth_init.sql 구 wing_auth DB 초기 스키마 +``` + +## 마이그레이션 규칙 + +- 번호 체계: `NNN_설명.sql` (예: `017_add_feature.sql`) +- DDL(CREATE/ALTER)과 seed(INSERT)를 분리 + - DDL만 포함하는 파일: `NNN_설명.sql` + - seed만 포함하는 파일: `NNN_설명_seed.sql` +- 새 테이블 추가 시 `schema/` 파일도 함께 업데이트 + +## PostGIS 컬럼 + +| 테이블 | 컬럼 | 타입 | 인덱스 | +|--------|------|------|--------| +| acdnt | loc_geom | Point, 4326 | GiST | +| aerial_media | geom | Point, 4326 | GiST | +| asset_org | geom | Point, 4326 | GiST | +| backtrack | geom | Point, 4326 | GiST | +| boom_line | geom | LineString, 4326 | GiST | +| cctv_camera | geom | Point, 4326 | GiST | +| cst_sect | geom | Point, 4326 | GiST | +| cst_srvy_zone | geom | Point, 4326 | GiST | +| hns_analysis | geom | Point, 4326 | - | +| rescue_ops | geom | Point, 4326 | GiST | +| sat_request | geom | Point, 4326 | GiST | + +## 접속 정보 + +| 항목 | 값 | +|------|------| +| Host | 211.208.115.83 | +| Port | 5432 | +| Database | wing | +| User | wing | +| Schema | wing, auth, public | +| search_path | wing, auth, public | diff --git a/database/auth_init.sql b/database/_deprecated/auth_init.sql similarity index 100% rename from database/auth_init.sql rename to database/_deprecated/auth_init.sql diff --git a/database/init.sql b/database/_deprecated/init.sql similarity index 100% rename from database/init.sql rename to database/_deprecated/init.sql diff --git a/database/schema/00_extensions.sql b/database/schema/00_extensions.sql new file mode 100644 index 0000000..2ff96cb --- /dev/null +++ b/database/schema/00_extensions.sql @@ -0,0 +1,21 @@ +-- WING-OPS Database Schema: Extensions & Schemas +-- PostgreSQL 16 + PostGIS + +-- ============================================================ +-- 스키마 생성 +-- ============================================================ +CREATE SCHEMA IF NOT EXISTS auth; +CREATE SCHEMA IF NOT EXISTS wing; + +-- ============================================================ +-- 확장 설치 +-- (postgis_topology는 운영 환경 전용, 여기서는 제외) +-- ============================================================ +CREATE EXTENSION IF NOT EXISTS pg_trgm WITH SCHEMA public; +CREATE EXTENSION IF NOT EXISTS postgis WITH SCHEMA public; +CREATE EXTENSION IF NOT EXISTS "uuid-ossp" WITH SCHEMA public; + +-- ============================================================ +-- 기본 검색 경로 +-- ============================================================ +SET search_path = wing, auth, public; diff --git a/database/schema/01_auth_tables.sql b/database/schema/01_auth_tables.sql new file mode 100644 index 0000000..b535038 --- /dev/null +++ b/database/schema/01_auth_tables.sql @@ -0,0 +1,252 @@ +-- WING-OPS Database Schema: Auth Tables +-- auth 스키마 9개 테이블 (인증/인가/감사) + +-- ============================================================ +-- 1. auth_role — 역할 (FK 없음, 최상위) +-- ============================================================ +CREATE TABLE auth.auth_role ( + role_sn integer NOT NULL, + role_cd character varying(20) NOT NULL, + role_nm character varying(50) NOT NULL, + role_dc character varying(200), + dflt_yn character(1) DEFAULT 'N'::bpchar NOT NULL, + reg_dtm timestamp with time zone DEFAULT now() NOT NULL, + CONSTRAINT pk_auth_role PRIMARY KEY (role_sn), + CONSTRAINT uk_auth_role_cd UNIQUE (role_cd), + CONSTRAINT ck_auth_role_dflt CHECK ((dflt_yn = ANY (ARRAY['Y'::bpchar, 'N'::bpchar]))) +); + +CREATE SEQUENCE auth.auth_role_role_sn_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER SEQUENCE auth.auth_role_role_sn_seq OWNED BY auth.auth_role.role_sn; +ALTER TABLE ONLY auth.auth_role ALTER COLUMN role_sn SET DEFAULT nextval('auth.auth_role_role_sn_seq'::regclass); + + +-- ============================================================ +-- 2. auth_org — 조직 (자기참조 FK) +-- ============================================================ +CREATE TABLE auth.auth_org ( + org_sn integer NOT NULL, + org_nm character varying(100) NOT NULL, + org_abbr_nm character varying(20) NOT NULL, + org_tp_cd character varying(20) NOT NULL, + upper_org_sn integer, + reg_dtm timestamp with time zone DEFAULT now() NOT NULL, + CONSTRAINT pk_auth_org PRIMARY KEY (org_sn) +); + +CREATE SEQUENCE auth.auth_org_org_sn_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER SEQUENCE auth.auth_org_org_sn_seq OWNED BY auth.auth_org.org_sn; +ALTER TABLE ONLY auth.auth_org ALTER COLUMN org_sn SET DEFAULT nextval('auth.auth_org_org_sn_seq'::regclass); + + +-- ============================================================ +-- 3. auth_user — 사용자 (FK: auth_org) +-- ============================================================ +CREATE TABLE auth.auth_user ( + user_id uuid DEFAULT public.uuid_generate_v4() NOT NULL, + user_acnt character varying(50) NOT NULL, + pswd_hash character varying(255), + user_nm character varying(50) NOT NULL, + rnkp_nm character varying(30), + org_sn integer, + user_stts_cd character varying(20) DEFAULT 'ACTIVE'::character varying NOT NULL, + fail_cnt integer DEFAULT 0 NOT NULL, + last_login_dtm timestamp with time zone, + reg_dtm timestamp with time zone DEFAULT now() NOT NULL, + mdfcn_dtm timestamp with time zone DEFAULT now() NOT NULL, + oauth_provider character varying(20), + oauth_sub character varying(255), + email character varying(255), + CONSTRAINT pk_auth_user PRIMARY KEY (user_id), + CONSTRAINT uk_auth_user_acnt UNIQUE (user_acnt), + CONSTRAINT ck_auth_user_stts CHECK (((user_stts_cd)::text = ANY (ARRAY[('ACTIVE'::character varying)::text, ('LOCKED'::character varying)::text, ('INACTIVE'::character varying)::text]))) +); + +-- 조건부 유니크 인덱스 (NULL 값 제외) +CREATE UNIQUE INDEX uk_auth_user_email ON auth.auth_user USING btree (email) WHERE (email IS NOT NULL); +CREATE UNIQUE INDEX uk_auth_user_oauth ON auth.auth_user USING btree (oauth_provider, oauth_sub) WHERE (oauth_provider IS NOT NULL); + + +-- ============================================================ +-- 4. auth_user_role — 사용자-역할 매핑 (FK: auth_user, auth_role) +-- ============================================================ +CREATE TABLE auth.auth_user_role ( + user_id uuid NOT NULL, + role_sn integer NOT NULL, + reg_dtm timestamp with time zone DEFAULT now() NOT NULL, + CONSTRAINT pk_auth_user_role PRIMARY KEY (user_id, role_sn) +); + + +-- ============================================================ +-- 5. auth_perm_tree — 권한 트리 (자기참조 FK) +-- ============================================================ +CREATE TABLE auth.auth_perm_tree ( + rsrc_cd character varying(50) NOT NULL, + parent_cd character varying(50), + rsrc_nm character varying(100) NOT NULL, + rsrc_desc character varying(200), + icon character varying(20), + rsrc_level smallint DEFAULT 0 NOT NULL, + sort_ord smallint DEFAULT 0 NOT NULL, + use_yn character(1) DEFAULT 'Y'::bpchar NOT NULL, + CONSTRAINT pk_perm_tree PRIMARY KEY (rsrc_cd) +); + + +-- ============================================================ +-- 6. auth_perm — 역할별 권한 (FK: auth_role) +-- ============================================================ +CREATE TABLE auth.auth_perm ( + perm_sn integer NOT NULL, + role_sn integer NOT NULL, + rsrc_cd character varying(50) NOT NULL, + grant_yn character(1) DEFAULT 'Y'::bpchar NOT NULL, + reg_dtm timestamp with time zone DEFAULT now() NOT NULL, + oper_cd character varying(20) NOT NULL, + CONSTRAINT pk_auth_perm PRIMARY KEY (perm_sn), + CONSTRAINT uk_auth_perm UNIQUE (role_sn, rsrc_cd, oper_cd), + CONSTRAINT ck_auth_perm_grant CHECK ((grant_yn = ANY (ARRAY['Y'::bpchar, 'N'::bpchar]))), + CONSTRAINT ck_auth_perm_oper CHECK (((oper_cd)::text = ANY (ARRAY[('READ'::character varying)::text, ('CREATE'::character varying)::text, ('UPDATE'::character varying)::text, ('DELETE'::character varying)::text, ('MANAGE'::character varying)::text, ('EXPORT'::character varying)::text]))) +); + +CREATE SEQUENCE auth.auth_perm_perm_sn_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER SEQUENCE auth.auth_perm_perm_sn_seq OWNED BY auth.auth_perm.perm_sn; +ALTER TABLE ONLY auth.auth_perm ALTER COLUMN perm_sn SET DEFAULT nextval('auth.auth_perm_perm_sn_seq'::regclass); + + +-- ============================================================ +-- 7. auth_setting — 시스템 설정 +-- ============================================================ +CREATE TABLE auth.auth_setting ( + setting_key character varying(100) NOT NULL, + setting_val text NOT NULL, + setting_dc character varying(500), + mdfcn_dtm timestamp without time zone DEFAULT now(), + CONSTRAINT auth_setting_pkey PRIMARY KEY (setting_key) +); + + +-- ============================================================ +-- 8. auth_login_hist — 로그인 이력 (FK: auth_user) +-- ============================================================ +CREATE TABLE auth.auth_login_hist ( + hist_sn integer NOT NULL, + user_id uuid NOT NULL, + login_dtm timestamp with time zone DEFAULT now() NOT NULL, + ip_addr character varying(45), + user_agent character varying(500), + success_yn character(1) DEFAULT 'Y'::bpchar NOT NULL, + CONSTRAINT pk_auth_login_hist PRIMARY KEY (hist_sn), + CONSTRAINT ck_alh_success CHECK ((success_yn = ANY (ARRAY['Y'::bpchar, 'N'::bpchar]))) +); + +CREATE SEQUENCE auth.auth_login_hist_hist_sn_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER SEQUENCE auth.auth_login_hist_hist_sn_seq OWNED BY auth.auth_login_hist.hist_sn; +ALTER TABLE ONLY auth.auth_login_hist ALTER COLUMN hist_sn SET DEFAULT nextval('auth.auth_login_hist_hist_sn_seq'::regclass); + + +-- ============================================================ +-- 9. auth_audit_log — 감사 로그 +-- ============================================================ +CREATE TABLE auth.auth_audit_log ( + log_sn integer NOT NULL, + user_id uuid, + action_cd character varying(30) NOT NULL, + action_dtl character varying(100), + http_method character varying(10), + crud_type character varying(10), + req_url character varying(500), + req_dtm timestamp with time zone DEFAULT now() NOT NULL, + res_dtm timestamp with time zone, + res_status smallint, + res_size integer, + ip_addr character varying(45), + user_agent character varying(500), + extra jsonb, + CONSTRAINT pk_auth_audit_log PRIMARY KEY (log_sn) +); + +CREATE SEQUENCE auth.auth_audit_log_log_sn_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER SEQUENCE auth.auth_audit_log_log_sn_seq OWNED BY auth.auth_audit_log.log_sn; +ALTER TABLE ONLY auth.auth_audit_log ALTER COLUMN log_sn SET DEFAULT nextval('auth.auth_audit_log_log_sn_seq'::regclass); + + +-- ============================================================ +-- FK 제약조건 +-- ============================================================ +ALTER TABLE ONLY auth.auth_org + ADD CONSTRAINT fk_auth_org_upper FOREIGN KEY (upper_org_sn) REFERENCES auth.auth_org(org_sn); + +ALTER TABLE ONLY auth.auth_user + ADD CONSTRAINT fk_auth_user_org FOREIGN KEY (org_sn) REFERENCES auth.auth_org(org_sn); + +ALTER TABLE ONLY auth.auth_user_role + ADD CONSTRAINT fk_aur_user FOREIGN KEY (user_id) REFERENCES auth.auth_user(user_id) ON DELETE CASCADE; + +ALTER TABLE ONLY auth.auth_user_role + ADD CONSTRAINT fk_aur_role FOREIGN KEY (role_sn) REFERENCES auth.auth_role(role_sn) ON DELETE CASCADE; + +ALTER TABLE ONLY auth.auth_perm_tree + ADD CONSTRAINT fk_perm_tree_parent FOREIGN KEY (parent_cd) REFERENCES auth.auth_perm_tree(rsrc_cd); + +ALTER TABLE ONLY auth.auth_perm + ADD CONSTRAINT fk_ap_role FOREIGN KEY (role_sn) REFERENCES auth.auth_role(role_sn) ON DELETE CASCADE; + +ALTER TABLE ONLY auth.auth_login_hist + ADD CONSTRAINT fk_alh_user FOREIGN KEY (user_id) REFERENCES auth.auth_user(user_id) ON DELETE CASCADE; + + +-- ============================================================ +-- 인덱스 +-- ============================================================ +CREATE INDEX idx_auth_user_org ON auth.auth_user USING btree (org_sn); +CREATE INDEX idx_auth_user_stts ON auth.auth_user USING btree (user_stts_cd); + +CREATE INDEX idx_auth_perm_role ON auth.auth_perm USING btree (role_sn); +CREATE INDEX idx_auth_perm_rsrc ON auth.auth_perm USING btree (rsrc_cd); +CREATE INDEX idx_auth_perm_oper ON auth.auth_perm USING btree (oper_cd); + +CREATE INDEX idx_perm_tree_parent ON auth.auth_perm_tree USING btree (parent_cd); + +CREATE INDEX idx_auth_login_user ON auth.auth_login_hist USING btree (user_id); +CREATE INDEX idx_auth_login_dtm ON auth.auth_login_hist USING btree (login_dtm); + +CREATE INDEX idx_audit_log_user ON auth.auth_audit_log USING btree (user_id); +CREATE INDEX idx_audit_log_action ON auth.auth_audit_log USING btree (action_cd); +CREATE INDEX idx_audit_log_dtm ON auth.auth_audit_log USING btree (req_dtm); diff --git a/database/schema/02_common_tables.sql b/database/schema/02_common_tables.sql new file mode 100644 index 0000000..7d311ed --- /dev/null +++ b/database/schema/02_common_tables.sql @@ -0,0 +1,88 @@ +-- WING-OPS Database Schema: Common Tables +-- wing 스키마 공통 테이블 (공통코드, 레이어) + +-- ============================================================ +-- 1. cmn_cd_grp — 공통코드그룹 +-- (pg_dump에 없음, init.sql 기반으로 wing 스키마 변환) +-- ============================================================ +CREATE TABLE wing.cmn_cd_grp ( + cmn_cd_grp_id character varying(20) NOT NULL, + cmn_cd_grp_nm character varying(100) NOT NULL, + grp_dc character varying(500), + use_yn character(1) DEFAULT 'Y'::bpchar NOT NULL, + reg_dtm timestamp with time zone DEFAULT now() NOT NULL, + CONSTRAINT pk_cmn_cd_grp PRIMARY KEY (cmn_cd_grp_id), + CONSTRAINT ck_cmn_cd_grp_use_yn CHECK ((use_yn = ANY (ARRAY['Y'::bpchar, 'N'::bpchar]))) +); + +COMMENT ON TABLE wing.cmn_cd_grp IS '공통코드그룹'; +COMMENT ON COLUMN wing.cmn_cd_grp.cmn_cd_grp_id IS '공통코드그룹아이디'; +COMMENT ON COLUMN wing.cmn_cd_grp.cmn_cd_grp_nm IS '공통코드그룹명'; +COMMENT ON COLUMN wing.cmn_cd_grp.grp_dc IS '그룹설명'; +COMMENT ON COLUMN wing.cmn_cd_grp.use_yn IS '사용여부 (Y:사용, N:미사용)'; +COMMENT ON COLUMN wing.cmn_cd_grp.reg_dtm IS '등록일시'; + + +-- ============================================================ +-- 2. cmn_cd — 공통코드 +-- (pg_dump에 없음, init.sql 기반으로 wing 스키마 변환) +-- ============================================================ +CREATE TABLE wing.cmn_cd ( + cmn_cd_grp_id character varying(20) NOT NULL, + cmn_cd character varying(20) NOT NULL, + cmn_cd_nm character varying(100) NOT NULL, + cd_dc character varying(500), + sort_ord integer DEFAULT 0, + use_yn character(1) DEFAULT 'Y'::bpchar NOT NULL, + reg_dtm timestamp with time zone DEFAULT now() NOT NULL, + CONSTRAINT pk_cmn_cd PRIMARY KEY (cmn_cd_grp_id, cmn_cd), + CONSTRAINT ck_cmn_cd_use_yn CHECK ((use_yn = ANY (ARRAY['Y'::bpchar, 'N'::bpchar]))) +); + +COMMENT ON TABLE wing.cmn_cd IS '공통코드'; +COMMENT ON COLUMN wing.cmn_cd.cmn_cd_grp_id IS '공통코드그룹아이디'; +COMMENT ON COLUMN wing.cmn_cd.cmn_cd IS '공통코드'; +COMMENT ON COLUMN wing.cmn_cd.cmn_cd_nm IS '공통코드명'; +COMMENT ON COLUMN wing.cmn_cd.cd_dc IS '코드설명'; +COMMENT ON COLUMN wing.cmn_cd.sort_ord IS '정렬순서'; +COMMENT ON COLUMN wing.cmn_cd.use_yn IS '사용여부 (Y:사용, N:미사용)'; +COMMENT ON COLUMN wing.cmn_cd.reg_dtm IS '등록일시'; + + +-- ============================================================ +-- 3. layer — 레이어 (자기참조 FK) +-- ============================================================ +CREATE TABLE wing.layer ( + layer_cd character varying(50) NOT NULL, + up_layer_cd character varying(50), + layer_full_nm character varying(200) NOT NULL, + layer_nm character varying(100) NOT NULL, + layer_level integer NOT NULL, + wms_layer_nm character varying(100), + use_yn character(1) DEFAULT 'Y'::bpchar NOT NULL, + sort_ord integer DEFAULT 0, + reg_dtm timestamp with time zone DEFAULT now() NOT NULL, + CONSTRAINT pk_layer PRIMARY KEY (layer_cd), + CONSTRAINT ck_layer_use_yn CHECK ((use_yn = ANY (ARRAY['Y'::bpchar, 'N'::bpchar]))) +); + + +-- ============================================================ +-- FK 제약조건 +-- ============================================================ +ALTER TABLE ONLY wing.cmn_cd + ADD CONSTRAINT fk_cmn_cd_grp FOREIGN KEY (cmn_cd_grp_id) REFERENCES wing.cmn_cd_grp(cmn_cd_grp_id); + +ALTER TABLE ONLY wing.layer + ADD CONSTRAINT fk_layer_up FOREIGN KEY (up_layer_cd) REFERENCES wing.layer(layer_cd); + + +-- ============================================================ +-- 인덱스 +-- ============================================================ +CREATE INDEX idx_cmn_cd_grp_use ON wing.cmn_cd_grp USING btree (use_yn); +CREATE INDEX idx_cmn_cd_use ON wing.cmn_cd USING btree (use_yn); + +CREATE INDEX idx_layer_up ON wing.layer USING btree (up_layer_cd); +CREATE INDEX idx_layer_level ON wing.layer USING btree (layer_level); +CREATE INDEX idx_layer_use ON wing.layer USING btree (use_yn); diff --git a/database/schema/03_org_user_tables.sql b/database/schema/03_org_user_tables.sql new file mode 100644 index 0000000..03638f6 --- /dev/null +++ b/database/schema/03_org_user_tables.sql @@ -0,0 +1,83 @@ +-- WING-OPS Database Schema: Org & User Tables +-- wing 스키마 조직/사용자 테이블 +-- (pg_dump에 없음, init.sql 기반으로 wing 스키마 변환) + +-- ============================================================ +-- 1. org — 조직 (자기참조 FK) +-- ============================================================ +CREATE TABLE wing.org ( + org_sn integer NOT NULL, + org_nm character varying(100) NOT NULL, + org_abbr_nm character varying(20) NOT NULL, + org_tp_cd character varying(20) NOT NULL, + upper_org_sn integer, + reg_dtm timestamp with time zone DEFAULT now() NOT NULL, + CONSTRAINT pk_org PRIMARY KEY (org_sn) +); + +CREATE SEQUENCE wing.org_org_sn_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER SEQUENCE wing.org_org_sn_seq OWNED BY wing.org.org_sn; +ALTER TABLE ONLY wing.org ALTER COLUMN org_sn SET DEFAULT nextval('wing.org_org_sn_seq'::regclass); + +COMMENT ON TABLE wing.org IS '조직'; +COMMENT ON COLUMN wing.org.org_sn IS '조직순번'; +COMMENT ON COLUMN wing.org.org_nm IS '조직명 (기관 전체 명칭)'; +COMMENT ON COLUMN wing.org.org_abbr_nm IS '조직약칭명 (기관 약칭)'; +COMMENT ON COLUMN wing.org.org_tp_cd IS '조직유형코드 (ORG_TP: headquarters, regional, station, agency)'; +COMMENT ON COLUMN wing.org.upper_org_sn IS '상위조직순번 (상위 기관 참조)'; +COMMENT ON COLUMN wing.org.reg_dtm IS '등록일시'; + + +-- ============================================================ +-- 2. user_info — 사용자정보 (FK: wing.org) +-- (USER는 PostgreSQL 예약어이므로 user_info 사용) +-- ============================================================ +CREATE TABLE wing.user_info ( + user_id uuid DEFAULT public.uuid_generate_v4() NOT NULL, + user_acnt character varying(50) NOT NULL, + pswd_hash character varying(255) NOT NULL, + user_nm character varying(50) NOT NULL, + rnkp_nm character varying(30), + org_sn integer, + role_cd character varying(20) DEFAULT 'USER'::character varying NOT NULL, + actv_yn character(1) DEFAULT 'Y'::bpchar NOT NULL, + reg_dtm timestamp with time zone DEFAULT now() NOT NULL, + CONSTRAINT pk_user_info PRIMARY KEY (user_id), + CONSTRAINT uk_user_acnt UNIQUE (user_acnt), + CONSTRAINT ck_user_role_cd CHECK (((role_cd)::text = ANY (ARRAY[('ADMIN'::character varying)::text, ('MANAGER'::character varying)::text, ('USER'::character varying)::text]))), + CONSTRAINT ck_user_actv_yn CHECK ((actv_yn = ANY (ARRAY['Y'::bpchar, 'N'::bpchar]))) +); + +COMMENT ON TABLE wing.user_info IS '사용자정보'; +COMMENT ON COLUMN wing.user_info.user_id IS '사용자아이디 (UUID)'; +COMMENT ON COLUMN wing.user_info.user_acnt IS '사용자계정 (로그인 ID)'; +COMMENT ON COLUMN wing.user_info.pswd_hash IS '비밀번호해시 (bcrypt 해시값)'; +COMMENT ON COLUMN wing.user_info.user_nm IS '사용자명'; +COMMENT ON COLUMN wing.user_info.rnkp_nm IS '직급명'; +COMMENT ON COLUMN wing.user_info.org_sn IS '조직순번 (소속 조직)'; +COMMENT ON COLUMN wing.user_info.role_cd IS '역할코드 (ADMIN, MANAGER, USER)'; +COMMENT ON COLUMN wing.user_info.actv_yn IS '활성여부 (Y:활성, N:비활성)'; +COMMENT ON COLUMN wing.user_info.reg_dtm IS '등록일시'; + + +-- ============================================================ +-- FK 제약조건 +-- ============================================================ +ALTER TABLE ONLY wing.org + ADD CONSTRAINT fk_org_upper FOREIGN KEY (upper_org_sn) REFERENCES wing.org(org_sn); + +ALTER TABLE ONLY wing.user_info + ADD CONSTRAINT fk_user_org FOREIGN KEY (org_sn) REFERENCES wing.org(org_sn); + + +-- ============================================================ +-- 인덱스 +-- ============================================================ +CREATE INDEX idx_user_org_sn ON wing.user_info USING btree (org_sn); diff --git a/database/schema/04_accident_tables.sql b/database/schema/04_accident_tables.sql new file mode 100644 index 0000000..4346fbc --- /dev/null +++ b/database/schema/04_accident_tables.sql @@ -0,0 +1,202 @@ +-- WING-OPS Database Schema: Accident Tables +-- wing 스키마 사고 관련 테이블 + +-- ============================================================ +-- 1. acdnt — 사고 +-- ============================================================ +CREATE TABLE wing.acdnt ( + acdnt_sn integer NOT NULL, + acdnt_cd character varying(20) NOT NULL, + acdnt_nm character varying(200) NOT NULL, + acdnt_tp_cd character varying(50) NOT NULL, + acdnt_stts_cd character varying(20) DEFAULT 'ACTIVE'::character varying NOT NULL, + lat numeric(9,6), + lng numeric(10,6), + loc_dc character varying(200), + occrn_dtm timestamp with time zone NOT NULL, + rpt_dtm timestamp with time zone, + region_nm character varying(20), + office_nm character varying(30), + svrt_cd character varying(10), + vessel_tp character varying(30), + phase_cd character varying(20) DEFAULT 'RESPONSE'::character varying, + analyst_nm character varying(50), + rgtr_id uuid, + use_yn character(1) DEFAULT 'Y'::bpchar, + reg_dtm timestamp with time zone DEFAULT now() NOT NULL, + mdfcn_dtm timestamp with time zone, + loc_geom public.geometry(Point,4326), + CONSTRAINT pk_acdnt PRIMARY KEY (acdnt_sn), + CONSTRAINT uk_acdnt_cd UNIQUE (acdnt_cd), + CONSTRAINT ck_acdnt_stts CHECK (((acdnt_stts_cd)::text = ANY (ARRAY[('ACTIVE'::character varying)::text, ('INVESTIGATING'::character varying)::text, ('CLOSED'::character varying)::text]))), + CONSTRAINT ck_acdnt_svrt CHECK (((svrt_cd IS NULL) OR ((svrt_cd)::text = ANY (ARRAY[('DANGER'::character varying)::text, ('ALERT'::character varying)::text, ('CAUTION'::character varying)::text, ('INTEREST'::character varying)::text])))), + CONSTRAINT ck_acdnt_phase CHECK (((phase_cd IS NULL) OR ((phase_cd)::text = ANY (ARRAY[('RESPONSE'::character varying)::text, ('STANDBY'::character varying)::text, ('CLOSED'::character varying)::text])))) +); + +CREATE SEQUENCE wing.acdnt_acdnt_sn_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER SEQUENCE wing.acdnt_acdnt_sn_seq OWNED BY wing.acdnt.acdnt_sn; +ALTER TABLE ONLY wing.acdnt ALTER COLUMN acdnt_sn SET DEFAULT nextval('wing.acdnt_acdnt_sn_seq'::regclass); + + +-- ============================================================ +-- 2. spil_data — 유출정보 +-- ============================================================ +CREATE TABLE wing.spil_data ( + spil_data_sn integer NOT NULL, + acdnt_sn integer NOT NULL, + oil_tp_cd character varying(50) NOT NULL, + spil_qty numeric(12,2), + spil_unit_cd character varying(10) DEFAULT 'KL'::character varying, + spil_tp_cd character varying(20), + fcst_hr integer, + reg_dtm timestamp with time zone DEFAULT now() NOT NULL, + CONSTRAINT pk_spil_data PRIMARY KEY (spil_data_sn) +); + +CREATE SEQUENCE wing.spil_data_spil_data_sn_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER SEQUENCE wing.spil_data_spil_data_sn_seq OWNED BY wing.spil_data.spil_data_sn; +ALTER TABLE ONLY wing.spil_data ALTER COLUMN spil_data_sn SET DEFAULT nextval('wing.spil_data_spil_data_sn_seq'::regclass); + + +-- ============================================================ +-- 3. pred_exec — 예측실행 +-- ============================================================ +CREATE TABLE wing.pred_exec ( + pred_exec_sn integer NOT NULL, + acdnt_sn integer NOT NULL, + algo_cd character varying(20) NOT NULL, + exec_stts_cd character varying(20) DEFAULT 'PENDING'::character varying NOT NULL, + bgng_dtm timestamp with time zone, + cmpl_dtm timestamp with time zone, + reqd_sec integer, + rslt_data jsonb, + err_msg text, + CONSTRAINT pk_pred_exec PRIMARY KEY (pred_exec_sn), + CONSTRAINT ck_pred_stts CHECK (((exec_stts_cd)::text = ANY (ARRAY[('PENDING'::character varying)::text, ('RUNNING'::character varying)::text, ('COMPLETED'::character varying)::text, ('FAILED'::character varying)::text]))) +); + +CREATE SEQUENCE wing.pred_exec_pred_exec_sn_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER SEQUENCE wing.pred_exec_pred_exec_sn_seq OWNED BY wing.pred_exec.pred_exec_sn; +ALTER TABLE ONLY wing.pred_exec ALTER COLUMN pred_exec_sn SET DEFAULT nextval('wing.pred_exec_pred_exec_sn_seq'::regclass); + + +-- ============================================================ +-- 4. acdnt_weather — 사고기상정보 +-- ============================================================ +CREATE TABLE wing.acdnt_weather ( + weather_sn integer NOT NULL, + acdnt_sn integer NOT NULL, + loc_nm character varying(100), + obs_dtm timestamp with time zone, + icon character varying(10), + temp character varying(20), + weather_dc character varying(50), + wind character varying(30), + wave character varying(20), + humid character varying(20), + vis character varying(20), + sst character varying(20), + tide character varying(30), + high_tide character varying(30), + low_tide character varying(30), + forecast jsonb, + impact_dc text, + reg_dtm timestamp with time zone DEFAULT now(), + CONSTRAINT acdnt_weather_pkey PRIMARY KEY (weather_sn) +); + +CREATE SEQUENCE wing.acdnt_weather_weather_sn_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER SEQUENCE wing.acdnt_weather_weather_sn_seq OWNED BY wing.acdnt_weather.weather_sn; +ALTER TABLE ONLY wing.acdnt_weather ALTER COLUMN weather_sn SET DEFAULT nextval('wing.acdnt_weather_weather_sn_seq'::regclass); + + +-- ============================================================ +-- 5. acdnt_media — 사고미디어 +-- ============================================================ +CREATE TABLE wing.acdnt_media ( + media_sn integer NOT NULL, + acdnt_sn integer NOT NULL, + photo_cnt smallint DEFAULT 0, + video_cnt smallint DEFAULT 0, + sat_cnt smallint DEFAULT 0, + cctv_cnt smallint DEFAULT 0, + photo_meta jsonb, + drone_meta jsonb, + sat_meta jsonb, + cctv_meta jsonb, + reg_dtm timestamp with time zone DEFAULT now(), + CONSTRAINT acdnt_media_pkey PRIMARY KEY (media_sn) +); + +CREATE SEQUENCE wing.acdnt_media_media_sn_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER SEQUENCE wing.acdnt_media_media_sn_seq OWNED BY wing.acdnt_media.media_sn; +ALTER TABLE ONLY wing.acdnt_media ALTER COLUMN media_sn SET DEFAULT nextval('wing.acdnt_media_media_sn_seq'::regclass); + + +-- ============================================================ +-- FK 제약조건 +-- ============================================================ +ALTER TABLE ONLY wing.spil_data + ADD CONSTRAINT fk_spil_acdnt FOREIGN KEY (acdnt_sn) REFERENCES wing.acdnt(acdnt_sn) ON DELETE CASCADE; + +ALTER TABLE ONLY wing.pred_exec + ADD CONSTRAINT fk_pred_acdnt FOREIGN KEY (acdnt_sn) REFERENCES wing.acdnt(acdnt_sn) ON DELETE CASCADE; + +ALTER TABLE ONLY wing.acdnt_weather + ADD CONSTRAINT acdnt_weather_acdnt_sn_fkey FOREIGN KEY (acdnt_sn) REFERENCES wing.acdnt(acdnt_sn) ON DELETE CASCADE; + +ALTER TABLE ONLY wing.acdnt_media + ADD CONSTRAINT acdnt_media_acdnt_sn_fkey FOREIGN KEY (acdnt_sn) REFERENCES wing.acdnt(acdnt_sn) ON DELETE CASCADE; + + +-- ============================================================ +-- 인덱스 +-- ============================================================ +-- 공간 인덱스 +CREATE INDEX idx_acdnt_loc_geom ON wing.acdnt USING gist (loc_geom); + +-- 상태/분류 인덱스 +CREATE INDEX idx_acdnt_stts ON wing.acdnt USING btree (acdnt_stts_cd); +CREATE INDEX idx_acdnt_occrn ON wing.acdnt USING btree (occrn_dtm DESC); +CREATE INDEX idx_acdnt_region ON wing.acdnt USING btree (region_nm); + +-- 외래키 인덱스 +CREATE INDEX idx_spil_acdnt ON wing.spil_data USING btree (acdnt_sn); +CREATE INDEX idx_pred_acdnt ON wing.pred_exec USING btree (acdnt_sn); +CREATE INDEX idx_weather_acdnt ON wing.acdnt_weather USING btree (acdnt_sn); +CREATE INDEX idx_media_acdnt ON wing.acdnt_media USING btree (acdnt_sn); diff --git a/database/schema/05_hns_tables.sql b/database/schema/05_hns_tables.sql new file mode 100644 index 0000000..17fd949 --- /dev/null +++ b/database/schema/05_hns_tables.sql @@ -0,0 +1,106 @@ +-- WING-OPS Database Schema: HNS Tables +-- wing 스키마 HNS(위험유해물질) 분석 테이블 + +-- ============================================================ +-- 1. hns_substance — HNS물질정보 +-- ============================================================ +CREATE TABLE wing.hns_substance ( + sbst_sn integer NOT NULL, + abbreviation character varying(50), + nm_kr character varying(200) NOT NULL, + nm_en character varying(200), + un_no character varying(10), + cas_no character varying(20), + sebc character varying(50), + data jsonb NOT NULL, + use_yn character(1) DEFAULT 'Y'::bpchar NOT NULL, + reg_dtm timestamp with time zone DEFAULT now() NOT NULL, + CONSTRAINT pk_hns_substance PRIMARY KEY (sbst_sn), + CONSTRAINT ck_hns_sbst_use CHECK ((use_yn = ANY (ARRAY['Y'::bpchar, 'N'::bpchar]))) +); + +CREATE SEQUENCE wing.hns_substance_sbst_sn_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER SEQUENCE wing.hns_substance_sbst_sn_seq OWNED BY wing.hns_substance.sbst_sn; +ALTER TABLE ONLY wing.hns_substance ALTER COLUMN sbst_sn SET DEFAULT nextval('wing.hns_substance_sbst_sn_seq'::regclass); + + +-- ============================================================ +-- 2. hns_analysis — HNS분석 +-- ============================================================ +CREATE TABLE wing.hns_analysis ( + hns_anlys_sn integer NOT NULL, + acdnt_sn integer, + anlys_nm character varying(200) NOT NULL, + acdnt_dtm timestamp with time zone, + loc_nm character varying(200), + lon numeric(10,6), + lat numeric(9,6), + geom public.geometry(Point,4326), + loc_dc character varying(100), + sbst_sn integer, + sbst_nm character varying(100), + un_no character varying(10), + cas_no character varying(20), + spil_qty numeric(10,2), + spil_unit_cd character varying(10) DEFAULT 'KL'::character varying, + spil_tp_cd character varying(20), + fcst_hr integer, + algo_cd character varying(20), + crit_mdl_cd character varying(10), + wind_spd numeric(5,1), + wind_dir character varying(10), + temp numeric(4,1), + humid numeric(4,1), + atm_stbl_cd character varying(10), + exec_stts_cd character varying(20) DEFAULT 'COMPLETED'::character varying, + risk_cd character varying(20), + analyst_nm character varying(50), + rslt_data jsonb, + use_yn character(1) DEFAULT 'Y'::bpchar, + reg_dtm timestamp with time zone DEFAULT now(), + mdfcn_dtm timestamp with time zone DEFAULT now(), + CONSTRAINT hns_analysis_pkey PRIMARY KEY (hns_anlys_sn), + CONSTRAINT ck_hns_stts CHECK (((exec_stts_cd)::text = ANY (ARRAY[('PENDING'::character varying)::text, ('RUNNING'::character varying)::text, ('COMPLETED'::character varying)::text, ('FAILED'::character varying)::text]))) +); + +CREATE SEQUENCE wing.hns_analysis_hns_anlys_sn_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER SEQUENCE wing.hns_analysis_hns_anlys_sn_seq OWNED BY wing.hns_analysis.hns_anlys_sn; +ALTER TABLE ONLY wing.hns_analysis ALTER COLUMN hns_anlys_sn SET DEFAULT nextval('wing.hns_analysis_hns_anlys_sn_seq'::regclass); + + +-- ============================================================ +-- FK 제약조건 +-- ============================================================ +ALTER TABLE ONLY wing.hns_analysis + ADD CONSTRAINT hns_analysis_acdnt_sn_fkey FOREIGN KEY (acdnt_sn) REFERENCES wing.acdnt(acdnt_sn); + + +-- ============================================================ +-- 인덱스 +-- ============================================================ +-- hns_substance 검색 인덱스 (GIN trgm — 텍스트 검색) +CREATE INDEX idx_hns_sbst_nm_kr ON wing.hns_substance USING gin (nm_kr public.gin_trgm_ops); +CREATE INDEX idx_hns_sbst_nm_en ON wing.hns_substance USING gin (nm_en public.gin_trgm_ops); +CREATE INDEX idx_hns_sbst_abbr ON wing.hns_substance USING gin (abbreviation public.gin_trgm_ops); + +-- hns_substance 코드 인덱스 +CREATE INDEX idx_hns_sbst_un ON wing.hns_substance USING btree (un_no); +CREATE INDEX idx_hns_sbst_cas ON wing.hns_substance USING btree (cas_no); +CREATE INDEX idx_hns_sbst_sebc ON wing.hns_substance USING btree (sebc); + +-- hns_substance JSONB 인덱스 +CREATE INDEX idx_hns_sbst_data ON wing.hns_substance USING gin (data jsonb_path_ops); diff --git a/database/schema/06_scat_tables.sql b/database/schema/06_scat_tables.sql new file mode 100644 index 0000000..f981123 --- /dev/null +++ b/database/schema/06_scat_tables.sql @@ -0,0 +1,188 @@ +-- WING-OPS Database Schema: SCAT Tables +-- wing 스키마 해안조사/SCAT 테이블 + +-- ============================================================ +-- 1. cst_srvy_zone — 해안조사구역 +-- ============================================================ +CREATE TABLE wing.cst_srvy_zone ( + cst_srvy_zone_sn integer NOT NULL, + zone_cd character varying(10) NOT NULL, + zone_nm character varying(100) NOT NULL, + jrsd_nm character varying(20), + sect_cnt integer DEFAULT 0, + lat_center numeric(9,6), + lng_center numeric(9,6), + lat_range numeric(9,6), + lng_range numeric(9,6), + geom public.geometry(Point,4326), + use_yn character(1) DEFAULT 'Y'::bpchar, + reg_dtm timestamp with time zone DEFAULT now(), + CONSTRAINT cst_srvy_zone_pkey PRIMARY KEY (cst_srvy_zone_sn), + CONSTRAINT cst_srvy_zone_zone_cd_key UNIQUE (zone_cd) +); + +CREATE SEQUENCE wing.cst_srvy_zone_cst_srvy_zone_sn_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER SEQUENCE wing.cst_srvy_zone_cst_srvy_zone_sn_seq OWNED BY wing.cst_srvy_zone.cst_srvy_zone_sn; +ALTER TABLE ONLY wing.cst_srvy_zone ALTER COLUMN cst_srvy_zone_sn SET DEFAULT nextval('wing.cst_srvy_zone_cst_srvy_zone_sn_seq'::regclass); + + +-- ============================================================ +-- 2. cst_sect — 해안구간 +-- ============================================================ +CREATE TABLE wing.cst_sect ( + cst_sect_sn integer NOT NULL, + cst_srvy_zone_sn integer, + sect_cd character varying(20) NOT NULL, + sect_nm character varying(200), + cst_tp_cd character varying(30), + esi_cd character varying(5), + esi_num smallint, + len_m numeric(8,1), + snstvt_cd character varying(10), + srvy_stts_cd character varying(10) DEFAULT '미조사'::character varying, + lat numeric(9,6), + lng numeric(9,6), + geom public.geometry(Point,4326), + tags jsonb DEFAULT '[]'::jsonb, + shore_tp character varying(20), + access_dc character varying(500), + access_pt character varying(200), + sensitive_info jsonb DEFAULT '[]'::jsonb, + cleanup_methods jsonb DEFAULT '[]'::jsonb, + end_criteria jsonb DEFAULT '[]'::jsonb, + notes jsonb DEFAULT '[]'::jsonb, + use_yn character(1) DEFAULT 'Y'::bpchar, + reg_dtm timestamp with time zone DEFAULT now(), + CONSTRAINT cst_sect_pkey PRIMARY KEY (cst_sect_sn), + CONSTRAINT cst_sect_sect_cd_key UNIQUE (sect_cd) +); + +CREATE SEQUENCE wing.cst_sect_cst_sect_sn_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER SEQUENCE wing.cst_sect_cst_sect_sn_seq OWNED BY wing.cst_sect.cst_sect_sn; +ALTER TABLE ONLY wing.cst_sect ALTER COLUMN cst_sect_sn SET DEFAULT nextval('wing.cst_sect_cst_sect_sn_seq'::regclass); + + +-- ============================================================ +-- 3. scat_srvy — SCAT조사 +-- (pg_dump에 없음, init.sql 기반으로 wing 스키마 변환) +-- ============================================================ +CREATE TABLE wing.scat_srvy ( + scat_srvy_sn integer NOT NULL, + cst_sect_sn integer NOT NULL, + acdnt_sn integer, + srvy_ymd character(8), + srvy_tp_cd character varying(20), + srvy_phase_cd character varying(20), + polut_cd character varying(20), + srvyr_id uuid, + rmk character varying(500), + reg_dtm timestamp with time zone DEFAULT now() NOT NULL, + CONSTRAINT pk_scat_srvy PRIMARY KEY (scat_srvy_sn) +); + +CREATE SEQUENCE wing.scat_srvy_scat_srvy_sn_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER SEQUENCE wing.scat_srvy_scat_srvy_sn_seq OWNED BY wing.scat_srvy.scat_srvy_sn; +ALTER TABLE ONLY wing.scat_srvy ALTER COLUMN scat_srvy_sn SET DEFAULT nextval('wing.scat_srvy_scat_srvy_sn_seq'::regclass); + +COMMENT ON TABLE wing.scat_srvy IS 'SCAT조사'; +COMMENT ON COLUMN wing.scat_srvy.scat_srvy_sn IS 'SCAT조사순번'; +COMMENT ON COLUMN wing.scat_srvy.cst_sect_sn IS '해안구간순번 (해안구간 참조)'; +COMMENT ON COLUMN wing.scat_srvy.acdnt_sn IS '사고순번 (관련 사고 참조)'; +COMMENT ON COLUMN wing.scat_srvy.srvy_ymd IS '조사일자 (YYYYMMDD 저장형식)'; +COMMENT ON COLUMN wing.scat_srvy.srvy_tp_cd IS '조사유형코드 (SRVY_TP: Pre-SCAT, SCAT, Post-SCAT)'; +COMMENT ON COLUMN wing.scat_srvy.srvy_phase_cd IS '조사단계코드 (SRVY_PHASE: 1차, 2차, 3차 등)'; +COMMENT ON COLUMN wing.scat_srvy.polut_cd IS '오염도코드 (POLUT: HEAVY, MODERATE, LIGHT, CLEAN)'; +COMMENT ON COLUMN wing.scat_srvy.srvyr_id IS '조사자아이디'; +COMMENT ON COLUMN wing.scat_srvy.rmk IS '비고'; +COMMENT ON COLUMN wing.scat_srvy.reg_dtm IS '등록일시'; + + +-- ============================================================ +-- 4. scat_srvy_photo — SCAT조사사진 +-- (pg_dump에 없음, init.sql 기반으로 wing 스키마 변환) +-- ============================================================ +CREATE TABLE wing.scat_srvy_photo ( + scat_photo_sn integer NOT NULL, + scat_srvy_sn integer NOT NULL, + photo_file_path character varying(500) NOT NULL, + photo_dc character varying(200), + takng_dtm timestamp with time zone, + sort_ord integer DEFAULT 0, + reg_dtm timestamp with time zone DEFAULT now() NOT NULL, + CONSTRAINT pk_scat_photo PRIMARY KEY (scat_photo_sn) +); + +CREATE SEQUENCE wing.scat_srvy_photo_scat_photo_sn_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER SEQUENCE wing.scat_srvy_photo_scat_photo_sn_seq OWNED BY wing.scat_srvy_photo.scat_photo_sn; +ALTER TABLE ONLY wing.scat_srvy_photo ALTER COLUMN scat_photo_sn SET DEFAULT nextval('wing.scat_srvy_photo_scat_photo_sn_seq'::regclass); + +COMMENT ON TABLE wing.scat_srvy_photo IS 'SCAT조사사진'; +COMMENT ON COLUMN wing.scat_srvy_photo.scat_photo_sn IS 'SCAT조사사진순번'; +COMMENT ON COLUMN wing.scat_srvy_photo.scat_srvy_sn IS 'SCAT조사순번 (SCAT조사 참조)'; +COMMENT ON COLUMN wing.scat_srvy_photo.photo_file_path IS '사진파일경로'; +COMMENT ON COLUMN wing.scat_srvy_photo.photo_dc IS '사진설명'; +COMMENT ON COLUMN wing.scat_srvy_photo.takng_dtm IS '촬영일시'; +COMMENT ON COLUMN wing.scat_srvy_photo.sort_ord IS '정렬순서'; +COMMENT ON COLUMN wing.scat_srvy_photo.reg_dtm IS '등록일시'; + + +-- ============================================================ +-- FK 제약조건 +-- ============================================================ +ALTER TABLE ONLY wing.cst_sect + ADD CONSTRAINT cst_sect_cst_srvy_zone_sn_fkey FOREIGN KEY (cst_srvy_zone_sn) REFERENCES wing.cst_srvy_zone(cst_srvy_zone_sn); + +ALTER TABLE ONLY wing.scat_srvy + ADD CONSTRAINT fk_scat_sect FOREIGN KEY (cst_sect_sn) REFERENCES wing.cst_sect(cst_sect_sn); + +ALTER TABLE ONLY wing.scat_srvy + ADD CONSTRAINT fk_scat_acdnt FOREIGN KEY (acdnt_sn) REFERENCES wing.acdnt(acdnt_sn); + +ALTER TABLE ONLY wing.scat_srvy_photo + ADD CONSTRAINT fk_photo_srvy FOREIGN KEY (scat_srvy_sn) REFERENCES wing.scat_srvy(scat_srvy_sn) ON DELETE CASCADE; + + +-- ============================================================ +-- 인덱스 +-- ============================================================ +-- 공간 인덱스 +CREATE INDEX idx_cst_srvy_zone_geom ON wing.cst_srvy_zone USING gist (geom); +CREATE INDEX idx_cst_sect_geom ON wing.cst_sect USING gist (geom); + +-- 외래키/상태 인덱스 +CREATE INDEX idx_cst_sect_zone ON wing.cst_sect USING btree (cst_srvy_zone_sn); +CREATE INDEX idx_cst_sect_stts ON wing.cst_sect USING btree (srvy_stts_cd); +CREATE INDEX idx_cst_sect_esi ON wing.cst_sect USING btree (esi_num); + +-- SCAT 조사 인덱스 +CREATE INDEX idx_scat_sect_sn ON wing.scat_srvy USING btree (cst_sect_sn); +CREATE INDEX idx_scat_acdnt_sn ON wing.scat_srvy USING btree (acdnt_sn); +CREATE INDEX idx_photo_srvy_sn ON wing.scat_srvy_photo USING btree (scat_srvy_sn); diff --git a/database/schema/07_boom_backtrack_tables.sql b/database/schema/07_boom_backtrack_tables.sql new file mode 100644 index 0000000..2ec8bc9 --- /dev/null +++ b/database/schema/07_boom_backtrack_tables.sql @@ -0,0 +1,128 @@ +-- WING-OPS Database Schema: Boom & Backtrack Tables +-- wing 스키마 오일펜스/역추적/선박 테이블 + +-- ============================================================ +-- 1. boom_line — 오일펜스배치 +-- ============================================================ +CREATE TABLE wing.boom_line ( + boom_line_sn integer NOT NULL, + acdnt_sn integer NOT NULL, + boom_nm character varying(100), + priority_ord integer DEFAULT 0, + geom public.geometry(LineString,4326), + length_m numeric(8,1), + efficiency_pct numeric(5,1), + deploy_dtm timestamp with time zone, + stts_cd character varying(20) DEFAULT 'PLANNED'::character varying, + use_yn character(1) DEFAULT 'Y'::bpchar, + reg_dtm timestamp with time zone DEFAULT now(), + CONSTRAINT boom_line_pkey PRIMARY KEY (boom_line_sn), + CONSTRAINT ck_boom_stts CHECK (((stts_cd)::text = ANY (ARRAY[('PLANNED'::character varying)::text, ('DEPLOYED'::character varying)::text, ('RETRIEVED'::character varying)::text]))) +); + +CREATE SEQUENCE wing.boom_line_boom_line_sn_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER SEQUENCE wing.boom_line_boom_line_sn_seq OWNED BY wing.boom_line.boom_line_sn; +ALTER TABLE ONLY wing.boom_line ALTER COLUMN boom_line_sn SET DEFAULT nextval('wing.boom_line_boom_line_sn_seq'::regclass); + + +-- ============================================================ +-- 2. backtrack — 역추적분석 +-- ============================================================ +CREATE TABLE wing.backtrack ( + backtrack_sn integer NOT NULL, + acdnt_sn integer NOT NULL, + est_spil_dtm timestamp with time zone, + anlys_range character varying(20), + lon numeric(10,6), + lat numeric(9,6), + geom public.geometry(Point,4326), + loc_dc character varying(100), + srch_radius_nm numeric(5,1), + total_vessels integer, + exec_stts_cd character varying(20) DEFAULT 'PENDING'::character varying, + rslt_data jsonb, + use_yn character(1) DEFAULT 'Y'::bpchar, + reg_dtm timestamp with time zone DEFAULT now(), + CONSTRAINT backtrack_pkey PRIMARY KEY (backtrack_sn), + CONSTRAINT ck_backtrack_stts CHECK (((exec_stts_cd)::text = ANY (ARRAY[('PENDING'::character varying)::text, ('RUNNING'::character varying)::text, ('COMPLETED'::character varying)::text, ('FAILED'::character varying)::text]))) +); + +CREATE SEQUENCE wing.backtrack_backtrack_sn_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER SEQUENCE wing.backtrack_backtrack_sn_seq OWNED BY wing.backtrack.backtrack_sn; +ALTER TABLE ONLY wing.backtrack ALTER COLUMN backtrack_sn SET DEFAULT nextval('wing.backtrack_backtrack_sn_seq'::regclass); + + +-- ============================================================ +-- 3. vessel_info — 선박정보 +-- ============================================================ +CREATE TABLE wing.vessel_info ( + vessel_info_sn integer NOT NULL, + acdnt_sn integer NOT NULL, + imo_no character varying(10), + mmsi_no character varying(10), + vessel_nm character varying(100), + vessel_tp character varying(30), + loa_m numeric(6,1), + breadth_m numeric(5,1), + draft_m numeric(5,1), + gt numeric(10,0), + dwt numeric(10,0), + built_yr smallint, + flag_cd character varying(5), + callsign character varying(10), + engine_dc character varying(100), + insurance_data jsonb, + reg_dtm timestamp with time zone DEFAULT now(), + CONSTRAINT vessel_info_pkey PRIMARY KEY (vessel_info_sn) +); + +CREATE SEQUENCE wing.vessel_info_vessel_info_sn_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER SEQUENCE wing.vessel_info_vessel_info_sn_seq OWNED BY wing.vessel_info.vessel_info_sn; +ALTER TABLE ONLY wing.vessel_info ALTER COLUMN vessel_info_sn SET DEFAULT nextval('wing.vessel_info_vessel_info_sn_seq'::regclass); + + +-- ============================================================ +-- FK 제약조건 +-- ============================================================ +ALTER TABLE ONLY wing.boom_line + ADD CONSTRAINT boom_line_acdnt_sn_fkey FOREIGN KEY (acdnt_sn) REFERENCES wing.acdnt(acdnt_sn) ON DELETE CASCADE; + +ALTER TABLE ONLY wing.backtrack + ADD CONSTRAINT backtrack_acdnt_sn_fkey FOREIGN KEY (acdnt_sn) REFERENCES wing.acdnt(acdnt_sn) ON DELETE CASCADE; + +ALTER TABLE ONLY wing.vessel_info + ADD CONSTRAINT vessel_info_acdnt_sn_fkey FOREIGN KEY (acdnt_sn) REFERENCES wing.acdnt(acdnt_sn) ON DELETE CASCADE; + + +-- ============================================================ +-- 인덱스 +-- ============================================================ +-- 공간 인덱스 +CREATE INDEX idx_boom_geom ON wing.boom_line USING gist (geom); +CREATE INDEX idx_backtrack_geom ON wing.backtrack USING gist (geom); + +-- 외래키 인덱스 +CREATE INDEX idx_boom_acdnt ON wing.boom_line USING btree (acdnt_sn); +CREATE INDEX idx_backtrack_acdnt ON wing.backtrack USING btree (acdnt_sn); +CREATE INDEX idx_vessel_acdnt ON wing.vessel_info USING btree (acdnt_sn); diff --git a/database/schema/08_asset_tables.sql b/database/schema/08_asset_tables.sql new file mode 100644 index 0000000..2427b49 --- /dev/null +++ b/database/schema/08_asset_tables.sql @@ -0,0 +1,130 @@ +-- WING-OPS Database Schema: Asset Management Tables +-- wing 스키마: asset_org, asset_equip, asset_contact, asset_upload_log + +-- ============================================================ +-- 1. 자산 기관 (asset_org) +-- ============================================================ +CREATE TABLE wing.asset_org ( + org_sn integer NOT NULL, + org_tp character varying(30) NOT NULL, + jrsd_nm character varying(50) NOT NULL, + area_nm character varying(30) NOT NULL, + org_nm character varying(100) NOT NULL, + addr character varying(300), + tel character varying(30), + lat numeric(9,6), + lng numeric(10,6), + pin_size character varying(5) DEFAULT 'md'::character varying, + vessel_cnt smallint DEFAULT 0, + skimmer_cnt smallint DEFAULT 0, + pump_cnt smallint DEFAULT 0, + vehicle_cnt smallint DEFAULT 0, + sprayer_cnt smallint DEFAULT 0, + total_assets smallint DEFAULT 0, + use_yn character(1) DEFAULT 'Y'::bpchar, + reg_dtm timestamp with time zone DEFAULT now(), + mdfcn_dtm timestamp with time zone, + geom public.geometry(Point, 4326), + CONSTRAINT asset_org_pkey PRIMARY KEY (org_sn), + CONSTRAINT ck_asset_org_pin CHECK ( + ((pin_size)::text = ANY ((ARRAY['hq'::character varying, 'lg'::character varying, 'md'::character varying])::text[])) + ) +); + +CREATE SEQUENCE wing.asset_org_org_sn_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER SEQUENCE wing.asset_org_org_sn_seq OWNED BY wing.asset_org.org_sn; +ALTER TABLE ONLY wing.asset_org ALTER COLUMN org_sn SET DEFAULT nextval('wing.asset_org_org_sn_seq'::regclass); + + +-- ============================================================ +-- 2. 자산 장비 (asset_equip) +-- ============================================================ +CREATE TABLE wing.asset_equip ( + equip_sn integer NOT NULL, + org_sn integer NOT NULL, + ctgr_nm character varying(50) NOT NULL, + icon character varying(10), + qty smallint DEFAULT 0, + sort_ord smallint DEFAULT 0, + CONSTRAINT asset_equip_pkey PRIMARY KEY (equip_sn), + CONSTRAINT asset_equip_org_sn_ctgr_nm_key UNIQUE (org_sn, ctgr_nm) +); + +CREATE SEQUENCE wing.asset_equip_equip_sn_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER SEQUENCE wing.asset_equip_equip_sn_seq OWNED BY wing.asset_equip.equip_sn; +ALTER TABLE ONLY wing.asset_equip ALTER COLUMN equip_sn SET DEFAULT nextval('wing.asset_equip_equip_sn_seq'::regclass); + + +-- ============================================================ +-- 3. 자산 연락처 (asset_contact) +-- ============================================================ +CREATE TABLE wing.asset_contact ( + contact_sn integer NOT NULL, + org_sn integer NOT NULL, + role_nm character varying(50), + contact_nm character varying(50), + tel character varying(30), + sort_ord smallint DEFAULT 0, + CONSTRAINT asset_contact_pkey PRIMARY KEY (contact_sn) +); + +CREATE SEQUENCE wing.asset_contact_contact_sn_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER SEQUENCE wing.asset_contact_contact_sn_seq OWNED BY wing.asset_contact.contact_sn; +ALTER TABLE ONLY wing.asset_contact ALTER COLUMN contact_sn SET DEFAULT nextval('wing.asset_contact_contact_sn_seq'::regclass); + + +-- ============================================================ +-- 4. 자산 업로드 로그 (asset_upload_log) +-- ============================================================ +CREATE TABLE wing.asset_upload_log ( + log_sn integer NOT NULL, + file_nm character varying(200) NOT NULL, + uploader_nm character varying(50), + upload_cnt integer DEFAULT 0, + reg_dtm timestamp with time zone DEFAULT now(), + CONSTRAINT asset_upload_log_pkey PRIMARY KEY (log_sn) +); + +CREATE SEQUENCE wing.asset_upload_log_log_sn_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER SEQUENCE wing.asset_upload_log_log_sn_seq OWNED BY wing.asset_upload_log.log_sn; +ALTER TABLE ONLY wing.asset_upload_log ALTER COLUMN log_sn SET DEFAULT nextval('wing.asset_upload_log_log_sn_seq'::regclass); + + +-- ============================================================ +-- FK 제약조건 +-- ============================================================ +ALTER TABLE ONLY wing.asset_equip + ADD CONSTRAINT asset_equip_org_sn_fkey + FOREIGN KEY (org_sn) REFERENCES wing.asset_org(org_sn) ON DELETE CASCADE; + +ALTER TABLE ONLY wing.asset_contact + ADD CONSTRAINT asset_contact_org_sn_fkey + FOREIGN KEY (org_sn) REFERENCES wing.asset_org(org_sn) ON DELETE CASCADE; diff --git a/database/schema/09_board_manual_tables.sql b/database/schema/09_board_manual_tables.sql new file mode 100644 index 0000000..2fe9f17 --- /dev/null +++ b/database/schema/09_board_manual_tables.sql @@ -0,0 +1,109 @@ +-- WING-OPS Database Schema: Board & Manual Tables +-- wing 스키마: board_post, board_attach, manual_file +-- 주의: board_post.author_id 는 auth.auth_user(user_id) 크로스스키마 FK + +-- ============================================================ +-- 1. 게시글 (board_post) +-- ============================================================ +CREATE TABLE wing.board_post ( + post_sn integer NOT NULL, + category_cd character varying(20) NOT NULL, + title character varying(200) NOT NULL, + content text, + author_id uuid NOT NULL, + view_cnt integer DEFAULT 0 NOT NULL, + pinned_yn character(1) DEFAULT 'N'::bpchar NOT NULL, + use_yn character(1) DEFAULT 'Y'::bpchar NOT NULL, + reg_dtm timestamp with time zone DEFAULT now() NOT NULL, + mdfcn_dtm timestamp with time zone, + CONSTRAINT board_post_pkey PRIMARY KEY (post_sn), + CONSTRAINT ck_board_category CHECK ( + ((category_cd)::text = ANY ((ARRAY['NOTICE'::character varying, 'DATA'::character varying, 'QNA'::character varying, 'MANUAL'::character varying])::text[])) + ), + CONSTRAINT ck_board_pinned CHECK ((pinned_yn = ANY (ARRAY['Y'::bpchar, 'N'::bpchar]))), + CONSTRAINT ck_board_use CHECK ((use_yn = ANY (ARRAY['Y'::bpchar, 'N'::bpchar]))) +); + +CREATE SEQUENCE wing.board_post_post_sn_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER SEQUENCE wing.board_post_post_sn_seq OWNED BY wing.board_post.post_sn; +ALTER TABLE ONLY wing.board_post ALTER COLUMN post_sn SET DEFAULT nextval('wing.board_post_post_sn_seq'::regclass); + + +-- ============================================================ +-- 2. 첨부파일 (board_attach) +-- ============================================================ +CREATE TABLE wing.board_attach ( + attach_sn integer NOT NULL, + post_sn integer NOT NULL, + file_nm character varying(200) NOT NULL, + orgnl_nm character varying(200), + file_path character varying(500), + file_sz integer, + file_ext character varying(10), + sort_ord integer DEFAULT 0, + reg_dtm timestamp with time zone DEFAULT now(), + CONSTRAINT board_attach_pkey PRIMARY KEY (attach_sn) +); + +CREATE SEQUENCE wing.board_attach_attach_sn_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER SEQUENCE wing.board_attach_attach_sn_seq OWNED BY wing.board_attach.attach_sn; +ALTER TABLE ONLY wing.board_attach ALTER COLUMN attach_sn SET DEFAULT nextval('wing.board_attach_attach_sn_seq'::regclass); + + +-- ============================================================ +-- 3. 매뉴얼 파일 (manual_file) +-- ============================================================ +CREATE TABLE wing.manual_file ( + manual_sn integer NOT NULL, + catg_nm character varying(50), + title character varying(200) NOT NULL, + version character varying(20), + file_tp character varying(20), + file_sz character varying(20), + file_path character varying(500), + author_nm character varying(50), + dwnld_cnt integer DEFAULT 0, + use_yn character(1) DEFAULT 'Y'::bpchar, + reg_dtm timestamp with time zone DEFAULT now(), + mdfcn_dtm timestamp with time zone DEFAULT now(), + CONSTRAINT manual_file_pkey PRIMARY KEY (manual_sn) +); + +CREATE SEQUENCE wing.manual_file_manual_sn_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER SEQUENCE wing.manual_file_manual_sn_seq OWNED BY wing.manual_file.manual_sn; +ALTER TABLE ONLY wing.manual_file ALTER COLUMN manual_sn SET DEFAULT nextval('wing.manual_file_manual_sn_seq'::regclass); + + +-- ============================================================ +-- FK 제약조건 +-- ============================================================ +-- 크로스스키마 FK: board_post.author_id → auth.auth_user(user_id) +ALTER TABLE ONLY wing.board_post + ADD CONSTRAINT fk_board_author + FOREIGN KEY (author_id) REFERENCES auth.auth_user(user_id); + +-- 동일스키마 FK: board_attach.post_sn → board_post +ALTER TABLE ONLY wing.board_attach + ADD CONSTRAINT board_attach_post_sn_fkey + FOREIGN KEY (post_sn) REFERENCES wing.board_post(post_sn) ON DELETE CASCADE; diff --git a/database/schema/10_report_tables.sql b/database/schema/10_report_tables.sql new file mode 100644 index 0000000..11830a5 --- /dev/null +++ b/database/schema/10_report_tables.sql @@ -0,0 +1,235 @@ +-- WING-OPS Database Schema: Report Tables +-- wing 스키마: report_tmpl, report_tmpl_sect, report_analysis_ctgr, +-- report_ctgr_tmpl, report_ctgr_sect, report, report_sect_data +-- FK 의존성 순서: report_tmpl, report_analysis_ctgr → report → report_sect_data + +-- ============================================================ +-- 1. 보고서 템플릿 (report_tmpl) +-- ============================================================ +CREATE TABLE wing.report_tmpl ( + tmpl_sn integer NOT NULL, + tmpl_cd character varying(30) NOT NULL, + tmpl_nm character varying(100) NOT NULL, + icon character varying(10), + tmpl_dc character varying(500), + sort_ord smallint DEFAULT 0, + use_yn character(1) DEFAULT 'Y'::bpchar, + reg_dtm timestamp with time zone DEFAULT now(), + CONSTRAINT report_tmpl_pkey PRIMARY KEY (tmpl_sn), + CONSTRAINT report_tmpl_tmpl_cd_key UNIQUE (tmpl_cd) +); + +CREATE SEQUENCE wing.report_tmpl_tmpl_sn_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER SEQUENCE wing.report_tmpl_tmpl_sn_seq OWNED BY wing.report_tmpl.tmpl_sn; +ALTER TABLE ONLY wing.report_tmpl ALTER COLUMN tmpl_sn SET DEFAULT nextval('wing.report_tmpl_tmpl_sn_seq'::regclass); + + +-- ============================================================ +-- 2. 템플릿 섹션 (report_tmpl_sect) +-- ============================================================ +CREATE TABLE wing.report_tmpl_sect ( + sect_sn integer NOT NULL, + tmpl_sn integer NOT NULL, + sect_cd character varying(50) NOT NULL, + sect_nm character varying(100) NOT NULL, + field_def jsonb NOT NULL, + sort_ord smallint DEFAULT 0, + use_yn character(1) DEFAULT 'Y'::bpchar, + CONSTRAINT report_tmpl_sect_pkey PRIMARY KEY (sect_sn), + CONSTRAINT report_tmpl_sect_tmpl_sn_sect_cd_key UNIQUE (tmpl_sn, sect_cd) +); + +CREATE SEQUENCE wing.report_tmpl_sect_sect_sn_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER SEQUENCE wing.report_tmpl_sect_sect_sn_seq OWNED BY wing.report_tmpl_sect.sect_sn; +ALTER TABLE ONLY wing.report_tmpl_sect ALTER COLUMN sect_sn SET DEFAULT nextval('wing.report_tmpl_sect_sect_sn_seq'::regclass); + + +-- ============================================================ +-- 3. 분석 카테고리 (report_analysis_ctgr) +-- ============================================================ +CREATE TABLE wing.report_analysis_ctgr ( + ctgr_sn integer NOT NULL, + ctgr_cd character varying(30) NOT NULL, + ctgr_nm character varying(100) NOT NULL, + icon character varying(10), + ctgr_dc character varying(500), + color_cd character varying(30), + border_color character varying(50), + bg_active character varying(50), + report_nm character varying(100), + sort_ord smallint DEFAULT 0, + use_yn character(1) DEFAULT 'Y'::bpchar, + CONSTRAINT report_analysis_ctgr_pkey PRIMARY KEY (ctgr_sn), + CONSTRAINT report_analysis_ctgr_ctgr_cd_key UNIQUE (ctgr_cd) +); + +CREATE SEQUENCE wing.report_analysis_ctgr_ctgr_sn_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER SEQUENCE wing.report_analysis_ctgr_ctgr_sn_seq OWNED BY wing.report_analysis_ctgr.ctgr_sn; +ALTER TABLE ONLY wing.report_analysis_ctgr ALTER COLUMN ctgr_sn SET DEFAULT nextval('wing.report_analysis_ctgr_ctgr_sn_seq'::regclass); + + +-- ============================================================ +-- 4. 카테고리 템플릿 (report_ctgr_tmpl) +-- ============================================================ +CREATE TABLE wing.report_ctgr_tmpl ( + ctgr_tmpl_sn integer NOT NULL, + ctgr_sn integer NOT NULL, + icon character varying(10), + label character varying(50) NOT NULL, + sort_ord smallint DEFAULT 0, + CONSTRAINT report_ctgr_tmpl_pkey PRIMARY KEY (ctgr_tmpl_sn) +); + +CREATE SEQUENCE wing.report_ctgr_tmpl_ctgr_tmpl_sn_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER SEQUENCE wing.report_ctgr_tmpl_ctgr_tmpl_sn_seq OWNED BY wing.report_ctgr_tmpl.ctgr_tmpl_sn; +ALTER TABLE ONLY wing.report_ctgr_tmpl ALTER COLUMN ctgr_tmpl_sn SET DEFAULT nextval('wing.report_ctgr_tmpl_ctgr_tmpl_sn_seq'::regclass); + + +-- ============================================================ +-- 5. 카테고리 섹션 (report_ctgr_sect) +-- ============================================================ +CREATE TABLE wing.report_ctgr_sect ( + sect_sn integer NOT NULL, + ctgr_sn integer NOT NULL, + sect_cd character varying(50) NOT NULL, + sect_nm character varying(100) NOT NULL, + icon character varying(10), + sect_dc character varying(200), + dflt_yn character(1) DEFAULT 'Y'::bpchar, + sort_ord smallint DEFAULT 0, + CONSTRAINT report_ctgr_sect_pkey PRIMARY KEY (sect_sn), + CONSTRAINT report_ctgr_sect_ctgr_sn_sect_cd_key UNIQUE (ctgr_sn, sect_cd) +); + +CREATE SEQUENCE wing.report_ctgr_sect_sect_sn_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER SEQUENCE wing.report_ctgr_sect_sect_sn_seq OWNED BY wing.report_ctgr_sect.sect_sn; +ALTER TABLE ONLY wing.report_ctgr_sect ALTER COLUMN sect_sn SET DEFAULT nextval('wing.report_ctgr_sect_sect_sn_seq'::regclass); + + +-- ============================================================ +-- 6. 보고서 (report) +-- 의존: report_tmpl, report_analysis_ctgr, auth.auth_user, wing.acdnt +-- ============================================================ +CREATE TABLE wing.report ( + report_sn integer NOT NULL, + tmpl_sn integer, + ctgr_sn integer, + acdnt_sn integer, + title character varying(200) NOT NULL, + jrsd_cd character varying(20), + stts_cd character varying(20) DEFAULT 'DRAFT'::character varying, + author_id uuid NOT NULL, + use_yn character(1) DEFAULT 'Y'::bpchar, + reg_dtm timestamp with time zone DEFAULT now(), + mdfcn_dtm timestamp with time zone, + CONSTRAINT report_pkey PRIMARY KEY (report_sn), + CONSTRAINT ck_report_status CHECK ( + ((stts_cd)::text = ANY ((ARRAY['DRAFT'::character varying, 'IN_PROGRESS'::character varying, 'COMPLETED'::character varying])::text[])) + ) +); + +CREATE SEQUENCE wing.report_report_sn_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER SEQUENCE wing.report_report_sn_seq OWNED BY wing.report.report_sn; +ALTER TABLE ONLY wing.report ALTER COLUMN report_sn SET DEFAULT nextval('wing.report_report_sn_seq'::regclass); + + +-- ============================================================ +-- 7. 보고서 섹션 데이터 (report_sect_data) +-- 의존: report +-- ============================================================ +CREATE TABLE wing.report_sect_data ( + data_sn integer NOT NULL, + report_sn integer NOT NULL, + sect_cd character varying(50) NOT NULL, + include_yn character(1) DEFAULT 'Y'::bpchar, + sect_data jsonb DEFAULT '{}'::jsonb NOT NULL, + sort_ord smallint DEFAULT 0, + CONSTRAINT report_sect_data_pkey PRIMARY KEY (data_sn), + CONSTRAINT report_sect_data_report_sn_sect_cd_key UNIQUE (report_sn, sect_cd) +); + +CREATE SEQUENCE wing.report_sect_data_data_sn_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER SEQUENCE wing.report_sect_data_data_sn_seq OWNED BY wing.report_sect_data.data_sn; +ALTER TABLE ONLY wing.report_sect_data ALTER COLUMN data_sn SET DEFAULT nextval('wing.report_sect_data_data_sn_seq'::regclass); + + +-- ============================================================ +-- FK 제약조건 +-- ============================================================ +ALTER TABLE ONLY wing.report_tmpl_sect + ADD CONSTRAINT report_tmpl_sect_tmpl_sn_fkey + FOREIGN KEY (tmpl_sn) REFERENCES wing.report_tmpl(tmpl_sn); + +ALTER TABLE ONLY wing.report_ctgr_tmpl + ADD CONSTRAINT report_ctgr_tmpl_ctgr_sn_fkey + FOREIGN KEY (ctgr_sn) REFERENCES wing.report_analysis_ctgr(ctgr_sn); + +ALTER TABLE ONLY wing.report_ctgr_sect + ADD CONSTRAINT report_ctgr_sect_ctgr_sn_fkey + FOREIGN KEY (ctgr_sn) REFERENCES wing.report_analysis_ctgr(ctgr_sn); + +-- 크로스스키마 FK: report.author_id → auth.auth_user +ALTER TABLE ONLY wing.report + ADD CONSTRAINT report_author_id_fkey + FOREIGN KEY (author_id) REFERENCES auth.auth_user(user_id); + +ALTER TABLE ONLY wing.report + ADD CONSTRAINT report_tmpl_sn_fkey + FOREIGN KEY (tmpl_sn) REFERENCES wing.report_tmpl(tmpl_sn); + +ALTER TABLE ONLY wing.report + ADD CONSTRAINT report_ctgr_sn_fkey + FOREIGN KEY (ctgr_sn) REFERENCES wing.report_analysis_ctgr(ctgr_sn); + +ALTER TABLE ONLY wing.report_sect_data + ADD CONSTRAINT report_sect_data_report_sn_fkey + FOREIGN KEY (report_sn) REFERENCES wing.report(report_sn) ON DELETE CASCADE; diff --git a/database/schema/11_aerial_rescue_tables.sql b/database/schema/11_aerial_rescue_tables.sql new file mode 100644 index 0000000..289398d --- /dev/null +++ b/database/schema/11_aerial_rescue_tables.sql @@ -0,0 +1,217 @@ +-- WING-OPS Database Schema: Aerial & Rescue Tables +-- wing 스키마: aerial_media, cctv_camera, sat_request, rescue_ops, rescue_scenario +-- FK 의존성: rescue_scenario → rescue_ops + +-- ============================================================ +-- 1. 항공 미디어 (aerial_media) +-- ============================================================ +CREATE TABLE wing.aerial_media ( + aerial_media_sn integer NOT NULL, + acdnt_sn integer, + file_nm character varying(200) NOT NULL, + orgnl_nm character varying(200), + file_path character varying(500), + lon numeric(10,6), + lat numeric(9,6), + geom public.geometry(Point, 4326), + loc_dc character varying(100), + equip_tp_cd character varying(20), + equip_nm character varying(50), + media_tp_cd character varying(20), + takng_dtm timestamp with time zone, + file_sz character varying(20), + resolution character varying(30), + use_yn character(1) DEFAULT 'Y'::bpchar, + reg_dtm timestamp with time zone DEFAULT now(), + CONSTRAINT aerial_media_pkey PRIMARY KEY (aerial_media_sn) +); + +CREATE SEQUENCE wing.aerial_media_aerial_media_sn_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER SEQUENCE wing.aerial_media_aerial_media_sn_seq OWNED BY wing.aerial_media.aerial_media_sn; +ALTER TABLE ONLY wing.aerial_media ALTER COLUMN aerial_media_sn SET DEFAULT nextval('wing.aerial_media_aerial_media_sn_seq'::regclass); + + +-- ============================================================ +-- 2. CCTV 카메라 (cctv_camera) +-- ============================================================ +CREATE TABLE wing.cctv_camera ( + cctv_sn integer NOT NULL, + camera_nm character varying(100) NOT NULL, + region_nm character varying(20), + lon numeric(10,6), + lat numeric(9,6), + geom public.geometry(Point, 4326), + loc_dc character varying(200), + coord_dc character varying(50), + stts_cd character varying(20) DEFAULT 'LIVE'::character varying, + ptz_yn character(1) DEFAULT 'N'::bpchar, + source_nm character varying(50), + stream_url character varying(500), + use_yn character(1) DEFAULT 'Y'::bpchar, + reg_dtm timestamp with time zone DEFAULT now(), + CONSTRAINT cctv_camera_pkey PRIMARY KEY (cctv_sn) +); + +CREATE SEQUENCE wing.cctv_camera_cctv_sn_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER SEQUENCE wing.cctv_camera_cctv_sn_seq OWNED BY wing.cctv_camera.cctv_sn; +ALTER TABLE ONLY wing.cctv_camera ALTER COLUMN cctv_sn SET DEFAULT nextval('wing.cctv_camera_cctv_sn_seq'::regclass); + + +-- ============================================================ +-- 3. 위성 촬영 요청 (sat_request) +-- ============================================================ +CREATE TABLE wing.sat_request ( + sat_req_sn integer NOT NULL, + req_cd character varying(20) NOT NULL, + acdnt_sn integer, + lon numeric(10,6), + lat numeric(9,6), + geom public.geometry(Point, 4326), + zone_dc character varying(200), + coord_dc character varying(50), + zone_area_km2 numeric(8,2), + sat_nm character varying(50), + provider_nm character varying(50), + resolution character varying(20), + purpose_dc character varying(200), + reqstr_nm character varying(50), + req_dtm timestamp with time zone, + expected_rcv_dtm timestamp with time zone, + stts_cd character varying(20) DEFAULT 'PENDING'::character varying, + use_yn character(1) DEFAULT 'Y'::bpchar, + reg_dtm timestamp with time zone DEFAULT now(), + CONSTRAINT sat_request_pkey PRIMARY KEY (sat_req_sn), + CONSTRAINT sat_request_req_cd_key UNIQUE (req_cd), + CONSTRAINT ck_sat_stts CHECK ( + ((stts_cd)::text = ANY ((ARRAY['PENDING'::character varying, 'SHOOTING'::character varying, 'COMPLETED'::character varying, 'CANCELLED'::character varying])::text[])) + ) +); + +CREATE SEQUENCE wing.sat_request_sat_req_sn_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER SEQUENCE wing.sat_request_sat_req_sn_seq OWNED BY wing.sat_request.sat_req_sn; +ALTER TABLE ONLY wing.sat_request ALTER COLUMN sat_req_sn SET DEFAULT nextval('wing.sat_request_sat_req_sn_seq'::regclass); + + +-- ============================================================ +-- 4. 구조 작전 (rescue_ops) +-- ============================================================ +CREATE TABLE wing.rescue_ops ( + rescue_ops_sn integer NOT NULL, + acdnt_sn integer, + ops_cd character varying(20) NOT NULL, + acdnt_tp_cd character varying(20), + vessel_nm character varying(100), + commander_nm character varying(50), + lon numeric(10,6), + lat numeric(9,6), + geom public.geometry(Point, 4326), + loc_dc character varying(100), + depth_m numeric(6,1), + current_dc character varying(50), + gm_m numeric(5,2), + list_deg numeric(5,1), + trim_m numeric(5,2), + buoyancy_pct numeric(5,1), + oil_rate_lpm numeric(8,1), + bm_ratio_pct numeric(5,1), + total_crew integer, + survivors integer, + missing integer, + hydro_data jsonb, + gmdss_data jsonb, + stts_cd character varying(20) DEFAULT 'ACTIVE'::character varying, + use_yn character(1) DEFAULT 'Y'::bpchar, + reg_dtm timestamp with time zone DEFAULT now(), + mdfcn_dtm timestamp with time zone DEFAULT now(), + CONSTRAINT rescue_ops_pkey PRIMARY KEY (rescue_ops_sn), + CONSTRAINT rescue_ops_ops_cd_key UNIQUE (ops_cd) +); + +CREATE SEQUENCE wing.rescue_ops_rescue_ops_sn_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER SEQUENCE wing.rescue_ops_rescue_ops_sn_seq OWNED BY wing.rescue_ops.rescue_ops_sn; +ALTER TABLE ONLY wing.rescue_ops ALTER COLUMN rescue_ops_sn SET DEFAULT nextval('wing.rescue_ops_rescue_ops_sn_seq'::regclass); + + +-- ============================================================ +-- 5. 구조 시나리오 (rescue_scenario) +-- 의존: rescue_ops +-- ============================================================ +CREATE TABLE wing.rescue_scenario ( + scenario_sn integer NOT NULL, + rescue_ops_sn integer NOT NULL, + time_step character varying(10) NOT NULL, + scenario_dtm timestamp with time zone, + svrt_cd character varying(20), + gm_m numeric(5,2), + list_deg numeric(5,1), + trim_m numeric(5,2), + buoyancy_pct numeric(5,1), + oil_rate_lpm numeric(8,1), + bm_ratio_pct numeric(5,1), + description text, + compartments jsonb, + assessment jsonb, + actions jsonb, + sort_ord integer DEFAULT 0, + reg_dtm timestamp with time zone DEFAULT now(), + CONSTRAINT rescue_scenario_pkey PRIMARY KEY (scenario_sn) +); + +CREATE SEQUENCE wing.rescue_scenario_scenario_sn_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER SEQUENCE wing.rescue_scenario_scenario_sn_seq OWNED BY wing.rescue_scenario.scenario_sn; +ALTER TABLE ONLY wing.rescue_scenario ALTER COLUMN scenario_sn SET DEFAULT nextval('wing.rescue_scenario_scenario_sn_seq'::regclass); + + +-- ============================================================ +-- FK 제약조건 +-- ============================================================ +ALTER TABLE ONLY wing.aerial_media + ADD CONSTRAINT aerial_media_acdnt_sn_fkey + FOREIGN KEY (acdnt_sn) REFERENCES wing.acdnt(acdnt_sn); + +ALTER TABLE ONLY wing.sat_request + ADD CONSTRAINT sat_request_acdnt_sn_fkey + FOREIGN KEY (acdnt_sn) REFERENCES wing.acdnt(acdnt_sn); + +ALTER TABLE ONLY wing.rescue_ops + ADD CONSTRAINT rescue_ops_acdnt_sn_fkey + FOREIGN KEY (acdnt_sn) REFERENCES wing.acdnt(acdnt_sn); + +ALTER TABLE ONLY wing.rescue_scenario + ADD CONSTRAINT rescue_scenario_rescue_ops_sn_fkey + FOREIGN KEY (rescue_ops_sn) REFERENCES wing.rescue_ops(rescue_ops_sn) ON DELETE CASCADE; diff --git a/database/schema/12_incidents_tables.sql b/database/schema/12_incidents_tables.sql new file mode 100644 index 0000000..c8f0594 --- /dev/null +++ b/database/schema/12_incidents_tables.sql @@ -0,0 +1,190 @@ +-- WING-OPS Database Schema: Incidents Tables +-- wing 스키마: acdnt, spil_data, pred_exec, acdnt_weather, acdnt_media +-- 출처: pg_dump 현행 DDL (migration/009_incidents.sql 기반, wing 스키마 적용) +-- 주의: pg_dump 기준 acdnt에는 loc_geom(geometry) 컬럼이 추가되어 있음 + +-- ============================================================ +-- 1. 사고 (acdnt) +-- ============================================================ +CREATE TABLE wing.acdnt ( + acdnt_sn integer NOT NULL, + acdnt_cd character varying(20) NOT NULL, + acdnt_nm character varying(200) NOT NULL, + acdnt_tp_cd character varying(50) NOT NULL, + acdnt_stts_cd character varying(20) NOT NULL DEFAULT 'ACTIVE'::character varying, + lat numeric(9,6), + lng numeric(10,6), + loc_dc character varying(200), + occrn_dtm timestamp with time zone NOT NULL, + rpt_dtm timestamp with time zone, + region_nm character varying(20), + office_nm character varying(30), + svrt_cd character varying(10), + vessel_tp character varying(30), + phase_cd character varying(20) DEFAULT 'RESPONSE'::character varying, + analyst_nm character varying(50), + rgtr_id uuid, + use_yn character(1) DEFAULT 'Y'::bpchar, + reg_dtm timestamp with time zone NOT NULL DEFAULT now(), + mdfcn_dtm timestamp with time zone, + loc_geom public.geometry(Point, 4326), + CONSTRAINT pk_acdnt PRIMARY KEY (acdnt_sn), + CONSTRAINT uk_acdnt_cd UNIQUE (acdnt_cd), + CONSTRAINT ck_acdnt_stts CHECK (((acdnt_stts_cd)::text = ANY ((ARRAY['ACTIVE'::character varying, 'INVESTIGATING'::character varying, 'CLOSED'::character varying])::text[]))), + CONSTRAINT ck_acdnt_svrt CHECK (((svrt_cd IS NULL) OR ((svrt_cd)::text = ANY ((ARRAY['DANGER'::character varying, 'ALERT'::character varying, 'CAUTION'::character varying, 'INTEREST'::character varying])::text[])))), + CONSTRAINT ck_acdnt_phase CHECK (((phase_cd IS NULL) OR ((phase_cd)::text = ANY ((ARRAY['RESPONSE'::character varying, 'STANDBY'::character varying, 'CLOSED'::character varying])::text[])))) +); + +CREATE SEQUENCE wing.acdnt_acdnt_sn_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER SEQUENCE wing.acdnt_acdnt_sn_seq OWNED BY wing.acdnt.acdnt_sn; +ALTER TABLE ONLY wing.acdnt ALTER COLUMN acdnt_sn SET DEFAULT nextval('wing.acdnt_acdnt_sn_seq'::regclass); + + +-- ============================================================ +-- 2. 유출정보 (spil_data) +-- ============================================================ +CREATE TABLE wing.spil_data ( + spil_data_sn integer NOT NULL, + acdnt_sn integer NOT NULL, + oil_tp_cd character varying(50) NOT NULL, + spil_qty numeric(12,2), + spil_unit_cd character varying(10) DEFAULT 'KL'::character varying, + spil_tp_cd character varying(20), + fcst_hr integer, + reg_dtm timestamp with time zone NOT NULL DEFAULT now(), + CONSTRAINT pk_spil_data PRIMARY KEY (spil_data_sn) +); + +CREATE SEQUENCE wing.spil_data_spil_data_sn_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER SEQUENCE wing.spil_data_spil_data_sn_seq OWNED BY wing.spil_data.spil_data_sn; +ALTER TABLE ONLY wing.spil_data ALTER COLUMN spil_data_sn SET DEFAULT nextval('wing.spil_data_spil_data_sn_seq'::regclass); + + +-- ============================================================ +-- 3. 예측 실행 (pred_exec) +-- ============================================================ +CREATE TABLE wing.pred_exec ( + pred_exec_sn integer NOT NULL, + acdnt_sn integer NOT NULL, + algo_cd character varying(20) NOT NULL, + exec_stts_cd character varying(20) NOT NULL DEFAULT 'PENDING'::character varying, + bgng_dtm timestamp with time zone, + cmpl_dtm timestamp with time zone, + reqd_sec integer, + rslt_data jsonb, + err_msg text, + CONSTRAINT pk_pred_exec PRIMARY KEY (pred_exec_sn), + CONSTRAINT ck_pred_stts CHECK (((exec_stts_cd)::text = ANY ((ARRAY['PENDING'::character varying, 'RUNNING'::character varying, 'COMPLETED'::character varying, 'FAILED'::character varying])::text[]))) +); + +CREATE SEQUENCE wing.pred_exec_pred_exec_sn_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER SEQUENCE wing.pred_exec_pred_exec_sn_seq OWNED BY wing.pred_exec.pred_exec_sn; +ALTER TABLE ONLY wing.pred_exec ALTER COLUMN pred_exec_sn SET DEFAULT nextval('wing.pred_exec_pred_exec_sn_seq'::regclass); + + +-- ============================================================ +-- 4. 사고별 기상정보 스냅샷 (acdnt_weather) +-- ============================================================ +CREATE TABLE wing.acdnt_weather ( + weather_sn integer NOT NULL, + acdnt_sn integer NOT NULL, + loc_nm character varying(100), + obs_dtm timestamp with time zone, + icon character varying(10), + temp character varying(20), + weather_dc character varying(50), + wind character varying(30), + wave character varying(20), + humid character varying(20), + vis character varying(20), + sst character varying(20), + tide character varying(30), + high_tide character varying(30), + low_tide character varying(30), + forecast jsonb, + impact_dc text, + reg_dtm timestamp with time zone DEFAULT now(), + CONSTRAINT acdnt_weather_pkey PRIMARY KEY (weather_sn) +); + +CREATE SEQUENCE wing.acdnt_weather_weather_sn_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER SEQUENCE wing.acdnt_weather_weather_sn_seq OWNED BY wing.acdnt_weather.weather_sn; +ALTER TABLE ONLY wing.acdnt_weather ALTER COLUMN weather_sn SET DEFAULT nextval('wing.acdnt_weather_weather_sn_seq'::regclass); + + +-- ============================================================ +-- 5. 사고별 미디어 메타데이터 (acdnt_media) +-- ============================================================ +CREATE TABLE wing.acdnt_media ( + media_sn integer NOT NULL, + acdnt_sn integer NOT NULL, + photo_cnt smallint DEFAULT 0, + video_cnt smallint DEFAULT 0, + sat_cnt smallint DEFAULT 0, + cctv_cnt smallint DEFAULT 0, + photo_meta jsonb, + drone_meta jsonb, + sat_meta jsonb, + cctv_meta jsonb, + reg_dtm timestamp with time zone DEFAULT now(), + CONSTRAINT acdnt_media_pkey PRIMARY KEY (media_sn) +); + +CREATE SEQUENCE wing.acdnt_media_media_sn_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER SEQUENCE wing.acdnt_media_media_sn_seq OWNED BY wing.acdnt_media.media_sn; +ALTER TABLE ONLY wing.acdnt_media ALTER COLUMN media_sn SET DEFAULT nextval('wing.acdnt_media_media_sn_seq'::regclass); + + +-- ============================================================ +-- FK 제약조건 +-- ============================================================ +ALTER TABLE ONLY wing.spil_data + ADD CONSTRAINT fk_spil_acdnt + FOREIGN KEY (acdnt_sn) REFERENCES wing.acdnt(acdnt_sn) ON DELETE CASCADE; + +ALTER TABLE ONLY wing.pred_exec + ADD CONSTRAINT fk_pred_acdnt + FOREIGN KEY (acdnt_sn) REFERENCES wing.acdnt(acdnt_sn) ON DELETE CASCADE; + +ALTER TABLE ONLY wing.acdnt_weather + ADD CONSTRAINT acdnt_weather_acdnt_sn_fkey + FOREIGN KEY (acdnt_sn) REFERENCES wing.acdnt(acdnt_sn) ON DELETE CASCADE; + +ALTER TABLE ONLY wing.acdnt_media + ADD CONSTRAINT acdnt_media_acdnt_sn_fkey + FOREIGN KEY (acdnt_sn) REFERENCES wing.acdnt(acdnt_sn) ON DELETE CASCADE; diff --git a/database/schema/13_indexes.sql b/database/schema/13_indexes.sql new file mode 100644 index 0000000..4eccba4 --- /dev/null +++ b/database/schema/13_indexes.sql @@ -0,0 +1,135 @@ +-- WING-OPS Database Schema: Additional Indexes +-- 01~12 파일의 PK/UNIQUE 제약조건을 제외한 추가 인덱스 모음 +-- (CREATE INDEX 만 포함, ALTER TABLE ... ADD CONSTRAINT 형태의 UNIQUE는 각 테이블 파일에 포함) + +-- ============================================================ +-- auth 스키마 인덱스 +-- ============================================================ + +-- auth_audit_log +CREATE INDEX idx_audit_log_action ON auth.auth_audit_log USING btree (action_cd); +CREATE INDEX idx_audit_log_dtm ON auth.auth_audit_log USING btree (req_dtm); +CREATE INDEX idx_audit_log_user ON auth.auth_audit_log USING btree (user_id); + +-- auth_login_hist +CREATE INDEX idx_auth_login_dtm ON auth.auth_login_hist USING btree (login_dtm); +CREATE INDEX idx_auth_login_user ON auth.auth_login_hist USING btree (user_id); + +-- auth_perm +CREATE INDEX idx_auth_perm_oper ON auth.auth_perm USING btree (oper_cd); +CREATE INDEX idx_auth_perm_role ON auth.auth_perm USING btree (role_sn); +CREATE INDEX idx_auth_perm_rsrc ON auth.auth_perm USING btree (rsrc_cd); + +-- auth_user +CREATE INDEX idx_auth_user_org ON auth.auth_user USING btree (org_sn); +CREATE INDEX idx_auth_user_stts ON auth.auth_user USING btree (user_stts_cd); + +-- auth_perm_tree +CREATE INDEX idx_perm_tree_parent ON auth.auth_perm_tree USING btree (parent_cd); + +-- auth_user — 부분 유니크 인덱스 (조건부) +CREATE UNIQUE INDEX uk_auth_user_email ON auth.auth_user USING btree (email) + WHERE (email IS NOT NULL); + +CREATE UNIQUE INDEX uk_auth_user_oauth ON auth.auth_user USING btree (oauth_provider, oauth_sub) + WHERE (oauth_provider IS NOT NULL); + + +-- ============================================================ +-- wing 스키마 인덱스 — GiST 공간 인덱스 +-- ============================================================ +CREATE INDEX idx_acdnt_loc_geom ON wing.acdnt USING gist (loc_geom); +CREATE INDEX idx_aerial_media_geom ON wing.aerial_media USING gist (geom); +CREATE INDEX idx_asset_org_geom ON wing.asset_org USING gist (geom); +CREATE INDEX idx_backtrack_geom ON wing.backtrack USING gist (geom); +CREATE INDEX idx_boom_geom ON wing.boom_line USING gist (geom); +CREATE INDEX idx_cctv_geom ON wing.cctv_camera USING gist (geom); +CREATE INDEX idx_cst_sect_geom ON wing.cst_sect USING gist (geom); +CREATE INDEX idx_cst_srvy_zone_geom ON wing.cst_srvy_zone USING gist (geom); +CREATE INDEX idx_rescue_ops_geom ON wing.rescue_ops USING gist (geom); +CREATE INDEX idx_sat_req_geom ON wing.sat_request USING gist (geom); + + +-- ============================================================ +-- wing 스키마 인덱스 — GIN (전문검색 / jsonb) +-- ============================================================ +CREATE INDEX idx_hns_sbst_abbr ON wing.hns_substance USING gin (abbreviation public.gin_trgm_ops); +CREATE INDEX idx_hns_sbst_nm_en ON wing.hns_substance USING gin (nm_en public.gin_trgm_ops); +CREATE INDEX idx_hns_sbst_nm_kr ON wing.hns_substance USING gin (nm_kr public.gin_trgm_ops); +CREATE INDEX idx_hns_sbst_data ON wing.hns_substance USING gin (data jsonb_path_ops); + + +-- ============================================================ +-- wing 스키마 인덱스 — btree +-- ============================================================ + +-- acdnt +CREATE INDEX idx_acdnt_occrn ON wing.acdnt USING btree (occrn_dtm DESC); +CREATE INDEX idx_acdnt_region ON wing.acdnt USING btree (region_nm); +CREATE INDEX idx_acdnt_stts ON wing.acdnt USING btree (acdnt_stts_cd); + +-- aerial_media +CREATE INDEX idx_aerial_media_acdnt ON wing.aerial_media USING btree (acdnt_sn); + +-- asset_org +CREATE INDEX idx_asset_org_jrsd ON wing.asset_org USING btree (jrsd_nm); +CREATE INDEX idx_asset_org_tp ON wing.asset_org USING btree (org_tp); + +-- asset_equip / asset_contact +CREATE INDEX idx_asset_equip_org ON wing.asset_equip USING btree (org_sn); +CREATE INDEX idx_asset_contact_org ON wing.asset_contact USING btree (org_sn); + +-- backtrack +CREATE INDEX idx_backtrack_acdnt ON wing.backtrack USING btree (acdnt_sn); + +-- board_post +CREATE INDEX idx_board_author ON wing.board_post USING btree (author_id); +CREATE INDEX idx_board_category ON wing.board_post USING btree (category_cd); +CREATE INDEX idx_board_reg_dtm ON wing.board_post USING btree (reg_dtm DESC); + +-- boom_line +CREATE INDEX idx_boom_acdnt ON wing.boom_line USING btree (acdnt_sn); + +-- cst_sect +CREATE INDEX idx_cst_sect_esi ON wing.cst_sect USING btree (esi_num); +CREATE INDEX idx_cst_sect_stts ON wing.cst_sect USING btree (srvy_stts_cd); +CREATE INDEX idx_cst_sect_zone ON wing.cst_sect USING btree (cst_srvy_zone_sn); + +-- hns_substance (btree) +CREATE INDEX idx_hns_sbst_cas ON wing.hns_substance USING btree (cas_no); +CREATE INDEX idx_hns_sbst_sebc ON wing.hns_substance USING btree (sebc); +CREATE INDEX idx_hns_sbst_un ON wing.hns_substance USING btree (un_no); + +-- layer +CREATE INDEX idx_layer_level ON wing.layer USING btree (layer_level); +CREATE INDEX idx_layer_up ON wing.layer USING btree (up_layer_cd); +CREATE INDEX idx_layer_use ON wing.layer USING btree (use_yn); + +-- acdnt_media +CREATE INDEX idx_media_acdnt ON wing.acdnt_media USING btree (acdnt_sn); + +-- pred_exec +CREATE INDEX idx_pred_acdnt ON wing.pred_exec USING btree (acdnt_sn); + +-- report +CREATE INDEX idx_report_author ON wing.report USING btree (author_id); +CREATE INDEX idx_report_ctgr ON wing.report USING btree (ctgr_sn); +CREATE INDEX idx_report_reg_dtm ON wing.report USING btree (reg_dtm DESC); +CREATE INDEX idx_report_stts ON wing.report USING btree (stts_cd); +CREATE INDEX idx_report_tmpl ON wing.report USING btree (tmpl_sn); + +-- report_sect_data +CREATE INDEX idx_report_sect_report ON wing.report_sect_data USING btree (report_sn); + +-- rescue_ops / rescue_scenario +CREATE INDEX idx_rescue_ops_acdnt ON wing.rescue_ops USING btree (acdnt_sn); +CREATE INDEX idx_rescue_scenario_ops ON wing.rescue_scenario USING btree (rescue_ops_sn); + +-- spil_data +CREATE INDEX idx_spil_acdnt ON wing.spil_data USING btree (acdnt_sn); + +-- vessel_info +CREATE INDEX idx_vessel_acdnt ON wing.vessel_info USING btree (acdnt_sn); + +-- acdnt_weather +CREATE INDEX idx_weather_acdnt ON wing.acdnt_weather USING btree (acdnt_sn); diff --git a/database/seed/01_auth_roles.sql b/database/seed/01_auth_roles.sql new file mode 100644 index 0000000..8d44f42 --- /dev/null +++ b/database/seed/01_auth_roles.sql @@ -0,0 +1,11 @@ +-- WING-OPS Seed Data: Auth Roles +-- 역할 4건 (ADMIN, MANAGER, USER, VIEWER) +-- 출처: database/auth_init.sql + +SET search_path TO public; + +INSERT INTO auth.auth_role (role_cd, role_nm, role_dc, dflt_yn) VALUES +('ADMIN', '관리자', '시스템 전체 관리 권한', 'N'), +('MANAGER', '운영자', '운영 및 사용자 관리 권한', 'N'), +('USER', '일반사용자', '기본 업무 기능 접근 권한', 'Y'), +('VIEWER', '뷰어', '조회 전용 접근 권한', 'N'); diff --git a/database/seed/02_auth_perms.sql b/database/seed/02_auth_perms.sql new file mode 100644 index 0000000..c0d7fdd --- /dev/null +++ b/database/seed/02_auth_perms.sql @@ -0,0 +1,147 @@ +-- WING-OPS Seed Data: Auth Permissions (RCUD 2차원 권한 모델) +-- 역할별 리소스 × 오퍼레이션 매트릭스 +-- 출처: database/auth_init.sql (004_oper_cd.sql 적용 후 최종 상태) +-- role_sn 1=ADMIN, 2=MANAGER, 3=USER, 4=VIEWER + +-- ============================================================ +-- ADMIN (role_sn=1): 모든 탭 × RCUD 전체 허용 +-- ============================================================ +INSERT INTO auth.auth_perm (role_sn, rsrc_cd, oper_cd, grant_yn) VALUES +(1, 'prediction', 'READ', 'Y'), +(1, 'prediction', 'CREATE', 'Y'), +(1, 'prediction', 'UPDATE', 'Y'), +(1, 'prediction', 'DELETE', 'Y'), +(1, 'hns', 'READ', 'Y'), +(1, 'hns', 'CREATE', 'Y'), +(1, 'hns', 'UPDATE', 'Y'), +(1, 'hns', 'DELETE', 'Y'), +(1, 'rescue', 'READ', 'Y'), +(1, 'rescue', 'CREATE', 'Y'), +(1, 'rescue', 'UPDATE', 'Y'), +(1, 'rescue', 'DELETE', 'Y'), +(1, 'reports', 'READ', 'Y'), +(1, 'reports', 'CREATE', 'Y'), +(1, 'reports', 'UPDATE', 'Y'), +(1, 'reports', 'DELETE', 'Y'), +(1, 'aerial', 'READ', 'Y'), +(1, 'aerial', 'CREATE', 'Y'), +(1, 'aerial', 'UPDATE', 'Y'), +(1, 'aerial', 'DELETE', 'Y'), +(1, 'assets', 'READ', 'Y'), +(1, 'assets', 'CREATE', 'Y'), +(1, 'assets', 'UPDATE', 'Y'), +(1, 'assets', 'DELETE', 'Y'), +(1, 'scat', 'READ', 'Y'), +(1, 'scat', 'CREATE', 'Y'), +(1, 'scat', 'UPDATE', 'Y'), +(1, 'scat', 'DELETE', 'Y'), +(1, 'incidents', 'READ', 'Y'), +(1, 'incidents', 'CREATE', 'Y'), +(1, 'incidents', 'UPDATE', 'Y'), +(1, 'incidents', 'DELETE', 'Y'), +(1, 'board', 'READ', 'Y'), +(1, 'board', 'CREATE', 'Y'), +(1, 'board', 'UPDATE', 'Y'), +(1, 'board', 'DELETE', 'Y'), +(1, 'weather', 'READ', 'Y'), +(1, 'weather', 'CREATE', 'Y'), +(1, 'weather', 'UPDATE', 'Y'), +(1, 'weather', 'DELETE', 'Y'), +(1, 'admin', 'READ', 'Y'), +(1, 'admin', 'CREATE', 'Y'), +(1, 'admin', 'UPDATE', 'Y'), +(1, 'admin', 'DELETE', 'Y'); + +-- ============================================================ +-- MANAGER (role_sn=2): admin 탭 제외, 나머지 탭 RCUD 전체 허용 +-- ============================================================ +INSERT INTO auth.auth_perm (role_sn, rsrc_cd, oper_cd, grant_yn) VALUES +(2, 'prediction', 'READ', 'Y'), +(2, 'prediction', 'CREATE', 'Y'), +(2, 'prediction', 'UPDATE', 'Y'), +(2, 'prediction', 'DELETE', 'Y'), +(2, 'hns', 'READ', 'Y'), +(2, 'hns', 'CREATE', 'Y'), +(2, 'hns', 'UPDATE', 'Y'), +(2, 'hns', 'DELETE', 'Y'), +(2, 'rescue', 'READ', 'Y'), +(2, 'rescue', 'CREATE', 'Y'), +(2, 'rescue', 'UPDATE', 'Y'), +(2, 'rescue', 'DELETE', 'Y'), +(2, 'reports', 'READ', 'Y'), +(2, 'reports', 'CREATE', 'Y'), +(2, 'reports', 'UPDATE', 'Y'), +(2, 'reports', 'DELETE', 'Y'), +(2, 'aerial', 'READ', 'Y'), +(2, 'aerial', 'CREATE', 'Y'), +(2, 'aerial', 'UPDATE', 'Y'), +(2, 'aerial', 'DELETE', 'Y'), +(2, 'assets', 'READ', 'Y'), +(2, 'assets', 'CREATE', 'Y'), +(2, 'assets', 'UPDATE', 'Y'), +(2, 'assets', 'DELETE', 'Y'), +(2, 'scat', 'READ', 'Y'), +(2, 'scat', 'CREATE', 'Y'), +(2, 'scat', 'UPDATE', 'Y'), +(2, 'scat', 'DELETE', 'Y'), +(2, 'incidents', 'READ', 'Y'), +(2, 'incidents', 'CREATE', 'Y'), +(2, 'incidents', 'UPDATE', 'Y'), +(2, 'incidents', 'DELETE', 'Y'), +(2, 'board', 'READ', 'Y'), +(2, 'board', 'CREATE', 'Y'), +(2, 'board', 'UPDATE', 'Y'), +(2, 'board', 'DELETE', 'Y'), +(2, 'weather', 'READ', 'Y'), +(2, 'weather', 'CREATE', 'Y'), +(2, 'weather', 'UPDATE', 'Y'), +(2, 'weather', 'DELETE', 'Y'), +(2, 'admin', 'READ', 'N'); + +-- ============================================================ +-- USER (role_sn=3): assets/admin 조회 거부, 나머지는 READ/CREATE/UPDATE +-- ============================================================ +INSERT INTO auth.auth_perm (role_sn, rsrc_cd, oper_cd, grant_yn) VALUES +(3, 'prediction', 'READ', 'Y'), +(3, 'prediction', 'CREATE', 'Y'), +(3, 'prediction', 'UPDATE', 'Y'), +(3, 'hns', 'READ', 'Y'), +(3, 'hns', 'CREATE', 'Y'), +(3, 'hns', 'UPDATE', 'Y'), +(3, 'rescue', 'READ', 'Y'), +(3, 'rescue', 'CREATE', 'Y'), +(3, 'rescue', 'UPDATE', 'Y'), +(3, 'reports', 'READ', 'Y'), +(3, 'reports', 'CREATE', 'Y'), +(3, 'reports', 'UPDATE', 'Y'), +(3, 'aerial', 'READ', 'Y'), +(3, 'aerial', 'CREATE', 'Y'), +(3, 'aerial', 'UPDATE', 'Y'), +(3, 'assets', 'READ', 'N'), +(3, 'scat', 'READ', 'Y'), +(3, 'scat', 'CREATE', 'Y'), +(3, 'scat', 'UPDATE', 'Y'), +(3, 'incidents', 'READ', 'Y'), +(3, 'incidents', 'CREATE', 'Y'), +(3, 'incidents', 'UPDATE', 'Y'), +(3, 'board', 'READ', 'Y'), +(3, 'board', 'CREATE', 'Y'), +(3, 'board', 'UPDATE', 'Y'), +(3, 'weather', 'READ', 'Y'), +(3, 'admin', 'READ', 'N'); + +-- ============================================================ +-- VIEWER (role_sn=4): 제한된 탭 READ만 허용 (CUD 없음) +-- ============================================================ +INSERT INTO auth.auth_perm (role_sn, rsrc_cd, oper_cd, grant_yn) VALUES +(4, 'prediction', 'READ', 'Y'), +(4, 'hns', 'READ', 'Y'), +(4, 'rescue', 'READ', 'Y'), +(4, 'reports', 'READ', 'N'), +(4, 'aerial', 'READ', 'Y'), +(4, 'assets', 'READ', 'N'), +(4, 'scat', 'READ', 'N'), +(4, 'incidents', 'READ', 'Y'), +(4, 'board', 'READ', 'Y'), +(4, 'weather', 'READ', 'Y'), +(4, 'admin', 'READ', 'N'); diff --git a/database/seed/03_auth_perm_tree.sql b/database/seed/03_auth_perm_tree.sql new file mode 100644 index 0000000..57b2c43 --- /dev/null +++ b/database/seed/03_auth_perm_tree.sql @@ -0,0 +1,96 @@ +-- WING-OPS Seed Data: Auth Permission Tree +-- 트리 구조 기반 리소스(메뉴) 권한 정의 (48건) +-- 출처: database/migration/003_perm_tree.sql + +-- ============================================================ +-- Level 0: 메인 탭 (11건) +-- ============================================================ +INSERT INTO auth.auth_perm_tree (rsrc_cd, parent_cd, rsrc_nm, rsrc_desc, rsrc_level, sort_ord) VALUES +('prediction', NULL, '유출유 확산예측', '확산 예측 실행 및 결과 조회', 0, 1), +('hns', NULL, 'HNS·대기확산', '대기확산 분석 실행 및 조회', 0, 2), +('rescue', NULL, '긴급구난', '구난 예측 실행 및 조회', 0, 3), +('reports', NULL, '보고자료', '사고 보고서 작성 및 조회', 0, 4), +('aerial', NULL, '항공탐색', '항공 탐색 데이터 조회', 0, 5), +('assets', NULL, '방제자산 관리', '방제 장비 및 자산 관리', 0, 6), +('scat', NULL, '해안평가', 'SCAT 조사 실행 및 조회', 0, 7), +('board', NULL, '게시판', '자료실 및 공지사항 조회', 0, 8), +('weather', NULL, '기상정보', '기상 및 해상 정보 조회', 0, 9), +('incidents', NULL, '통합조회', '사고 상세 정보 조회', 0, 10), +('admin', NULL, '관리', '사용자 및 권한 관리', 0, 11); + +-- ============================================================ +-- Level 1: prediction 하위 (4건) +-- ============================================================ +INSERT INTO auth.auth_perm_tree (rsrc_cd, parent_cd, rsrc_nm, rsrc_level, sort_ord) VALUES +('prediction:analysis', 'prediction', '확산분석', 1, 1), +('prediction:list', 'prediction', '분석 목록', 1, 2), +('prediction:theory', 'prediction', '확산모델 이론', 1, 3), +('prediction:boom-theory', 'prediction', '오일펜스 배치 이론', 1, 4); + +-- ============================================================ +-- Level 1: hns 하위 (6건) +-- ============================================================ +INSERT INTO auth.auth_perm_tree (rsrc_cd, parent_cd, rsrc_nm, rsrc_level, sort_ord) VALUES +('hns:analysis', 'hns', '대기확산 분석', 1, 1), +('hns:list', 'hns', '분석 목록', 1, 2), +('hns:scenario', 'hns', '시나리오 관리', 1, 3), +('hns:manual', 'hns', 'HNS 대응매뉴얼', 1, 4), +('hns:theory', 'hns', '확산모델 이론', 1, 5), +('hns:substance', 'hns', 'HNS 물질정보', 1, 6); + +-- ============================================================ +-- Level 1: rescue 하위 (4건) +-- ============================================================ +INSERT INTO auth.auth_perm_tree (rsrc_cd, parent_cd, rsrc_nm, rsrc_level, sort_ord) VALUES +('rescue:rescue', 'rescue', '긴급구난예측', 1, 1), +('rescue:list', 'rescue', '긴급구난 목록', 1, 2), +('rescue:scenario', 'rescue', '시나리오 관리', 1, 3), +('rescue:theory', 'rescue', '긴급구난모델 이론', 1, 4); + +-- ============================================================ +-- Level 1: reports 하위 (3건) +-- ============================================================ +INSERT INTO auth.auth_perm_tree (rsrc_cd, parent_cd, rsrc_nm, rsrc_level, sort_ord) VALUES +('reports:report-list', 'reports', '보고서 목록', 1, 1), +('reports:template', 'reports', '표준보고서 템플릿', 1, 2), +('reports:generate', 'reports', '보고서 생성', 1, 3); + +-- ============================================================ +-- Level 1: aerial 하위 (7건) +-- ============================================================ +INSERT INTO auth.auth_perm_tree (rsrc_cd, parent_cd, rsrc_nm, rsrc_level, sort_ord) VALUES +('aerial:media', 'aerial', '영상사진관리', 1, 1), +('aerial:analysis', 'aerial', '유출유면적분석', 1, 2), +('aerial:realtime', 'aerial', '실시간드론', 1, 3), +('aerial:sensor', 'aerial', '오염/선박3D분석', 1, 4), +('aerial:satellite', 'aerial', '위성요청', 1, 5), +('aerial:cctv', 'aerial', 'CCTV 조회', 1, 6), +('aerial:theory', 'aerial', '항공탐색 이론', 1, 7); + +-- ============================================================ +-- Level 1: assets 하위 (4건) +-- ============================================================ +INSERT INTO auth.auth_perm_tree (rsrc_cd, parent_cd, rsrc_nm, rsrc_level, sort_ord) VALUES +('assets:management', 'assets', '자산 관리', 1, 1), +('assets:upload', 'assets', '자산 현행화', 1, 2), +('assets:theory', 'assets', '방제자원 이론', 1, 3), +('assets:insurance', 'assets', '선박 보험정보', 1, 4); + +-- ============================================================ +-- Level 1: board 하위 (5건) +-- ============================================================ +INSERT INTO auth.auth_perm_tree (rsrc_cd, parent_cd, rsrc_nm, rsrc_level, sort_ord) VALUES +('board:all', 'board', '전체', 1, 1), +('board:notice', 'board', '공지사항', 1, 2), +('board:data', 'board', '자료실', 1, 3), +('board:qna', 'board', 'Q&A', 1, 4), +('board:manual', 'board', '해경매뉴얼', 1, 5); + +-- ============================================================ +-- Level 1: admin 하위 (4건) +-- ============================================================ +INSERT INTO auth.auth_perm_tree (rsrc_cd, parent_cd, rsrc_nm, rsrc_level, sort_ord) VALUES +('admin:users', 'admin', '사용자 관리', 1, 1), +('admin:permissions', 'admin', '권한 관리', 1, 2), +('admin:menus', 'admin', '메뉴 관리', 1, 3), +('admin:settings', 'admin', '시스템 설정', 1, 4); diff --git a/database/seed/04_auth_orgs.sql b/database/seed/04_auth_orgs.sql new file mode 100644 index 0000000..4a6d887 --- /dev/null +++ b/database/seed/04_auth_orgs.sql @@ -0,0 +1,16 @@ +-- WING-OPS Seed Data: Auth Organizations +-- 인증 조직 6건 +-- 출처: database/auth_init.sql + +INSERT INTO auth.auth_org (org_nm, org_abbr_nm, org_tp_cd) VALUES +('해양경찰청', '해경청', 'HEADQUARTERS'), +('남해지방해양경찰청', '남해청', 'REGIONAL'), +('제주지방해양경찰청', '제주청', 'REGIONAL'), +('여수해양경찰서', '여수서', 'STATION'), +('서귀포해양경찰서', '서귀포서', 'STATION'), +('제주해양경찰서', '제주서', 'STATION'); + +-- 상위조직 설정 (org_sn은 SERIAL 순서: 1=해경청, 2=남해청, 3=제주청, 4=여수서, 5=서귀포서, 6=제주서) +UPDATE auth.auth_org SET upper_org_sn = 1 WHERE org_sn IN (2, 3); +UPDATE auth.auth_org SET upper_org_sn = 2 WHERE org_sn = 4; +UPDATE auth.auth_org SET upper_org_sn = 3 WHERE org_sn IN (5, 6); diff --git a/database/seed/05_auth_settings.sql b/database/seed/05_auth_settings.sql new file mode 100644 index 0000000..a7e927c --- /dev/null +++ b/database/seed/05_auth_settings.sql @@ -0,0 +1,20 @@ +-- WING-OPS Seed Data: Auth Settings +-- 시스템 설정 4건 +-- 출처: database/auth_init.sql + +INSERT INTO auth.auth_setting (setting_key, setting_val, setting_dc) VALUES +('registration.auto-approve', + 'true', + '신규 사용자 자동 승인 여부 (true: 즉시 ACTIVE, false: PENDING 대기)'), + +('registration.default-role', + 'true', + '신규 사용자에게 기본 역할(dflt_yn=Y) 자동 할당 여부'), + +('oauth.auto-approve-domains', + 'gcsc.co.kr', + 'OAuth 자동 승인 도메인 (쉼표 구분)'), + +('menu.config', + '[{"id":"prediction","label":"유출유 확산예측","icon":"🛢️","enabled":true,"order":1},{"id":"hns","label":"HNS·대기확산","icon":"🧪","enabled":true,"order":2},{"id":"rescue","label":"긴급구난","icon":"🚨","enabled":true,"order":3},{"id":"reports","label":"보고자료","icon":"📊","enabled":true,"order":4},{"id":"aerial","label":"항공탐색","icon":"✈️","enabled":true,"order":5},{"id":"assets","label":"방제자산 관리","icon":"🚢","enabled":true,"order":6},{"id":"scat","label":"해안평가","icon":"🏖️","enabled":true,"order":7},{"id":"board","label":"게시판","icon":"📌","enabled":true,"order":8},{"id":"weather","label":"기상정보","icon":"⛅","enabled":true,"order":9},{"id":"incidents","label":"통합조회","icon":"🔍","enabled":true,"order":10}]', + '메뉴 표시 여부 및 순서 설정 (JSON 배열)'); diff --git a/database/seed/06_auth_admin.sql b/database/seed/06_auth_admin.sql new file mode 100644 index 0000000..b5cde2a --- /dev/null +++ b/database/seed/06_auth_admin.sql @@ -0,0 +1,19 @@ +-- WING-OPS Seed Data: Auth Admin User +-- 관리자 계정 생성 (admin / admin1234) +-- 출처: database/auth_init.sql +-- 주의: pgcrypto 확장이 설치되어 있어야 함 (CREATE EXTENSION IF NOT EXISTS pgcrypto) + +-- 관리자 계정 (비밀번호: admin1234, bcrypt 해시) +INSERT INTO auth.auth_user (user_acnt, pswd_hash, user_nm, rnkp_nm, org_sn, user_stts_cd) +VALUES ( + 'admin', + crypt('admin1234', gen_salt('bf', 10)), + '관리자', + '경정', + 1, + 'ACTIVE' +); + +-- admin 사용자에 ADMIN 역할 할당 (role_sn=1) +INSERT INTO auth.auth_user_role (user_id, role_sn) +SELECT user_id, 1 FROM auth.auth_user WHERE user_acnt = 'admin'; diff --git a/database/seed/07_common_codes.sql b/database/seed/07_common_codes.sql new file mode 100644 index 0000000..86fde5b --- /dev/null +++ b/database/seed/07_common_codes.sql @@ -0,0 +1,192 @@ +-- WING-OPS Seed Data: Common Codes +-- 공통코드그룹 16건, 공통코드 약 100건 +-- 출처: database/init.sql + +-- ============================================================ +-- 공통코드그룹 (CMN_CD_GRP) +-- ============================================================ + +-- 조직유형 (ORG_TP) +INSERT INTO wing.cmn_cd_grp (cmn_cd_grp_id, cmn_cd_grp_nm, grp_dc) VALUES +('ORG_TP', '조직유형', '조직의 유형을 구분하는 코드'); +INSERT INTO wing.cmn_cd (cmn_cd_grp_id, cmn_cd, cmn_cd_nm, sort_ord) VALUES +('ORG_TP', 'HEADQUARTERS', '본청', 1), +('ORG_TP', 'REGIONAL', '지방청', 2), +('ORG_TP', 'STATION', '해양경찰서', 3), +('ORG_TP', 'AGENCY', '유관기관', 4); + +-- 사용자역할 (USER_ROLE) +INSERT INTO wing.cmn_cd_grp (cmn_cd_grp_id, cmn_cd_grp_nm, grp_dc) VALUES +('USER_ROLE', '사용자역할', '사용자의 시스템 역할을 구분하는 코드'); +INSERT INTO wing.cmn_cd (cmn_cd_grp_id, cmn_cd, cmn_cd_nm, sort_ord) VALUES +('USER_ROLE', 'ADMIN', '관리자', 1), +('USER_ROLE', 'MANAGER', '운영자', 2), +('USER_ROLE', 'USER', '일반사용자', 3); + +-- 사고유형 (ACDNT_TP) +INSERT INTO wing.cmn_cd_grp (cmn_cd_grp_id, cmn_cd_grp_nm, grp_dc) VALUES +('ACDNT_TP', '사고유형', '해양사고의 유형을 구분하는 코드'); +INSERT INTO wing.cmn_cd (cmn_cd_grp_id, cmn_cd, cmn_cd_nm, sort_ord) VALUES +('ACDNT_TP', 'COLLISION', '충돌', 1), +('ACDNT_TP', 'GROUNDING', '좌초', 2), +('ACDNT_TP', 'SINKING', '침몰', 3), +('ACDNT_TP', 'LEAK', '누출', 4), +('ACDNT_TP', 'EXPLOSION', '폭발', 5), +('ACDNT_TP', 'ETC', '기타', 99); + +-- 사고상태 (ACDNT_STTS) +INSERT INTO wing.cmn_cd_grp (cmn_cd_grp_id, cmn_cd_grp_nm, grp_dc) VALUES +('ACDNT_STTS', '사고상태', '사고의 진행 상태를 구분하는 코드'); +INSERT INTO wing.cmn_cd (cmn_cd_grp_id, cmn_cd, cmn_cd_nm, sort_ord) VALUES +('ACDNT_STTS', 'ACTIVE', '진행중', 1), +('ACDNT_STTS', 'MONITORING', '감시중', 2), +('ACDNT_STTS', 'CLOSED', '종료', 3); + +-- 심각도 (SVRT) +INSERT INTO wing.cmn_cd_grp (cmn_cd_grp_id, cmn_cd_grp_nm, grp_dc) VALUES +('SVRT', '심각도', '사고의 심각도를 구분하는 코드'); +INSERT INTO wing.cmn_cd (cmn_cd_grp_id, cmn_cd, cmn_cd_nm, sort_ord) VALUES +('SVRT', 'DANGER', '위험', 1), +('SVRT', 'ALERT', '경계', 2), +('SVRT', 'CAUTION', '주의', 3), +('SVRT', 'INTEREST', '관심', 4); + +-- 유종 (OIL_TP) +INSERT INTO wing.cmn_cd_grp (cmn_cd_grp_id, cmn_cd_grp_nm, grp_dc) VALUES +('OIL_TP', '유종', '유출유의 종류를 구분하는 코드'); +INSERT INTO wing.cmn_cd (cmn_cd_grp_id, cmn_cd, cmn_cd_nm, sort_ord) VALUES +('OIL_TP', 'CRUDE', '원유', 1), +('OIL_TP', 'BUNKER_C', '벙커C유', 2), +('OIL_TP', 'DIESEL', '경유', 3), +('OIL_TP', 'GASOLINE', '휘발유', 4), +('OIL_TP', 'KEROSENE', '등유', 5), +('OIL_TP', 'LUBE', '윤활유', 6), +('OIL_TP', 'ETC', '기타', 99); + +-- 유출단위 (SPIL_UNIT) +INSERT INTO wing.cmn_cd_grp (cmn_cd_grp_id, cmn_cd_grp_nm, grp_dc) VALUES +('SPIL_UNIT', '유출단위', '유출량의 측정 단위를 구분하는 코드'); +INSERT INTO wing.cmn_cd (cmn_cd_grp_id, cmn_cd, cmn_cd_nm, sort_ord) VALUES +('SPIL_UNIT', 'KL', '킬로리터', 1), +('SPIL_UNIT', 'L', '리터', 2), +('SPIL_UNIT', 'BBL', '배럴', 3), +('SPIL_UNIT', 'TON', '톤', 4); + +-- 유출유형 (SPIL_TP) +INSERT INTO wing.cmn_cd_grp (cmn_cd_grp_id, cmn_cd_grp_nm, grp_dc) VALUES +('SPIL_TP', '유출유형', '유출의 형태를 구분하는 코드'); +INSERT INTO wing.cmn_cd (cmn_cd_grp_id, cmn_cd, cmn_cd_nm, sort_ord) VALUES +('SPIL_TP', 'CONTINUOUS', '연속유출', 1), +('SPIL_TP', 'INSTANTANEOUS', '순간유출', 2); + +-- 예측알고리즘 (ALGO) +INSERT INTO wing.cmn_cd_grp (cmn_cd_grp_id, cmn_cd_grp_nm, grp_dc) VALUES +('ALGO', '예측알고리즘', '유출 확산 예측에 사용되는 알고리즘 코드'); +INSERT INTO wing.cmn_cd (cmn_cd_grp_id, cmn_cd, cmn_cd_nm, sort_ord) VALUES +('ALGO', 'GNOME', 'GNOME 모델', 1), +('ALGO', 'OSCAR', 'OSCAR 모델', 2), +('ALGO', 'MOHID', 'MOHID 모델', 3); + +-- 실행상태 (EXEC_STTS) +INSERT INTO wing.cmn_cd_grp (cmn_cd_grp_id, cmn_cd_grp_nm, grp_dc) VALUES +('EXEC_STTS', '실행상태', '예측 실행의 상태를 구분하는 코드'); +INSERT INTO wing.cmn_cd (cmn_cd_grp_id, cmn_cd, cmn_cd_nm, sort_ord) VALUES +('EXEC_STTS', 'PENDING', '대기', 1), +('EXEC_STTS', 'RUNNING', '실행중', 2), +('EXEC_STTS', 'COMPLETED', '완료', 3), +('EXEC_STTS', 'FAILED', '실패', 4); + +-- 오일펜스우선순위 (BOOM_PRIORT) +INSERT INTO wing.cmn_cd_grp (cmn_cd_grp_id, cmn_cd_grp_nm, grp_dc) VALUES +('BOOM_PRIORT', '오일펜스우선순위', '오일펜스 배치 우선순위를 구분하는 코드'); +INSERT INTO wing.cmn_cd (cmn_cd_grp_id, cmn_cd, cmn_cd_nm, sort_ord) VALUES +('BOOM_PRIORT', 'CRITICAL', '긴급', 1), +('BOOM_PRIORT', 'HIGH', '높음', 2), +('BOOM_PRIORT', 'MEDIUM', '보통', 3), +('BOOM_PRIORT', 'LOW', '낮음', 4); + +-- 오일펜스배치상태 (DPLY_STTS) +INSERT INTO wing.cmn_cd_grp (cmn_cd_grp_id, cmn_cd_grp_nm, grp_dc) VALUES +('DPLY_STTS', '배치상태', '오일펜스의 배치 상태를 구분하는 코드'); +INSERT INTO wing.cmn_cd (cmn_cd_grp_id, cmn_cd, cmn_cd_nm, sort_ord) VALUES +('DPLY_STTS', 'PLANNED', '계획', 1), +('DPLY_STTS', 'DEPLOYING', '배치중', 2), +('DPLY_STTS', 'DEPLOYED', '배치완료', 3), +('DPLY_STTS', 'REMOVED', '회수', 4); + +-- 해안유형 (CST_TP) +INSERT INTO wing.cmn_cd_grp (cmn_cd_grp_id, cmn_cd_grp_nm, grp_dc) VALUES +('CST_TP', '해안유형', '해안의 지형 유형을 구분하는 코드'); +INSERT INTO wing.cmn_cd (cmn_cd_grp_id, cmn_cd, cmn_cd_nm, sort_ord) VALUES +('CST_TP', 'ROCK', '암반해안', 1), +('CST_TP', 'GRAVEL', '자갈해안', 2), +('CST_TP', 'SAND', '모래해안', 3), +('CST_TP', 'MUD', '갯벌', 4), +('CST_TP', 'MIXED', '혼합해안', 5), +('CST_TP', 'SEAWALL', '인공구조물', 6), +('CST_TP', 'TETRAPOD', '테트라포드', 7); + +-- 민감도 (SNSTVT) +INSERT INTO wing.cmn_cd_grp (cmn_cd_grp_id, cmn_cd_grp_nm, grp_dc) VALUES +('SNSTVT', '민감도', '해안구간의 환경 민감도를 구분하는 코드'); +INSERT INTO wing.cmn_cd (cmn_cd_grp_id, cmn_cd, cmn_cd_nm, sort_ord) VALUES +('SNSTVT', 'HIGH', '고', 1), +('SNSTVT', 'MEDIUM', '중', 2), +('SNSTVT', 'LOW', '저', 3); + +-- 조사상태 (SRVY_STTS) +INSERT INTO wing.cmn_cd_grp (cmn_cd_grp_id, cmn_cd_grp_nm, grp_dc) VALUES +('SRVY_STTS', '조사상태', '해안구간 조사의 진행 상태를 구분하는 코드'); +INSERT INTO wing.cmn_cd (cmn_cd_grp_id, cmn_cd, cmn_cd_nm, sort_ord) VALUES +('SRVY_STTS', 'PENDING', '미조사', 1), +('SRVY_STTS', 'PROGRESS', '진행중', 2), +('SRVY_STTS', 'COMPLETED', '완료', 3); + +-- 조사유형 (SRVY_TP) +INSERT INTO wing.cmn_cd_grp (cmn_cd_grp_id, cmn_cd_grp_nm, grp_dc) VALUES +('SRVY_TP', '조사유형', 'SCAT 조사의 유형을 구분하는 코드'); +INSERT INTO wing.cmn_cd (cmn_cd_grp_id, cmn_cd, cmn_cd_nm, sort_ord) VALUES +('SRVY_TP', 'PRE_SCAT', 'Pre-SCAT', 1), +('SRVY_TP', 'SCAT', 'SCAT', 2), +('SRVY_TP', 'POST_SCAT', 'Post-SCAT', 3); + +-- 조사단계 (SRVY_PHASE) +INSERT INTO wing.cmn_cd_grp (cmn_cd_grp_id, cmn_cd_grp_nm, grp_dc) VALUES +('SRVY_PHASE', '조사단계', 'SCAT 조사의 단계를 구분하는 코드'); +INSERT INTO wing.cmn_cd (cmn_cd_grp_id, cmn_cd, cmn_cd_nm, sort_ord) VALUES +('SRVY_PHASE', 'PHASE_1', '1차 조사', 1), +('SRVY_PHASE', 'PHASE_2', '2차 조사', 2), +('SRVY_PHASE', 'PHASE_3', '3차 조사', 3); + +-- 오염도 (POLUT) +INSERT INTO wing.cmn_cd_grp (cmn_cd_grp_id, cmn_cd_grp_nm, grp_dc) VALUES +('POLUT', '오염도', '해안구간의 오염 정도를 구분하는 코드'); +INSERT INTO wing.cmn_cd (cmn_cd_grp_id, cmn_cd, cmn_cd_nm, sort_ord) VALUES +('POLUT', 'HEAVY', '심각', 1), +('POLUT', 'MODERATE', '보통', 2), +('POLUT', 'LIGHT', '경미', 3), +('POLUT', 'CLEAN', '깨끗', 4); + +-- 물리상태 (PHYS_STATE) +INSERT INTO wing.cmn_cd_grp (cmn_cd_grp_id, cmn_cd_grp_nm, grp_dc) VALUES +('PHYS_STATE', '물리상태', '화학물질의 물리적 상태를 구분하는 코드'); +INSERT INTO wing.cmn_cd (cmn_cd_grp_id, cmn_cd, cmn_cd_nm, sort_ord) VALUES +('PHYS_STATE', 'SOLID', '고체', 1), +('PHYS_STATE', 'LIQUID', '액체', 2), +('PHYS_STATE', 'GAS', '기체', 3); + +-- 수용성 (WATER_SLBLT) +INSERT INTO wing.cmn_cd_grp (cmn_cd_grp_id, cmn_cd_grp_nm, grp_dc) VALUES +('WATER_SLBLT', '수용성', '화학물질의 수용성 정도를 구분하는 코드'); +INSERT INTO wing.cmn_cd (cmn_cd_grp_id, cmn_cd, cmn_cd_nm, sort_ord) VALUES +('WATER_SLBLT', 'SOLUBLE', '수용성', 1), +('WATER_SLBLT', 'PARTIAL', '부분용해', 2), +('WATER_SLBLT', 'INSOLUBLE', '불용성', 3); + +-- 독성 (TOXICITY) +INSERT INTO wing.cmn_cd_grp (cmn_cd_grp_id, cmn_cd_grp_nm, grp_dc) VALUES +('TOXICITY', '독성', '화학물질의 독성 수준을 구분하는 코드'); +INSERT INTO wing.cmn_cd (cmn_cd_grp_id, cmn_cd, cmn_cd_nm, sort_ord) VALUES +('TOXICITY', 'HIGH', '고독성', 1), +('TOXICITY', 'MEDIUM', '중독성', 2), +('TOXICITY', 'LOW', '저독성', 3); diff --git a/database/seed/08_organizations.sql b/database/seed/08_organizations.sql new file mode 100644 index 0000000..00c2602 --- /dev/null +++ b/database/seed/08_organizations.sql @@ -0,0 +1,16 @@ +-- WING-OPS Seed Data: Organizations +-- wing.org 6건 +-- 출처: database/init.sql + +INSERT INTO wing.org (org_nm, org_abbr_nm, org_tp_cd) VALUES +('해양경찰청', '해경청', 'HEADQUARTERS'), +('남해지방해양경찰청', '남해청', 'REGIONAL'), +('제주지방해양경찰청', '제주청', 'REGIONAL'), +('여수해양경찰서', '여수서', 'STATION'), +('서귀포해양경찰서', '서귀포서', 'STATION'), +('제주해양경찰서', '제주서', 'STATION'); + +-- 상위조직 설정 (org_sn은 SERIAL 순서: 1=해경청, 2=남해청, 3=제주청, 4=여수서, 5=서귀포서, 6=제주서) +UPDATE wing.org SET upper_org_sn = 1 WHERE org_sn IN (2, 3); +UPDATE wing.org SET upper_org_sn = 2 WHERE org_sn = 4; +UPDATE wing.org SET upper_org_sn = 3 WHERE org_sn IN (5, 6); diff --git a/database/seed/09_layers.sql b/database/seed/09_layers.sql new file mode 100644 index 0000000..743d361 --- /dev/null +++ b/database/seed/09_layers.sql @@ -0,0 +1,4 @@ +-- WING-OPS Seed Data: Layers +-- layer 테이블 데이터는 Node.js 스크립트로 삽입됩니다. +-- 실행: cd backend && npm run db:seed +-- 소스: backend/src/db/seed.ts (CSV 파싱 → 160건 INSERT) diff --git a/database/seed/10_hns_substances.sql b/database/seed/10_hns_substances.sql new file mode 100644 index 0000000..0773f49 --- /dev/null +++ b/database/seed/10_hns_substances.sql @@ -0,0 +1,4 @@ +-- WING-OPS Seed Data: HNS Substances +-- hns_substance 테이블 데이터는 Node.js 스크립트로 삽입됩니다. +-- 실행: cd backend && npm run db:seed +-- 소스: backend/src/db/seedHns.ts (프론트엔드 데이터 → 1,316건 INSERT) diff --git a/database/seed/11_sample_accident.sql b/database/seed/11_sample_accident.sql new file mode 100644 index 0000000..74e41b1 --- /dev/null +++ b/database/seed/11_sample_accident.sql @@ -0,0 +1,29 @@ +-- WING-OPS Seed Data: Sample Accident +-- 샘플 사고 및 유출정보 +-- 출처: database/init.sql +-- 주의: 08_organizations.sql 실행 후 실행 (org_sn=4 여수해양경찰서 참조) +-- 06_auth_admin.sql 실행 후 실행 (rgtr_id 생략, NULL로 삽입) + +-- 샘플 사고 (여수 앞바다) +INSERT INTO wing.acdnt (acdnt_cd, acdnt_nm, acdnt_tp_cd, loc_geom, loc_dc, occrn_dtm, rspns_org_sn, svrt_cd) VALUES +('INC-2025-0042', + '여수 앞바다 유조선 충돌', + 'COLLISION', + ST_SetSRID(ST_MakePoint(127.8, 34.5), 4326), + '여수 돌산 남방 5NM', + '2025-02-10 06:30:00+09', + 4, + 'DANGER'); + +-- 유출정보 (샘플 사고 연결) +INSERT INTO wing.spil_data (acdnt_sn, oil_tp_cd, spil_qty, spil_unit_cd, spil_tp_cd, spil_loc_geom, fcst_hr) +SELECT + acdnt_sn, + 'BUNKER_C', + 150.00, + 'KL', + 'INSTANTANEOUS', + ST_SetSRID(ST_MakePoint(127.8, 34.5), 4326), + 72 +FROM wing.acdnt +WHERE acdnt_cd = 'INC-2025-0042'; diff --git a/database/seed/12_assets.sql b/database/seed/12_assets.sql new file mode 100644 index 0000000..e1c90c8 --- /dev/null +++ b/database/seed/12_assets.sql @@ -0,0 +1,999 @@ +-- WING-OPS Seed Data: Assets (기관, 장비, 연락처) +-- Source: database/migration/008_assets_seed.sql + +-- 자동 생성: assetMockData.ts → SQL INSERT +-- 생성일: 2026-02-28 + +SET search_path TO wing; + +-- ============================================================ +-- ASSET_ORG 초기 데이터 (84건) +-- ============================================================ +INSERT INTO ASSET_ORG (ORG_TP, JRSD_NM, AREA_NM, ORG_NM, ADDR, TEL, LAT, LNG, PIN_SIZE, VESSEL_CNT, SKIMMER_CNT, PUMP_CNT, VEHICLE_CNT, SPRAYER_CNT, TOTAL_ASSETS) VALUES + ('해경관할', '중부지방해양경찰청', '인천', '인천해양경찰서', '인천광역시 중구 북성동1가 80-8', '010-4779-4191', 37.4563, 126.5922, 'hq', 19, 30, 18, 2, 15, 234), + ('해경경찰서', '중부지방해양경찰청', '평택', '평택해양경찰서', '평택시 만호리 706번지', '010-9812-8102', 36.9694, 126.83, 'lg', 14, 27, 33, 3, 22, 193), + ('해경경찰서', '중부지방해양경찰청', '태안', '태안해양경찰서', '충남 태안군 근흥면 신진부두길2', '010-2965-4423', 36.7456, 126.2978, 'lg', 10, 27, 21, 8, 15, 185), + ('파출소', '중부지방해양경찰청', '보령', '보령해양경찰서', '보령시 해안로 740', '010-2940-6343', 36.3335, 126.5874, 'md', 3, 8, 5, 3, 11, 80), + ('해경관할', '서해지방해양경찰청', '여수', '여수해양경찰서', '광양시 항만9로 89', '010-2785-2493', 34.7407, 127.7385, 'hq', 55, 92, 63, 12, 47, 464), + ('해경경찰서', '서해지방해양경찰청', '목포', '목포해양경찰서', '목포시 고하대로 597번길 99-64', '010-9812-8439', 34.7936, 126.3839, 'lg', 10, 19, 18, 3, 16, 169), + ('해경경찰서', '서해지방해양경찰청', '군산', '군산해양경찰서', '전북 군산시 오식도동 506', '010-2618-3406', 35.99, 126.7133, 'lg', 6, 22, 12, 3, 17, 155), + ('해경경찰서', '서해지방해양경찰청', '완도', '완도해양경찰서', '완도군 완도읍 장보고대로 383', '061-550-2183', 34.311, 126.755, 'lg', 3, 9, 7, 3, 11, 75), + ('파출소', '서해지방해양경찰청', '부안', '부안해양경찰서', '전북 군산시 오식도동 506', '063-928-xxxx', 35.7316, 126.7328, 'md', 2, 8, 7, 2, 7, 66), + ('해경관할', '남해지방해양경찰청', '부산', '부산해양경찰서', '부산시 영도구 해양로 293', '010-2609-1456', 35.0746, 129.0686, 'hq', 108, 22, 25, 10, 24, 313), + ('해경관할', '남해지방해양경찰청', '울산', '울산해양경찰서', '울산광역시 남구 장생포 고래로 166', '010-9812-8210', 35.5008, 129.3824, 'hq', 46, 69, 26, 11, 20, 311), + ('해경경찰서', '남해지방해양경찰청', '창원', '창원해양경찰서', '창원시 마산합포구 신포동 1가', '010-4634-7364', 35.1796, 128.5681, 'lg', 12, 25, 14, 10, 10, 139), + ('해경경찰서', '남해지방해양경찰청', '통영', '통영해양경찰서', '통영시 광도면 죽림리 1564-4', '010-9812-8495', 34.8544, 128.4331, 'lg', 6, 15, 9, 5, 13, 104), + ('해경경찰서', '남해지방해양경찰청', '사천', '사천해양경찰서', '사천시 신항만길 1길 17', '010-9812-8352', 34.931, 128.066, 'lg', 2, 9, 6, 2, 7, 80), + ('해경경찰서', '동해지방해양경찰청', '동해', '동해해양경찰서', '동해시 임항로 130', '010-9812-8073', 37.5247, 129.1143, 'lg', 6, 23, 11, 6, 14, 156), + ('해경경찰서', '동해지방해양경찰청', '포항', '포항해양경찰서', '포항시 남구 희망대로 1341', '010-3108-2183', 36.019, 129.3651, 'lg', 10, 13, 21, 4, 21, 135), + ('파출소', '동해지방해양경찰청', '속초', '속초해양경찰서', '속초시 설악금강대교로 206', '033-634-2186', 38.207, 128.5918, 'md', 2, 6, 4, 1, 17, 85), + ('파출소', '동해지방해양경찰청', '울진', '울진해양경찰서', '울진군 후포면 후포리 623-148', '010-9812-8076', 36.9932, 129.4003, 'md', 2, 6, 4, 1, 8, 66), + ('해경경찰서', '제주지방해양경찰청', '제주', '제주해양경찰서', '제주시 임항로 85', '064-766-2691', 33.5154, 126.5268, 'lg', 4, 21, 17, 3, 16, 113), + ('해경경찰서', '제주지방해양경찰청', '서귀포', '서귀포해양경찰서', '서귀포시 안덕면 화순해안로69', '064-793-2186', 33.2469, 126.56, 'lg', 3, 9, 15, 3, 14, 67), + ('관련기관', '해양경찰청(중앙)', '중앙', '중앙특수구조단', '부산광역시 영도구 해양로 301', '051-580-2044', 35.058, 129.059, 'md', 1, 0, 5, 2, 0, 39), + ('기름저장시설', '서해지방해양경찰청', '여수', '오일허브코리아여수㈜ 외 4개', '전남 여수시 신덕동 325', '061-686-3611', 34.745, 127.745, 'md', 0, 0, 0, 0, 0, 1), + ('기름저장시설', '남해지방해양경찰청', '부산', 'SK에너지 외 2개', '부산시 영도구 해양로 1', '051-643-3331', 35.175, 129.075, 'md', 0, 0, 0, 0, 0, 3), + ('기름저장시설', '남해지방해양경찰청', '울산', 'SK지오센트릭 외 5개', '울산광역시 남구 신여천로 2', '052-208-2851', 35.535, 129.305, 'md', 0, 0, 0, 0, 0, 9), + ('기름저장시설', '남해지방해양경찰청', '통영', '한국가스공사 통영기지본부', '통영시 광도면 안정로 770', '055-640-6014', 35.05, 128.41, 'md', 0, 0, 0, 0, 0, 1), + ('기름저장시설', '동해지방해양경찰청', '동해', 'HD현대오일뱅크㈜ 외 4개', '강릉시 옥계면 동해대로 206', '033-534-2093', 37.52, 129.11, 'md', 0, 0, 0, 0, 0, 9), + ('기름저장시설', '동해지방해양경찰청', '포항', '포스코케미칼 외 1개', '포항시 남구 동해안로 6262', '054-290-8222', 37.73, 129.01, 'md', 0, 0, 0, 0, 1, 2), + ('기름저장시설', '서해지방해양경찰청', '목포', '흑산도내연발전소 외 2개', '전남 신안군 흑산일주로70', '061-351-2342', 35.04, 126.58, 'md', 0, 0, 0, 0, 0, 4), + ('기름저장시설', '서해지방해양경찰청', '여수', '오일허브코리아여수㈜ 외 4개', '전남 여수시 신덕동 325', '061-686-3611', 34.75, 127.735, 'md', 0, 0, 0, 0, 0, 4), + ('기름저장시설', '중부지방해양경찰청', '인천', 'GS칼텍스㈜ 외 10개', '인천광역시 중구 월미로 182', '010-8777-6922', 37.45, 126.505, 'md', 0, 0, 0, 0, 0, 13), + ('기름저장시설', '중부지방해양경찰청', '태안', 'HD현대케미칼 외 4개', '충남 서산시 대산읍 평신2로 26', '041-924-1068', 36.91, 126.415, 'md', 0, 0, 0, 0, 0, 4), + ('기름저장시설', '중부지방해양경찰청', '평택', '현대오일터미널(주) 외 4개', '평택시 포승읍 포승공단순환로 11', '031-683-5101', 36.985, 126.835, 'md', 0, 0, 0, 0, 1, 4), + ('기타', '남해지방해양경찰청', '사천', '한국남동발전(주) 외 2개', '고성군 하이면 하이로1', '070-4486-7474', 34.965, 128.56, 'md', 0, 0, 0, 0, 0, 8), + ('기타', '남해지방해양경찰청', '울산', 'HD현대미포', '울산광역시 동구 방어진순환도로100', '052-250-3551', 35.53, 129.315, 'md', 0, 0, 0, 0, 0, 1), + ('기타', '남해지방해양경찰청', '통영', '삼성중공업 외 1개', '거제시 장평3로 80', '055-630-5373', 35.05, 128.41, 'md', 0, 0, 0, 0, 0, 2), + ('기타', '동해지방해양경찰청', '동해', '한국남부발전㈜', '삼척시 원덕읍 삼척로 734', '070-7713-5153', 37.45, 129.17, 'md', 0, 0, 0, 0, 0, 2), + ('기타', '동해지방해양경찰청', '울진', '한울원전', '울진군 북면 울진북로 2040', '054-785-4833', 37.065, 129.39, 'md', 0, 0, 0, 0, 0, 4), + ('기타', '서해지방해양경찰청', '여수', '㈜HR-PORT 외 5개', '여수시 제철로', '061-791-0358', 34.755, 127.73, 'md', 0, 0, 0, 0, 0, 16), + ('기타', '중부지방해양경찰청', '인천', '삼광조선공업㈜ 외 1개', '인천 동구 보세로42번길41', '010-3321-2959', 37.45, 126.505, 'md', 0, 0, 0, 0, 0, 5), + ('업체', '중부지방해양경찰청', '인천', '방제유창청소업체(㈜클린포트)', '㈜클린포트', '032-882-8279', 37.45, 126.505, 'md', 0, 0, 0, 0, 0, 3), + ('업체', '남해지방해양경찰청', '부산', '방제유창청소업체(대용환경㈜ 외 38개)', '㈜태평양해양산업', '051-242-0622', 35.18, 129.085, 'md', 0, 0, 0, 0, 0, 51), + ('업체', '남해지방해양경찰청', '울산', '방제유창청소업체((주)한유마린서비스 외 8개)', '대상해운(주)', '010-5499-7401', 35.54, 129.295, 'md', 0, 0, 0, 0, 0, 12), + ('업체', '동해지방해양경찰청', '포항', '방제유창청소업체(블루씨 외 1개)', '(주)블루씨', '054-278-8200', 36.015, 129.365, 'md', 0, 0, 0, 0, 1, 3), + ('업체', '서해지방해양경찰청', '목포', '방제유창청소업체(㈜한국해운 외 1개)', '㈜한국해운 목포지사', '010-8615-4326', 35.04, 126.58, 'md', 0, 0, 0, 0, 0, 1), + ('업체', '서해지방해양경찰청', '여수', '방제유창청소업체(마로해운 외 11개)', '㈜우진실업', '061-654-9603', 34.74, 127.75, 'md', 0, 0, 0, 0, 2, 54), + ('업체', '중부지방해양경찰청', '태안', '방제유창청소업체(우진해운㈜)', '우진해운㈜', '010-4384-6817', 36.905, 126.42, 'md', 0, 0, 0, 0, 0, 6), + ('업체', '중부지방해양경찰청', '평택', '방제유창청소업체((주)씨앤 외 3개)', '㈜씨앤', '031-683-2389', 36.99, 126.825, 'md', 0, 0, 0, 0, 0, 6), + ('업체', '남해지방해양경찰청', '부산', '방제유창청소업체(㈜지앤비마린서비스)', '㈜지앤비마린서비스', '051-242-0622', 35.185, 129.07, 'md', 0, 0, 0, 0, 0, 1), + ('정유사', '남해지방해양경찰청', '울산', 'SK엔텀(주) 외 4개', '울산광역시 남구 고사동 110-64', '052-231-2318', 35.545, 129.31, 'md', 0, 0, 0, 0, 0, 5), + ('정유사', '서해지방해양경찰청', '여수', 'GS칼텍스㈜', '여수시 낙포단지길 251', '061-680-2121', 34.735, 127.755, 'md', 0, 0, 0, 0, 20, 27), + ('정유사', '중부지방해양경찰청', '태안', 'HD현대오일뱅크㈜', '서산시 대산읍 평신2로 182', '010-2050-5291', 36.915, 126.41, 'md', 0, 0, 0, 0, 0, 2), + ('지자체', '남해지방해양경찰청', '부산', '부산광역시 외 8개', '부산광역시 동구 좌천동', '051-607-4484', 35.17, 129.08, 'md', 0, 0, 0, 0, 0, 12), + ('지자체', '남해지방해양경찰청', '사천', '사천시 외 3개', '사천시 신항로 3', '055-670-2484', 34.935, 128.075, 'md', 0, 0, 0, 0, 0, 6), + ('지자체', '남해지방해양경찰청', '울산', '울산북구청 외 2개', '울산광역시 북구 구유동 654-2', '051-709-4611', 35.55, 129.32, 'md', 0, 0, 0, 0, 0, 4), + ('지자체', '남해지방해양경찰청', '창원', '창원 진해구 외 1개', '창원시 진해구 천자로 105', '051-970-4482', 35.055, 128.645, 'md', 0, 0, 0, 0, 0, 2), + ('지자체', '동해지방해양경찰청', '동해', '삼척시 외 1개', '삼척시 근덕면 덕산리 107-74', '033-640-5284', 37.45, 129.16, 'md', 0, 0, 0, 0, 0, 4), + ('지자체', '동해지방해양경찰청', '울진', '영덕군', '남정면 장사리 74-1', '054-730-6562', 36.35, 129.4, 'md', 0, 0, 0, 0, 0, 3), + ('지자체', '서해지방해양경찰청', '목포', '영광군 외 1개', '영광군 염산면 향화로', '061-270-3419', 35.04, 126.58, 'md', 0, 0, 0, 0, 0, 5), + ('지자체', '서해지방해양경찰청', '여수', '광양시 외 1개', '순천시 진상면 성지로 8', '061-797-2791', 34.76, 127.725, 'md', 0, 0, 0, 0, 0, 3), + ('지자체', '중부지방해양경찰청', '인천', '옹진군청 외 4개', '인천광역시 옹진군 덕적면 진리 387', '010-2740-9388', 37.45, 126.505, 'md', 0, 0, 0, 0, 0, 10), + ('지자체', '중부지방해양경찰청', '태안', '태안군청', '충남 태안군 근흥면 신진도리 75-36', '041-670-2877', 36.745, 126.305, 'md', 0, 0, 0, 0, 0, 1), + ('지자체', '중부지방해양경찰청', '평택', '안산시청 외 2개', '경기도 안산시 단원구 진두길 97', '041-350-4292', 37.32, 126.83, 'md', 0, 0, 0, 0, 0, 6), + ('기타', '서해지방해양경찰청', '여수', '㈜HR-PORT 외 5개', '여수시 제철로', '061-791-0358', 34.748, 127.74, 'md', 0, 0, 0, 0, 0, 1), + ('해군', '동해지방해양경찰청', '동해', '해군1함대사령부 외 1개', '동해시 대동로 430', '033-539-7323', 37.525, 129.115, 'md', 0, 0, 0, 0, 0, 1), + ('해군', '중부지방해양경찰청', '인천', '해병대 제9518부대', '인천광역시 옹진군', '010-4801-3473', 37.45, 126.505, 'md', 0, 0, 0, 0, 0, 2), + ('해양환경공단', '남해지방해양경찰청', '부산', '부산지사', '창원시 진해구 안골동', '051-466-3944', 35.105, 128.715, 'md', 0, 0, 0, 0, 6, 14), + ('해양환경공단', '남해지방해양경찰청', '사천', '마산지사', '사천시 신항만1길 23', '010-3598-4202', 34.925, 128.065, 'md', 0, 0, 0, 0, 0, 9), + ('해양환경공단', '남해지방해양경찰청', '울산', '울산지사', '울산광역시 남구 장생포고래로 276번길 27', '052-238-7718', 35.538, 129.3, 'md', 0, 0, 0, 0, 1, 16), + ('해양환경공단', '남해지방해양경찰청', '창원', '마산지사', '창원시 마산합포구 드림베이대로59', '010-2265-3928', 35.055, 128.645, 'md', 0, 0, 0, 0, 1, 7), + ('해양환경공단', '남해지방해양경찰청', '통영', '마산지사', '거제시 장승로 112', '010-2636-5313', 35.05, 128.41, 'md', 0, 0, 0, 0, 0, 8), + ('해양환경공단', '동해지방해양경찰청', '동해', '동해지사', '동해시 대동로 210', '010-7499-0257', 37.515, 129.105, 'md', 0, 0, 0, 0, 2, 17), + ('해양환경공단', '동해지방해양경찰청', '울진', '포항지사', '울진군 죽변면 죽변리 36-88', '054-273-5595', 37.06, 129.42, 'md', 0, 0, 0, 0, 0, 2), + ('해양환경공단', '동해지방해양경찰청', '포항', '포항지사', '포항시 북구 해안로 44-10', '054-273-5595', 36.025, 129.375, 'md', 0, 0, 0, 0, 2, 8), + ('해양환경공단', '서해지방해양경찰청', '군산', '군산지사', '군산시 임해로 452', '063-443-4813', 35.975, 126.715, 'md', 0, 0, 0, 0, 4, 12), + ('해양환경공단', '서해지방해양경찰청', '목포', '목포지사', '전남 목포시 죽교동 683', '061-242-9663', 35.04, 126.58, 'md', 0, 0, 0, 0, 0, 10), + ('해양환경공단', '서해지방해양경찰청', '여수', '여수지사', '여수시 덕충동', '061-654-6431', 34.742, 127.748, 'md', 0, 0, 0, 0, 3, 12), + ('해양환경공단', '서해지방해양경찰청', '완도', '목포지사 완도사업소', '완도군 완도읍 해변공원로 20-1', '061-242-9663', 34.315, 126.755, 'md', 0, 0, 0, 0, 0, 3), + ('해양환경공단', '제주지방해양경찰청', '서귀포', '제주지사(서귀포)', '서귀포시 칠십리로72번길 14', '064-753-4356', 33.245, 126.565, 'md', 0, 0, 0, 0, 1, 1), + ('해양환경공단', '제주지방해양경찰청', '제주', '제주지사(제주)', '제주시 임항로97', '064-753-4356', 33.517, 126.528, 'md', 0, 0, 0, 0, 3, 20), + ('해양환경공단', '중부지방해양경찰청', '보령', '대산지사(보령)', '보령시 해안로 740', '041-664-9101', 36.333, 126.612, 'md', 0, 0, 0, 0, 0, 5), + ('해양환경공단', '중부지방해양경찰청', '인천', '인천지사', '인천광역시 중구 연안부두로 128번길 35', '010-7133-2167', 37.45, 126.505, 'md', 0, 0, 0, 0, 0, 11), + ('해양환경공단', '중부지방해양경찰청', '태안', '대산지사(태안)', '서산시 대산읍 대죽1로 325', '041-664-9101', 36.908, 126.413, 'md', 0, 0, 0, 0, 1, 17), + ('해양환경공단', '중부지방해양경찰청', '평택', '평택지사', '당진시 송악읍 고대공단2길', '031-683-7973', 36.905, 126.635, 'md', 0, 0, 0, 0, 1, 13), + ('기타', '남해지방해양경찰청', '통영', '삼성중공업 외 1개', '거제시 장평3로 80', '055-630-5373', 35.05, 128.41, 'md', 0, 0, 0, 0, 0, 1); + +-- ============================================================ +-- ASSET_EQUIP 초기 데이터 +-- ============================================================ +-- 인천해양경찰서 +INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '인천해양경찰서' LIMIT 1), '방제선', '🚢', 19, 0), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '인천해양경찰서' LIMIT 1), '유회수기', '⚙', 30, 1), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '인천해양경찰서' LIMIT 1), '비치크리너', '🏖', 2, 2), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '인천해양경찰서' LIMIT 1), '이송펌프', '🔧', 18, 3), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '인천해양경찰서' LIMIT 1), '방제차량', '🚛', 2, 4), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '인천해양경찰서' LIMIT 1), '해안운반차', '🚜', 1, 5), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '인천해양경찰서' LIMIT 1), '고압세척기', '💧', 26, 6), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '인천해양경찰서' LIMIT 1), '저압세척기', '🚿', 3, 7), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '인천해양경찰서' LIMIT 1), '동력분무기', '💨', 14, 8), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '인천해양경찰서' LIMIT 1), '유량계측기', '📏', 1, 9), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '인천해양경찰서' LIMIT 1), '방제창고', '🏭', 19, 10), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '인천해양경찰서' LIMIT 1), '발전기', '⚡', 9, 11), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '인천해양경찰서' LIMIT 1), '현장지휘소', '🏕', 2, 12), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '인천해양경찰서' LIMIT 1), '지원장비', '🔩', 9, 13), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '인천해양경찰서' LIMIT 1), '장비부품', '🔗', 46, 14), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '인천해양경찰서' LIMIT 1), '경비함정방제', '⚓', 18, 15), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '인천해양경찰서' LIMIT 1), '살포장치', '🌊', 15, 16); + +-- 평택해양경찰서 +INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '평택해양경찰서' LIMIT 1), '방제선', '🚢', 14, 0), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '평택해양경찰서' LIMIT 1), '유회수기', '⚙', 27, 1), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '평택해양경찰서' LIMIT 1), '비치크리너', '🏖', 1, 2), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '평택해양경찰서' LIMIT 1), '이송펌프', '🔧', 33, 3), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '평택해양경찰서' LIMIT 1), '방제차량', '🚛', 3, 4), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '평택해양경찰서' LIMIT 1), '해안운반차', '🚜', 1, 5), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '평택해양경찰서' LIMIT 1), '고압세척기', '💧', 12, 6), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '평택해양경찰서' LIMIT 1), '저압세척기', '🚿', 5, 7), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '평택해양경찰서' LIMIT 1), '동력분무기', '💨', 2, 8), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '평택해양경찰서' LIMIT 1), '유량계측기', '📏', 1, 9), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '평택해양경찰서' LIMIT 1), '방제창고', '🏭', 35, 10), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '평택해양경찰서' LIMIT 1), '발전기', '⚡', 9, 11), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '평택해양경찰서' LIMIT 1), '지원장비', '🔩', 10, 12), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '평택해양경찰서' LIMIT 1), '장비부품', '🔗', 4, 13), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '평택해양경찰서' LIMIT 1), '경비함정방제', '⚓', 14, 14), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '평택해양경찰서' LIMIT 1), '살포장치', '🌊', 22, 15); + +-- 태안해양경찰서 +INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '태안해양경찰서' LIMIT 1), '방제선', '🚢', 10, 0), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '태안해양경찰서' LIMIT 1), '유회수기', '⚙', 27, 1), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '태안해양경찰서' LIMIT 1), '비치크리너', '🏖', 4, 2), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '태안해양경찰서' LIMIT 1), '이송펌프', '🔧', 21, 3), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '태안해양경찰서' LIMIT 1), '방제차량', '🚛', 8, 4), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '태안해양경찰서' LIMIT 1), '해안운반차', '🚜', 8, 5), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '태안해양경찰서' LIMIT 1), '고압세척기', '💧', 14, 6), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '태안해양경찰서' LIMIT 1), '저압세척기', '🚿', 8, 7), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '태안해양경찰서' LIMIT 1), '동력분무기', '💨', 6, 8), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '태안해양경찰서' LIMIT 1), '유량계측기', '📏', 1, 9), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '태안해양경찰서' LIMIT 1), '방제창고', '🏭', 28, 10), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '태안해양경찰서' LIMIT 1), '발전기', '⚡', 11, 11), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '태안해양경찰서' LIMIT 1), '지원장비', '🔩', 16, 12), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '태안해양경찰서' LIMIT 1), '경비함정방제', '⚓', 8, 13), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '태안해양경찰서' LIMIT 1), '살포장치', '🌊', 15, 14); + +-- 보령해양경찰서 +INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '보령해양경찰서' LIMIT 1), '방제선', '🚢', 3, 0), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '보령해양경찰서' LIMIT 1), '유회수기', '⚙', 8, 1), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '보령해양경찰서' LIMIT 1), '이송펌프', '🔧', 5, 2), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '보령해양경찰서' LIMIT 1), '방제차량', '🚛', 3, 3), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '보령해양경찰서' LIMIT 1), '해안운반차', '🚜', 1, 4), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '보령해양경찰서' LIMIT 1), '고압세척기', '💧', 5, 5), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '보령해양경찰서' LIMIT 1), '저압세척기', '🚿', 2, 6), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '보령해양경찰서' LIMIT 1), '동력분무기', '💨', 1, 7), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '보령해양경찰서' LIMIT 1), '유량계측기', '📏', 1, 8), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '보령해양경찰서' LIMIT 1), '방제창고', '🏭', 22, 9), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '보령해양경찰서' LIMIT 1), '발전기', '⚡', 2, 10), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '보령해양경찰서' LIMIT 1), '지원장비', '🔩', 6, 11), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '보령해양경찰서' LIMIT 1), '장비부품', '🔗', 4, 12), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '보령해양경찰서' LIMIT 1), '경비함정방제', '⚓', 6, 13), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '보령해양경찰서' LIMIT 1), '살포장치', '🌊', 11, 14); + +-- 여수해양경찰서 +INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '여수해양경찰서' LIMIT 1), '방제선', '🚢', 55, 0), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '여수해양경찰서' LIMIT 1), '유회수기', '⚙', 92, 1), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '여수해양경찰서' LIMIT 1), '비치크리너', '🏖', 5, 2), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '여수해양경찰서' LIMIT 1), '이송펌프', '🔧', 63, 3), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '여수해양경찰서' LIMIT 1), '방제차량', '🚛', 12, 4), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '여수해양경찰서' LIMIT 1), '해안운반차', '🚜', 4, 5), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '여수해양경찰서' LIMIT 1), '고압세척기', '💧', 48, 6), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '여수해양경찰서' LIMIT 1), '저압세척기', '🚿', 7, 7), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '여수해양경찰서' LIMIT 1), '동력분무기', '💨', 25, 8), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '여수해양경찰서' LIMIT 1), '유량계측기', '📏', 1, 9), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '여수해양경찰서' LIMIT 1), '방제창고', '🏭', 37, 10), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '여수해양경찰서' LIMIT 1), '발전기', '⚡', 16, 11), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '여수해양경찰서' LIMIT 1), '현장지휘소', '🏕', 2, 12), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '여수해양경찰서' LIMIT 1), '지원장비', '🔩', 14, 13), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '여수해양경찰서' LIMIT 1), '장비부품', '🔗', 14, 14), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '여수해양경찰서' LIMIT 1), '경비함정방제', '⚓', 22, 15), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '여수해양경찰서' LIMIT 1), '살포장치', '🌊', 47, 16); + +-- 목포해양경찰서 +INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '목포해양경찰서' LIMIT 1), '방제선', '🚢', 10, 0), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '목포해양경찰서' LIMIT 1), '유회수기', '⚙', 19, 1), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '목포해양경찰서' LIMIT 1), '이송펌프', '🔧', 18, 2), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '목포해양경찰서' LIMIT 1), '방제차량', '🚛', 3, 3), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '목포해양경찰서' LIMIT 1), '해안운반차', '🚜', 1, 4), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '목포해양경찰서' LIMIT 1), '고압세척기', '💧', 7, 5), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '목포해양경찰서' LIMIT 1), '저압세척기', '🚿', 4, 6), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '목포해양경찰서' LIMIT 1), '동력분무기', '💨', 2, 7), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '목포해양경찰서' LIMIT 1), '유량계측기', '📏', 1, 8), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '목포해양경찰서' LIMIT 1), '방제창고', '🏭', 21, 9), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '목포해양경찰서' LIMIT 1), '발전기', '⚡', 4, 10), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '목포해양경찰서' LIMIT 1), '지원장비', '🔩', 31, 11), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '목포해양경찰서' LIMIT 1), '장비부품', '🔗', 17, 12), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '목포해양경찰서' LIMIT 1), '경비함정방제', '⚓', 15, 13), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '목포해양경찰서' LIMIT 1), '살포장치', '🌊', 16, 14); + +-- 군산해양경찰서 +INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '군산해양경찰서' LIMIT 1), '방제선', '🚢', 6, 0), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '군산해양경찰서' LIMIT 1), '유회수기', '⚙', 22, 1), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '군산해양경찰서' LIMIT 1), '비치크리너', '🏖', 2, 2), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '군산해양경찰서' LIMIT 1), '이송펌프', '🔧', 12, 3), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '군산해양경찰서' LIMIT 1), '방제차량', '🚛', 3, 4), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '군산해양경찰서' LIMIT 1), '해안운반차', '🚜', 1, 5), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '군산해양경찰서' LIMIT 1), '고압세척기', '💧', 5, 6), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '군산해양경찰서' LIMIT 1), '저압세척기', '🚿', 4, 7), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '군산해양경찰서' LIMIT 1), '동력분무기', '💨', 2, 8), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '군산해양경찰서' LIMIT 1), '유량계측기', '📏', 1, 9), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '군산해양경찰서' LIMIT 1), '방제창고', '🏭', 6, 10), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '군산해양경찰서' LIMIT 1), '발전기', '⚡', 5, 11), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '군산해양경찰서' LIMIT 1), '현장지휘소', '🏕', 3, 12), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '군산해양경찰서' LIMIT 1), '지원장비', '🔩', 11, 13), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '군산해양경찰서' LIMIT 1), '장비부품', '🔗', 50, 14), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '군산해양경찰서' LIMIT 1), '경비함정방제', '⚓', 5, 15), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '군산해양경찰서' LIMIT 1), '살포장치', '🌊', 17, 16); + +-- 완도해양경찰서 +INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '완도해양경찰서' LIMIT 1), '방제선', '🚢', 3, 0), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '완도해양경찰서' LIMIT 1), '유회수기', '⚙', 9, 1), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '완도해양경찰서' LIMIT 1), '이송펌프', '🔧', 7, 2), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '완도해양경찰서' LIMIT 1), '방제차량', '🚛', 3, 3), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '완도해양경찰서' LIMIT 1), '해안운반차', '🚜', 1, 4), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '완도해양경찰서' LIMIT 1), '고압세척기', '💧', 3, 5), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '완도해양경찰서' LIMIT 1), '저압세척기', '🚿', 2, 6), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '완도해양경찰서' LIMIT 1), '동력분무기', '💨', 1, 7), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '완도해양경찰서' LIMIT 1), '유량계측기', '📏', 1, 8), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '완도해양경찰서' LIMIT 1), '방제창고', '🏭', 24, 9), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '완도해양경찰서' LIMIT 1), '발전기', '⚡', 2, 10), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '완도해양경찰서' LIMIT 1), '경비함정방제', '⚓', 8, 11), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '완도해양경찰서' LIMIT 1), '살포장치', '🌊', 11, 12); + +-- 부안해양경찰서 +INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '부안해양경찰서' LIMIT 1), '방제선', '🚢', 2, 0), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '부안해양경찰서' LIMIT 1), '유회수기', '⚙', 8, 1), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '부안해양경찰서' LIMIT 1), '이송펌프', '🔧', 7, 2), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '부안해양경찰서' LIMIT 1), '방제차량', '🚛', 2, 3), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '부안해양경찰서' LIMIT 1), '해안운반차', '🚜', 1, 4), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '부안해양경찰서' LIMIT 1), '고압세척기', '💧', 2, 5), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '부안해양경찰서' LIMIT 1), '저압세척기', '🚿', 2, 6), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '부안해양경찰서' LIMIT 1), '동력분무기', '💨', 1, 7), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '부안해양경찰서' LIMIT 1), '유량계측기', '📏', 1, 8), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '부안해양경찰서' LIMIT 1), '방제창고', '🏭', 15, 9), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '부안해양경찰서' LIMIT 1), '발전기', '⚡', 3, 10), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '부안해양경찰서' LIMIT 1), '현장지휘소', '🏕', 2, 11), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '부안해양경찰서' LIMIT 1), '지원장비', '🔩', 6, 12), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '부안해양경찰서' LIMIT 1), '경비함정방제', '⚓', 7, 13), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '부안해양경찰서' LIMIT 1), '살포장치', '🌊', 7, 14); + +-- 부산해양경찰서 +INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '부산해양경찰서' LIMIT 1), '방제선', '🚢', 108, 0), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '부산해양경찰서' LIMIT 1), '유회수기', '⚙', 22, 1), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '부산해양경찰서' LIMIT 1), '이송펌프', '🔧', 25, 2), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '부산해양경찰서' LIMIT 1), '방제차량', '🚛', 10, 3), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '부산해양경찰서' LIMIT 1), '해안운반차', '🚜', 1, 4), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '부산해양경찰서' LIMIT 1), '고압세척기', '💧', 38, 5), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '부산해양경찰서' LIMIT 1), '저압세척기', '🚿', 8, 6), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '부산해양경찰서' LIMIT 1), '동력분무기', '💨', 6, 7), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '부산해양경찰서' LIMIT 1), '유량계측기', '📏', 1, 8), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '부산해양경찰서' LIMIT 1), '방제창고', '🏭', 21, 9), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '부산해양경찰서' LIMIT 1), '발전기', '⚡', 11, 10), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '부산해양경찰서' LIMIT 1), '현장지휘소', '🏕', 2, 11), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '부산해양경찰서' LIMIT 1), '지원장비', '🔩', 20, 12), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '부산해양경찰서' LIMIT 1), '경비함정방제', '⚓', 16, 13), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '부산해양경찰서' LIMIT 1), '살포장치', '🌊', 24, 14); + +-- 울산해양경찰서 +INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '울산해양경찰서' LIMIT 1), '방제선', '🚢', 46, 0), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '울산해양경찰서' LIMIT 1), '유회수기', '⚙', 69, 1), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '울산해양경찰서' LIMIT 1), '비치크리너', '🏖', 4, 2), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '울산해양경찰서' LIMIT 1), '이송펌프', '🔧', 26, 3), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '울산해양경찰서' LIMIT 1), '방제차량', '🚛', 11, 4), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '울산해양경찰서' LIMIT 1), '해안운반차', '🚜', 5, 5), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '울산해양경찰서' LIMIT 1), '고압세척기', '💧', 23, 6), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '울산해양경찰서' LIMIT 1), '저압세척기', '🚿', 6, 7), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '울산해양경찰서' LIMIT 1), '동력분무기', '💨', 6, 8), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '울산해양경찰서' LIMIT 1), '유량계측기', '📏', 1, 9), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '울산해양경찰서' LIMIT 1), '방제창고', '🏭', 32, 10), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '울산해양경찰서' LIMIT 1), '발전기', '⚡', 7, 11), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '울산해양경찰서' LIMIT 1), '현장지휘소', '🏕', 1, 12), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '울산해양경찰서' LIMIT 1), '지원장비', '🔩', 40, 13), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '울산해양경찰서' LIMIT 1), '경비함정방제', '⚓', 14, 14), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '울산해양경찰서' LIMIT 1), '살포장치', '🌊', 20, 15); + +-- 창원해양경찰서 +INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '창원해양경찰서' LIMIT 1), '방제선', '🚢', 12, 0), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '창원해양경찰서' LIMIT 1), '유회수기', '⚙', 25, 1), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '창원해양경찰서' LIMIT 1), '비치크리너', '🏖', 2, 2), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '창원해양경찰서' LIMIT 1), '이송펌프', '🔧', 14, 3), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '창원해양경찰서' LIMIT 1), '방제차량', '🚛', 10, 4), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '창원해양경찰서' LIMIT 1), '해안운반차', '🚜', 1, 5), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '창원해양경찰서' LIMIT 1), '고압세척기', '💧', 7, 6), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '창원해양경찰서' LIMIT 1), '저압세척기', '🚿', 2, 7), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '창원해양경찰서' LIMIT 1), '동력분무기', '💨', 1, 8), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '창원해양경찰서' LIMIT 1), '유량계측기', '📏', 1, 9), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '창원해양경찰서' LIMIT 1), '방제창고', '🏭', 21, 10), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '창원해양경찰서' LIMIT 1), '발전기', '⚡', 4, 11), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '창원해양경찰서' LIMIT 1), '현장지휘소', '🏕', 2, 12), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '창원해양경찰서' LIMIT 1), '지원장비', '🔩', 20, 13), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '창원해양경찰서' LIMIT 1), '경비함정방제', '⚓', 7, 14), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '창원해양경찰서' LIMIT 1), '살포장치', '🌊', 10, 15); + +-- 통영해양경찰서 +INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '통영해양경찰서' LIMIT 1), '방제선', '🚢', 6, 0), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '통영해양경찰서' LIMIT 1), '유회수기', '⚙', 15, 1), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '통영해양경찰서' LIMIT 1), '비치크리너', '🏖', 2, 2), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '통영해양경찰서' LIMIT 1), '이송펌프', '🔧', 9, 3), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '통영해양경찰서' LIMIT 1), '방제차량', '🚛', 5, 4), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '통영해양경찰서' LIMIT 1), '해안운반차', '🚜', 1, 5), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '통영해양경찰서' LIMIT 1), '고압세척기', '💧', 3, 6), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '통영해양경찰서' LIMIT 1), '저압세척기', '🚿', 2, 7), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '통영해양경찰서' LIMIT 1), '동력분무기', '💨', 1, 8), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '통영해양경찰서' LIMIT 1), '유량계측기', '📏', 1, 9), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '통영해양경찰서' LIMIT 1), '방제창고', '🏭', 18, 10), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '통영해양경찰서' LIMIT 1), '발전기', '⚡', 4, 11), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '통영해양경찰서' LIMIT 1), '현장지휘소', '🏕', 1, 12), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '통영해양경찰서' LIMIT 1), '지원장비', '🔩', 11, 13), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '통영해양경찰서' LIMIT 1), '경비함정방제', '⚓', 12, 14), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '통영해양경찰서' LIMIT 1), '살포장치', '🌊', 13, 15); + +-- 사천해양경찰서 +INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '사천해양경찰서' LIMIT 1), '방제선', '🚢', 2, 0), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '사천해양경찰서' LIMIT 1), '유회수기', '⚙', 9, 1), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '사천해양경찰서' LIMIT 1), '이송펌프', '🔧', 6, 2), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '사천해양경찰서' LIMIT 1), '방제차량', '🚛', 2, 3), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '사천해양경찰서' LIMIT 1), '해안운반차', '🚜', 1, 4), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '사천해양경찰서' LIMIT 1), '고압세척기', '💧', 2, 5), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '사천해양경찰서' LIMIT 1), '저압세척기', '🚿', 2, 6), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '사천해양경찰서' LIMIT 1), '동력분무기', '💨', 4, 7), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '사천해양경찰서' LIMIT 1), '유량계측기', '📏', 1, 8), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '사천해양경찰서' LIMIT 1), '방제창고', '🏭', 31, 9), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '사천해양경찰서' LIMIT 1), '발전기', '⚡', 2, 10), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '사천해양경찰서' LIMIT 1), '현장지휘소', '🏕', 2, 11), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '사천해양경찰서' LIMIT 1), '지원장비', '🔩', 1, 12), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '사천해양경찰서' LIMIT 1), '경비함정방제', '⚓', 8, 13), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '사천해양경찰서' LIMIT 1), '살포장치', '🌊', 7, 14); + +-- 동해해양경찰서 +INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '동해해양경찰서' LIMIT 1), '방제선', '🚢', 6, 0), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '동해해양경찰서' LIMIT 1), '유회수기', '⚙', 23, 1), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '동해해양경찰서' LIMIT 1), '비치크리너', '🏖', 1, 2), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '동해해양경찰서' LIMIT 1), '이송펌프', '🔧', 11, 3), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '동해해양경찰서' LIMIT 1), '방제차량', '🚛', 6, 4), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '동해해양경찰서' LIMIT 1), '해안운반차', '🚜', 3, 5), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '동해해양경찰서' LIMIT 1), '고압세척기', '💧', 5, 6), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '동해해양경찰서' LIMIT 1), '저압세척기', '🚿', 2, 7), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '동해해양경찰서' LIMIT 1), '동력분무기', '💨', 5, 8), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '동해해양경찰서' LIMIT 1), '유량계측기', '📏', 1, 9), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '동해해양경찰서' LIMIT 1), '방제창고', '🏭', 38, 10), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '동해해양경찰서' LIMIT 1), '발전기', '⚡', 2, 11), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '동해해양경찰서' LIMIT 1), '현장지휘소', '🏕', 1, 12), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '동해해양경찰서' LIMIT 1), '지원장비', '🔩', 20, 13), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '동해해양경찰서' LIMIT 1), '장비부품', '🔗', 10, 14), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '동해해양경찰서' LIMIT 1), '경비함정방제', '⚓', 8, 15), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '동해해양경찰서' LIMIT 1), '살포장치', '🌊', 14, 16); + +-- 포항해양경찰서 +INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '포항해양경찰서' LIMIT 1), '방제선', '🚢', 10, 0), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '포항해양경찰서' LIMIT 1), '유회수기', '⚙', 13, 1), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '포항해양경찰서' LIMIT 1), '비치크리너', '🏖', 1, 2), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '포항해양경찰서' LIMIT 1), '이송펌프', '🔧', 21, 3), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '포항해양경찰서' LIMIT 1), '방제차량', '🚛', 4, 4), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '포항해양경찰서' LIMIT 1), '해안운반차', '🚜', 1, 5), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '포항해양경찰서' LIMIT 1), '고압세척기', '💧', 7, 6), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '포항해양경찰서' LIMIT 1), '저압세척기', '🚿', 2, 7), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '포항해양경찰서' LIMIT 1), '동력분무기', '💨', 3, 8), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '포항해양경찰서' LIMIT 1), '유량계측기', '📏', 1, 9), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '포항해양경찰서' LIMIT 1), '방제창고', '🏭', 15, 10), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '포항해양경찰서' LIMIT 1), '발전기', '⚡', 5, 11), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '포항해양경찰서' LIMIT 1), '현장지휘소', '🏕', 1, 12), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '포항해양경찰서' LIMIT 1), '지원장비', '🔩', 20, 13), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '포항해양경찰서' LIMIT 1), '경비함정방제', '⚓', 10, 14), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '포항해양경찰서' LIMIT 1), '살포장치', '🌊', 21, 15); + +-- 속초해양경찰서 +INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '속초해양경찰서' LIMIT 1), '방제선', '🚢', 2, 0), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '속초해양경찰서' LIMIT 1), '유회수기', '⚙', 6, 1), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '속초해양경찰서' LIMIT 1), '이송펌프', '🔧', 4, 2), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '속초해양경찰서' LIMIT 1), '방제차량', '🚛', 1, 3), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '속초해양경찰서' LIMIT 1), '해안운반차', '🚜', 1, 4), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '속초해양경찰서' LIMIT 1), '고압세척기', '💧', 2, 5), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '속초해양경찰서' LIMIT 1), '저압세척기', '🚿', 2, 6), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '속초해양경찰서' LIMIT 1), '동력분무기', '💨', 1, 7), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '속초해양경찰서' LIMIT 1), '유량계측기', '📏', 1, 8), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '속초해양경찰서' LIMIT 1), '방제창고', '🏭', 16, 9), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '속초해양경찰서' LIMIT 1), '발전기', '⚡', 2, 10), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '속초해양경찰서' LIMIT 1), '지원장비', '🔩', 11, 11), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '속초해양경찰서' LIMIT 1), '장비부품', '🔗', 11, 12), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '속초해양경찰서' LIMIT 1), '경비함정방제', '⚓', 8, 13), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '속초해양경찰서' LIMIT 1), '살포장치', '🌊', 17, 14); + +-- 울진해양경찰서 +INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '울진해양경찰서' LIMIT 1), '방제선', '🚢', 2, 0), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '울진해양경찰서' LIMIT 1), '유회수기', '⚙', 6, 1), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '울진해양경찰서' LIMIT 1), '이송펌프', '🔧', 4, 2), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '울진해양경찰서' LIMIT 1), '방제차량', '🚛', 1, 3), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '울진해양경찰서' LIMIT 1), '해안운반차', '🚜', 1, 4), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '울진해양경찰서' LIMIT 1), '고압세척기', '💧', 3, 5), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '울진해양경찰서' LIMIT 1), '저압세척기', '🚿', 2, 6), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '울진해양경찰서' LIMIT 1), '동력분무기', '💨', 1, 7), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '울진해양경찰서' LIMIT 1), '유량계측기', '📏', 1, 8), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '울진해양경찰서' LIMIT 1), '방제창고', '🏭', 13, 9), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '울진해양경찰서' LIMIT 1), '발전기', '⚡', 4, 10), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '울진해양경찰서' LIMIT 1), '현장지휘소', '🏕', 2, 11), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '울진해양경찰서' LIMIT 1), '지원장비', '🔩', 4, 12), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '울진해양경찰서' LIMIT 1), '장비부품', '🔗', 4, 13), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '울진해양경찰서' LIMIT 1), '경비함정방제', '⚓', 10, 14), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '울진해양경찰서' LIMIT 1), '살포장치', '🌊', 8, 15); + +-- 제주해양경찰서 +INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '제주해양경찰서' LIMIT 1), '방제선', '🚢', 4, 0), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '제주해양경찰서' LIMIT 1), '유회수기', '⚙', 21, 1), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '제주해양경찰서' LIMIT 1), '비치크리너', '🏖', 2, 2), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '제주해양경찰서' LIMIT 1), '이송펌프', '🔧', 17, 3), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '제주해양경찰서' LIMIT 1), '방제차량', '🚛', 3, 4), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '제주해양경찰서' LIMIT 1), '해안운반차', '🚜', 1, 5), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '제주해양경찰서' LIMIT 1), '고압세척기', '💧', 5, 6), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '제주해양경찰서' LIMIT 1), '저압세척기', '🚿', 3, 7), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '제주해양경찰서' LIMIT 1), '동력분무기', '💨', 4, 8), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '제주해양경찰서' LIMIT 1), '유량계측기', '📏', 1, 9), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '제주해양경찰서' LIMIT 1), '방제창고', '🏭', 24, 10), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '제주해양경찰서' LIMIT 1), '발전기', '⚡', 6, 11), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '제주해양경찰서' LIMIT 1), '현장지휘소', '🏕', 2, 12), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '제주해양경찰서' LIMIT 1), '경비함정방제', '⚓', 4, 13), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '제주해양경찰서' LIMIT 1), '살포장치', '🌊', 16, 14); + +-- 서귀포해양경찰서 +INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '서귀포해양경찰서' LIMIT 1), '방제선', '🚢', 3, 0), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '서귀포해양경찰서' LIMIT 1), '유회수기', '⚙', 9, 1), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '서귀포해양경찰서' LIMIT 1), '비치크리너', '🏖', 1, 2), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '서귀포해양경찰서' LIMIT 1), '이송펌프', '🔧', 15, 3), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '서귀포해양경찰서' LIMIT 1), '방제차량', '🚛', 3, 4), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '서귀포해양경찰서' LIMIT 1), '해안운반차', '🚜', 1, 5), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '서귀포해양경찰서' LIMIT 1), '고압세척기', '💧', 2, 6), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '서귀포해양경찰서' LIMIT 1), '동력분무기', '💨', 1, 7), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '서귀포해양경찰서' LIMIT 1), '유량계측기', '📏', 1, 8), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '서귀포해양경찰서' LIMIT 1), '방제창고', '🏭', 10, 9), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '서귀포해양경찰서' LIMIT 1), '발전기', '⚡', 3, 10), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '서귀포해양경찰서' LIMIT 1), '경비함정방제', '⚓', 4, 11), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '서귀포해양경찰서' LIMIT 1), '살포장치', '🌊', 14, 12); + +-- 중앙특수구조단 +INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '중앙특수구조단' LIMIT 1), '방제선', '🚢', 1, 0), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '중앙특수구조단' LIMIT 1), '이송펌프', '🔧', 5, 1), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '중앙특수구조단' LIMIT 1), '방제차량', '🚛', 2, 2), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '중앙특수구조단' LIMIT 1), '유량계측기', '📏', 1, 3), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '중앙특수구조단' LIMIT 1), '발전기', '⚡', 2, 4), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '중앙특수구조단' LIMIT 1), '지원장비', '🔩', 27, 5), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '중앙특수구조단' LIMIT 1), '경비함정방제', '⚓', 1, 6); + +-- 오일허브코리아여수㈜ 외 4개 +INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '오일허브코리아여수㈜ 외 4개' LIMIT 1), '고압세척기', '💧', 1, 0); + +-- SK에너지 외 2개 +INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = 'SK에너지 외 2개' LIMIT 1), '고압세척기', '💧', 1, 0), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = 'SK에너지 외 2개' LIMIT 1), '동력분무기', '💨', 1, 1), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = 'SK에너지 외 2개' LIMIT 1), '방제창고', '🏭', 1, 2); + +-- SK지오센트릭 외 5개 +INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = 'SK지오센트릭 외 5개' LIMIT 1), '동력분무기', '💨', 4, 0), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = 'SK지오센트릭 외 5개' LIMIT 1), '방제창고', '🏭', 5, 1); + +-- 한국가스공사 통영기지본부 +INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '한국가스공사 통영기지본부' LIMIT 1), '방제창고', '🏭', 1, 0); + +-- HD현대오일뱅크㈜ 외 4개 +INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = 'HD현대오일뱅크㈜ 외 4개' LIMIT 1), '동력분무기', '💨', 2, 0), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = 'HD현대오일뱅크㈜ 외 4개' LIMIT 1), '방제창고', '🏭', 6, 1), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = 'HD현대오일뱅크㈜ 외 4개' LIMIT 1), '발전기', '⚡', 1, 2); + +-- 포스코케미칼 외 1개 +INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '포스코케미칼 외 1개' LIMIT 1), '동력분무기', '💨', 1, 0), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '포스코케미칼 외 1개' LIMIT 1), '살포장치', '🌊', 1, 1); + +-- 흑산도내연발전소 외 2개 +INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '흑산도내연발전소 외 2개' LIMIT 1), '고압세척기', '💧', 3, 0), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '흑산도내연발전소 외 2개' LIMIT 1), '발전기', '⚡', 1, 1); + +-- 오일허브코리아여수㈜ 외 4개 +INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '오일허브코리아여수㈜ 외 4개' LIMIT 1), '동력분무기', '💨', 1, 0), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '오일허브코리아여수㈜ 외 4개' LIMIT 1), '방제창고', '🏭', 3, 1); + +-- GS칼텍스㈜ 외 10개 +INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = 'GS칼텍스㈜ 외 10개' LIMIT 1), '고압세척기', '💧', 7, 0), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = 'GS칼텍스㈜ 외 10개' LIMIT 1), '동력분무기', '💨', 6, 1); + +-- HD현대케미칼 외 4개 +INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = 'HD현대케미칼 외 4개' LIMIT 1), '해안운반차', '🚜', 2, 0), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = 'HD현대케미칼 외 4개' LIMIT 1), '동력분무기', '💨', 2, 1); + +-- 현대오일터미널(주) 외 4개 +INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '현대오일터미널(주) 외 4개' LIMIT 1), '고압세척기', '💧', 2, 0), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '현대오일터미널(주) 외 4개' LIMIT 1), '발전기', '⚡', 1, 1), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '현대오일터미널(주) 외 4개' LIMIT 1), '살포장치', '🌊', 1, 2); + +-- 한국남동발전(주) 외 2개 +INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '한국남동발전(주) 외 2개' LIMIT 1), '동력분무기', '💨', 3, 0), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '한국남동발전(주) 외 2개' LIMIT 1), '방제창고', '🏭', 5, 1); + +-- HD현대미포 +INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = 'HD현대미포' LIMIT 1), '방제창고', '🏭', 1, 0); + +-- 삼성중공업 외 1개 +INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '삼성중공업 외 1개' LIMIT 1), '동력분무기', '💨', 2, 0); + +-- 한국남부발전㈜ +INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '한국남부발전㈜' LIMIT 1), '방제창고', '🏭', 2, 0); + +-- 한울원전 +INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '한울원전' LIMIT 1), '방제창고', '🏭', 2, 0), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '한울원전' LIMIT 1), '지원장비', '🔩', 2, 1); + +-- ㈜HR-PORT 외 5개 +INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '㈜HR-PORT 외 5개' LIMIT 1), '고압세척기', '💧', 1, 0), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '㈜HR-PORT 외 5개' LIMIT 1), '저압세척기', '🚿', 1, 1), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '㈜HR-PORT 외 5개' LIMIT 1), '동력분무기', '💨', 7, 2), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '㈜HR-PORT 외 5개' LIMIT 1), '방제창고', '🏭', 3, 3), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '㈜HR-PORT 외 5개' LIMIT 1), '발전기', '⚡', 1, 4), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '㈜HR-PORT 외 5개' LIMIT 1), '지원장비', '🔩', 3, 5); + +-- 삼광조선공업㈜ 외 1개 +INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '삼광조선공업㈜ 외 1개' LIMIT 1), '고압세척기', '💧', 5, 0); + +-- 방제유창청소업체(㈜클린포트) +INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '방제유창청소업체(㈜클린포트)' LIMIT 1), '고압세척기', '💧', 2, 0), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '방제유창청소업체(㈜클린포트)' LIMIT 1), '발전기', '⚡', 1, 1); + +-- 방제유창청소업체(대용환경㈜ 외 38개) +INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '방제유창청소업체(대용환경㈜ 외 38개)' LIMIT 1), '고압세척기', '💧', 31, 0), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '방제유창청소업체(대용환경㈜ 외 38개)' LIMIT 1), '저압세척기', '🚿', 5, 1), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '방제유창청소업체(대용환경㈜ 외 38개)' LIMIT 1), '동력분무기', '💨', 3, 2), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '방제유창청소업체(대용환경㈜ 외 38개)' LIMIT 1), '방제창고', '🏭', 5, 3), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '방제유창청소업체(대용환경㈜ 외 38개)' LIMIT 1), '발전기', '⚡', 6, 4), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '방제유창청소업체(대용환경㈜ 외 38개)' LIMIT 1), '현장지휘소', '🏕', 1, 5); + +-- 방제유창청소업체((주)한유마린서비스 외 8개) +INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '방제유창청소업체((주)한유마린서비스 외 8개)' LIMIT 1), '고압세척기', '💧', 11, 0), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '방제유창청소업체((주)한유마린서비스 외 8개)' LIMIT 1), '방제창고', '🏭', 1, 1); + +-- 방제유창청소업체(블루씨 외 1개) +INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '방제유창청소업체(블루씨 외 1개)' LIMIT 1), '고압세척기', '💧', 2, 0), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '방제유창청소업체(블루씨 외 1개)' LIMIT 1), '살포장치', '🌊', 1, 1); + +-- 방제유창청소업체(㈜한국해운 외 1개) +INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '방제유창청소업체(㈜한국해운 외 1개)' LIMIT 1), '고압세척기', '💧', 1, 0); + +-- 방제유창청소업체(마로해운 외 11개) +INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '방제유창청소업체(마로해운 외 11개)' LIMIT 1), '고압세척기', '💧', 28, 0), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '방제유창청소업체(마로해운 외 11개)' LIMIT 1), '동력분무기', '💨', 15, 1), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '방제유창청소업체(마로해운 외 11개)' LIMIT 1), '방제창고', '🏭', 5, 2), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '방제유창청소업체(마로해운 외 11개)' LIMIT 1), '발전기', '⚡', 4, 3), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '방제유창청소업체(마로해운 외 11개)' LIMIT 1), '살포장치', '🌊', 2, 4); + +-- 방제유창청소업체(우진해운㈜) +INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '방제유창청소업체(우진해운㈜)' LIMIT 1), '고압세척기', '💧', 3, 0), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '방제유창청소업체(우진해운㈜)' LIMIT 1), '저압세척기', '🚿', 1, 1), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '방제유창청소업체(우진해운㈜)' LIMIT 1), '발전기', '⚡', 2, 2); + +-- 방제유창청소업체((주)씨앤 외 3개) +INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '방제유창청소업체((주)씨앤 외 3개)' LIMIT 1), '고압세척기', '💧', 3, 0), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '방제유창청소업체((주)씨앤 외 3개)' LIMIT 1), '저압세척기', '🚿', 1, 1), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '방제유창청소업체((주)씨앤 외 3개)' LIMIT 1), '발전기', '⚡', 2, 2); + +-- 방제유창청소업체(㈜지앤비마린서비스) +INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '방제유창청소업체(㈜지앤비마린서비스)' LIMIT 1), '방제창고', '🏭', 1, 0); + +-- SK엔텀(주) 외 4개 +INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = 'SK엔텀(주) 외 4개' LIMIT 1), '방제창고', '🏭', 5, 0); + +-- GS칼텍스㈜ +INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = 'GS칼텍스㈜' LIMIT 1), '고압세척기', '💧', 3, 0), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = 'GS칼텍스㈜' LIMIT 1), '방제창고', '🏭', 4, 1), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = 'GS칼텍스㈜' LIMIT 1), '살포장치', '🌊', 20, 2); + +-- HD현대오일뱅크㈜ +INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = 'HD현대오일뱅크㈜' LIMIT 1), '동력분무기', '💨', 2, 0); + +-- 부산광역시 외 8개 +INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '부산광역시 외 8개' LIMIT 1), '고압세척기', '💧', 1, 0), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '부산광역시 외 8개' LIMIT 1), '방제창고', '🏭', 11, 1); + +-- 사천시 외 3개 +INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '사천시 외 3개' LIMIT 1), '방제창고', '🏭', 6, 0); + +-- 울산북구청 외 2개 +INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '울산북구청 외 2개' LIMIT 1), '방제창고', '🏭', 4, 0); + +-- 창원 진해구 외 1개 +INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '창원 진해구 외 1개' LIMIT 1), '고압세척기', '💧', 1, 0), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '창원 진해구 외 1개' LIMIT 1), '방제창고', '🏭', 1, 1); + +-- 삼척시 외 1개 +INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '삼척시 외 1개' LIMIT 1), '고압세척기', '💧', 1, 0), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '삼척시 외 1개' LIMIT 1), '방제창고', '🏭', 3, 1); + +-- 영덕군 +INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '영덕군' LIMIT 1), '방제창고', '🏭', 3, 0); + +-- 영광군 외 1개 +INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '영광군 외 1개' LIMIT 1), '방제창고', '🏭', 5, 0); + +-- 광양시 외 1개 +INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '광양시 외 1개' LIMIT 1), '방제창고', '🏭', 3, 0); + +-- 옹진군청 외 4개 +INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '옹진군청 외 4개' LIMIT 1), '고압세척기', '💧', 5, 0), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '옹진군청 외 4개' LIMIT 1), '동력분무기', '💨', 4, 1), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '옹진군청 외 4개' LIMIT 1), '발전기', '⚡', 1, 2); + +-- 태안군청 +INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '태안군청' LIMIT 1), '방제창고', '🏭', 1, 0); + +-- 안산시청 외 2개 +INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '안산시청 외 2개' LIMIT 1), '동력분무기', '💨', 1, 0), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '안산시청 외 2개' LIMIT 1), '방제창고', '🏭', 5, 1); + +-- ㈜HR-PORT 외 5개 +INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '㈜HR-PORT 외 5개' LIMIT 1), '방제창고', '🏭', 1, 0); + +-- 해군1함대사령부 외 1개 +INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '해군1함대사령부 외 1개' LIMIT 1), '방제창고', '🏭', 1, 0); + +-- 해병대 제9518부대 +INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '해병대 제9518부대' LIMIT 1), '발전기', '⚡', 2, 0); + +-- 부산지사 +INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '부산지사' LIMIT 1), '고압세척기', '💧', 3, 0), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '부산지사' LIMIT 1), '저압세척기', '🚿', 1, 1), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '부산지사' LIMIT 1), '방제창고', '🏭', 1, 2), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '부산지사' LIMIT 1), '발전기', '⚡', 2, 3), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '부산지사' LIMIT 1), '현장지휘소', '🏕', 1, 4), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '부산지사' LIMIT 1), '살포장치', '🌊', 6, 5); + +-- 마산지사 +INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '마산지사' LIMIT 1), '방제창고', '🏭', 8, 0), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '마산지사' LIMIT 1), '발전기', '⚡', 1, 1); + +-- 울산지사 +INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '울산지사' LIMIT 1), '고압세척기', '💧', 6, 0), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '울산지사' LIMIT 1), '저압세척기', '🚿', 1, 1), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '울산지사' LIMIT 1), '방제창고', '🏭', 4, 2), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '울산지사' LIMIT 1), '발전기', '⚡', 4, 3), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '울산지사' LIMIT 1), '살포장치', '🌊', 1, 4); + +-- 마산지사 +INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '마산지사' LIMIT 1), '고압세척기', '💧', 4, 0), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '마산지사' LIMIT 1), '발전기', '⚡', 2, 1), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '마산지사' LIMIT 1), '살포장치', '🌊', 1, 2); + +-- 마산지사 +INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '마산지사' LIMIT 1), '방제창고', '🏭', 8, 0); + +-- 동해지사 +INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '동해지사' LIMIT 1), '해안운반차', '🚜', 2, 0), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '동해지사' LIMIT 1), '고압세척기', '💧', 2, 1), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '동해지사' LIMIT 1), '동력분무기', '💨', 2, 2), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '동해지사' LIMIT 1), '방제창고', '🏭', 8, 3), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '동해지사' LIMIT 1), '발전기', '⚡', 1, 4), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '동해지사' LIMIT 1), '살포장치', '🌊', 2, 5); + +-- 포항지사 +INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '포항지사' LIMIT 1), '방제창고', '🏭', 2, 0); + +-- 포항지사 +INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '포항지사' LIMIT 1), '고압세척기', '💧', 2, 0), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '포항지사' LIMIT 1), '동력분무기', '💨', 1, 1), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '포항지사' LIMIT 1), '발전기', '⚡', 2, 2), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '포항지사' LIMIT 1), '현장지휘소', '🏕', 1, 3), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '포항지사' LIMIT 1), '살포장치', '🌊', 2, 4); + +-- 군산지사 +INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '군산지사' LIMIT 1), '고압세척기', '💧', 2, 0), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '군산지사' LIMIT 1), '저압세척기', '🚿', 2, 1), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '군산지사' LIMIT 1), '동력분무기', '💨', 1, 2), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '군산지사' LIMIT 1), '발전기', '⚡', 3, 3), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '군산지사' LIMIT 1), '살포장치', '🌊', 4, 4); + +-- 목포지사 +INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '목포지사' LIMIT 1), '고압세척기', '💧', 1, 0), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '목포지사' LIMIT 1), '저압세척기', '🚿', 2, 1), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '목포지사' LIMIT 1), '방제창고', '🏭', 6, 2), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '목포지사' LIMIT 1), '발전기', '⚡', 1, 3); + +-- 여수지사 +INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '여수지사' LIMIT 1), '고압세척기', '💧', 5, 0), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '여수지사' LIMIT 1), '저압세척기', '🚿', 1, 1), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '여수지사' LIMIT 1), '발전기', '⚡', 3, 2), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '여수지사' LIMIT 1), '살포장치', '🌊', 3, 3); + +-- 목포지사 완도사업소 +INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '목포지사 완도사업소' LIMIT 1), '고압세척기', '💧', 1, 0), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '목포지사 완도사업소' LIMIT 1), '방제창고', '🏭', 2, 1); + +-- 제주지사(서귀포) +INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '제주지사(서귀포)' LIMIT 1), '살포장치', '🌊', 1, 0); + +-- 제주지사(제주) +INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '제주지사(제주)' LIMIT 1), '고압세척기', '💧', 3, 0), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '제주지사(제주)' LIMIT 1), '저압세척기', '🚿', 1, 1), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '제주지사(제주)' LIMIT 1), '동력분무기', '💨', 2, 2), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '제주지사(제주)' LIMIT 1), '방제창고', '🏭', 10, 3), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '제주지사(제주)' LIMIT 1), '발전기', '⚡', 1, 4), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '제주지사(제주)' LIMIT 1), '살포장치', '🌊', 3, 5); + +-- 대산지사(보령) +INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '대산지사(보령)' LIMIT 1), '고압세척기', '💧', 1, 0), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '대산지사(보령)' LIMIT 1), '방제창고', '🏭', 4, 1); + +-- 인천지사 +INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '인천지사' LIMIT 1), '고압세척기', '💧', 5, 0), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '인천지사' LIMIT 1), '저압세척기', '🚿', 1, 1), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '인천지사' LIMIT 1), '동력분무기', '💨', 3, 2), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '인천지사' LIMIT 1), '발전기', '⚡', 2, 3); + +-- 대산지사(태안) +INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '대산지사(태안)' LIMIT 1), '해안운반차', '🚜', 1, 0), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '대산지사(태안)' LIMIT 1), '고압세척기', '💧', 5, 1), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '대산지사(태안)' LIMIT 1), '저압세척기', '🚿', 1, 2), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '대산지사(태안)' LIMIT 1), '동력분무기', '💨', 1, 3), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '대산지사(태안)' LIMIT 1), '방제창고', '🏭', 5, 4), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '대산지사(태안)' LIMIT 1), '발전기', '⚡', 3, 5), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '대산지사(태안)' LIMIT 1), '살포장치', '🌊', 1, 6); + +-- 평택지사 +INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '평택지사' LIMIT 1), '고압세척기', '💧', 3, 0), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '평택지사' LIMIT 1), '저압세척기', '🚿', 2, 1), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '평택지사' LIMIT 1), '방제창고', '🏭', 3, 2), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '평택지사' LIMIT 1), '발전기', '⚡', 4, 3), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '평택지사' LIMIT 1), '살포장치', '🌊', 1, 4); + +-- 삼성중공업 외 1개 +INSERT INTO ASSET_EQUIP (ORG_SN, CTGR_NM, ICON, QTY, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '삼성중공업 외 1개' LIMIT 1), '고압세척기', '💧', 1, 0); + +-- ============================================================ +-- ASSET_CONTACT 초기 데이터 +-- ============================================================ +INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '인천해양경찰서' LIMIT 1), '방제과장', '김○○', '032-835-0001', 0), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '인천해양경찰서' LIMIT 1), '방제담당', '이○○', '032-835-0002', 1); +INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '평택해양경찰서' LIMIT 1), '방제담당', '박○○', '031-682-0001', 0); +INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '태안해양경찰서' LIMIT 1), '방제담당', '최○○', '041-674-0001', 0); +INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '보령해양경찰서' LIMIT 1), '방제담당', '정○○', '041-931-0001', 0); +INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '여수해양경찰서' LIMIT 1), '방제과장', '윤○○', '061-660-0001', 0), + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '여수해양경찰서' LIMIT 1), '방제담당', '장○○', '061-660-0002', 1); +INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '목포해양경찰서' LIMIT 1), '방제담당', '조○○', '061-244-0001', 0); +INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '군산해양경찰서' LIMIT 1), '방제담당', '한○○', '063-462-0001', 0); +INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '완도해양경찰서' LIMIT 1), '방제담당', '이○○', '061-550-0001', 0); +INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '부안해양경찰서' LIMIT 1), '방제담당', '김○○', '063-928-0001', 0); +INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '부산해양경찰서' LIMIT 1), '방제과장', '임○○', '051-400-0001', 0); +INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '울산해양경찰서' LIMIT 1), '방제과장', '강○○', '052-228-0001', 0); +INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '창원해양경찰서' LIMIT 1), '방제담당', '송○○', '055-220-0001', 0); +INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '통영해양경찰서' LIMIT 1), '방제담당', '서○○', '055-640-0001', 0); +INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '사천해양경찰서' LIMIT 1), '방제담당', '박○○', '055-830-0001', 0); +INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '동해해양경찰서' LIMIT 1), '방제담당', '남○○', '033-530-0001', 0); +INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '포항해양경찰서' LIMIT 1), '방제담당', '오○○', '054-244-0001', 0); +INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '속초해양경찰서' LIMIT 1), '방제담당', '양○○', '033-633-0001', 0); +INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '울진해양경찰서' LIMIT 1), '방제담당', '배○○', '054-782-0001', 0); +INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '제주해양경찰서' LIMIT 1), '방제담당', '문○○', '064-750-0001', 0); +INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '서귀포해양경찰서' LIMIT 1), '방제담당', '고○○', '064-730-0001', 0); +INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '중앙특수구조단' LIMIT 1), '구조단장', '김○○', '051-580-2044', 0); +INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '오일허브코리아여수㈜ 외 4개' LIMIT 1), '담당', '오일허브코리아여수㈜', '061-686-3611', 0); +INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = 'SK에너지 외 2개' LIMIT 1), '담당', 'HD현대오일뱅크㈜', '051-643-3331', 0); +INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = 'SK지오센트릭 외 5개' LIMIT 1), '담당', 'SK엔텀㈜', '052-208-2851', 0); +INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '한국가스공사 통영기지본부' LIMIT 1), '담당', '한국가스공사', '055-640-6014', 0); +INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = 'HD현대오일뱅크㈜ 외 4개' LIMIT 1), '담당', 'HD현대오일뱅크㈜', '033-534-2093', 0); +INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '포스코케미칼 외 1개' LIMIT 1), '담당', 'OCI(주)', '054-290-8222', 0); +INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '흑산도내연발전소 외 2개' LIMIT 1), '담당', '안마도내연발전소', '061-351-2342', 0); +INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '오일허브코리아여수㈜ 외 4개' LIMIT 1), '담당', '오일허브코리아여수㈜', '061-686-3611', 0); +INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = 'GS칼텍스㈜ 외 10개' LIMIT 1), '담당', 'GS칼텍스㈜', '010-8777-6922', 0); +INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = 'HD현대케미칼 외 4개' LIMIT 1), '담당', 'HD현대케미칼', '041-924-1068', 0); +INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '현대오일터미널(주) 외 4개' LIMIT 1), '담당', '(주)경동탱크터미널', '031-683-5101', 0); +INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '한국남동발전(주) 외 2개' LIMIT 1), '담당', '(주)고성그린파워', '070-4486-7474', 0); +INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = 'HD현대미포' LIMIT 1), '담당', 'HD현대미포', '052-250-3551', 0); +INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '삼성중공업 외 1개' LIMIT 1), '담당', '삼성중공업', '055-630-5373', 0); +INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '한국남부발전㈜' LIMIT 1), '담당', '한국남부발전㈜', '070-7713-5153', 0); +INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '한울원전' LIMIT 1), '담당', '한울원전', '054-785-4833', 0); +INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '㈜HR-PORT 외 5개' LIMIT 1), '담당', '㈜ 한진', '061-791-0358', 0); +INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '삼광조선공업㈜ 외 1개' LIMIT 1), '담당', '삼광조선공업㈜', '010-3321-2959', 0); +INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '방제유창청소업체(㈜클린포트)' LIMIT 1), '담당', '㈜클린포트', '032-882-8279', 0); +INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '방제유창청소업체(대용환경㈜ 외 38개)' LIMIT 1), '담당', '(주)경원마린서비스', '051-242-0622', 0); +INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '방제유창청소업체((주)한유마린서비스 외 8개)' LIMIT 1), '담당', '(주)골든씨', '010-5499-7401', 0); +INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '방제유창청소업체(블루씨 외 1개)' LIMIT 1), '담당', '(주)블루씨', '054-278-8200', 0); +INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '방제유창청소업체(㈜한국해운 외 1개)' LIMIT 1), '담당', '㈜아라', '010-8615-4326', 0); +INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '방제유창청소업체(마로해운 외 11개)' LIMIT 1), '담당', '(유)피케이엘', '061-654-9603', 0); +INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '방제유창청소업체(우진해운㈜)' LIMIT 1), '담당', '우진해운㈜', '010-4384-6817', 0); +INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '방제유창청소업체((주)씨앤 외 3개)' LIMIT 1), '담당', '(주)소스코리아', '031-683-2389', 0); +INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '방제유창청소업체(㈜지앤비마린서비스)' LIMIT 1), '담당', '(주)경원마린서비스', '051-242-0622', 0); +INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = 'SK엔텀(주) 외 4개' LIMIT 1), '담당', 'S-OIL㈜', '052-231-2318', 0); +INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = 'GS칼텍스㈜' LIMIT 1), '담당', 'GS칼텍스㈜', '061-680-2121', 0); +INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = 'HD현대오일뱅크㈜' LIMIT 1), '담당', 'HD현대오일뱅크㈜', '010-2050-5291', 0); +INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '부산광역시 외 8개' LIMIT 1), '담당', '남구청', '051-607-4484', 0); +INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '사천시 외 3개' LIMIT 1), '담당', '고성군', '055-670-2484', 0); +INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '울산북구청 외 2개' LIMIT 1), '담당', '부산기장군청', '051-709-4611', 0); +INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '창원 진해구 외 1개' LIMIT 1), '담당', '부산 강서구', '051-970-4482', 0); +INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '삼척시 외 1개' LIMIT 1), '담당', '강릉시', '033-640-5284', 0); +INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '영덕군' LIMIT 1), '담당', '영덕군', '054-730-6562', 0); +INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '영광군 외 1개' LIMIT 1), '담당', '목포시', '061-270-3419', 0); +INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '광양시 외 1개' LIMIT 1), '담당', '광양시', '061-797-2791', 0); +INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '옹진군청 외 4개' LIMIT 1), '담당', '김포시청', '010-2740-9388', 0); +INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '태안군청' LIMIT 1), '담당', '태안군청', '041-670-2877', 0); +INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '안산시청 외 2개' LIMIT 1), '담당', '당진시청', '041-350-4292', 0); +INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '㈜HR-PORT 외 5개' LIMIT 1), '담당', '㈜ 한진', '061-791-0358', 0); +INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '해군1함대사령부 외 1개' LIMIT 1), '담당', '1함대 사령부', '033-539-7323', 0); +INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '해병대 제9518부대' LIMIT 1), '담당', '해병대 제9518부대', '010-4801-3473', 0); +INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '부산지사' LIMIT 1), '담당', '부산지사', '051-466-3944', 0); +INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '마산지사' LIMIT 1), '담당', '마산지사', '010-3598-4202', 0); +INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '울산지사' LIMIT 1), '담당', '울산지사', '052-238-7718', 0); +INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '마산지사' LIMIT 1), '담당', '마산지사', '010-2265-3928', 0); +INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '마산지사' LIMIT 1), '담당', '마산지사', '010-2636-5313', 0); +INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '동해지사' LIMIT 1), '담당', '동해지사', '010-7499-0257', 0); +INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '포항지사' LIMIT 1), '담당', '포항지사', '054-273-5595', 0); +INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '포항지사' LIMIT 1), '담당', '포항지사', '054-273-5595', 0); +INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '군산지사' LIMIT 1), '담당', '군산지사', '063-443-4813', 0); +INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '목포지사' LIMIT 1), '담당', '목포지사', '061-242-9663', 0); +INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '여수지사' LIMIT 1), '담당', '여수지사', '061-654-6431', 0); +INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '목포지사 완도사업소' LIMIT 1), '담당', '목포지사', '061-242-9663', 0); +INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '제주지사(서귀포)' LIMIT 1), '담당', '제주지사', '064-753-4356', 0); +INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '제주지사(제주)' LIMIT 1), '담당', '제주지사', '064-753-4356', 0); +INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '대산지사(보령)' LIMIT 1), '담당', '대산지사', '041-664-9101', 0); +INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '인천지사' LIMIT 1), '담당', '인천지사', '010-7133-2167', 0); +INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '대산지사(태안)' LIMIT 1), '담당', '대산지사', '041-664-9101', 0); +INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '평택지사' LIMIT 1), '담당', '평택지사', '031-683-7973', 0); +INSERT INTO ASSET_CONTACT (ORG_SN, ROLE_NM, CONTACT_NM, TEL, SORT_ORD) VALUES + ((SELECT ORG_SN FROM ASSET_ORG WHERE ORG_NM = '삼성중공업 외 1개' LIMIT 1), '담당', '삼성중공업', '055-630-5373', 0); diff --git a/database/seed/13_reports_templates.sql b/database/seed/13_reports_templates.sql new file mode 100644 index 0000000..b0ed5d7 --- /dev/null +++ b/database/seed/13_reports_templates.sql @@ -0,0 +1,137 @@ +-- WING-OPS Seed Data: Reports Templates +-- Source: database/migration/007_reports.sql (INSERT 부분) + +SET search_path TO wing; + +-- ============================================================ +-- 보고서 템플릿 (5종) +-- ============================================================ +INSERT INTO REPORT_TMPL (TMPL_CD, TMPL_NM, ICON, TMPL_DC, SORT_ORD) VALUES + ('INITIAL', '초기보고서', '📋', '사고 발생 직후 초기 상황 보고', 1), + ('COMMAND', '지휘부 보고', '📊', '지휘부 보고용 요약 문서', 2), + ('FORECAST', '예측보고서', '📈', '확산예측 결과 및 민감자원 분석', 3), + ('COMPREHENSIVE', '종합보고서', '📑', '전체 사고 대응 종합 보고 문서', 4), + ('SPILL', '유출유 보고', '🛢️', '유류오염사고 대응지원 상황도', 5) +ON CONFLICT (TMPL_CD) DO NOTHING; + +-- ============================================================ +-- 초기보고서 섹션 (5 섹션) +-- ============================================================ +INSERT INTO REPORT_TMPL_SECT (TMPL_SN, SECT_CD, SECT_NM, FIELD_DEF, SORT_ORD) +SELECT t.TMPL_SN, v.SECT_CD, v.SECT_NM, v.FIELD_DEF::jsonb, v.SORT_ORD +FROM REPORT_TMPL t, (VALUES + ('basic-info', '1. 기본정보', '[{"key":"incident.writeTime","label":"보고일시","type":"text"},{"key":"author","label":"작성자","type":"text"},{"key":"targets","label":"보고대상","type":"checkbox-group","options":["본청","지방청","유관기서"]}]', 1), + ('incident-overview','2. 사고개요', '[{"key":"incident.name","label":"사고명","type":"text"},{"key":"incident.occurTime","label":"발생일시","type":"text"},{"key":"incident.location","label":"발생위치","type":"text"},{"key":"incident.shipName","label":"사고선박","type":"text"},{"key":"incident.accidentType","label":"사고유형","type":"text"}]', 2), + ('spill-status', '3. 유출현황', '[{"key":"incident.pollutant","label":"유출유종","type":"text"},{"key":"incident.spillAmount","label":"유출량","type":"text"},{"key":"spillPattern","label":"유출형태","type":"text"},{"key":"spreadStatus","label":"확산현황","type":"text"}]', 3), + ('initial-response', '4. 초동조치 사항', '[{"key":"initialResponse","label":"","type":"textarea"}]', 4), + ('future-plan', '5. 향후 대응계획', '[{"key":"futurePlan","label":"","type":"textarea"}]', 5) +) AS v(SECT_CD, SECT_NM, FIELD_DEF, SORT_ORD) +WHERE t.TMPL_CD = 'INITIAL' +ON CONFLICT (TMPL_SN, SECT_CD) DO NOTHING; + +-- 지휘부 보고 (4 섹션) +INSERT INTO REPORT_TMPL_SECT (TMPL_SN, SECT_CD, SECT_NM, FIELD_DEF, SORT_ORD) +SELECT t.TMPL_SN, v.SECT_CD, v.SECT_NM, v.FIELD_DEF::jsonb, v.SORT_ORD +FROM REPORT_TMPL t, (VALUES + ('basic-info', '1. 기본정보', '[{"key":"incident.writeTime","label":"보고일시","type":"text"},{"key":"author","label":"작성자","type":"text"}]', 1), + ('incident-summary','2. 사고 요약', '[{"key":"incident.name","label":"사고명","type":"text"},{"key":"incident.occurTime","label":"발생일시","type":"text"},{"key":"incident.location","label":"발생위치","type":"text"},{"key":"incident.pollutant","label":"유출유종","type":"text"},{"key":"incident.spillAmount","label":"유출량","type":"text"}]', 2), + ('response-status', '3. 대응현황', '[{"key":"responseStatus","label":"","type":"textarea"}]', 3), + ('suggestions', '4. 건의사항', '[{"key":"suggestions","label":"","type":"textarea"}]', 4) +) AS v(SECT_CD, SECT_NM, FIELD_DEF, SORT_ORD) +WHERE t.TMPL_CD = 'COMMAND' +ON CONFLICT (TMPL_SN, SECT_CD) DO NOTHING; + +-- 예측보고서 (5 섹션) +INSERT INTO REPORT_TMPL_SECT (TMPL_SN, SECT_CD, SECT_NM, FIELD_DEF, SORT_ORD) +SELECT t.TMPL_SN, v.SECT_CD, v.SECT_NM, v.FIELD_DEF::jsonb, v.SORT_ORD +FROM REPORT_TMPL t, (VALUES + ('basic-info', '1. 기본정보', '[{"key":"incident.writeTime","label":"보고일시","type":"text"},{"key":"author","label":"작성자","type":"text"}]', 1), + ('incident-overview','2. 사고개요', '[{"key":"incident.name","label":"사고명","type":"text"},{"key":"incident.occurTime","label":"발생일시","type":"text"},{"key":"incident.location","label":"발생위치","type":"text"},{"key":"incident.pollutant","label":"유출유종","type":"text"},{"key":"incident.spillAmount","label":"유출량","type":"text"}]', 2), + ('weather-summary', '3. 해양기상 현황', '[{"key":"weatherSummary","label":"","type":"textarea"}]', 3), + ('spread-result', '4. 확산예측 결과', '[{"key":"spreadResult","label":"","type":"textarea"}]', 4), + ('sensitive-impact', '5. 민감자원 영향', '[{"key":"sensitiveImpact","label":"","type":"textarea"}]', 5) +) AS v(SECT_CD, SECT_NM, FIELD_DEF, SORT_ORD) +WHERE t.TMPL_CD = 'FORECAST' +ON CONFLICT (TMPL_SN, SECT_CD) DO NOTHING; + +-- 종합보고서 (6 섹션) +INSERT INTO REPORT_TMPL_SECT (TMPL_SN, SECT_CD, SECT_NM, FIELD_DEF, SORT_ORD) +SELECT t.TMPL_SN, v.SECT_CD, v.SECT_NM, v.FIELD_DEF::jsonb, v.SORT_ORD +FROM REPORT_TMPL t, (VALUES + ('basic-info', '1. 기본정보', '[{"key":"incident.writeTime","label":"보고일시","type":"text"},{"key":"author","label":"작성자","type":"text"}]', 1), + ('incident-overview', '2. 사고개요', '[{"key":"incident.name","label":"사고명","type":"text"},{"key":"incident.occurTime","label":"발생일시","type":"text"},{"key":"incident.location","label":"발생위치","type":"text"},{"key":"incident.shipName","label":"사고선박","type":"text"},{"key":"incident.accidentType","label":"사고유형","type":"text"}]', 2), + ('spill-spread', '3. 유출 및 확산현황', '[{"key":"incident.pollutant","label":"유출유종","type":"text"},{"key":"incident.spillAmount","label":"유출량","type":"text"},{"key":"spreadSummary","label":"확산현황","type":"textarea"}]', 3), + ('response-detail', '4. 대응현황', '[{"key":"responseDetail","label":"","type":"textarea"}]', 4), + ('damage-report', '5. 피해현황', '[{"key":"damageReport","label":"","type":"textarea"}]', 5), + ('future-plan-detail','6. 향후계획', '[{"key":"futurePlanDetail","label":"","type":"textarea"}]', 6) +) AS v(SECT_CD, SECT_NM, FIELD_DEF, SORT_ORD) +WHERE t.TMPL_CD = 'COMPREHENSIVE' +ON CONFLICT (TMPL_SN, SECT_CD) DO NOTHING; + +-- 유출유 보고 (14 섹션) +INSERT INTO REPORT_TMPL_SECT (TMPL_SN, SECT_CD, SECT_NM, FIELD_DEF, SORT_ORD) +SELECT t.TMPL_SN, v.SECT_CD, v.SECT_NM, v.FIELD_DEF::jsonb, v.SORT_ORD +FROM REPORT_TMPL t, (VALUES + ('incident-info', '1. 사고 정보', '[{"key":"incident.name","label":"사고명","type":"text"},{"key":"incident.writeTime","label":"작성시간","type":"text"},{"key":"incident.shipName","label":"선명(시설명)","type":"text"},{"key":"incident.location","label":"사고위치","type":"text"},{"key":"incident.occurTime","label":"발생시각","type":"text"},{"key":"incident.accidentType","label":"사고유형","type":"text"},{"key":"incident.pollutant","label":"오염물질","type":"text"},{"key":"incident.spillAmount","label":"유출 추정량(㎘)","type":"text"}]', 1), + ('tide-info', '2. 조석정보', '[{"key":"tideDate","label":"일자","type":"text"},{"key":"tideName","label":"물때","type":"text"},{"key":"lowTide1","label":"저조(1차)","type":"text"},{"key":"highTide1","label":"고조(1차)","type":"text"}]', 2), + ('weather-info', '2. 기상예보', '[{"key":"weatherTime","label":"기상 예측시간","type":"text"},{"key":"windDir","label":"풍향","type":"text"},{"key":"windSpeed","label":"풍속(m/s)","type":"text"},{"key":"waveHeight","label":"파고(m)","type":"text"}]', 3), + ('spread-detail', '3. 확산예측 상세', '[{"key":"spread3h_weathered","label":"3시간 풍화량(kL)","type":"text"},{"key":"spread3h_seaRemain","label":"3시간 해상잔존량(kL)","type":"text"},{"key":"spread3h_area","label":"3시간 오염면적(km²)","type":"text"}]', 4), + ('analysis', '3. 분석', '[{"key":"spreadAnalysis","label":"","type":"textarea"}]', 5), + ('aquaculture', '4. 양식장 분포', '[{"key":"aquaculture","label":"","type":"textarea"}]', 6), + ('beaches', '4. 해수욕장/수산시장', '[{"key":"beaches","label":"","type":"textarea"}]', 7), + ('esi-coast', '4. 해안선(ESI) 분포', '[{"key":"esi1_vertical","label":"ESI 1 수직암반(km)","type":"text"},{"key":"esi3_finesand","label":"ESI 3 세립질 모래(km)","type":"text"}]', 8), + ('bio-species', '4. 생물종(보호종)', '[{"key":"bioSpecies","label":"","type":"textarea"}]', 9), + ('sensitivity', '4. 통합민감도 평가', '[{"key":"sens_veryHigh","label":"매우 높음(km²)","type":"text"},{"key":"sens_high","label":"높음(km²)","type":"text"}]', 10), + ('defense-strategy', '5. 방제자원 배치', '[{"key":"defenseShips","label":"","type":"textarea"}]', 11), + ('other-equipment', '5. 기타 장비', '[{"key":"otherEquipment","label":"","type":"textarea"}]', 12), + ('oil-recovery', '6. 방제선/자원 동원', '[{"key":"oilRecovery","label":"","type":"textarea"}]', 13), + ('result-summary', '6. 동원 방제선 내역', '[{"key":"totalSpill","label":"유출량(kL)","type":"text"},{"key":"totalRecovered","label":"누적회수량(kL)","type":"text"}]', 14) +) AS v(SECT_CD, SECT_NM, FIELD_DEF, SORT_ORD) +WHERE t.TMPL_CD = 'SPILL' +ON CONFLICT (TMPL_SN, SECT_CD) DO NOTHING; + +-- ============================================================ +-- 분석 카테고리 (3종) +-- ============================================================ +INSERT INTO REPORT_ANALYSIS_CTGR (CTGR_CD, CTGR_NM, ICON, CTGR_DC, COLOR_CD, BORDER_COLOR, BG_ACTIVE, REPORT_NM, SORT_ORD) VALUES + ('OIL', '유출유 확산예측', '🛢', 'KOSPS · OpenDrift · POSEIDON', 'var(--cyan)', 'rgba(6,182,212,0.4)', 'rgba(6,182,212,0.08)', '유출유 확산예측 보고서', 1), + ('HNS', 'HNS 대기확산', '🧪', 'ALOHA · WRF-Chem', 'var(--orange)', 'rgba(249,115,22,0.4)', 'rgba(249,115,22,0.08)','HNS 대기확산 예측보고서', 2), + ('RESCUE', '긴급구난', '🚨', '복원성 · 좌초위험 분석', 'var(--red)', 'rgba(239,68,68,0.4)', 'rgba(239,68,68,0.08)', '긴급구난 상황보고서', 3) +ON CONFLICT (CTGR_CD) DO NOTHING; + +-- 카테고리별 템플릿 목록 +INSERT INTO REPORT_CTGR_TMPL (CTGR_SN, ICON, LABEL, SORT_ORD) +SELECT c.CTGR_SN, v.ICON, v.LABEL, v.SORT_ORD +FROM REPORT_ANALYSIS_CTGR c, (VALUES + ('OIL', '🔬', '예측보고서', 1), ('OIL', '📋', '초기보고서', 2), ('OIL', '📊', '지휘부 보고', 3), ('OIL', '📑', '종합보고서', 4), + ('HNS', '🧪', 'HNS 예측보고서', 1), ('HNS', '📋', '초기보고서', 2), ('HNS', '📊', '지휘부 보고', 3), ('HNS', '🆘', 'EmS 대응보고', 4), + ('RESCUE', '🚨', '긴급구난 상황보고', 1), ('RESCUE', '📋', '초기보고서', 2), ('RESCUE', '📊', '지휘부 보고', 3), ('RESCUE', '📑', '종합보고서', 4) +) AS v(CTGR_CD, ICON, LABEL, SORT_ORD) +WHERE c.CTGR_CD = v.CTGR_CD; + +-- 카테고리별 섹션 +INSERT INTO REPORT_CTGR_SECT (CTGR_SN, SECT_CD, SECT_NM, ICON, SECT_DC, DFLT_YN, SORT_ORD) +SELECT c.CTGR_SN, v.SECT_CD, v.SECT_NM, v.ICON, v.SECT_DC, v.DFLT_YN, v.SORT_ORD +FROM REPORT_ANALYSIS_CTGR c, (VALUES + ('OIL', 'oil-spread', '유출유 확산예측 결과', '🌊', 'KOSPS/OpenDrift/POSEIDON 예측 결과 및 비교', 'Y', 1), + ('OIL', 'oil-pollution', '오염종합상황', '📊', '유출량, 풍화량, 해상잔유량, 오염면적', 'Y', 2), + ('OIL', 'oil-sensitive', '민감자원 현황', '🐟', '어장정보, 양식업, 환경생태 자원', 'Y', 3), + ('OIL', 'oil-coastal', '해안부착 현황', '🏖', '해안 부착 위치, 시간, 오염 길이', 'Y', 4), + ('OIL', 'oil-defense', '방제전략·자원배치', '🛡', '방제방법, 방제자원 배치 계획', 'Y', 5), + ('OIL', 'oil-tide', '조석·기상정보', '🌊', '사고 해역 조석·기상 현황', 'Y', 6), + ('HNS', 'hns-atm', '대기확산 예측 결과', '💨', 'ALOHA/WRF-Chem 모델 확산 결과', 'Y', 1), + ('HNS', 'hns-hazard', '위험구역·방제거리', '⚠️', 'ERPGs 기준 위험구역 범위', 'Y', 2), + ('HNS', 'hns-substance', 'HNS 물질정보', '🧬', '물질 특성, 독성, UN번호', 'Y', 3), + ('HNS', 'hns-ppe', 'PPE·대응장비', '🛡', '개인보호장비, 대응장비 배치', 'Y', 4), + ('HNS', 'hns-facility', '영향시설·대피현황', '🏫', '주변 시설, 인구, 대피 계획', 'Y', 5), + ('HNS', 'hns-3d', '3D 공간분포', '📐', '수직·수평 농도 분포', 'N', 6), + ('HNS', 'hns-weather', '기상·해상 조건', '🌊', '풍향·풍속, 대기안정도', 'Y', 7), + ('RESCUE', 'rescue-safety', '선박 안전성 평가', '🚢', 'GM, 경사각, 트림 분석', 'Y', 1), + ('RESCUE', 'rescue-timeline', '사고 유형·경과', '⚡', '사고 유형별 타임라인', 'Y', 2), + ('RESCUE', 'rescue-casualty', '인명현황', '👥', '인원 현황, 구조 상태', 'Y', 3), + ('RESCUE', 'rescue-resource', '구난 자원 현황', '🛟', '예인선, 헬기, 구난 장비 배치', 'Y', 4), + ('RESCUE', 'rescue-grounding', '좌초위험 해역', '🗺', '좌초 위험 구역 분석', 'Y', 5), + ('RESCUE', 'rescue-weather', '기상·해상 조건', '🌊', '파고, 풍속, 조류 현황', 'Y', 6) +) AS v(CTGR_CD, SECT_CD, SECT_NM, ICON, SECT_DC, DFLT_YN, SORT_ORD) +WHERE c.CTGR_CD = v.CTGR_CD +ON CONFLICT (CTGR_SN, SECT_CD) DO NOTHING; diff --git a/database/seed/14_cctv_cameras.sql b/database/seed/14_cctv_cameras.sql new file mode 100644 index 0000000..1747270 --- /dev/null +++ b/database/seed/14_cctv_cameras.sql @@ -0,0 +1,18 @@ +-- WING-OPS Seed Data: CCTV Cameras (12건) +-- Source: database/migration/015_aerial.sql + +SET search_path TO wing; + +INSERT INTO CCTV_CAMERA (CAMERA_NM, REGION_NM, LON, LAT, GEOM, LOC_DC, COORD_DC, STTS_CD, PTZ_YN, SOURCE_NM) VALUES +('서귀포항 동측', '제주', 126.57, 33.24, ST_SetSRID(ST_MakePoint(126.57::float, 33.24::float), 4326), '제주 서귀포시 서귀동', '33.24°N 126.57°E', 'LIVE', 'Y', 'TAGO'), +('제주항 외항', '제주', 126.53, 33.52, ST_SetSRID(ST_MakePoint(126.53::float, 33.52::float), 4326), '제주 제주시 건입동', '33.52°N 126.53°E', 'LIVE', 'Y', 'TAGO'), +('성산포항', '제주', 126.93, 33.46, ST_SetSRID(ST_MakePoint(126.93::float, 33.46::float), 4326), '제주 서귀포시 성산읍', '33.46°N 126.93°E', 'LIVE', 'N', 'KBS'), +('한림항', '제주', 126.27, 33.41, ST_SetSRID(ST_MakePoint(126.27::float, 33.41::float), 4326), '제주 제주시 한림읍', '33.41°N 126.27°E', 'LIVE', 'N', 'TAGO'), +('여수 돌산대교', '남해', 127.75, 34.74, ST_SetSRID(ST_MakePoint(127.75::float, 34.74::float), 4326), '전남 여수시 돌산읍', '34.74°N 127.75°E', 'LIVE', 'Y', 'KBS'), +('통영 해상공원', '남해', 128.42, 34.84, ST_SetSRID(ST_MakePoint(128.42::float, 34.84::float), 4326), '경남 통영시 동호동', '34.84°N 128.42°E', 'LIVE', 'N', 'TAGO'), +('거제 장승포항', '남해', 128.69, 34.87, ST_SetSRID(ST_MakePoint(128.69::float, 34.87::float), 4326), '경남 거제시 장승포동', '34.87°N 128.69°E', 'OFFLINE', 'N', 'TAGO'), +('목포 영산강', '서해', 126.39, 34.79, ST_SetSRID(ST_MakePoint(126.39::float, 34.79::float), 4326), '전남 목포시 산정동', '34.79°N 126.39°E', 'LIVE', 'Y', 'KBS'), +('인천 송도', '서해', 126.64, 37.38, ST_SetSRID(ST_MakePoint(126.64::float, 37.38::float), 4326), '인천 연수구 송도동', '37.38°N 126.64°E', 'LIVE', 'N', 'TAGO'), +('태안 만리포', '서해', 126.14, 36.79, ST_SetSRID(ST_MakePoint(126.14::float, 36.79::float), 4326), '충남 태안군 소원면', '36.79°N 126.14°E', 'LIVE', 'N', 'TAGO'), +('포항 영일대', '동해', 129.38, 36.06, ST_SetSRID(ST_MakePoint(129.38::float, 36.06::float), 4326), '경북 포항시 북구', '36.06°N 129.38°E', 'LIVE', 'Y', 'KBS'), +('울산 대왕암', '동해', 129.43, 35.49, ST_SetSRID(ST_MakePoint(129.43::float, 35.49::float), 4326), '울산 동구 일산동', '35.49°N 129.43°E', 'LIVE', 'N', 'TAGO'); diff --git a/database/seed/README.md b/database/seed/README.md new file mode 100644 index 0000000..59e5770 --- /dev/null +++ b/database/seed/README.md @@ -0,0 +1,53 @@ +# Seed Data (초기 데이터) + +## 실행 순서 + +SQL seed 파일은 번호 순서대로 실행합니다. schema/ DDL이 먼저 실행되어야 합니다. + +```bash +# 1. auth 스키마 seed (01-06) +psql -U wing -d wing -f database/seed/01_auth_roles.sql +psql -U wing -d wing -f database/seed/02_auth_perms.sql +psql -U wing -d wing -f database/seed/03_auth_perm_tree.sql +psql -U wing -d wing -f database/seed/04_auth_orgs.sql +psql -U wing -d wing -f database/seed/05_auth_settings.sql +psql -U wing -d wing -f database/seed/06_auth_admin.sql + +# 2. wing 스키마 seed (07-08) +psql -U wing -d wing -f database/seed/07_common_codes.sql +psql -U wing -d wing -f database/seed/08_organizations.sql + +# 3. 도메인 데이터 (11-14) +psql -U wing -d wing -f database/seed/11_sample_accident.sql +psql -U wing -d wing -f database/seed/12_assets.sql +psql -U wing -d wing -f database/seed/13_reports_templates.sql +psql -U wing -d wing -f database/seed/14_cctv_cameras.sql + +# 4. Node.js seed (layer 160건 + HNS 1,316건) +cd backend && npm run db:seed +``` + +## 파일 목록 + +| 파일 | 내용 | 레코드 수 | +|------|------|-----------| +| 01_auth_roles.sql | 역할 (ADMIN, MANAGER, OPERATOR, VIEWER) | 4건 | +| 02_auth_perms.sql | 권한 매트릭스 (RCUD) | ~134건 | +| 03_auth_perm_tree.sql | 권한 리소스 트리 | ~48건 | +| 04_auth_orgs.sql | 기관 (해양경찰청 등) | 6건 | +| 05_auth_settings.sql | 시스템 설정 (세션, 메뉴, 비밀번호 정책) | 4건 | +| 06_auth_admin.sql | 관리자 계정 + 역할 매핑 | 2건 | +| 07_common_codes.sql | 공통코드 그룹 + 코드 | ~116건 | +| 08_organizations.sql | 조직 | 6건 | +| 09_layers.sql | 참조 안내 (seed.ts가 처리) | - | +| 10_hns_substances.sql | 참조 안내 (seedHns.ts가 처리) | - | +| 11_sample_accident.sql | 샘플 사고 + 유출 데이터 | 2건 | +| 12_assets.sql | 기관/장비/연락처 | ~640건 | +| 13_reports_templates.sql | 보고서 템플릿 + 섹션 + 카테고리 | ~70건 | +| 14_cctv_cameras.sql | CCTV 카메라 | 12건 | + +## 주의사항 + +- 09, 10번은 참조 안내 파일입니다. 실제 데이터는 `npm run db:seed`로 삽입됩니다. +- seed 실행 전에 반드시 `database/schema/` DDL이 먼저 실행되어야 합니다. +- 운영 환경에서 관리자 비밀번호는 반드시 변경하세요. diff --git a/docs/COMMON-GUIDE.md b/docs/COMMON-GUIDE.md index cc84ae5..3b438a4 100644 --- a/docs/COMMON-GUIDE.md +++ b/docs/COMMON-GUIDE.md @@ -1420,16 +1420,19 @@ backend/src/ +-- incidents/ 사건/사고 +-- scat/ Pre-SCAT 조사 +-- db/ -| +-- wingDb.ts wing DB Pool (운영 데이터) -| +-- authDb.ts wing_auth DB Pool (인증 데이터) +| +-- wingDb.ts wing DB Pool (운영 데이터, auth 스키마 포함) +| +-- authDb.ts wingPool re-export (하위 호환용, auth 스키마 접근) +-- middleware/ | +-- security.ts 입력 살균, 크기 제한, 서버 정보 제거 +-- server.ts Express 진입점 + 보안 미들웨어 + 라우터 등록 database/ -+-- auth_init.sql 인증 DB DDL + 초기 데이터 -+-- init.sql 운영 DB DDL ++-- schema/ +| +-- 00_init.sql wing DB 생성, PostGIS, 스키마 설정 +| +-- 01_auth_tables.sql auth 스키마 테이블 DDL ++-- seed/ 초기 데이터 (01~06) +-- migration/ 마이그레이션 스크립트 ++-- _deprecated/ 사용 중단 파일 (auth_init.sql, init.sql 등) +-- 003_perm_tree.sql 리소스 트리 (AUTH_PERM_TREE) +-- 004_oper_cd.sql 오퍼레이션 코드 (OPER_CD) 추가 +-- 006_board.sql 게시판 (BOARD_POST) @@ -1460,7 +1463,8 @@ database/ import { wingPool } from '../db/wingDb.js'; const result = await wingPool.query('SELECT * FROM LAYER WHERE LAYER_CD = $1', [id]); -// wing_auth DB (인증 데이터: 사용자, 역할, 권한, 감사로그 등) +// auth 스키마 (인증 데이터: 사용자, 역할, 권한, 감사로그 등) +// authPool은 wingPool과 동일한 Pool 인스턴스를 re-export한 하위 호환용 별칭이다 import { authPool } from '../db/authDb.js'; const result = await authPool.query('SELECT * FROM AUTH_USER WHERE USER_ID = $1', [id]); ``` diff --git a/docs/CRUD-API-GUIDE.md b/docs/CRUD-API-GUIDE.md index 7212063..72e8d44 100644 --- a/docs/CRUD-API-GUIDE.md +++ b/docs/CRUD-API-GUIDE.md @@ -23,7 +23,7 @@ DB 설계부터 백엔드 구현, 프론트엔드 연동까지 End-to-End 패턴 [Frontend] [Backend] [Database] tabs/{탭}/services/{tab}Api.ts src/{domain}/{domain}Router.ts PostgreSQL 16 Axios (withCredentials: true) requireAuth -> requirePermission - --HTTP--> src/{domain}/{domain}Service.ts wingPool / authPool + --HTTP--> src/{domain}/{domain}Service.ts wingPool (auth 스키마 포함) wingPool.query(SQL, params) --SQL--> ``` @@ -38,12 +38,12 @@ tabs/{탭}/services/{tab}Api.ts src/{domain}/{domain}Router.ts Postgre ### DB Pool 선택 기준 ```ts -import { wingPool } from '../db/wingDb.js'; // 업무 데이터 (BOARD_POST, LAYER 등) -import { authPool } from '../db/authDb.js'; // 인증 데이터 (AUTH_USER, AUTH_ROLE 등) +import { wingPool } from '../db/wingDb.js'; // 업무 데이터 (wing 스키마: BOARD_POST, LAYER 등) +import { authPool } from '../db/authDb.js'; // 인증 데이터 (auth 스키마: AUTH_USER, AUTH_ROLE 등) ``` -> **참고**: `authPool`은 `wingPool`의 re-export이다 (동일 서버, search_path = wing, auth, public). -> 신규 코드는 `wingPool`을 사용한다. 다만 의미적으로 인증 데이터를 다룰 때 `authPool`을 쓰는 것도 허용한다. +> **참고**: `authPool`은 `wingPool`의 re-export이다 (wing 단일 DB, search_path = wing, auth, public). +> 신규 코드는 `wingPool`을 사용한다. 다만 의미적으로 auth 스키마 데이터를 다룰 때 `authPool`을 쓰는 것도 허용한다. ### HTTP 메서드 정책 diff --git a/docs/DEVELOPMENT-GUIDE.md b/docs/DEVELOPMENT-GUIDE.md index 7029ed8..63602db 100644 --- a/docs/DEVELOPMENT-GUIDE.md +++ b/docs/DEVELOPMENT-GUIDE.md @@ -100,20 +100,13 @@ cd ../backend && npm install PORT=3001 NODE_ENV=development -# wing DB (운영 데이터) +# wing DB (운영 데이터 + auth 스키마 통합) WING_DB_HOST=211.208.115.83 WING_DB_PORT=5432 WING_DB_USER=wing WING_DB_PASS=<비밀번호> WING_DB_NAME=wing -# wing_auth DB (인증/권한) -AUTH_DB_HOST=211.208.115.83 -AUTH_DB_PORT=5432 -AUTH_DB_USER=wing_auth -AUTH_DB_PASS=<비밀번호> -AUTH_DB_NAME=wing_auth - # JWT JWT_SECRET=<시크릿> JWT_EXPIRES_IN=24h @@ -287,8 +280,8 @@ backend/src/ │ └── {도메인}Service.ts ├── middleware/ 보안 (입력 살균, rate-limit) └── db/ - ├── wingDb.ts wing DB Pool - ├── authDb.ts wing_auth DB Pool + ├── wingDb.ts wing DB Pool (auth 스키마 포함) + ├── authDb.ts wingPool re-export (하위 호환용) └── seed.ts 시드 데이터 ``` diff --git a/docs/INSTALL_GUIDE.md b/docs/INSTALL_GUIDE.md index 243e765..367c90b 100755 --- a/docs/INSTALL_GUIDE.md +++ b/docs/INSTALL_GUIDE.md @@ -23,7 +23,7 @@ |-----------|----------|----------|------| | Node.js | v20 | v20 LTS | Frontend/Backend 실행 | | npm | v10 | v10+ | 패키지 관리 (Node.js 포함) | -| PostgreSQL | v15 | v16 | 운영 DB + 인증 DB | +| PostgreSQL | v15 | v16 | 운영 DB (wing 단일 DB) | | PostGIS | v3.3 | v3.4 | 공간 데이터 처리 | | Git | v2.30 | v2.40+ | 소스 코드 관리 | @@ -95,15 +95,18 @@ wing/ │ │ ├── auth/ 인증 (JWT, Google OAuth) │ │ ├── users/ 사용자 관리 │ │ ├── roles/ 역할/권한 (RBAC 2차원 권한) -│ │ ├── db/ DB Pool (wingDb, authDb), seed +│ │ ├── db/ DB Pool (wingDb, authDb — re-export), seed │ │ ├── middleware/ 보안 (입력 살균, rate-limit) │ │ └── {도메인}/ 도메인별 모듈 (Router + Service) │ ├── package.json │ └── tsconfig.json ├── database/ SQL 스크립트 -│ ├── init.sql wing DB 초기 스키마 (PostGIS) -│ ├── auth_init.sql wing_auth DB 초기 스키마 -│ └── migration/ 마이그레이션 (001 ~ 016) +│ ├── schema/ DDL (스키마 정의) +│ │ ├── 00_init.sql wing DB 생성, PostGIS, 스키마 설정 +│ │ └── 01_auth_tables.sql auth 스키마 테이블 (사용자, 역할, 권한 등) +│ ├── seed/ 초기 데이터 (01~06) +│ ├── migration/ 마이그레이션 (001 ~ 016) +│ └── _deprecated/ 사용 중단 파일 (auth_init.sql 등) ├── docs/ 개발 문서 ├── .githooks/ Git Hooks (pre-commit, commit-msg) ├── .gitea/workflows/ CI/CD (Gitea Actions) @@ -166,15 +169,6 @@ WING_DB_USER=wing WING_DB_PASS=<비밀번호> WING_DB_NAME=wing -# =========================================== -# wing_auth DB (인증/권한 - PostgreSQL) -# =========================================== -AUTH_DB_HOST=211.208.115.83 -AUTH_DB_PORT=5432 -AUTH_DB_USER=wing_auth -AUTH_DB_PASS=<비밀번호> -AUTH_DB_NAME=wing_auth - # =========================================== # JWT 인증 # =========================================== @@ -290,34 +284,44 @@ cd frontend && npm run dev # 터미널 2 ### 5-1. DB 구성 -프로젝트는 동일 PostgreSQL 서버에 2개의 데이터베이스를 사용한다: +프로젝트는 단일 PostgreSQL 데이터베이스(wing)에 3개의 스키마를 사용한다: -| DB | 용도 | 확장 | -|----|------|------| -| `wing` | 운영 데이터 (사고, 예측, 자산 등) | PostGIS | -| `wing_auth` | 인증/권한 (사용자, 역할, 메뉴) | uuid-ossp, pgcrypto | +| 스키마 | 용도 | 주요 테이블 | +|--------|------|------------| +| `public` | 운영 데이터 (사고, 예측, 자산 등) | PostGIS 확장 포함 | +| `auth` | 인증/권한 (사용자, 역할, 메뉴, 감사로그) | AUTH_USER, AUTH_ROLE, AUTH_PERM 등 | +| `wing` | 기타 도메인 테이블 | 레이어, 시뮬레이션 등 | + +백엔드는 `search_path = wing, auth, public`으로 접속하므로 스키마 접두사 없이 테이블을 참조할 수 있다. ### 5-2. 신규 DB 초기화 PostgreSQL에 최초 설치 시 아래 순서로 실행한다. ```bash -# 1. wing DB 초기화 (PostgreSQL superuser로 실행) -psql -U postgres -f database/init.sql +# 1. wing DB 및 스키마 초기화 (PostgreSQL superuser로 실행) +psql -U postgres -f database/schema/00_init.sql -# 2. wing_auth DB 초기화 -psql -U postgres -f database/auth_init.sql +# 2. auth 스키마 테이블 생성 +psql -U postgres -d wing -f database/schema/01_auth_tables.sql + +# 3. 초기 데이터 적재 (01~06 순서대로) +psql -U postgres -d wing -f database/seed/01_common_code.sql +psql -U postgres -d wing -f database/seed/02_org.sql +psql -U postgres -d wing -f database/seed/03_role.sql +psql -U postgres -d wing -f database/seed/04_perm_tree.sql +psql -U postgres -d wing -f database/seed/05_menu.sql +psql -U postgres -d wing -f database/seed/06_admin_user.sql ``` -`init.sql`에 포함된 내용: +`database/schema/00_init.sql`에 포함된 내용: - 사용자(wing) 및 데이터베이스(wing) 생성 -- PostGIS 확장 설치 +- PostGIS, uuid-ossp, pgcrypto 확장 설치 +- public, auth, wing 스키마 생성 및 search_path 설정 - 공통코드, 시뮬레이션, 사고, 예측 등 핵심 테이블 생성 -`auth_init.sql`에 포함된 내용: -- 사용자(wing_auth) 및 데이터베이스(wing_auth) 생성 -- uuid-ossp, pgcrypto 확장 설치 -- 조직, 역할, 사용자, 권한, 메뉴, 설정, 감사로그 테이블 생성 +`database/schema/01_auth_tables.sql`에 포함된 내용: +- auth 스키마 내 조직, 역할, 사용자, 권한, 메뉴, 설정, 감사로그 테이블 생성 ### 5-3. 마이그레이션 적용 @@ -509,11 +513,6 @@ Environment=WING_DB_PORT=5432 Environment=WING_DB_USER=wing Environment=WING_DB_PASS=<비밀번호> Environment=WING_DB_NAME=wing -Environment=AUTH_DB_HOST=211.208.115.83 -Environment=AUTH_DB_PORT=5432 -Environment=AUTH_DB_USER=wing_auth -Environment=AUTH_DB_PASS=<비밀번호> -Environment=AUTH_DB_NAME=wing_auth Environment=JWT_SECRET=<시크릿> Environment=JWT_EXPIRES_IN=24h Environment=FRONTEND_URL=https://wing-demo.gc-si.dev @@ -647,7 +646,6 @@ curl -s https://wing-demo.gc-si.dev/api/ | Backend API | http://localhost:3001 | 3001 | | Backend 헬스 체크 | http://localhost:3001/health | 3001 | | PostgreSQL (wing) | 211.208.115.83:5432/wing | 5432 | -| PostgreSQL (wing_auth) | 211.208.115.83:5432/wing_auth | 5432 | ### 운영 환경 @@ -710,7 +708,7 @@ curl -s https://wing-demo.gc-si.dev/api/ | 증상 | 원인 | 해결 | |------|------|------| -| `relation "XXX" does not exist` | 테이블 미생성 | `database/init.sql` 또는 해당 마이그레이션 SQL 실행 | +| `relation "XXX" does not exist` | 테이블 미생성 | `database/schema/*.sql` 또는 해당 마이그레이션 SQL 실행 | | PostGIS 함수 에러 | PostGIS 확장 미설치 | `psql -U postgres -d wing -c "CREATE EXTENSION IF NOT EXISTS postgis"` | | 인코딩 문제 (한글 깨짐) | DB 인코딩 설정 | DB 생성 시 `ENCODING='UTF8' LC_COLLATE='ko_KR.UTF-8'` 지정 | | seed 실패 | 이미 데이터 존재 또는 FK 제약 | 에러 메시지 확인 후 해당 테이블 데이터 정리 | diff --git a/docs/MENU-TAB-GUIDE.md b/docs/MENU-TAB-GUIDE.md index 141a5d5..df60f6d 100644 --- a/docs/MENU-TAB-GUIDE.md +++ b/docs/MENU-TAB-GUIDE.md @@ -42,7 +42,7 @@ board 탭을 기준 템플릿으로 사용하며, 각 단계별 실제 코드 | | `backend/src/{도메인}/{domain}Service.ts` | 서비스 생성 | | **Step 5** | `backend/src/server.ts` | 라우트 등록 | | | `backend/src/settings/settingsService.ts` | DEFAULT_MENU_CONFIG 추가 | -| | `database/auth_init.sql` | menu.config 초기 JSON 추가 | +| | `database/seed/05_auth_settings.sql` | menu.config 초기 JSON 추가 | | | `database/migration/NNN_{domain}.sql` | DB 마이그레이션 | --- @@ -494,7 +494,7 @@ export async function createItem(input: CreateItemInput): Promise<{ sn: number } | 항목 | 패턴 | |------|------| -| DB Pool | `wingPool` (wing DB) 또는 `authPool` (wing_auth DB) | +| DB Pool | `wingPool` (wing DB, wing 스키마) 또는 `authPool` (wing DB re-export, auth 스키마) | | 에러 처리 | `AuthError(message, status)` 활용 | | 논리 삭제 | `USE_YN = 'Y'/'N'` 컬럼 사용, DELETE 대신 UPDATE | | 페이징 | `LIMIT $N OFFSET $M`, 기본 size 20, 최대 100 | @@ -531,10 +531,10 @@ const DEFAULT_MENU_CONFIG: MenuConfigItem[] = [ ]; ``` -### 5-3. auth_init.sql에 menu.config 초기 JSON 추가 +### 5-3. seed/05_auth_settings.sql에 menu.config 초기 JSON 추가 ```sql --- database/auth_init.sql 의 menu.config INSERT 문에 새 항목 추가 +-- database/seed/05_auth_settings.sql 의 menu.config INSERT 문에 새 항목 추가 -- (신규 설치 시에만 적용. 기존 운영 DB는 관리자 UI에서 관리) ``` @@ -629,7 +629,7 @@ mkdir -p frontend/src/tabs/monitoring/services ``` - `database/migration/017_monitoring.sql` 생성 -- `database/auth_init.sql` 의 menu.config JSON에 항목 추가 +- `database/seed/05_auth_settings.sql` 의 menu.config JSON에 항목 추가 ### 6단계: 검증 @@ -663,7 +663,7 @@ cd backend && npx tsc --noEmit # 백엔드 컴파일 검증 ### DB - [ ] `database/migration/NNN_{domain}.sql` 마이그레이션 작성 -- [ ] `database/auth_init.sql` menu.config 초기 JSON 업데이트 +- [ ] `database/seed/05_auth_settings.sql` menu.config 초기 JSON 업데이트 - [ ] SQL 실행 검증 ### 배포 후 diff --git a/docs/README.md b/docs/README.md index dea0ae3..15d0981 100755 --- a/docs/README.md +++ b/docs/README.md @@ -33,7 +33,7 @@ |--- WebSocket ------> [Socket.IO] | [PostgreSQL 16 + PostGIS] - wing DB (운영) + wing_auth DB (인증) + wing 단일 DB (public, auth, wing 3개 스키마) ``` ### HTTP 정책 @@ -90,11 +90,12 @@ wing/ │ ├── aerial/, rescue/ 업무 도메인 │ ├── routes/ layers, simulation │ ├── middleware/ security (sanitize, rate-limit) -│ └── db/ wingDb, authDb, seed +│ └── db/ wingDb (authDb는 re-export), seed ├── database/ -│ ├── init.sql wing DB 스키마 -│ ├── auth_init.sql wing_auth DB 스키마 -│ └── migration/ 001~016 마이그레이션 +│ ├── schema/ ERD용 순수 DDL (14파일) +│ ├── seed/ 초기 데이터 (14파일) +│ ├── migration/ 증분 마이그레이션 (001~016) +│ └── _deprecated/ 구 스크립트 (통합 이전) └── .gitea/workflows/ CI/CD 파이프라인 ``` @@ -165,12 +166,17 @@ wing/ ## 데이터베이스 -| DB | 용도 | 비고 | -|----|------|------| -| wing | 운영 데이터 | PostgreSQL 16 + PostGIS | -| wing_auth | 인증/권한 | 동일 서버, 별도 DB | +wing 단일 DB에 3개 스키마로 구성: -마이그레이션 파일: `database/migration/001~016` +| 스키마 | 용도 | 테이블 수 | +|--------|------|-----------| +| public | PostGIS 확장 | - | +| auth | 인증/권한/감사로그 | 9 | +| wing | 운영 데이터 | 35 | + +- 초기 DDL: `database/schema/` (14파일) +- 초기 데이터: `database/seed/` (14파일) +- 마이그레이션: `database/migration/001~016` --- @@ -223,11 +229,11 @@ VITE_GOOGLE_CLIENT_ID=your-google-client-id PORT=3001 NODE_ENV=development JWT_SECRET=your-jwt-secret -AUTH_DB_HOST=localhost -AUTH_DB_PORT=5432 -AUTH_DB_NAME=wing_auth -AUTH_DB_USER=wing_auth -AUTH_DB_PASSWORD=your-password +DB_HOST=211.208.115.83 +DB_PORT=5432 +DB_NAME=wing +DB_USER=wing +DB_PASSWORD=your-password GOOGLE_CLIENT_ID=your-google-client-id ```