feat: SWG 가스켓 전체 구성 정보 표시 개선
Some checks failed
SonarQube Analysis / SonarQube Scan (push) Has been cancelled
Some checks failed
SonarQube Analysis / SonarQube Scan (push) Has been cancelled
- H/F/I/O SS304/GRAPHITE/CS/CS 패턴에서 4개 구성요소 모두 표시 - 기존 SS304 + GRAPHITE → SS304/GRAPHITE/CS/CS로 완전한 구성 표시 - 외부링/필러/내부링/추가구성 모든 정보 포함 - 구매수량 계산 모달에서 정확한 재질 정보 확인 가능
This commit is contained in:
184
backend/scripts/15_create_tubing_system.sql
Normal file
184
backend/scripts/15_create_tubing_system.sql
Normal file
@@ -0,0 +1,184 @@
|
||||
-- ================================
|
||||
-- Tubing 제품 관리 시스템
|
||||
-- 실행일: 2025.08.01
|
||||
-- ================================
|
||||
|
||||
-- 1. Tubing 카테고리 테이블 (일반, VCR, 기타 등)
|
||||
CREATE TABLE IF NOT EXISTS tubing_categories (
|
||||
id SERIAL PRIMARY KEY,
|
||||
category_code VARCHAR(20) UNIQUE NOT NULL,
|
||||
category_name VARCHAR(100) NOT NULL,
|
||||
description TEXT,
|
||||
is_active BOOLEAN DEFAULT TRUE,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
-- 2. Tubing 규격 마스터 테이블
|
||||
CREATE TABLE IF NOT EXISTS tubing_specifications (
|
||||
id SERIAL PRIMARY KEY,
|
||||
category_id INTEGER REFERENCES tubing_categories(id),
|
||||
spec_code VARCHAR(50) UNIQUE NOT NULL,
|
||||
spec_name VARCHAR(200) NOT NULL,
|
||||
|
||||
-- 물리적 규격
|
||||
outer_diameter_mm DECIMAL(8,3), -- 외경 (mm)
|
||||
wall_thickness_mm DECIMAL(6,3), -- 두께 (mm)
|
||||
inner_diameter_mm DECIMAL(8,3), -- 내경 (mm, 계산 또는 실측)
|
||||
|
||||
-- 재질 정보
|
||||
material_grade VARCHAR(100), -- SS316, SS316L, Inconel625 등
|
||||
material_standard VARCHAR(100), -- ASTM A269, JIS G3463 등
|
||||
|
||||
-- 압력/온도 등급
|
||||
max_pressure_bar DECIMAL(8,2), -- 최대 압력 (bar)
|
||||
max_temperature_c DECIMAL(6,2), -- 최대 온도 (°C)
|
||||
min_temperature_c DECIMAL(6,2), -- 최소 온도 (°C)
|
||||
|
||||
-- 표준 규격
|
||||
standard_length_m DECIMAL(8,3), -- 표준 길이 (m)
|
||||
bend_radius_min_mm DECIMAL(8,2), -- 최소 벤딩 반경 (mm)
|
||||
|
||||
-- 기타 정보
|
||||
surface_finish VARCHAR(100), -- 표면 마감 (BA, #4, 2B 등)
|
||||
hardness VARCHAR(50), -- 경도
|
||||
notes TEXT,
|
||||
|
||||
is_active BOOLEAN DEFAULT TRUE,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
-- 3. 제조사 정보 테이블
|
||||
CREATE TABLE IF NOT EXISTS tubing_manufacturers (
|
||||
id SERIAL PRIMARY KEY,
|
||||
manufacturer_code VARCHAR(20) UNIQUE NOT NULL,
|
||||
manufacturer_name VARCHAR(200) NOT NULL,
|
||||
country VARCHAR(100),
|
||||
website VARCHAR(500),
|
||||
contact_info JSONB, -- 연락처 정보 (JSON)
|
||||
quality_certs JSONB, -- 품질 인증서 정보 (ISO, API 등)
|
||||
is_active BOOLEAN DEFAULT TRUE,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
-- 4. 제조사별 제품 테이블 (품목번호 매핑)
|
||||
CREATE TABLE IF NOT EXISTS tubing_products (
|
||||
id SERIAL PRIMARY KEY,
|
||||
specification_id INTEGER REFERENCES tubing_specifications(id),
|
||||
manufacturer_id INTEGER REFERENCES tubing_manufacturers(id),
|
||||
|
||||
-- 제조사 품목번호 정보
|
||||
manufacturer_part_number VARCHAR(200) NOT NULL, -- 제조사 품목번호
|
||||
manufacturer_product_name VARCHAR(300), -- 제조사 제품명
|
||||
|
||||
-- 가격/공급 정보
|
||||
list_price DECIMAL(12,2), -- 정가
|
||||
currency VARCHAR(10) DEFAULT 'KRW', -- 통화
|
||||
lead_time_days INTEGER, -- 리드타임 (일)
|
||||
minimum_order_qty DECIMAL(10,3), -- 최소 주문 수량
|
||||
standard_packaging_qty DECIMAL(10,3), -- 표준 포장 수량
|
||||
|
||||
-- 가용성 정보
|
||||
availability_status VARCHAR(50), -- 재고 상태
|
||||
last_price_update DATE, -- 마지막 가격 업데이트
|
||||
|
||||
-- 추가 정보
|
||||
datasheet_url VARCHAR(500), -- 데이터시트 URL
|
||||
catalog_page VARCHAR(100), -- 카탈로그 페이지
|
||||
notes TEXT,
|
||||
|
||||
is_active BOOLEAN DEFAULT TRUE,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
-- 유니크 제약 (같은 규격의 같은 제조사 제품은 하나만)
|
||||
UNIQUE(specification_id, manufacturer_id, manufacturer_part_number)
|
||||
);
|
||||
|
||||
-- 5. BOM에서 사용되는 Tubing 매핑 테이블
|
||||
CREATE TABLE IF NOT EXISTS material_tubing_mapping (
|
||||
id SERIAL PRIMARY KEY,
|
||||
material_id INTEGER REFERENCES materials(id) ON DELETE CASCADE,
|
||||
tubing_product_id INTEGER REFERENCES tubing_products(id),
|
||||
|
||||
-- 매핑 정보
|
||||
confidence_score DECIMAL(3,2), -- 매핑 신뢰도 (0.00-1.00)
|
||||
mapping_method VARCHAR(50), -- 매핑 방법 (auto/manual)
|
||||
mapped_by VARCHAR(100), -- 매핑한 사용자
|
||||
mapped_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
-- 수량 정보
|
||||
required_length_m DECIMAL(10,3), -- 필요 길이 (m)
|
||||
calculated_quantity DECIMAL(10,3), -- 계산된 주문 수량
|
||||
|
||||
-- 검증 정보
|
||||
is_verified BOOLEAN DEFAULT FALSE,
|
||||
verified_by VARCHAR(100),
|
||||
verified_at TIMESTAMP,
|
||||
|
||||
notes TEXT,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
-- ================================
|
||||
-- 인덱스 생성
|
||||
-- ================================
|
||||
|
||||
-- Tubing 규격 관련 인덱스
|
||||
CREATE INDEX idx_tubing_specs_category ON tubing_specifications(category_id);
|
||||
CREATE INDEX idx_tubing_specs_material ON tubing_specifications(material_grade);
|
||||
CREATE INDEX idx_tubing_specs_diameter ON tubing_specifications(outer_diameter_mm, wall_thickness_mm);
|
||||
|
||||
-- 제품 관련 인덱스
|
||||
CREATE INDEX idx_tubing_products_spec ON tubing_products(specification_id);
|
||||
CREATE INDEX idx_tubing_products_manufacturer ON tubing_products(manufacturer_id);
|
||||
CREATE INDEX idx_tubing_products_part_number ON tubing_products(manufacturer_part_number);
|
||||
|
||||
-- 매핑 관련 인덱스
|
||||
CREATE INDEX idx_material_tubing_mapping_material ON material_tubing_mapping(material_id);
|
||||
CREATE INDEX idx_material_tubing_mapping_product ON material_tubing_mapping(tubing_product_id);
|
||||
|
||||
-- ================================
|
||||
-- 기초 데이터 입력
|
||||
-- ================================
|
||||
|
||||
-- Tubing 카테고리 기초 데이터
|
||||
INSERT INTO tubing_categories (category_code, category_name, description) VALUES
|
||||
('GENERAL', '일반 Tubing', '일반적인 스테인리스 스틸 튜빙'),
|
||||
('VCR', 'VCR Tubing', 'VCR (Vacuum Coupling Radiation) 연결용 튜빙'),
|
||||
('SANITARY', 'Sanitary Tubing', '위생용 튜빙 (식품, 제약 등)'),
|
||||
('HVAC', 'HVAC Tubing', '공조용 튜빙'),
|
||||
('HYDRAULIC', 'Hydraulic Tubing', '유압용 튜빙'),
|
||||
('PNEUMATIC', 'Pneumatic Tubing', '공압용 튜빙'),
|
||||
('PROCESS', 'Process Tubing', '공정용 특수 튜빙'),
|
||||
('EXOTIC', 'Exotic Material', '특수 재질 튜빙 (Hastelloy, Inconel 등)')
|
||||
ON CONFLICT (category_code) DO NOTHING;
|
||||
|
||||
-- 주요 제조사 기초 데이터
|
||||
INSERT INTO tubing_manufacturers (manufacturer_code, manufacturer_name, country) VALUES
|
||||
('SWAGELOK', 'Swagelok Company', 'USA'),
|
||||
('PARKER', 'Parker Hannifin', 'USA'),
|
||||
('HAM_LET', 'Ham-Let Group', 'Israel'),
|
||||
('SUPERLOK', 'Superlok USA', 'USA'),
|
||||
('FITOK', 'Fitok Group', 'China'),
|
||||
('DK_LOK', 'DK-Lok Corporation', 'South Korea'),
|
||||
('GYROLOK', 'Gyrolok (Oliver Valves)', 'UK'),
|
||||
('AS_ONE', 'AS ONE Corporation', 'Japan')
|
||||
ON CONFLICT (manufacturer_code) DO NOTHING;
|
||||
|
||||
-- 기본 스테인리스 스틸 튜빙 규격 예시
|
||||
INSERT INTO tubing_specifications (
|
||||
category_id, spec_code, spec_name,
|
||||
outer_diameter_mm, wall_thickness_mm, inner_diameter_mm,
|
||||
material_grade, material_standard,
|
||||
max_pressure_bar, max_temperature_c, min_temperature_c,
|
||||
standard_length_m
|
||||
) VALUES
|
||||
(1, 'SS316-6MM-1MM', '6mm OD x 1mm WT SS316 Tubing', 6.0, 1.0, 4.0, 'SS316', 'ASTM A269', 413, 815, -196, 6.0),
|
||||
(1, 'SS316-8MM-1MM', '8mm OD x 1mm WT SS316 Tubing', 8.0, 1.0, 6.0, 'SS316', 'ASTM A269', 310, 815, -196, 6.0),
|
||||
(1, 'SS316-10MM-1MM', '10mm OD x 1mm WT SS316 Tubing', 10.0, 1.0, 8.0, 'SS316', 'ASTM A269', 248, 815, -196, 6.0),
|
||||
(1, 'SS316-12MM-1.5MM', '12mm OD x 1.5mm WT SS316 Tubing', 12.0, 1.5, 9.0, 'SS316', 'ASTM A269', 310, 815, -196, 6.0),
|
||||
(1, 'SS316L-6MM-1MM', '6mm OD x 1mm WT SS316L Tubing', 6.0, 1.0, 4.0, 'SS316L', 'ASTM A269', 413, 815, -196, 6.0)
|
||||
ON CONFLICT (spec_code) DO NOTHING;
|
||||
163
backend/scripts/16_performance_indexes.sql
Normal file
163
backend/scripts/16_performance_indexes.sql
Normal file
@@ -0,0 +1,163 @@
|
||||
-- ================================
|
||||
-- 성능 최적화를 위한 추가 인덱스
|
||||
-- 생성일: 2025.01 (Phase 2)
|
||||
-- ================================
|
||||
|
||||
-- 1. 복합 인덱스 (자주 함께 사용되는 컬럼들)
|
||||
-- ================================
|
||||
|
||||
-- files 테이블: job_no + revision 조합 (리비전 비교 시 자주 사용)
|
||||
CREATE INDEX IF NOT EXISTS idx_files_job_revision
|
||||
ON files(job_no, revision)
|
||||
WHERE is_active = true;
|
||||
|
||||
-- files 테이블: job_no + upload_date (최신 파일 조회)
|
||||
CREATE INDEX IF NOT EXISTS idx_files_job_date
|
||||
ON files(job_no, upload_date DESC)
|
||||
WHERE is_active = true;
|
||||
|
||||
-- materials 테이블: file_id + category (자재 분류별 조회)
|
||||
CREATE INDEX IF NOT EXISTS idx_materials_file_category
|
||||
ON materials(file_id, classified_category);
|
||||
|
||||
-- materials 테이블: category + material_grade (자재 종류별 재질 검색)
|
||||
CREATE INDEX IF NOT EXISTS idx_materials_category_grade
|
||||
ON materials(classified_category, material_grade);
|
||||
|
||||
-- 2. 검색 성능 향상 인덱스
|
||||
-- ================================
|
||||
|
||||
-- materials 테이블: description 텍스트 검색 (GIN 인덱스)
|
||||
CREATE INDEX IF NOT EXISTS idx_materials_description_gin
|
||||
ON materials USING gin(to_tsvector('english', original_description));
|
||||
|
||||
-- materials 테이블: 해시 기반 중복 검색
|
||||
CREATE INDEX IF NOT EXISTS idx_materials_hash
|
||||
ON materials(material_hash)
|
||||
WHERE material_hash IS NOT NULL;
|
||||
|
||||
-- 3. 정렬 성능 향상 인덱스
|
||||
-- ================================
|
||||
|
||||
-- jobs 테이블: 상태별 생성일 정렬
|
||||
CREATE INDEX IF NOT EXISTS idx_jobs_status_created
|
||||
ON jobs(status, created_at DESC)
|
||||
WHERE is_active = true;
|
||||
|
||||
-- materials 테이블: 수량별 정렬 (대용량 자재 우선 표시)
|
||||
CREATE INDEX IF NOT EXISTS idx_materials_quantity_desc
|
||||
ON materials(quantity DESC);
|
||||
|
||||
-- 4. 조건부 인덱스 (특정 조건에서만 사용)
|
||||
-- ================================
|
||||
|
||||
-- 검증되지 않은 자재만 (분류 검토 필요한 항목)
|
||||
CREATE INDEX IF NOT EXISTS idx_materials_unverified
|
||||
ON materials(classified_category, classification_confidence)
|
||||
WHERE is_verified = false;
|
||||
|
||||
-- 신뢰도가 낮은 분류 (0.8 미만)
|
||||
CREATE INDEX IF NOT EXISTS idx_materials_low_confidence
|
||||
ON materials(file_id, classified_category)
|
||||
WHERE classification_confidence < 0.8;
|
||||
|
||||
-- 5. 외래키 성능 향상
|
||||
-- ================================
|
||||
|
||||
-- pipe_details 테이블
|
||||
CREATE INDEX IF NOT EXISTS idx_pipe_details_material
|
||||
ON pipe_details(material_id);
|
||||
|
||||
-- fitting_details 테이블
|
||||
CREATE INDEX IF NOT EXISTS idx_fitting_details_material
|
||||
ON fitting_details(material_id);
|
||||
|
||||
-- valve_details 테이블
|
||||
CREATE INDEX IF NOT EXISTS idx_valve_details_material
|
||||
ON valve_details(material_id);
|
||||
|
||||
-- flange_details 테이블
|
||||
CREATE INDEX IF NOT EXISTS idx_flange_details_material
|
||||
ON flange_details(material_id);
|
||||
|
||||
-- bolt_details 테이블
|
||||
CREATE INDEX IF NOT EXISTS idx_bolt_details_material
|
||||
ON bolt_details(material_id);
|
||||
|
||||
-- gasket_details 테이블
|
||||
CREATE INDEX IF NOT EXISTS idx_gasket_details_material
|
||||
ON gasket_details(material_id);
|
||||
|
||||
-- instrument_details 테이블
|
||||
CREATE INDEX IF NOT EXISTS idx_instrument_details_material
|
||||
ON instrument_details(material_id);
|
||||
|
||||
-- 6. 통계 및 집계 성능 향상
|
||||
-- ================================
|
||||
|
||||
-- 프로젝트별 자재 통계 (job_no 기준)
|
||||
CREATE INDEX IF NOT EXISTS idx_materials_job_stats
|
||||
ON materials(
|
||||
(SELECT job_no FROM files WHERE files.id = materials.file_id),
|
||||
classified_category
|
||||
);
|
||||
|
||||
-- 파이프 길이 집계용 (파이프 cutting 계산)
|
||||
CREATE INDEX IF NOT EXISTS idx_pipe_length_aggregation
|
||||
ON pipe_details(material_id, length_mm)
|
||||
WHERE length_mm > 0;
|
||||
|
||||
-- 7. 성능 모니터링을 위한 뷰 생성
|
||||
-- ================================
|
||||
|
||||
-- 인덱스 사용률 모니터링 뷰
|
||||
CREATE OR REPLACE VIEW index_usage_stats AS
|
||||
SELECT
|
||||
schemaname,
|
||||
tablename,
|
||||
indexname,
|
||||
idx_tup_read,
|
||||
idx_tup_fetch,
|
||||
idx_scan,
|
||||
CASE
|
||||
WHEN idx_scan = 0 THEN 'UNUSED'
|
||||
WHEN idx_scan < 10 THEN 'LOW_USAGE'
|
||||
WHEN idx_scan < 100 THEN 'MEDIUM_USAGE'
|
||||
ELSE 'HIGH_USAGE'
|
||||
END as usage_level
|
||||
FROM pg_stat_user_indexes
|
||||
WHERE schemaname = 'public'
|
||||
ORDER BY idx_scan DESC;
|
||||
|
||||
-- 테이블 크기 및 성능 모니터링 뷰
|
||||
CREATE OR REPLACE VIEW table_performance_stats AS
|
||||
SELECT
|
||||
schemaname,
|
||||
tablename,
|
||||
n_tup_ins as inserts,
|
||||
n_tup_upd as updates,
|
||||
n_tup_del as deletes,
|
||||
seq_scan as sequential_scans,
|
||||
seq_tup_read as sequential_reads,
|
||||
idx_scan as index_scans,
|
||||
idx_tup_fetch as index_reads,
|
||||
CASE
|
||||
WHEN seq_scan + idx_scan = 0 THEN 0
|
||||
ELSE ROUND((idx_scan::numeric / (seq_scan + idx_scan)) * 100, 2)
|
||||
END as index_usage_percentage
|
||||
FROM pg_stat_user_tables
|
||||
WHERE schemaname = 'public'
|
||||
ORDER BY seq_scan + idx_scan DESC;
|
||||
|
||||
-- ================================
|
||||
-- 인덱스 생성 완료 로그
|
||||
-- ================================
|
||||
|
||||
-- 성능 최적화 인덱스 생성 완료 확인
|
||||
DO $$
|
||||
BEGIN
|
||||
RAISE NOTICE '성능 최적화 인덱스 생성 완료 - Phase 2 (2025.01)';
|
||||
RAISE NOTICE '총 생성된 인덱스: 복합 인덱스 4개, 검색 인덱스 2개, 정렬 인덱스 2개';
|
||||
RAISE NOTICE '조건부 인덱스 2개, 외래키 인덱스 7개, 집계 인덱스 2개';
|
||||
RAISE NOTICE '모니터링 뷰 2개 생성';
|
||||
END $$;
|
||||
29
backend/scripts/17_add_project_type_column.sql
Normal file
29
backend/scripts/17_add_project_type_column.sql
Normal file
@@ -0,0 +1,29 @@
|
||||
-- jobs 테이블에 project_type 컬럼 추가
|
||||
-- TK-MP-Project 프로젝트 유형 관리를 위한 스키마 업데이트
|
||||
|
||||
-- project_type 컬럼 추가 (기존 데이터가 있을 수 있으므로 안전하게 추가)
|
||||
DO $$
|
||||
BEGIN
|
||||
-- project_type 컬럼이 존재하지 않으면 추가
|
||||
IF NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM information_schema.columns
|
||||
WHERE table_name = 'jobs'
|
||||
AND column_name = 'project_type'
|
||||
) THEN
|
||||
ALTER TABLE jobs ADD COLUMN project_type VARCHAR(50) DEFAULT '냉동기';
|
||||
|
||||
-- 기존 데이터에 대한 기본값 설정
|
||||
UPDATE jobs SET project_type = '냉동기' WHERE project_type IS NULL;
|
||||
|
||||
-- NOT NULL 제약 조건 추가
|
||||
ALTER TABLE jobs ALTER COLUMN project_type SET NOT NULL;
|
||||
|
||||
-- 인덱스 추가 (프로젝트 유형별 조회 성능 향상)
|
||||
CREATE INDEX IF NOT EXISTS idx_jobs_project_type ON jobs(project_type);
|
||||
|
||||
RAISE NOTICE 'project_type 컬럼이 성공적으로 추가되었습니다.';
|
||||
ELSE
|
||||
RAISE NOTICE 'project_type 컬럼이 이미 존재합니다.';
|
||||
END IF;
|
||||
END $$;
|
||||
220
backend/scripts/18_create_auth_tables.sql
Normal file
220
backend/scripts/18_create_auth_tables.sql
Normal file
@@ -0,0 +1,220 @@
|
||||
-- TK-MP-Project 인증 시스템을 위한 사용자 및 로그인 테이블 생성
|
||||
-- TK-FB-Project 인증 시스템을 참고하여 구현
|
||||
|
||||
-- 1. 사용자 테이블 생성
|
||||
CREATE TABLE IF NOT EXISTS users (
|
||||
user_id SERIAL PRIMARY KEY,
|
||||
username VARCHAR(50) UNIQUE NOT NULL,
|
||||
password VARCHAR(255) NOT NULL,
|
||||
name VARCHAR(100) NOT NULL,
|
||||
email VARCHAR(100),
|
||||
|
||||
-- 권한 관리
|
||||
role VARCHAR(20) DEFAULT 'user' CHECK (role IN ('admin', 'system', 'leader', 'support', 'user')),
|
||||
access_level VARCHAR(20) DEFAULT 'worker' CHECK (access_level IN ('admin', 'system', 'group_leader', 'support_team', 'worker')),
|
||||
|
||||
-- 계정 상태 관리
|
||||
is_active BOOLEAN DEFAULT true,
|
||||
failed_login_attempts INT DEFAULT 0,
|
||||
locked_until TIMESTAMP NULL,
|
||||
|
||||
-- 추가 정보
|
||||
department VARCHAR(50),
|
||||
position VARCHAR(50),
|
||||
phone VARCHAR(20),
|
||||
|
||||
-- 타임스탬프
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
last_login_at TIMESTAMP NULL
|
||||
);
|
||||
|
||||
-- 2. 로그인 이력 테이블 생성
|
||||
CREATE TABLE IF NOT EXISTS login_logs (
|
||||
log_id SERIAL PRIMARY KEY,
|
||||
user_id INT REFERENCES users(user_id) ON DELETE CASCADE,
|
||||
login_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
ip_address VARCHAR(45),
|
||||
user_agent TEXT,
|
||||
login_status VARCHAR(20) CHECK (login_status IN ('success', 'failed')),
|
||||
failure_reason VARCHAR(100),
|
||||
session_duration INT, -- 세션 지속 시간 (초)
|
||||
|
||||
-- 인덱스를 위한 컬럼
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
-- 3. 사용자 세션 테이블 (JWT Refresh Token 관리)
|
||||
CREATE TABLE IF NOT EXISTS user_sessions (
|
||||
session_id SERIAL PRIMARY KEY,
|
||||
user_id INT REFERENCES users(user_id) ON DELETE CASCADE,
|
||||
refresh_token VARCHAR(500) NOT NULL,
|
||||
expires_at TIMESTAMP NOT NULL,
|
||||
ip_address VARCHAR(45),
|
||||
user_agent TEXT,
|
||||
is_active BOOLEAN DEFAULT true,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
-- 4. 권한 테이블 (확장 가능한 권한 시스템)
|
||||
CREATE TABLE IF NOT EXISTS permissions (
|
||||
permission_id SERIAL PRIMARY KEY,
|
||||
permission_name VARCHAR(50) UNIQUE NOT NULL,
|
||||
description TEXT,
|
||||
module VARCHAR(30), -- 모듈별 권한 관리 (bom, project, purchase 등)
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
-- 5. 역할-권한 매핑 테이블
|
||||
CREATE TABLE IF NOT EXISTS role_permissions (
|
||||
role_permission_id SERIAL PRIMARY KEY,
|
||||
role VARCHAR(20) NOT NULL,
|
||||
permission_id INT REFERENCES permissions(permission_id) ON DELETE CASCADE,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
UNIQUE(role, permission_id)
|
||||
);
|
||||
|
||||
-- 6. 인덱스 생성 (성능 최적화)
|
||||
CREATE INDEX IF NOT EXISTS idx_users_username ON users(username);
|
||||
CREATE INDEX IF NOT EXISTS idx_users_email ON users(email);
|
||||
CREATE INDEX IF NOT EXISTS idx_users_role ON users(role);
|
||||
CREATE INDEX IF NOT EXISTS idx_users_is_active ON users(is_active);
|
||||
CREATE INDEX IF NOT EXISTS idx_users_created_at ON users(created_at);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_login_logs_user_id ON login_logs(user_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_login_logs_login_time ON login_logs(login_time);
|
||||
CREATE INDEX IF NOT EXISTS idx_login_logs_ip_address ON login_logs(ip_address);
|
||||
CREATE INDEX IF NOT EXISTS idx_login_logs_status ON login_logs(login_status);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_user_sessions_user_id ON user_sessions(user_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_user_sessions_refresh_token ON user_sessions(refresh_token);
|
||||
CREATE INDEX IF NOT EXISTS idx_user_sessions_expires_at ON user_sessions(expires_at);
|
||||
CREATE INDEX IF NOT EXISTS idx_user_sessions_is_active ON user_sessions(is_active);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_permissions_module ON permissions(module);
|
||||
CREATE INDEX IF NOT EXISTS idx_role_permissions_role ON role_permissions(role);
|
||||
|
||||
-- 7. 기본 권한 데이터 삽입
|
||||
INSERT INTO permissions (permission_name, description, module) VALUES
|
||||
-- BOM 관리 권한
|
||||
('bom.view', 'BOM 조회 권한', 'bom'),
|
||||
('bom.create', 'BOM 생성 권한', 'bom'),
|
||||
('bom.edit', 'BOM 수정 권한', 'bom'),
|
||||
('bom.delete', 'BOM 삭제 권한', 'bom'),
|
||||
('bom.approve', 'BOM 승인 권한', 'bom'),
|
||||
|
||||
-- 프로젝트 관리 권한
|
||||
('project.view', '프로젝트 조회 권한', 'project'),
|
||||
('project.create', '프로젝트 생성 권한', 'project'),
|
||||
('project.edit', '프로젝트 수정 권한', 'project'),
|
||||
('project.delete', '프로젝트 삭제 권한', 'project'),
|
||||
('project.manage', '프로젝트 관리 권한', 'project'),
|
||||
|
||||
-- 파일 관리 권한
|
||||
('file.upload', '파일 업로드 권한', 'file'),
|
||||
('file.download', '파일 다운로드 권한', 'file'),
|
||||
('file.delete', '파일 삭제 권한', 'file'),
|
||||
|
||||
-- 사용자 관리 권한
|
||||
('user.view', '사용자 조회 권한', 'user'),
|
||||
('user.create', '사용자 생성 권한', 'user'),
|
||||
('user.edit', '사용자 수정 권한', 'user'),
|
||||
('user.delete', '사용자 삭제 권한', 'user'),
|
||||
|
||||
-- 시스템 관리 권한
|
||||
('system.admin', '시스템 관리 권한', 'system'),
|
||||
('system.logs', '로그 조회 권한', 'system'),
|
||||
('system.settings', '시스템 설정 권한', 'system')
|
||||
|
||||
ON CONFLICT (permission_name) DO NOTHING;
|
||||
|
||||
-- 8. 역할별 기본 권한 할당
|
||||
INSERT INTO role_permissions (role, permission_id)
|
||||
SELECT 'admin', permission_id FROM permissions
|
||||
ON CONFLICT (role, permission_id) DO NOTHING;
|
||||
|
||||
INSERT INTO role_permissions (role, permission_id)
|
||||
SELECT 'system', permission_id FROM permissions
|
||||
ON CONFLICT (role, permission_id) DO NOTHING;
|
||||
|
||||
INSERT INTO role_permissions (role, permission_id)
|
||||
SELECT 'leader', permission_id FROM permissions
|
||||
WHERE permission_name IN (
|
||||
'bom.view', 'bom.create', 'bom.edit', 'bom.approve',
|
||||
'project.view', 'project.create', 'project.edit', 'project.manage',
|
||||
'file.upload', 'file.download', 'file.delete',
|
||||
'user.view'
|
||||
)
|
||||
ON CONFLICT (role, permission_id) DO NOTHING;
|
||||
|
||||
INSERT INTO role_permissions (role, permission_id)
|
||||
SELECT 'support', permission_id FROM permissions
|
||||
WHERE permission_name IN (
|
||||
'bom.view', 'bom.create', 'bom.edit',
|
||||
'project.view', 'project.create', 'project.edit',
|
||||
'file.upload', 'file.download'
|
||||
)
|
||||
ON CONFLICT (role, permission_id) DO NOTHING;
|
||||
|
||||
INSERT INTO role_permissions (role, permission_id)
|
||||
SELECT 'user', permission_id FROM permissions
|
||||
WHERE permission_name IN (
|
||||
'bom.view',
|
||||
'project.view',
|
||||
'file.upload', 'file.download'
|
||||
)
|
||||
ON CONFLICT (role, permission_id) DO NOTHING;
|
||||
|
||||
-- 9. 기본 관리자 계정 생성 (비밀번호: admin123)
|
||||
-- bcrypt 해시: $2b$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi
|
||||
INSERT INTO users (username, password, name, email, role, access_level, department, position) VALUES
|
||||
('admin', '$2b$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', '시스템 관리자', 'admin@tkmp.com', 'admin', 'admin', 'IT', '시스템 관리자'),
|
||||
('system', '$2b$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', '시스템 계정', 'system@tkmp.com', 'system', 'system', 'IT', '시스템 계정')
|
||||
ON CONFLICT (username) DO NOTHING;
|
||||
|
||||
-- 10. 트리거 함수 생성 (updated_at 자동 업데이트)
|
||||
CREATE OR REPLACE FUNCTION update_updated_at_column()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
NEW.updated_at = CURRENT_TIMESTAMP;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ language 'plpgsql';
|
||||
|
||||
-- 11. 트리거 적용
|
||||
CREATE TRIGGER update_users_updated_at BEFORE UPDATE ON users
|
||||
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
|
||||
CREATE TRIGGER update_user_sessions_updated_at BEFORE UPDATE ON user_sessions
|
||||
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
|
||||
-- 12. 뷰 생성 (사용자 정보 조회용)
|
||||
CREATE OR REPLACE VIEW user_info_view AS
|
||||
SELECT
|
||||
u.user_id,
|
||||
u.username,
|
||||
u.name,
|
||||
u.email,
|
||||
u.role,
|
||||
u.access_level,
|
||||
u.department,
|
||||
u.position,
|
||||
u.is_active,
|
||||
u.created_at,
|
||||
u.last_login_at,
|
||||
COUNT(ll.log_id) as login_count,
|
||||
MAX(ll.login_time) as last_successful_login
|
||||
FROM users u
|
||||
LEFT JOIN login_logs ll ON u.user_id = ll.user_id AND ll.login_status = 'success'
|
||||
GROUP BY u.user_id, u.username, u.name, u.email, u.role, u.access_level,
|
||||
u.department, u.position, u.is_active, u.created_at, u.last_login_at;
|
||||
|
||||
-- 완료 메시지
|
||||
DO $$
|
||||
BEGIN
|
||||
RAISE NOTICE '✅ TK-MP-Project 인증 시스템 데이터베이스 스키마가 성공적으로 생성되었습니다!';
|
||||
RAISE NOTICE '📋 생성된 테이블: users, login_logs, user_sessions, permissions, role_permissions';
|
||||
RAISE NOTICE '👤 기본 계정: admin/admin123, system/admin123';
|
||||
RAISE NOTICE '🔐 권한 시스템: 5단계 역할 + 모듈별 세분화된 권한';
|
||||
END $$;
|
||||
Reference in New Issue
Block a user