feat: SWG 가스켓 전체 구성 정보 표시 개선
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:
Hyungi Ahn
2025-08-30 14:23:01 +09:00
parent 78d90c7a8f
commit 4f8e395f87
84 changed files with 16297 additions and 2161 deletions

View 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;

View 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 $$;

View 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 $$;

View 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 $$;