✅ 백엔드 구조 개선: - DatabaseService: 공통 DB 쿼리 로직 통합 - FileUploadService: 파일 업로드 로직 모듈화 및 트랜잭션 관리 개선 - 서비스 레이어 패턴 도입으로 코드 재사용성 향상 ✅ 프론트엔드 컴포넌트 개선: - LoadingSpinner, ErrorMessage, ConfirmDialog 공통 컴포넌트 생성 - 재사용 가능한 컴포넌트 라이브러리 구축 - deprecated/backup 파일들 완전 제거 ✅ 성능 최적화: - optimize_database.py: 핵심 DB 인덱스 자동 생성 - 쿼리 최적화 및 통계 업데이트 자동화 - VACUUM ANALYZE 자동 실행 ✅ 코드 정리: - 개별 SQL 마이그레이션 파일들을 legacy/ 폴더로 정리 - 중복된 마이그레이션 스크립트 정리 - 깔끔하고 체계적인 프로젝트 구조 완성 ✅ 자동 마이그레이션 시스템 강화: - complete_migrate.py: SQLAlchemy 기반 완전한 마이그레이션 - analyze_and_fix_schema.py: 백엔드 코드 분석 기반 스키마 수정 - fix_missing_tables.py: 누락된 테이블/컬럼 자동 생성 - start.sh: 배포 시 자동 실행 순서 최적화
This commit is contained in:
36
backend/scripts/legacy/01_create_jobs_table.sql
Normal file
36
backend/scripts/legacy/01_create_jobs_table.sql
Normal file
@@ -0,0 +1,36 @@
|
||||
-- jobs 테이블 생성
|
||||
CREATE TABLE IF NOT EXISTS jobs (
|
||||
-- 기본 정보
|
||||
job_no VARCHAR(50) PRIMARY KEY,
|
||||
job_name VARCHAR(200) NOT NULL,
|
||||
|
||||
-- 계약 관계 (핵심)
|
||||
client_name VARCHAR(100) NOT NULL,
|
||||
|
||||
-- 프로젝트 정보
|
||||
end_user VARCHAR(100),
|
||||
epc_company VARCHAR(100),
|
||||
project_site VARCHAR(200),
|
||||
|
||||
-- 상업 정보
|
||||
contract_date DATE,
|
||||
delivery_date DATE,
|
||||
delivery_terms VARCHAR(100),
|
||||
|
||||
-- 상태 관리 (핵심)
|
||||
status VARCHAR(20) DEFAULT '진행중',
|
||||
delivery_completed_date DATE,
|
||||
project_closed_date DATE,
|
||||
|
||||
-- 관리 정보
|
||||
description TEXT,
|
||||
created_by VARCHAR(50),
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
is_active BOOLEAN DEFAULT true
|
||||
);
|
||||
|
||||
-- 인덱스 생성
|
||||
CREATE INDEX IF NOT EXISTS idx_jobs_status ON jobs(status);
|
||||
CREATE INDEX IF NOT EXISTS idx_jobs_client ON jobs(client_name);
|
||||
CREATE INDEX IF NOT EXISTS idx_jobs_created_at ON jobs(created_at);
|
||||
19
backend/scripts/legacy/02_modify_files_table.sql
Normal file
19
backend/scripts/legacy/02_modify_files_table.sql
Normal file
@@ -0,0 +1,19 @@
|
||||
-- files 테이블에 job_no 컬럼 추가 및 project_id 대체
|
||||
|
||||
-- 새 컬럼 추가
|
||||
ALTER TABLE files ADD COLUMN IF NOT EXISTS job_no VARCHAR(50);
|
||||
|
||||
-- 외래키 제약조건 추가 (MySQL/PostgreSQL 문법)
|
||||
-- MySQL의 경우:
|
||||
-- ALTER TABLE files ADD CONSTRAINT fk_files_job_no FOREIGN KEY (job_no) REFERENCES jobs(job_no);
|
||||
|
||||
-- PostgreSQL의 경우:
|
||||
-- ALTER TABLE files ADD CONSTRAINT fk_files_job_no FOREIGN KEY (job_no) REFERENCES jobs(job_no);
|
||||
|
||||
-- SQLite의 경우는 외래키 제약조건을 나중에 추가하기 어려우므로 생략
|
||||
|
||||
-- 인덱스 생성
|
||||
CREATE INDEX IF NOT EXISTS idx_files_job_no ON files(job_no);
|
||||
|
||||
-- 기존 project_id 컬럼은 일단 유지 (호환성을 위해)
|
||||
-- 나중에 완전 이전 후 DROP 할 예정
|
||||
18
backend/scripts/legacy/04_add_job_no_to_files.sql
Normal file
18
backend/scripts/legacy/04_add_job_no_to_files.sql
Normal file
@@ -0,0 +1,18 @@
|
||||
-- files 테이블에 job_no 컬럼 추가
|
||||
-- 실행일: 2025.07.16
|
||||
|
||||
-- job_no 컬럼 추가
|
||||
ALTER TABLE files ADD COLUMN job_no VARCHAR(50);
|
||||
|
||||
-- job_no에 인덱스 추가
|
||||
CREATE INDEX idx_files_job_no ON files(job_no);
|
||||
|
||||
-- 기존 데이터가 있다면 project_id를 기반으로 job_no 설정
|
||||
-- (이 부분은 실제 데이터가 있을 때만 실행)
|
||||
-- UPDATE files SET job_no = (SELECT official_project_code FROM projects WHERE projects.id = files.project_id);
|
||||
|
||||
-- job_no를 NOT NULL로 설정 (데이터 마이그레이션 후)
|
||||
-- ALTER TABLE files ALTER COLUMN job_no SET NOT NULL;
|
||||
|
||||
-- project_id 컬럼 제거 (선택사항 - 기존 데이터 백업 후)
|
||||
-- ALTER TABLE files DROP COLUMN project_id;
|
||||
80
backend/scripts/legacy/05_add_classification_columns.sql
Normal file
80
backend/scripts/legacy/05_add_classification_columns.sql
Normal file
@@ -0,0 +1,80 @@
|
||||
-- 분류 결과 저장을 위한 컬럼 추가
|
||||
-- 2024년 BOM 분류 시스템 개선
|
||||
|
||||
-- materials 테이블에 분류 관련 컬럼 추가
|
||||
ALTER TABLE materials ADD COLUMN IF NOT EXISTS subcategory VARCHAR(100);
|
||||
ALTER TABLE materials ADD COLUMN IF NOT EXISTS standard VARCHAR(200);
|
||||
ALTER TABLE materials ADD COLUMN IF NOT EXISTS grade VARCHAR(200);
|
||||
ALTER TABLE materials ADD COLUMN IF NOT EXISTS classification_details JSONB;
|
||||
|
||||
-- files 테이블에 분류 통계 컬럼 추가
|
||||
ALTER TABLE files ADD COLUMN IF NOT EXISTS classification_stats JSONB;
|
||||
ALTER TABLE files ADD COLUMN IF NOT EXISTS classification_completed BOOLEAN DEFAULT FALSE;
|
||||
ALTER TABLE files ADD COLUMN IF NOT EXISTS classification_timestamp TIMESTAMP;
|
||||
|
||||
-- 인덱스 추가 (성능 향상)
|
||||
CREATE INDEX IF NOT EXISTS idx_materials_classified_category ON materials(classified_category);
|
||||
CREATE INDEX IF NOT EXISTS idx_materials_subcategory ON materials(subcategory);
|
||||
CREATE INDEX IF NOT EXISTS idx_materials_standard ON materials(standard);
|
||||
CREATE INDEX IF NOT EXISTS idx_materials_grade ON materials(grade);
|
||||
CREATE INDEX IF NOT EXISTS idx_materials_classification_confidence ON materials(classification_confidence);
|
||||
|
||||
-- 기존 데이터에 대한 기본값 설정
|
||||
UPDATE materials SET
|
||||
subcategory = COALESCE(subcategory, ''),
|
||||
standard = COALESCE(standard, ''),
|
||||
grade = COALESCE(grade, ''),
|
||||
classification_details = COALESCE(classification_details, '{}'::jsonb)
|
||||
WHERE subcategory IS NULL OR standard IS NULL OR grade IS NULL OR classification_details IS NULL;
|
||||
|
||||
-- 분류 완료된 파일들 업데이트
|
||||
UPDATE files SET
|
||||
classification_completed = TRUE,
|
||||
classification_timestamp = created_at
|
||||
WHERE parsed_count > 0;
|
||||
|
||||
-- 통계 뷰 생성 (분류 결과 통계 조회용)
|
||||
CREATE OR REPLACE VIEW classification_summary AS
|
||||
SELECT
|
||||
f.job_no,
|
||||
f.original_filename,
|
||||
f.parsed_count,
|
||||
f.classification_completed,
|
||||
f.classification_timestamp,
|
||||
COUNT(*) as total_materials,
|
||||
COUNT(CASE WHEN m.classified_category = 'BOLT' THEN 1 END) as bolt_count,
|
||||
COUNT(CASE WHEN m.classified_category = 'FLANGE' THEN 1 END) as flange_count,
|
||||
COUNT(CASE WHEN m.classified_category = 'FITTING' THEN 1 END) as fitting_count,
|
||||
COUNT(CASE WHEN m.classified_category = 'GASKET' THEN 1 END) as gasket_count,
|
||||
COUNT(CASE WHEN m.classified_category = 'INSTRUMENT' THEN 1 END) as instrument_count,
|
||||
COUNT(CASE WHEN m.classified_category = 'PIPE' THEN 1 END) as pipe_count,
|
||||
COUNT(CASE WHEN m.classified_category = 'VALVE' THEN 1 END) as valve_count,
|
||||
COUNT(CASE WHEN m.classified_category = 'MATERIAL' THEN 1 END) as material_count,
|
||||
COUNT(CASE WHEN m.classified_category = 'OTHER' THEN 1 END) as other_count,
|
||||
AVG(m.classification_confidence) as avg_confidence,
|
||||
COUNT(CASE WHEN m.is_verified = TRUE THEN 1 END) as verified_count
|
||||
FROM files f
|
||||
LEFT JOIN materials m ON f.id = m.file_id
|
||||
WHERE f.is_active = TRUE
|
||||
GROUP BY f.id, f.job_no, f.original_filename, f.parsed_count, f.classification_completed, f.classification_timestamp;
|
||||
|
||||
-- 분류 성능 통계 뷰
|
||||
CREATE OR REPLACE VIEW classification_performance AS
|
||||
SELECT
|
||||
classified_category,
|
||||
subcategory,
|
||||
standard,
|
||||
COUNT(*) as total_count,
|
||||
AVG(classification_confidence) as avg_confidence,
|
||||
COUNT(CASE WHEN is_verified = TRUE THEN 1 END) as verified_count,
|
||||
COUNT(CASE WHEN is_verified = FALSE THEN 1 END) as unverified_count,
|
||||
ROUND(
|
||||
(COUNT(CASE WHEN is_verified = TRUE THEN 1 END)::DECIMAL / COUNT(*) * 100), 2
|
||||
) as verification_rate
|
||||
FROM materials
|
||||
WHERE classified_category IS NOT NULL
|
||||
GROUP BY classified_category, subcategory, standard
|
||||
ORDER BY total_count DESC;
|
||||
|
||||
-- 변경사항 확인
|
||||
SELECT 'Database schema updated successfully' as status;
|
||||
5
backend/scripts/legacy/05_add_length_to_materials.sql
Normal file
5
backend/scripts/legacy/05_add_length_to_materials.sql
Normal file
@@ -0,0 +1,5 @@
|
||||
-- materials 테이블에 length 컬럼 추가
|
||||
ALTER TABLE materials ADD COLUMN length NUMERIC(10, 3);
|
||||
|
||||
-- 기존 데이터의 length 컬럼을 NULL로 초기화
|
||||
UPDATE materials SET length = NULL;
|
||||
137
backend/scripts/legacy/05_create_material_standards_tables.sql
Normal file
137
backend/scripts/legacy/05_create_material_standards_tables.sql
Normal file
@@ -0,0 +1,137 @@
|
||||
-- 자재 규격/재질 기준표 테이블 생성 스크립트
|
||||
-- 실행 순서: 1) material_standards, 2) material_categories, 3) material_specifications, 4) material_grades, 5) material_patterns
|
||||
-- 특수 재질: 6) special_materials, 7) special_material_grades, 8) special_material_patterns
|
||||
|
||||
-- 1. 자재 규격 표준 테이블
|
||||
CREATE TABLE IF NOT EXISTS material_standards (
|
||||
id SERIAL PRIMARY KEY,
|
||||
standard_code VARCHAR(20) UNIQUE NOT NULL,
|
||||
standard_name VARCHAR(100) NOT NULL,
|
||||
description TEXT,
|
||||
country VARCHAR(50),
|
||||
is_active BOOLEAN DEFAULT TRUE,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
-- 2. 제조방식별 카테고리 테이블
|
||||
CREATE TABLE IF NOT EXISTS material_categories (
|
||||
id SERIAL PRIMARY KEY,
|
||||
standard_id INTEGER REFERENCES material_standards(id),
|
||||
category_code VARCHAR(50) 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
|
||||
);
|
||||
|
||||
-- 3. 구체적인 규격 테이블
|
||||
CREATE TABLE IF NOT EXISTS material_specifications (
|
||||
id SERIAL PRIMARY KEY,
|
||||
category_id INTEGER REFERENCES material_categories(id),
|
||||
spec_code VARCHAR(20) NOT NULL,
|
||||
spec_name VARCHAR(100) NOT NULL,
|
||||
description TEXT,
|
||||
material_type VARCHAR(50),
|
||||
manufacturing VARCHAR(50),
|
||||
pressure_rating VARCHAR(100),
|
||||
is_active BOOLEAN DEFAULT TRUE,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
-- 4. 등급별 상세 정보 테이블
|
||||
CREATE TABLE IF NOT EXISTS material_grades (
|
||||
id SERIAL PRIMARY KEY,
|
||||
specification_id INTEGER REFERENCES material_specifications(id),
|
||||
grade_code VARCHAR(20) NOT NULL,
|
||||
grade_name VARCHAR(100),
|
||||
composition VARCHAR(200),
|
||||
applications VARCHAR(200),
|
||||
temp_max VARCHAR(50),
|
||||
temp_range VARCHAR(100),
|
||||
yield_strength VARCHAR(50),
|
||||
tensile_strength VARCHAR(50),
|
||||
corrosion_resistance VARCHAR(50),
|
||||
stabilizer VARCHAR(50),
|
||||
base_grade VARCHAR(20),
|
||||
is_active BOOLEAN DEFAULT TRUE,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
-- 5. 정규식 패턴 테이블
|
||||
CREATE TABLE IF NOT EXISTS material_patterns (
|
||||
id SERIAL PRIMARY KEY,
|
||||
specification_id INTEGER REFERENCES material_specifications(id),
|
||||
pattern TEXT NOT NULL,
|
||||
description VARCHAR(200),
|
||||
priority INTEGER DEFAULT 1,
|
||||
is_active BOOLEAN DEFAULT TRUE,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
-- 6. 특수 재질 테이블
|
||||
CREATE TABLE IF NOT EXISTS special_materials (
|
||||
id SERIAL PRIMARY KEY,
|
||||
material_type VARCHAR(50) NOT NULL,
|
||||
material_name VARCHAR(100) NOT NULL,
|
||||
description TEXT,
|
||||
composition VARCHAR(200),
|
||||
applications TEXT,
|
||||
temp_max VARCHAR(50),
|
||||
manufacturing VARCHAR(50),
|
||||
is_active BOOLEAN DEFAULT TRUE,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
-- 7. 특수 재질 등급 테이블
|
||||
CREATE TABLE IF NOT EXISTS special_material_grades (
|
||||
id SERIAL PRIMARY KEY,
|
||||
material_id INTEGER REFERENCES special_materials(id),
|
||||
grade_code VARCHAR(20) NOT NULL,
|
||||
composition VARCHAR(200),
|
||||
applications VARCHAR(200),
|
||||
temp_max VARCHAR(50),
|
||||
strength VARCHAR(50),
|
||||
purity VARCHAR(100),
|
||||
corrosion VARCHAR(50),
|
||||
is_active BOOLEAN DEFAULT TRUE,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
-- 8. 특수 재질 정규식 패턴 테이블
|
||||
CREATE TABLE IF NOT EXISTS special_material_patterns (
|
||||
id SERIAL PRIMARY KEY,
|
||||
material_id INTEGER REFERENCES special_materials(id),
|
||||
pattern TEXT NOT NULL,
|
||||
description VARCHAR(200),
|
||||
priority INTEGER DEFAULT 1,
|
||||
is_active BOOLEAN DEFAULT TRUE,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
-- 인덱스 생성
|
||||
CREATE INDEX IF NOT EXISTS idx_material_standards_code ON material_standards(standard_code);
|
||||
CREATE INDEX IF NOT EXISTS idx_material_categories_standard ON material_categories(standard_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_material_specifications_category ON material_specifications(category_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_material_grades_specification ON material_grades(specification_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_material_patterns_specification ON material_patterns(specification_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_special_materials_type ON special_materials(material_type);
|
||||
CREATE INDEX IF NOT EXISTS idx_special_material_grades_material ON special_material_grades(material_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_special_material_patterns_material ON special_material_patterns(material_id);
|
||||
|
||||
-- 활성 상태 인덱스
|
||||
CREATE INDEX IF NOT EXISTS idx_material_standards_active ON material_standards(is_active);
|
||||
CREATE INDEX IF NOT EXISTS idx_material_categories_active ON material_categories(is_active);
|
||||
CREATE INDEX IF NOT EXISTS idx_material_specifications_active ON material_specifications(is_active);
|
||||
CREATE INDEX IF NOT EXISTS idx_material_grades_active ON material_grades(is_active);
|
||||
CREATE INDEX IF NOT EXISTS idx_material_patterns_active ON material_patterns(is_active);
|
||||
CREATE INDEX IF NOT EXISTS idx_special_materials_active ON special_materials(is_active);
|
||||
CREATE INDEX IF NOT EXISTS idx_special_material_grades_active ON special_material_grades(is_active);
|
||||
CREATE INDEX IF NOT EXISTS idx_special_material_patterns_active ON special_material_patterns(is_active);
|
||||
@@ -0,0 +1,109 @@
|
||||
-- 파이프 상세 정보 및 사용자 요구사항 테이블 생성
|
||||
-- 2024-01-XX
|
||||
|
||||
-- 파이프 상세 정보 테이블
|
||||
CREATE TABLE IF NOT EXISTS pipe_details (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
file_id INTEGER NOT NULL,
|
||||
|
||||
-- 재질 정보
|
||||
material_standard TEXT, -- ASTM, KS, JIS 등
|
||||
material_grade TEXT, -- A106, A53, STPG370 등
|
||||
material_type TEXT, -- CARBON, STAINLESS 등
|
||||
|
||||
-- 파이프 특화 정보
|
||||
manufacturing_method TEXT, -- SEAMLESS, WELDED, CAST
|
||||
end_preparation TEXT, -- BOTH_ENDS_BEVELED, ONE_END_BEVELED, NO_BEVEL
|
||||
schedule TEXT, -- SCH 10, 20, 40, 80 등
|
||||
wall_thickness TEXT, -- 벽두께 정보
|
||||
|
||||
-- 치수 정보
|
||||
nominal_size TEXT, -- MAIN_NOM (인치, 직경)
|
||||
length_mm REAL, -- LENGTH (길이)
|
||||
|
||||
-- 신뢰도
|
||||
material_confidence REAL,
|
||||
manufacturing_confidence REAL,
|
||||
end_prep_confidence REAL,
|
||||
schedule_confidence REAL,
|
||||
|
||||
-- 메타데이터
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
FOREIGN KEY (file_id) REFERENCES files(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
-- 요구사항 타입 마스터 테이블
|
||||
CREATE TABLE IF NOT EXISTS requirement_types (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
type_code TEXT UNIQUE NOT NULL, -- 'IMPACT_TEST', 'HEAT_TREATMENT' 등
|
||||
type_name TEXT NOT NULL, -- '임팩테스트', '열처리' 등
|
||||
category TEXT NOT NULL, -- 'TEST', 'TREATMENT', 'CERTIFICATION', 'CUSTOM' 등
|
||||
description TEXT, -- 타입 설명
|
||||
is_active BOOLEAN DEFAULT TRUE,
|
||||
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
-- 사용자 추가 요구사항 테이블
|
||||
CREATE TABLE IF NOT EXISTS user_requirements (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
file_id INTEGER NOT NULL,
|
||||
|
||||
-- 요구사항 타입
|
||||
requirement_type TEXT NOT NULL, -- 'IMPACT_TEST', 'HEAT_TREATMENT', 'CUSTOM_SPEC', 'CERTIFICATION' 등
|
||||
|
||||
-- 요구사항 내용
|
||||
requirement_title TEXT NOT NULL, -- '임팩테스트', '열처리', '인증서' 등
|
||||
requirement_description TEXT, -- 상세 설명
|
||||
requirement_spec TEXT, -- 구체적 스펙 (예: "Charpy V-notch -20°C")
|
||||
|
||||
-- 상태 관리
|
||||
status TEXT DEFAULT 'PENDING', -- 'PENDING', 'IN_PROGRESS', 'COMPLETED', 'CANCELLED'
|
||||
priority TEXT DEFAULT 'NORMAL', -- 'LOW', 'NORMAL', 'HIGH', 'URGENT'
|
||||
|
||||
-- 담당자 정보
|
||||
assigned_to TEXT, -- 담당자명
|
||||
due_date DATE, -- 완료 예정일
|
||||
|
||||
-- 메타데이터
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
FOREIGN KEY (file_id) REFERENCES files(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
-- 인덱스 생성
|
||||
CREATE INDEX IF NOT EXISTS idx_pipe_details_file_id ON pipe_details(file_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_user_requirements_file_id ON user_requirements(file_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_user_requirements_status ON user_requirements(status);
|
||||
CREATE INDEX IF NOT EXISTS idx_user_requirements_type ON user_requirements(requirement_type);
|
||||
|
||||
-- 기본 요구사항 타입 데이터 삽입
|
||||
INSERT OR IGNORE INTO requirement_types (type_code, type_name, category, description) VALUES
|
||||
('IMPACT_TEST', '임팩테스트', 'TEST', 'Charpy V-notch, Izod 등의 충격 시험'),
|
||||
('HEAT_TREATMENT', '열처리', 'TREATMENT', '정규화, 어닐링, 템퍼링 등의 열처리'),
|
||||
('CERTIFICATION', '인증서', 'CERTIFICATION', '재질증명서, 시험성적서 등'),
|
||||
('CUSTOM_SPEC', '특수사양', 'CUSTOM', '고객 특별 요구사항'),
|
||||
('NDT_TEST', '비파괴검사', 'TEST', '초음파, 방사선, 자분탐상 등'),
|
||||
('CHEMICAL_ANALYSIS', '화학분석', 'TEST', '화학성분 분석'),
|
||||
('MECHANICAL_TEST', '기계적성질', 'TEST', '인장, 압축, 굽힘 시험'),
|
||||
('SURFACE_TREATMENT', '표면처리', 'TREATMENT', '도금, 도장, 패시베이션 등'),
|
||||
('DIMENSIONAL_CHECK', '치수검사', 'INSPECTION', '치수, 형상 검사'),
|
||||
('WELDING_SPEC', '용접사양', 'SPEC', '용접 방법, 용접재 등');
|
||||
|
||||
-- 트리거 생성 (updated_at 자동 업데이트)
|
||||
CREATE TRIGGER IF NOT EXISTS update_pipe_details_timestamp
|
||||
AFTER UPDATE ON pipe_details
|
||||
FOR EACH ROW
|
||||
BEGIN
|
||||
UPDATE pipe_details SET updated_at = CURRENT_TIMESTAMP WHERE id = NEW.id;
|
||||
END;
|
||||
|
||||
CREATE TRIGGER IF NOT EXISTS update_user_requirements_timestamp
|
||||
AFTER UPDATE ON user_requirements
|
||||
FOR EACH ROW
|
||||
BEGIN
|
||||
UPDATE user_requirements SET updated_at = CURRENT_TIMESTAMP WHERE id = NEW.id;
|
||||
END;
|
||||
@@ -0,0 +1,115 @@
|
||||
-- 파이프 상세 정보 및 사용자 요구사항 테이블 생성 (PostgreSQL)
|
||||
-- 2024-01-XX
|
||||
|
||||
-- 파이프 상세 정보 테이블
|
||||
CREATE TABLE IF NOT EXISTS pipe_details (
|
||||
id SERIAL PRIMARY KEY,
|
||||
file_id INTEGER NOT NULL,
|
||||
|
||||
-- 재질 정보
|
||||
material_standard VARCHAR(50), -- ASTM, KS, JIS 등
|
||||
material_grade VARCHAR(50), -- A106, A53, STPG370 등
|
||||
material_type VARCHAR(50), -- CARBON, STAINLESS 등
|
||||
|
||||
-- 파이프 특화 정보
|
||||
manufacturing_method VARCHAR(50), -- SEAMLESS, WELDED, CAST
|
||||
end_preparation VARCHAR(50), -- BOTH_ENDS_BEVELED, ONE_END_BEVELED, NO_BEVEL
|
||||
schedule VARCHAR(50), -- SCH 10, 20, 40, 80 등
|
||||
wall_thickness VARCHAR(50), -- 벽두께 정보
|
||||
|
||||
-- 치수 정보
|
||||
nominal_size VARCHAR(50), -- MAIN_NOM (인치, 직경)
|
||||
length_mm DECIMAL(10, 3), -- LENGTH (길이)
|
||||
|
||||
-- 신뢰도
|
||||
material_confidence DECIMAL(3, 2),
|
||||
manufacturing_confidence DECIMAL(3, 2),
|
||||
end_prep_confidence DECIMAL(3, 2),
|
||||
schedule_confidence DECIMAL(3, 2),
|
||||
|
||||
-- 메타데이터
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
FOREIGN KEY (file_id) REFERENCES files(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
-- 요구사항 타입 마스터 테이블
|
||||
CREATE TABLE IF NOT EXISTS requirement_types (
|
||||
id SERIAL PRIMARY KEY,
|
||||
type_code VARCHAR(50) UNIQUE NOT NULL, -- 'IMPACT_TEST', 'HEAT_TREATMENT' 등
|
||||
type_name VARCHAR(100) NOT NULL, -- '임팩테스트', '열처리' 등
|
||||
category VARCHAR(50) NOT NULL, -- 'TEST', 'TREATMENT', 'CERTIFICATION', 'CUSTOM' 등
|
||||
description TEXT, -- 타입 설명
|
||||
is_active BOOLEAN DEFAULT TRUE,
|
||||
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
-- 사용자 추가 요구사항 테이블
|
||||
CREATE TABLE IF NOT EXISTS user_requirements (
|
||||
id SERIAL PRIMARY KEY,
|
||||
file_id INTEGER NOT NULL,
|
||||
|
||||
-- 요구사항 타입
|
||||
requirement_type VARCHAR(50) NOT NULL, -- 'IMPACT_TEST', 'HEAT_TREATMENT', 'CUSTOM_SPEC', 'CERTIFICATION' 등
|
||||
|
||||
-- 요구사항 내용
|
||||
requirement_title VARCHAR(200) NOT NULL, -- '임팩테스트', '열처리', '인증서' 등
|
||||
requirement_description TEXT, -- 상세 설명
|
||||
requirement_spec TEXT, -- 구체적 스펙 (예: "Charpy V-notch -20°C")
|
||||
|
||||
-- 상태 관리
|
||||
status VARCHAR(20) DEFAULT 'PENDING', -- 'PENDING', 'IN_PROGRESS', 'COMPLETED', 'CANCELLED'
|
||||
priority VARCHAR(20) DEFAULT 'NORMAL', -- 'LOW', 'NORMAL', 'HIGH', 'URGENT'
|
||||
|
||||
-- 담당자 정보
|
||||
assigned_to VARCHAR(100), -- 담당자명
|
||||
due_date DATE, -- 완료 예정일
|
||||
|
||||
-- 메타데이터
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
FOREIGN KEY (file_id) REFERENCES files(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
-- 인덱스 생성
|
||||
CREATE INDEX IF NOT EXISTS idx_pipe_details_file_id ON pipe_details(file_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_user_requirements_file_id ON user_requirements(file_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_user_requirements_status ON user_requirements(status);
|
||||
CREATE INDEX IF NOT EXISTS idx_user_requirements_type ON user_requirements(requirement_type);
|
||||
|
||||
-- 기본 요구사항 타입 데이터 삽입
|
||||
INSERT INTO requirement_types (type_code, type_name, category, description) VALUES
|
||||
('IMPACT_TEST', '임팩테스트', 'TEST', 'Charpy V-notch, Izod 등의 충격 시험'),
|
||||
('HEAT_TREATMENT', '열처리', 'TREATMENT', '정규화, 어닐링, 템퍼링 등의 열처리'),
|
||||
('CERTIFICATION', '인증서', 'CERTIFICATION', '재질증명서, 시험성적서 등'),
|
||||
('CUSTOM_SPEC', '특수사양', 'CUSTOM', '고객 특별 요구사항'),
|
||||
('NDT_TEST', '비파괴검사', 'TEST', '초음파, 방사선, 자분탐상 등'),
|
||||
('CHEMICAL_ANALYSIS', '화학분석', 'TEST', '화학성분 분석'),
|
||||
('MECHANICAL_TEST', '기계적성질', 'TEST', '인장, 압축, 굽힘 시험'),
|
||||
('SURFACE_TREATMENT', '표면처리', 'TREATMENT', '도금, 도장, 패시베이션 등'),
|
||||
('DIMENSIONAL_CHECK', '치수검사', 'INSPECTION', '치수, 형상 검사'),
|
||||
('WELDING_SPEC', '용접사양', 'SPEC', '용접 방법, 용접재 등')
|
||||
ON CONFLICT (type_code) DO NOTHING;
|
||||
|
||||
-- 트리거 함수 생성 (updated_at 자동 업데이트)
|
||||
CREATE OR REPLACE FUNCTION update_updated_at_column()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
NEW.updated_at = CURRENT_TIMESTAMP;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ language 'plpgsql';
|
||||
|
||||
-- 트리거 생성
|
||||
CREATE TRIGGER update_pipe_details_timestamp
|
||||
BEFORE UPDATE ON pipe_details
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION update_updated_at_column();
|
||||
|
||||
CREATE TRIGGER update_user_requirements_timestamp
|
||||
BEFORE UPDATE ON user_requirements
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION update_updated_at_column();
|
||||
18
backend/scripts/legacy/06_add_main_red_nom_columns.sql
Normal file
18
backend/scripts/legacy/06_add_main_red_nom_columns.sql
Normal file
@@ -0,0 +1,18 @@
|
||||
-- main_nom, red_nom 컬럼 추가 스크립트
|
||||
-- 2025.01.17 - MAIN_NOM/RED_NOM 별도 저장을 위한 스키마 개선
|
||||
|
||||
-- materials 테이블에 main_nom, red_nom 컬럼 추가
|
||||
ALTER TABLE materials ADD COLUMN IF NOT EXISTS main_nom VARCHAR(50);
|
||||
ALTER TABLE materials ADD COLUMN IF NOT EXISTS red_nom VARCHAR(50);
|
||||
|
||||
-- 인덱스 추가 (검색 성능 향상)
|
||||
CREATE INDEX IF NOT EXISTS idx_materials_main_nom ON materials(main_nom);
|
||||
CREATE INDEX IF NOT EXISTS idx_materials_red_nom ON materials(red_nom);
|
||||
CREATE INDEX IF NOT EXISTS idx_materials_main_red_nom ON materials(main_nom, red_nom);
|
||||
|
||||
-- 기존 데이터에 대한 기본값 설정 (필요시)
|
||||
-- UPDATE materials SET main_nom = '', red_nom = '' WHERE main_nom IS NULL OR red_nom IS NULL;
|
||||
|
||||
-- 코멘트 추가
|
||||
COMMENT ON COLUMN materials.main_nom IS 'MAIN_NOM 필드 - 주 사이즈 (예: 4", 150A)';
|
||||
COMMENT ON COLUMN materials.red_nom IS 'RED_NOM 필드 - 축소 사이즈 (Reducing 피팅/플랜지용)';
|
||||
@@ -0,0 +1,16 @@
|
||||
-- bolt_details 테이블에 pressure_rating 컬럼 추가
|
||||
-- 2025.01.16 - 볼트 압력등급 정보 저장을 위한 스키마 개선
|
||||
|
||||
ALTER TABLE bolt_details ADD COLUMN IF NOT EXISTS pressure_rating VARCHAR(50);
|
||||
|
||||
-- 기존 레코드에 기본값 설정 (선택사항)
|
||||
UPDATE bolt_details SET pressure_rating = 'UNKNOWN' WHERE pressure_rating IS NULL;
|
||||
|
||||
-- 인덱스 추가 (성능 최적화)
|
||||
CREATE INDEX IF NOT EXISTS idx_bolt_details_pressure_rating ON bolt_details(pressure_rating);
|
||||
|
||||
-- 확인용 쿼리
|
||||
SELECT column_name, data_type, is_nullable
|
||||
FROM information_schema.columns
|
||||
WHERE table_name = 'bolt_details'
|
||||
AND column_name = 'pressure_rating';
|
||||
@@ -0,0 +1,193 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
자재 규격/재질 기준표 테이블 생성 및 데이터 삽입 스크립트
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
|
||||
from sqlalchemy import create_engine, text
|
||||
from app.database import DATABASE_URL
|
||||
|
||||
def create_tables():
|
||||
"""테이블 생성"""
|
||||
engine = create_engine(DATABASE_URL)
|
||||
|
||||
with engine.connect() as conn:
|
||||
print("자재 규격/재질 기준표 테이블 생성 시작...")
|
||||
|
||||
# 1. 자재 규격 표준 테이블
|
||||
conn.execute(text("""
|
||||
CREATE TABLE IF NOT EXISTS material_standards (
|
||||
id SERIAL PRIMARY KEY,
|
||||
standard_code VARCHAR(20) UNIQUE NOT NULL,
|
||||
standard_name VARCHAR(100) NOT NULL,
|
||||
description TEXT,
|
||||
country VARCHAR(50),
|
||||
is_active BOOLEAN DEFAULT TRUE,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
"""))
|
||||
print(" - material_standards 테이블 생성됨")
|
||||
|
||||
# 2. 제조방식별 카테고리 테이블
|
||||
conn.execute(text("""
|
||||
CREATE TABLE IF NOT EXISTS material_categories (
|
||||
id SERIAL PRIMARY KEY,
|
||||
standard_id INTEGER REFERENCES material_standards(id),
|
||||
category_code VARCHAR(50) 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
|
||||
);
|
||||
"""))
|
||||
print(" - material_categories 테이블 생성됨")
|
||||
|
||||
# 3. 구체적인 규격 테이블
|
||||
conn.execute(text("""
|
||||
CREATE TABLE IF NOT EXISTS material_specifications (
|
||||
id SERIAL PRIMARY KEY,
|
||||
category_id INTEGER REFERENCES material_categories(id),
|
||||
spec_code VARCHAR(20) NOT NULL,
|
||||
spec_name VARCHAR(100) NOT NULL,
|
||||
description TEXT,
|
||||
material_type VARCHAR(50),
|
||||
manufacturing VARCHAR(50),
|
||||
pressure_rating VARCHAR(100),
|
||||
is_active BOOLEAN DEFAULT TRUE,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
"""))
|
||||
print(" - material_specifications 테이블 생성됨")
|
||||
|
||||
# 4. 등급별 상세 정보 테이블
|
||||
conn.execute(text("""
|
||||
CREATE TABLE IF NOT EXISTS material_grades (
|
||||
id SERIAL PRIMARY KEY,
|
||||
specification_id INTEGER REFERENCES material_specifications(id),
|
||||
grade_code VARCHAR(20) NOT NULL,
|
||||
grade_name VARCHAR(100),
|
||||
composition VARCHAR(200),
|
||||
applications VARCHAR(200),
|
||||
temp_max VARCHAR(50),
|
||||
temp_range VARCHAR(100),
|
||||
yield_strength VARCHAR(50),
|
||||
tensile_strength VARCHAR(50),
|
||||
corrosion_resistance VARCHAR(50),
|
||||
stabilizer VARCHAR(50),
|
||||
base_grade VARCHAR(20),
|
||||
is_active BOOLEAN DEFAULT TRUE,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
"""))
|
||||
print(" - material_grades 테이블 생성됨")
|
||||
|
||||
# 5. 정규식 패턴 테이블
|
||||
conn.execute(text("""
|
||||
CREATE TABLE IF NOT EXISTS material_patterns (
|
||||
id SERIAL PRIMARY KEY,
|
||||
specification_id INTEGER REFERENCES material_specifications(id),
|
||||
pattern TEXT NOT NULL,
|
||||
description VARCHAR(200),
|
||||
priority INTEGER DEFAULT 1,
|
||||
is_active BOOLEAN DEFAULT TRUE,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
"""))
|
||||
print(" - material_patterns 테이블 생성됨")
|
||||
|
||||
# 6. 특수 재질 테이블
|
||||
conn.execute(text("""
|
||||
CREATE TABLE IF NOT EXISTS special_materials (
|
||||
id SERIAL PRIMARY KEY,
|
||||
material_type VARCHAR(50) NOT NULL,
|
||||
material_name VARCHAR(100) NOT NULL,
|
||||
description TEXT,
|
||||
composition VARCHAR(200),
|
||||
applications TEXT,
|
||||
temp_max VARCHAR(50),
|
||||
manufacturing VARCHAR(50),
|
||||
is_active BOOLEAN DEFAULT TRUE,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
"""))
|
||||
print(" - special_materials 테이블 생성됨")
|
||||
|
||||
# 7. 특수 재질 등급 테이블
|
||||
conn.execute(text("""
|
||||
CREATE TABLE IF NOT EXISTS special_material_grades (
|
||||
id SERIAL PRIMARY KEY,
|
||||
material_id INTEGER REFERENCES special_materials(id),
|
||||
grade_code VARCHAR(20) NOT NULL,
|
||||
composition VARCHAR(200),
|
||||
applications VARCHAR(200),
|
||||
temp_max VARCHAR(50),
|
||||
strength VARCHAR(50),
|
||||
purity VARCHAR(100),
|
||||
corrosion VARCHAR(50),
|
||||
is_active BOOLEAN DEFAULT TRUE,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
"""))
|
||||
print(" - special_material_grades 테이블 생성됨")
|
||||
|
||||
# 8. 특수 재질 정규식 패턴 테이블
|
||||
conn.execute(text("""
|
||||
CREATE TABLE IF NOT EXISTS special_material_patterns (
|
||||
id SERIAL PRIMARY KEY,
|
||||
material_id INTEGER REFERENCES special_materials(id),
|
||||
pattern TEXT NOT NULL,
|
||||
description VARCHAR(200),
|
||||
priority INTEGER DEFAULT 1,
|
||||
is_active BOOLEAN DEFAULT TRUE,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
"""))
|
||||
print(" - special_material_patterns 테이블 생성됨")
|
||||
|
||||
# 인덱스 생성
|
||||
conn.execute(text("CREATE INDEX IF NOT EXISTS idx_material_standards_code ON material_standards(standard_code);"))
|
||||
conn.execute(text("CREATE INDEX IF NOT EXISTS idx_material_categories_standard ON material_categories(standard_id);"))
|
||||
conn.execute(text("CREATE INDEX IF NOT EXISTS idx_material_specifications_category ON material_specifications(category_id);"))
|
||||
conn.execute(text("CREATE INDEX IF NOT EXISTS idx_material_grades_specification ON material_grades(specification_id);"))
|
||||
conn.execute(text("CREATE INDEX IF NOT EXISTS idx_material_patterns_specification ON material_patterns(specification_id);"))
|
||||
conn.execute(text("CREATE INDEX IF NOT EXISTS idx_special_materials_type ON special_materials(material_type);"))
|
||||
conn.execute(text("CREATE INDEX IF NOT EXISTS idx_special_material_grades_material ON special_material_grades(material_id);"))
|
||||
conn.execute(text("CREATE INDEX IF NOT EXISTS idx_special_material_patterns_material ON special_material_patterns(material_id);"))
|
||||
|
||||
# 활성 상태 인덱스
|
||||
conn.execute(text("CREATE INDEX IF NOT EXISTS idx_material_standards_active ON material_standards(is_active);"))
|
||||
conn.execute(text("CREATE INDEX IF NOT EXISTS idx_material_categories_active ON material_categories(is_active);"))
|
||||
conn.execute(text("CREATE INDEX IF NOT EXISTS idx_material_specifications_active ON material_specifications(is_active);"))
|
||||
conn.execute(text("CREATE INDEX IF NOT EXISTS idx_material_grades_active ON material_grades(is_active);"))
|
||||
conn.execute(text("CREATE INDEX IF NOT EXISTS idx_material_patterns_active ON material_patterns(is_active);"))
|
||||
conn.execute(text("CREATE INDEX IF NOT EXISTS idx_special_materials_active ON special_materials(is_active);"))
|
||||
conn.execute(text("CREATE INDEX IF NOT EXISTS idx_special_material_grades_active ON special_material_grades(is_active);"))
|
||||
conn.execute(text("CREATE INDEX IF NOT EXISTS idx_special_material_patterns_active ON special_material_patterns(is_active);"))
|
||||
|
||||
conn.commit()
|
||||
print("모든 테이블 및 인덱스 생성 완료!")
|
||||
|
||||
def main():
|
||||
"""메인 실행 함수"""
|
||||
print("자재 규격/재질 기준표 DB 마이그레이션 시작")
|
||||
print("=" * 50)
|
||||
|
||||
create_tables()
|
||||
|
||||
print("\n" + "=" * 50)
|
||||
print("마이그레이션 완료!")
|
||||
print("\n다음 단계: python scripts/06_insert_material_standards_data.py")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
68
backend/scripts/legacy/07_simplify_pipe_details_schema.sql
Normal file
68
backend/scripts/legacy/07_simplify_pipe_details_schema.sql
Normal file
@@ -0,0 +1,68 @@
|
||||
-- PIPE_DETAILS 테이블 간소화 스크립트
|
||||
-- 2025.01.17 - 실무 중심 구조 개선
|
||||
|
||||
-- 기존 테이블 백업
|
||||
CREATE TABLE IF NOT EXISTS pipe_details_backup AS SELECT * FROM pipe_details;
|
||||
|
||||
-- 새로운 간소화된 테이블 생성
|
||||
DROP TABLE IF EXISTS pipe_details_new;
|
||||
CREATE TABLE pipe_details_new (
|
||||
id SERIAL PRIMARY KEY,
|
||||
material_id INTEGER NOT NULL,
|
||||
file_id INTEGER NOT NULL,
|
||||
|
||||
-- 핵심 PIPE 정보 (실무 필수)
|
||||
outer_diameter VARCHAR(20), -- main_nom 기반 외경 정보
|
||||
schedule VARCHAR(10), -- SCH 40, SCH 80 등
|
||||
material_spec VARCHAR(100), -- 단일 재질 정보 (ASTM A106 GR B)
|
||||
manufacturing_method VARCHAR(20), -- SEAMLESS, WELDED, CAST
|
||||
end_preparation VARCHAR(20), -- POE, BOE, PEE, BEE 등
|
||||
length_mm DECIMAL(10,2), -- 길이 (mm)
|
||||
|
||||
-- 추가 정보 (cutting plan 용)
|
||||
area_number VARCHAR(10), -- 에리어 번호 (#01, #02)
|
||||
spool_number VARCHAR(10), -- 스풀 번호 (A, B, C)
|
||||
drawing_number VARCHAR(50), -- 도면 번호
|
||||
|
||||
-- 메타 정보
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
-- 외래키
|
||||
FOREIGN KEY (material_id) REFERENCES materials(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (file_id) REFERENCES files(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
-- 인덱스 추가 (성능)
|
||||
CREATE INDEX IF NOT EXISTS idx_pipe_details_material_id ON pipe_details_new(material_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_pipe_details_file_id ON pipe_details_new(file_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_pipe_details_outer_diameter ON pipe_details_new(outer_diameter);
|
||||
CREATE INDEX IF NOT EXISTS idx_pipe_details_schedule ON pipe_details_new(schedule);
|
||||
|
||||
-- 기존 데이터 마이그레이션 (가능한 것만)
|
||||
INSERT INTO pipe_details_new (
|
||||
material_id, file_id, outer_diameter, schedule, material_spec,
|
||||
manufacturing_method, length_mm
|
||||
)
|
||||
SELECT
|
||||
material_id,
|
||||
file_id,
|
||||
nominal_size as outer_diameter, -- nominal_size를 outer_diameter로
|
||||
schedule,
|
||||
COALESCE(material_standard, material_grade, material_type) as material_spec, -- 중복 필드 통합
|
||||
manufacturing_method,
|
||||
length_mm
|
||||
FROM pipe_details
|
||||
WHERE material_id IS NOT NULL;
|
||||
|
||||
-- 백업 테이블로 기존 테이블 교체
|
||||
DROP TABLE IF EXISTS pipe_details;
|
||||
ALTER TABLE pipe_details_new RENAME TO pipe_details;
|
||||
|
||||
-- 코멘트 추가
|
||||
COMMENT ON TABLE pipe_details IS '간소화된 PIPE 상세 정보 - 실무 중심 구조';
|
||||
COMMENT ON COLUMN pipe_details.outer_diameter IS '외경 정보 (main_nom에서 추출, 예: 4", 150A)';
|
||||
COMMENT ON COLUMN pipe_details.material_spec IS '통합 재질 정보 (예: ASTM A106 GR B)';
|
||||
COMMENT ON COLUMN pipe_details.end_preparation IS '끝단 가공 (POE-BOE, PEE, BEE 등)';
|
||||
COMMENT ON COLUMN pipe_details.area_number IS '에리어 번호 (cutting plan용)';
|
||||
COMMENT ON COLUMN pipe_details.spool_number IS '스풀 번호 (cutting plan용)';
|
||||
286
backend/scripts/legacy/08_create_purchase_tables.sql
Normal file
286
backend/scripts/legacy/08_create_purchase_tables.sql
Normal file
@@ -0,0 +1,286 @@
|
||||
-- 구매 관리 시스템 테이블들
|
||||
-- 실행일: 2025.01.21
|
||||
|
||||
-- ================================
|
||||
-- 1. 구매 품목 마스터 테이블
|
||||
-- ================================
|
||||
CREATE TABLE IF NOT EXISTS purchase_items (
|
||||
id SERIAL PRIMARY KEY,
|
||||
|
||||
-- 품목 식별
|
||||
item_code VARCHAR(100) UNIQUE NOT NULL, -- PI-PIPE-A106-4IN-001
|
||||
category VARCHAR(50) NOT NULL, -- PIPE, VALVE, FITTING 등
|
||||
specification TEXT NOT NULL, -- 상세 사양
|
||||
|
||||
-- 기본 정보
|
||||
material_spec VARCHAR(200), -- ASTM A106 GR B
|
||||
size_spec VARCHAR(100), -- 4", 1-1/2" x 1"
|
||||
unit VARCHAR(10) NOT NULL, -- EA, mm, SET
|
||||
|
||||
-- BOM 수량 정보
|
||||
bom_quantity DECIMAL(10,3) NOT NULL, -- BOM상 필요 수량
|
||||
|
||||
-- 구매 계산 정보
|
||||
safety_factor DECIMAL(3,2) DEFAULT 1.10, -- 여유율 (1.1 = 10% 추가)
|
||||
minimum_order_qty DECIMAL(10,3) DEFAULT 0, -- 최소 주문 수량
|
||||
order_unit_qty DECIMAL(10,3) DEFAULT 1, -- 주문 단위 (박스, 롤 등)
|
||||
calculated_qty DECIMAL(10,3), -- 최종 계산된 구매 수량
|
||||
|
||||
-- PIPE 전용 정보
|
||||
cutting_loss DECIMAL(10,3) DEFAULT 0, -- 절단 손실 (mm)
|
||||
standard_length DECIMAL(10,3), -- 표준 길이 (PIPE: 6000mm)
|
||||
pipes_count INTEGER, -- 필요한 파이프 본수
|
||||
waste_length DECIMAL(10,3), -- 여유분 길이
|
||||
|
||||
-- 상세 스펙 (JSON)
|
||||
detailed_spec JSONB, -- 압력등급, 연결방식 등 상세 정보
|
||||
|
||||
-- 구매 정보
|
||||
preferred_supplier VARCHAR(200), -- 선호 공급업체
|
||||
last_unit_price DECIMAL(10,2), -- 최근 단가
|
||||
currency VARCHAR(10) DEFAULT 'KRW', -- 통화
|
||||
lead_time_days INTEGER DEFAULT 30, -- 리드타임
|
||||
|
||||
-- 연결 정보
|
||||
job_no VARCHAR(50) NOT NULL, -- Job 번호
|
||||
revision VARCHAR(20) DEFAULT 'Rev.0', -- 리비전
|
||||
file_id INTEGER, -- 원본 파일 ID
|
||||
|
||||
-- 관리 정보
|
||||
is_active BOOLEAN DEFAULT TRUE,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
created_by VARCHAR(100),
|
||||
|
||||
-- 외래키
|
||||
FOREIGN KEY (file_id) REFERENCES files(id) ON DELETE SET NULL
|
||||
);
|
||||
|
||||
-- ================================
|
||||
-- 2. 개별 자재와 구매품목 연결 테이블
|
||||
-- ================================
|
||||
CREATE TABLE IF NOT EXISTS material_purchase_mapping (
|
||||
id SERIAL PRIMARY KEY,
|
||||
material_id INTEGER NOT NULL, -- materials 테이블 ID
|
||||
purchase_item_id INTEGER NOT NULL, -- purchase_items 테이블 ID
|
||||
quantity_ratio DECIMAL(5,2) DEFAULT 1.0, -- 변환 비율
|
||||
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
-- 외래키
|
||||
FOREIGN KEY (material_id) REFERENCES materials(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (purchase_item_id) REFERENCES purchase_items(id) ON DELETE CASCADE,
|
||||
|
||||
-- 유니크 제약
|
||||
UNIQUE(material_id, purchase_item_id)
|
||||
);
|
||||
|
||||
-- ================================
|
||||
-- 3. 구매 주문 테이블
|
||||
-- ================================
|
||||
CREATE TABLE IF NOT EXISTS purchase_orders (
|
||||
id SERIAL PRIMARY KEY,
|
||||
|
||||
-- 주문 기본 정보
|
||||
order_no VARCHAR(50) UNIQUE NOT NULL, -- PO-2024-001
|
||||
job_no VARCHAR(50) NOT NULL, -- Job 번호
|
||||
revision VARCHAR(20) NOT NULL, -- 리비전
|
||||
|
||||
-- 주문 상태
|
||||
status VARCHAR(20) DEFAULT 'DRAFT', -- DRAFT, APPROVED, ORDERED, RECEIVED, CANCELLED
|
||||
order_date DATE, -- 주문일
|
||||
required_date DATE, -- 요청일
|
||||
delivery_date DATE, -- 납기일
|
||||
|
||||
-- 공급업체 정보
|
||||
supplier_name VARCHAR(200),
|
||||
supplier_contact VARCHAR(200),
|
||||
|
||||
-- 금액 정보
|
||||
total_amount DECIMAL(12,2),
|
||||
currency VARCHAR(10) DEFAULT 'KRW',
|
||||
|
||||
-- 관리 정보
|
||||
created_by VARCHAR(100),
|
||||
approved_by VARCHAR(100),
|
||||
approved_at TIMESTAMP,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
notes TEXT
|
||||
);
|
||||
|
||||
-- ================================
|
||||
-- 4. 구매 주문 상세 테이블
|
||||
-- ================================
|
||||
CREATE TABLE IF NOT EXISTS purchase_order_items (
|
||||
id SERIAL PRIMARY KEY,
|
||||
|
||||
purchase_order_id INTEGER NOT NULL, -- purchase_orders 테이블 ID
|
||||
purchase_item_id INTEGER NOT NULL, -- purchase_items 테이블 ID
|
||||
|
||||
-- 수량 정보
|
||||
ordered_quantity DECIMAL(10,3) NOT NULL, -- 주문 수량
|
||||
received_quantity DECIMAL(10,3) DEFAULT 0, -- 입고 수량
|
||||
|
||||
-- 가격 정보
|
||||
unit_price DECIMAL(10,2), -- 단가
|
||||
total_price DECIMAL(12,2), -- 총액
|
||||
|
||||
-- 납기 정보
|
||||
required_date DATE, -- 요청일
|
||||
delivery_date DATE, -- 납기일
|
||||
|
||||
-- 상태
|
||||
status VARCHAR(20) DEFAULT 'ORDERED', -- ORDERED, PARTIAL, RECEIVED, CANCELLED
|
||||
|
||||
-- 관리 정보
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
notes TEXT,
|
||||
|
||||
-- 외래키
|
||||
FOREIGN KEY (purchase_order_id) REFERENCES purchase_orders(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (purchase_item_id) REFERENCES purchase_items(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
-- ================================
|
||||
-- 5. 구매 이력 테이블 (리비전 추적용)
|
||||
-- ================================
|
||||
CREATE TABLE IF NOT EXISTS purchase_history (
|
||||
id SERIAL PRIMARY KEY,
|
||||
|
||||
job_no VARCHAR(50) NOT NULL,
|
||||
revision VARCHAR(20) NOT NULL,
|
||||
|
||||
-- 변경 내용
|
||||
change_type VARCHAR(20) NOT NULL, -- ADDED, MODIFIED, REMOVED
|
||||
purchase_item_id INTEGER,
|
||||
|
||||
-- 수량 변경
|
||||
previous_quantity DECIMAL(10,3),
|
||||
new_quantity DECIMAL(10,3),
|
||||
quantity_diff DECIMAL(10,3), -- 차이 (음수면 감소, 양수면 증가)
|
||||
|
||||
-- 변경 사유
|
||||
change_reason VARCHAR(200),
|
||||
|
||||
-- 관리 정보
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
created_by VARCHAR(100),
|
||||
|
||||
-- 외래키
|
||||
FOREIGN KEY (purchase_item_id) REFERENCES purchase_items(id) ON DELETE SET NULL
|
||||
);
|
||||
|
||||
-- ================================
|
||||
-- 6. 인덱스 생성
|
||||
-- ================================
|
||||
|
||||
-- purchase_items 인덱스
|
||||
CREATE INDEX IF NOT EXISTS idx_purchase_items_job_revision ON purchase_items(job_no, revision);
|
||||
CREATE INDEX IF NOT EXISTS idx_purchase_items_category ON purchase_items(category);
|
||||
CREATE INDEX IF NOT EXISTS idx_purchase_items_item_code ON purchase_items(item_code);
|
||||
CREATE INDEX IF NOT EXISTS idx_purchase_items_active ON purchase_items(is_active);
|
||||
|
||||
-- material_purchase_mapping 인덱스
|
||||
CREATE INDEX IF NOT EXISTS idx_material_purchase_mapping_material ON material_purchase_mapping(material_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_material_purchase_mapping_purchase ON material_purchase_mapping(purchase_item_id);
|
||||
|
||||
-- purchase_orders 인덱스
|
||||
CREATE INDEX IF NOT EXISTS idx_purchase_orders_job_revision ON purchase_orders(job_no, revision);
|
||||
CREATE INDEX IF NOT EXISTS idx_purchase_orders_status ON purchase_orders(status);
|
||||
CREATE INDEX IF NOT EXISTS idx_purchase_orders_order_date ON purchase_orders(order_date);
|
||||
|
||||
-- purchase_order_items 인덱스
|
||||
CREATE INDEX IF NOT EXISTS idx_purchase_order_items_order ON purchase_order_items(purchase_order_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_purchase_order_items_item ON purchase_order_items(purchase_item_id);
|
||||
|
||||
-- purchase_history 인덱스
|
||||
CREATE INDEX IF NOT EXISTS idx_purchase_history_job_revision ON purchase_history(job_no, revision);
|
||||
CREATE INDEX IF NOT EXISTS idx_purchase_history_change_type ON purchase_history(change_type);
|
||||
CREATE INDEX IF NOT EXISTS idx_purchase_history_created_at ON purchase_history(created_at);
|
||||
|
||||
-- ================================
|
||||
-- 7. 트리거 생성 (updated_at 자동 업데이트)
|
||||
-- ================================
|
||||
|
||||
-- purchase_items updated_at 트리거
|
||||
CREATE OR REPLACE FUNCTION update_purchase_items_timestamp()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
NEW.updated_at = CURRENT_TIMESTAMP;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
CREATE TRIGGER trigger_update_purchase_items_timestamp
|
||||
BEFORE UPDATE ON purchase_items
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION update_purchase_items_timestamp();
|
||||
|
||||
-- purchase_orders updated_at 트리거
|
||||
CREATE OR REPLACE FUNCTION update_purchase_orders_timestamp()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
NEW.updated_at = CURRENT_TIMESTAMP;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
CREATE TRIGGER trigger_update_purchase_orders_timestamp
|
||||
BEFORE UPDATE ON purchase_orders
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION update_purchase_orders_timestamp();
|
||||
|
||||
-- purchase_order_items updated_at 트리거
|
||||
CREATE OR REPLACE FUNCTION update_purchase_order_items_timestamp()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
NEW.updated_at = CURRENT_TIMESTAMP;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
CREATE TRIGGER trigger_update_purchase_order_items_timestamp
|
||||
BEFORE UPDATE ON purchase_order_items
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION update_purchase_order_items_timestamp();
|
||||
|
||||
-- ================================
|
||||
-- 8. 기본 뷰 생성
|
||||
-- ================================
|
||||
|
||||
-- 구매 요약 뷰
|
||||
CREATE OR REPLACE VIEW purchase_summary AS
|
||||
SELECT
|
||||
pi.job_no,
|
||||
pi.revision,
|
||||
pi.category,
|
||||
COUNT(*) as item_count,
|
||||
SUM(pi.bom_quantity) as total_bom_quantity,
|
||||
SUM(pi.calculated_qty) as total_purchase_quantity,
|
||||
SUM(poi.ordered_quantity) as total_ordered_quantity,
|
||||
SUM(poi.received_quantity) as total_received_quantity,
|
||||
AVG(pi.safety_factor) as avg_safety_factor
|
||||
FROM purchase_items pi
|
||||
LEFT JOIN purchase_order_items poi ON pi.id = poi.purchase_item_id
|
||||
WHERE pi.is_active = TRUE
|
||||
GROUP BY pi.job_no, pi.revision, pi.category;
|
||||
|
||||
-- 리비전 변경 요약 뷰
|
||||
CREATE OR REPLACE VIEW revision_changes_summary AS
|
||||
SELECT
|
||||
ph.job_no,
|
||||
ph.revision,
|
||||
ph.change_type,
|
||||
COUNT(*) as change_count,
|
||||
SUM(CASE WHEN ph.quantity_diff > 0 THEN ph.quantity_diff ELSE 0 END) as total_increase,
|
||||
SUM(CASE WHEN ph.quantity_diff < 0 THEN ABS(ph.quantity_diff) ELSE 0 END) as total_decrease,
|
||||
SUM(ph.quantity_diff) as net_change
|
||||
FROM purchase_history ph
|
||||
GROUP BY ph.job_no, ph.revision, ph.change_type;
|
||||
|
||||
-- 스크립트 완료 확인
|
||||
SELECT 'Purchase management tables created successfully!' as status;
|
||||
243
backend/scripts/legacy/10_add_material_comparison_system.sql
Normal file
243
backend/scripts/legacy/10_add_material_comparison_system.sql
Normal file
@@ -0,0 +1,243 @@
|
||||
-- 자재 비교 및 발주 추적 시스템
|
||||
-- 실행일: 2025.01.22
|
||||
|
||||
-- ================================
|
||||
-- 1. materials 테이블에 해시 컬럼 추가
|
||||
-- ================================
|
||||
|
||||
-- 자재 비교를 위한 해시 컬럼 추가
|
||||
ALTER TABLE materials ADD COLUMN IF NOT EXISTS material_hash VARCHAR(32);
|
||||
ALTER TABLE materials ADD COLUMN IF NOT EXISTS normalized_description TEXT;
|
||||
|
||||
-- 해시 인덱스 추가 (성능 최적화)
|
||||
CREATE INDEX IF NOT EXISTS idx_materials_hash ON materials(material_hash);
|
||||
CREATE INDEX IF NOT EXISTS idx_materials_file_hash ON materials(file_id, material_hash);
|
||||
|
||||
-- ================================
|
||||
-- 2. 자재 비교 결과 저장 테이블
|
||||
-- ================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS material_revisions_comparison (
|
||||
id SERIAL PRIMARY KEY,
|
||||
|
||||
-- 비교 기본 정보
|
||||
job_no VARCHAR(50) NOT NULL,
|
||||
current_revision VARCHAR(20) NOT NULL,
|
||||
previous_revision VARCHAR(20) NOT NULL,
|
||||
current_file_id INTEGER NOT NULL,
|
||||
previous_file_id INTEGER NOT NULL,
|
||||
|
||||
-- 비교 결과 요약
|
||||
total_current_items INTEGER DEFAULT 0,
|
||||
total_previous_items INTEGER DEFAULT 0,
|
||||
new_items_count INTEGER DEFAULT 0,
|
||||
modified_items_count INTEGER DEFAULT 0,
|
||||
removed_items_count INTEGER DEFAULT 0,
|
||||
unchanged_items_count INTEGER DEFAULT 0,
|
||||
|
||||
-- 상세 결과 (JSON)
|
||||
comparison_details JSONB,
|
||||
|
||||
-- 관리 정보
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
created_by VARCHAR(100),
|
||||
|
||||
-- 외래키
|
||||
FOREIGN KEY (current_file_id) REFERENCES files(id),
|
||||
FOREIGN KEY (previous_file_id) REFERENCES files(id),
|
||||
|
||||
-- 유니크 제약 (같은 비교는 한 번만)
|
||||
UNIQUE(job_no, current_revision, previous_revision)
|
||||
);
|
||||
|
||||
-- ================================
|
||||
-- 3. 개별 자재 비교 상세 테이블
|
||||
-- ================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS material_comparison_details (
|
||||
id SERIAL PRIMARY KEY,
|
||||
|
||||
comparison_id INTEGER NOT NULL,
|
||||
material_hash VARCHAR(32) NOT NULL,
|
||||
|
||||
-- 비교 타입
|
||||
change_type VARCHAR(20) NOT NULL, -- 'NEW', 'MODIFIED', 'REMOVED', 'UNCHANGED'
|
||||
|
||||
-- 자재 정보
|
||||
description TEXT NOT NULL,
|
||||
size_spec VARCHAR(100),
|
||||
material_grade VARCHAR(100),
|
||||
|
||||
-- 수량 비교
|
||||
previous_quantity DECIMAL(10,3) DEFAULT 0,
|
||||
current_quantity DECIMAL(10,3) DEFAULT 0,
|
||||
quantity_diff DECIMAL(10,3) DEFAULT 0,
|
||||
|
||||
-- 추가 구매 필요량 (핵심!)
|
||||
additional_purchase_needed DECIMAL(10,3) DEFAULT 0,
|
||||
|
||||
-- 분류 정보
|
||||
classified_category VARCHAR(50),
|
||||
classification_confidence DECIMAL(3,2),
|
||||
|
||||
-- 관리 정보
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
-- 외래키
|
||||
FOREIGN KEY (comparison_id) REFERENCES material_revisions_comparison(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
-- ================================
|
||||
-- 4. 발주 추적 테이블 (실제 발주 관리)
|
||||
-- ================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS material_purchase_tracking (
|
||||
id SERIAL PRIMARY KEY,
|
||||
|
||||
-- 연결 정보
|
||||
job_no VARCHAR(50) NOT NULL,
|
||||
material_hash VARCHAR(32) NOT NULL,
|
||||
revision VARCHAR(20) NOT NULL,
|
||||
|
||||
-- 자재 정보
|
||||
description TEXT NOT NULL,
|
||||
size_spec VARCHAR(100),
|
||||
unit VARCHAR(10) DEFAULT 'EA',
|
||||
|
||||
-- 수량 정보
|
||||
bom_quantity DECIMAL(10,3) NOT NULL, -- BOM상 필요 수량
|
||||
safety_margin DECIMAL(3,2) DEFAULT 1.10, -- 여유율 (10%)
|
||||
calculated_quantity DECIMAL(10,3) NOT NULL, -- 계산된 구매 수량
|
||||
|
||||
-- 발주 상태
|
||||
purchase_status VARCHAR(20) DEFAULT 'PENDING', -- 'PENDING', 'CONFIRMED', 'ORDERED', 'RECEIVED'
|
||||
confirmed_quantity DECIMAL(10,3) DEFAULT 0, -- 확정된 발주 수량
|
||||
ordered_quantity DECIMAL(10,3) DEFAULT 0, -- 실제 주문 수량
|
||||
received_quantity DECIMAL(10,3) DEFAULT 0, -- 입고 수량
|
||||
|
||||
-- 발주 정보
|
||||
purchase_order_no VARCHAR(100),
|
||||
supplier_name VARCHAR(200),
|
||||
unit_price DECIMAL(10,2),
|
||||
total_price DECIMAL(12,2),
|
||||
order_date DATE,
|
||||
delivery_date DATE,
|
||||
|
||||
-- 관리 정보
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
confirmed_by VARCHAR(100),
|
||||
confirmed_at TIMESTAMP,
|
||||
|
||||
-- 외래키
|
||||
FOREIGN KEY (job_no) REFERENCES jobs(job_no),
|
||||
|
||||
-- 유니크 제약
|
||||
UNIQUE(job_no, material_hash, revision)
|
||||
);
|
||||
|
||||
-- ================================
|
||||
-- 5. 누적 재고 현황 뷰
|
||||
-- ================================
|
||||
|
||||
CREATE OR REPLACE VIEW material_inventory_status AS
|
||||
SELECT
|
||||
mpt.job_no,
|
||||
mpt.material_hash,
|
||||
mpt.description,
|
||||
mpt.size_spec,
|
||||
mpt.unit,
|
||||
|
||||
-- 누적 수량
|
||||
SUM(mpt.confirmed_quantity) as total_confirmed,
|
||||
SUM(mpt.ordered_quantity) as total_ordered,
|
||||
SUM(mpt.received_quantity) as total_received,
|
||||
|
||||
-- 현재 가용 재고
|
||||
SUM(mpt.received_quantity) as available_stock,
|
||||
|
||||
-- 최신 리비전 정보
|
||||
MAX(mpt.revision) as latest_revision,
|
||||
MAX(mpt.updated_at) as last_updated
|
||||
|
||||
FROM material_purchase_tracking mpt
|
||||
WHERE mpt.purchase_status != 'CANCELLED'
|
||||
GROUP BY mpt.job_no, mpt.material_hash, mpt.description, mpt.size_spec, mpt.unit;
|
||||
|
||||
-- ================================
|
||||
-- 6. 인덱스 생성
|
||||
-- ================================
|
||||
|
||||
-- material_revisions_comparison 인덱스
|
||||
CREATE INDEX IF NOT EXISTS idx_material_revisions_job ON material_revisions_comparison(job_no);
|
||||
CREATE INDEX IF NOT EXISTS idx_material_revisions_current ON material_revisions_comparison(current_revision);
|
||||
|
||||
-- material_comparison_details 인덱스
|
||||
CREATE INDEX IF NOT EXISTS idx_material_comparison_hash ON material_comparison_details(material_hash);
|
||||
CREATE INDEX IF NOT EXISTS idx_material_comparison_type ON material_comparison_details(change_type);
|
||||
|
||||
-- material_purchase_tracking 인덱스
|
||||
CREATE INDEX IF NOT EXISTS idx_purchase_tracking_job_hash ON material_purchase_tracking(job_no, material_hash);
|
||||
CREATE INDEX IF NOT EXISTS idx_purchase_tracking_status ON material_purchase_tracking(purchase_status);
|
||||
CREATE INDEX IF NOT EXISTS idx_purchase_tracking_revision ON material_purchase_tracking(revision);
|
||||
|
||||
-- ================================
|
||||
-- 7. 해시 생성 함수 (PostgreSQL)
|
||||
-- ================================
|
||||
|
||||
CREATE OR REPLACE FUNCTION generate_material_hash(
|
||||
description TEXT,
|
||||
size_spec TEXT DEFAULT '',
|
||||
material_grade TEXT DEFAULT ''
|
||||
) RETURNS VARCHAR(32) AS $$
|
||||
BEGIN
|
||||
-- 정규화: 대소문자 통일, 공백 정리
|
||||
description := UPPER(TRIM(REGEXP_REPLACE(description, '\s+', ' ', 'g')));
|
||||
size_spec := UPPER(TRIM(COALESCE(size_spec, '')));
|
||||
material_grade := UPPER(TRIM(COALESCE(material_grade, '')));
|
||||
|
||||
-- MD5 해시 생성 (32자리)
|
||||
RETURN MD5(description || '|' || size_spec || '|' || material_grade);
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
-- ================================
|
||||
-- 8. 기존 데이터에 해시 생성 (배치 처리)
|
||||
-- ================================
|
||||
|
||||
-- 기존 materials 데이터에 해시 추가
|
||||
UPDATE materials
|
||||
SET
|
||||
material_hash = generate_material_hash(original_description, size_spec, material_grade),
|
||||
normalized_description = UPPER(TRIM(REGEXP_REPLACE(original_description, '\s+', ' ', 'g')))
|
||||
WHERE material_hash IS NULL;
|
||||
|
||||
-- ================================
|
||||
-- 9. 트리거 생성 (자동 해시 생성)
|
||||
-- ================================
|
||||
|
||||
CREATE OR REPLACE FUNCTION auto_generate_material_hash()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
-- 새로 삽입되거나 업데이트될 때 자동으로 해시 생성
|
||||
NEW.material_hash := generate_material_hash(
|
||||
NEW.original_description,
|
||||
NEW.size_spec,
|
||||
NEW.material_grade
|
||||
);
|
||||
NEW.normalized_description := UPPER(TRIM(REGEXP_REPLACE(NEW.original_description, '\s+', ' ', 'g')));
|
||||
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
CREATE TRIGGER trigger_auto_material_hash
|
||||
BEFORE INSERT OR UPDATE ON materials
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION auto_generate_material_hash();
|
||||
|
||||
-- ================================
|
||||
-- 10. 완료 메시지
|
||||
-- ================================
|
||||
|
||||
SELECT 'Material comparison and purchase tracking system created successfully!' as status;
|
||||
184
backend/scripts/legacy/15_create_tubing_system.sql
Normal file
184
backend/scripts/legacy/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/legacy/16_performance_indexes.sql
Normal file
163
backend/scripts/legacy/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/legacy/17_add_project_type_column.sql
Normal file
29
backend/scripts/legacy/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 $$;
|
||||
242
backend/scripts/legacy/18_create_auth_tables.sql
Normal file
242
backend/scripts/legacy/18_create_auth_tables.sql
Normal file
@@ -0,0 +1,242 @@
|
||||
-- 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 $$;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
142
backend/scripts/legacy/19_add_user_tracking_fields.sql
Normal file
142
backend/scripts/legacy/19_add_user_tracking_fields.sql
Normal file
@@ -0,0 +1,142 @@
|
||||
-- 사용자 추적 및 담당자 기록 필드 추가
|
||||
-- 생성일: 2025.01
|
||||
-- 목적: RULES 가이드라인에 따른 사용자 추적 시스템 구축
|
||||
|
||||
-- ================================
|
||||
-- 1. 기존 테이블에 담당자 필드 추가
|
||||
-- ================================
|
||||
|
||||
-- files 테이블 수정 (uploaded_by는 이미 존재)
|
||||
ALTER TABLE files
|
||||
ADD COLUMN IF NOT EXISTS updated_by VARCHAR(100),
|
||||
ADD COLUMN IF NOT EXISTS updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP;
|
||||
|
||||
-- jobs 테이블 수정
|
||||
ALTER TABLE jobs
|
||||
ADD COLUMN IF NOT EXISTS created_by VARCHAR(100),
|
||||
ADD COLUMN IF NOT EXISTS updated_by VARCHAR(100),
|
||||
ADD COLUMN IF NOT EXISTS assigned_to VARCHAR(100);
|
||||
|
||||
-- materials 테이블 수정
|
||||
ALTER TABLE materials
|
||||
ADD COLUMN IF NOT EXISTS classified_by VARCHAR(100),
|
||||
ADD COLUMN IF NOT EXISTS classified_at TIMESTAMP,
|
||||
ADD COLUMN IF NOT EXISTS updated_by VARCHAR(100);
|
||||
|
||||
-- ================================
|
||||
-- 2. 사용자 활동 로그 테이블 생성
|
||||
-- ================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS user_activity_logs (
|
||||
id SERIAL PRIMARY KEY,
|
||||
user_id INTEGER, -- users 테이블 참조 (외래키 제약 없음 - 유연성)
|
||||
username VARCHAR(100) NOT NULL, -- 사용자명 (필수)
|
||||
|
||||
-- 활동 정보
|
||||
activity_type VARCHAR(50) NOT NULL, -- 'FILE_UPLOAD', 'PROJECT_CREATE', 'PURCHASE_CONFIRM' 등
|
||||
activity_description TEXT, -- 상세 활동 내용
|
||||
|
||||
-- 대상 정보
|
||||
target_id INTEGER, -- 대상 ID (파일, 프로젝트 등)
|
||||
target_type VARCHAR(50), -- 'FILE', 'PROJECT', 'MATERIAL', 'PURCHASE' 등
|
||||
|
||||
-- 세션 정보
|
||||
ip_address VARCHAR(45), -- IP 주소
|
||||
user_agent TEXT, -- 브라우저 정보
|
||||
|
||||
-- 추가 메타데이터 (JSON)
|
||||
metadata JSONB, -- 추가 정보 (파일 크기, 처리 시간 등)
|
||||
|
||||
-- 시간 정보
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
-- ================================
|
||||
-- 3. 구매 관련 테이블 수정
|
||||
-- ================================
|
||||
|
||||
-- purchase_items 테이블 수정 (이미 created_by 존재하는지 확인 후 추가)
|
||||
ALTER TABLE purchase_items
|
||||
ADD COLUMN IF NOT EXISTS updated_by VARCHAR(100),
|
||||
ADD COLUMN IF NOT EXISTS approved_by VARCHAR(100),
|
||||
ADD COLUMN IF NOT EXISTS approved_at TIMESTAMP;
|
||||
|
||||
-- material_purchase_tracking 테이블 수정 (이미 confirmed_by 존재)
|
||||
ALTER TABLE material_purchase_tracking
|
||||
ADD COLUMN IF NOT EXISTS ordered_by VARCHAR(100),
|
||||
ADD COLUMN IF NOT EXISTS ordered_at TIMESTAMP,
|
||||
ADD COLUMN IF NOT EXISTS approved_by VARCHAR(100),
|
||||
ADD COLUMN IF NOT EXISTS approved_at TIMESTAMP;
|
||||
|
||||
-- ================================
|
||||
-- 4. 인덱스 생성 (성능 최적화)
|
||||
-- ================================
|
||||
|
||||
-- 사용자 활동 로그 인덱스
|
||||
CREATE INDEX IF NOT EXISTS idx_user_activity_logs_username ON user_activity_logs(username);
|
||||
CREATE INDEX IF NOT EXISTS idx_user_activity_logs_activity_type ON user_activity_logs(activity_type);
|
||||
CREATE INDEX IF NOT EXISTS idx_user_activity_logs_created_at ON user_activity_logs(created_at);
|
||||
CREATE INDEX IF NOT EXISTS idx_user_activity_logs_target ON user_activity_logs(target_type, target_id);
|
||||
|
||||
-- 담당자 필드 인덱스
|
||||
CREATE INDEX IF NOT EXISTS idx_files_uploaded_by ON files(uploaded_by);
|
||||
CREATE INDEX IF NOT EXISTS idx_files_updated_by ON files(updated_by);
|
||||
CREATE INDEX IF NOT EXISTS idx_jobs_created_by ON jobs(created_by);
|
||||
CREATE INDEX IF NOT EXISTS idx_jobs_assigned_to ON jobs(assigned_to);
|
||||
CREATE INDEX IF NOT EXISTS idx_materials_classified_by ON materials(classified_by);
|
||||
|
||||
-- ================================
|
||||
-- 5. 트리거 생성 (자동 updated_at 갱신)
|
||||
-- ================================
|
||||
|
||||
-- files 테이블 updated_at 자동 갱신
|
||||
CREATE OR REPLACE FUNCTION update_files_updated_at()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
NEW.updated_at = CURRENT_TIMESTAMP;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ language 'plpgsql';
|
||||
|
||||
DROP TRIGGER IF EXISTS trigger_files_updated_at ON files;
|
||||
CREATE TRIGGER trigger_files_updated_at
|
||||
BEFORE UPDATE ON files
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION update_files_updated_at();
|
||||
|
||||
-- jobs 테이블 updated_at 자동 갱신
|
||||
CREATE OR REPLACE FUNCTION update_jobs_updated_at()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
NEW.updated_at = CURRENT_TIMESTAMP;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ language 'plpgsql';
|
||||
|
||||
DROP TRIGGER IF EXISTS trigger_jobs_updated_at ON jobs;
|
||||
CREATE TRIGGER trigger_jobs_updated_at
|
||||
BEFORE UPDATE ON jobs
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION update_jobs_updated_at();
|
||||
|
||||
-- ================================
|
||||
-- 6. 기본 데이터 설정
|
||||
-- ================================
|
||||
|
||||
-- 기존 데이터에 기본 담당자 설정 (시스템 마이그레이션용)
|
||||
UPDATE files SET uploaded_by = 'system' WHERE uploaded_by IS NULL;
|
||||
UPDATE jobs SET created_by = 'system' WHERE created_by IS NULL;
|
||||
|
||||
-- ================================
|
||||
-- 7. 권한 및 보안 설정
|
||||
-- ================================
|
||||
|
||||
-- 활동 로그 테이블은 INSERT만 허용 (수정/삭제 방지)
|
||||
-- 실제 운영에서는 별도 권한 관리 필요
|
||||
|
||||
COMMENT ON TABLE user_activity_logs IS '사용자 활동 로그 - 모든 업무 활동 추적';
|
||||
COMMENT ON COLUMN user_activity_logs.activity_type IS '활동 유형: FILE_UPLOAD, PROJECT_CREATE, PURCHASE_CONFIRM, MATERIAL_CLASSIFY 등';
|
||||
COMMENT ON COLUMN user_activity_logs.metadata IS '추가 정보 JSON: 파일크기, 처리시간, 변경내용 등';
|
||||
|
||||
-- 완료 메시지
|
||||
SELECT 'User tracking system tables created successfully!' as result;
|
||||
50
backend/scripts/legacy/20_add_pipe_end_preparation_table.sql
Normal file
50
backend/scripts/legacy/20_add_pipe_end_preparation_table.sql
Normal file
@@ -0,0 +1,50 @@
|
||||
-- 파이프 끝단 가공 정보 테이블 생성
|
||||
-- 각 파이프별로 끝단 가공 정보를 별도 저장
|
||||
|
||||
CREATE TABLE IF NOT EXISTS pipe_end_preparations (
|
||||
id SERIAL PRIMARY KEY,
|
||||
material_id INTEGER NOT NULL REFERENCES materials(id) ON DELETE CASCADE,
|
||||
file_id INTEGER NOT NULL REFERENCES files(id) ON DELETE CASCADE,
|
||||
|
||||
-- 끝단 가공 정보
|
||||
end_preparation_type VARCHAR(50) DEFAULT 'PBE', -- PBE(양쪽무개선), BBE(양쪽개선), POE(한쪽개선), PE(무개선)
|
||||
end_preparation_code VARCHAR(20), -- 원본 코드 (BBE, POE, PBE 등)
|
||||
machining_required BOOLEAN DEFAULT FALSE, -- 가공 필요 여부
|
||||
cutting_note TEXT, -- 가공 메모
|
||||
|
||||
-- 원본 정보 보존
|
||||
original_description TEXT NOT NULL, -- 끝단 가공 포함된 원본 설명
|
||||
clean_description TEXT NOT NULL, -- 끝단 가공 제외한 구매용 설명
|
||||
|
||||
-- 메타데이터
|
||||
confidence FLOAT DEFAULT 0.0, -- 분류 신뢰도
|
||||
matched_pattern VARCHAR(100), -- 매칭된 패턴
|
||||
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
-- 인덱스 생성
|
||||
CREATE INDEX IF NOT EXISTS idx_pipe_end_preparations_material_id ON pipe_end_preparations(material_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_pipe_end_preparations_file_id ON pipe_end_preparations(file_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_pipe_end_preparations_type ON pipe_end_preparations(end_preparation_type);
|
||||
|
||||
-- 기본 끝단 가공 타입 정의
|
||||
COMMENT ON COLUMN pipe_end_preparations.end_preparation_type IS 'PBE: 양쪽무개선(기본값), BBE: 양쪽개선, POE: 한쪽개선, PE: 무개선';
|
||||
COMMENT ON COLUMN pipe_end_preparations.machining_required IS '가공이 필요한지 여부 (개선 작업 등)';
|
||||
COMMENT ON COLUMN pipe_end_preparations.clean_description IS '구매 시 사용할 끝단 가공 정보가 제거된 설명';
|
||||
|
||||
-- 트리거: updated_at 자동 업데이트
|
||||
CREATE OR REPLACE FUNCTION update_pipe_end_preparations_updated_at()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
NEW.updated_at = CURRENT_TIMESTAMP;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ language 'plpgsql';
|
||||
|
||||
CREATE TRIGGER update_pipe_end_preparations_updated_at
|
||||
BEFORE UPDATE ON pipe_end_preparations
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION update_pipe_end_preparations_updated_at();
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
-- 사용자 요구사항 테이블에 material_id 컬럼 추가
|
||||
-- 2025.09.24 - 사용자 피드백 기반 개선사항 #1
|
||||
|
||||
-- material_id 컬럼 추가 (nullable로 시작)
|
||||
ALTER TABLE user_requirements
|
||||
ADD COLUMN IF NOT EXISTS material_id INTEGER;
|
||||
|
||||
-- 외래키 제약조건 추가
|
||||
ALTER TABLE user_requirements
|
||||
ADD CONSTRAINT fk_user_requirements_material_id
|
||||
FOREIGN KEY (material_id) REFERENCES materials(id) ON DELETE CASCADE;
|
||||
|
||||
-- 인덱스 추가 (성능 향상)
|
||||
CREATE INDEX IF NOT EXISTS idx_user_requirements_material_id ON user_requirements(material_id);
|
||||
|
||||
-- 기존 데이터 정리 (필요시)
|
||||
-- DELETE FROM user_requirements WHERE material_id IS NULL;
|
||||
|
||||
COMMENT ON COLUMN user_requirements.material_id IS '자재 ID (개별 자재별 요구사항 연결)';
|
||||
11
backend/scripts/legacy/22_add_full_material_grade.sql
Normal file
11
backend/scripts/legacy/22_add_full_material_grade.sql
Normal file
@@ -0,0 +1,11 @@
|
||||
-- 전체 재질명 표기를 위한 컬럼 추가
|
||||
-- 2025.09.24 - 사용자 피드백 기반 개선사항 #2
|
||||
|
||||
-- full_material_grade 컬럼 추가 (원본 설명에서 추출한 전체 재질명)
|
||||
ALTER TABLE materials
|
||||
ADD COLUMN IF NOT EXISTS full_material_grade TEXT;
|
||||
|
||||
-- 인덱스 추가 (검색 성능 향상)
|
||||
CREATE INDEX IF NOT EXISTS idx_materials_full_material_grade ON materials(full_material_grade);
|
||||
|
||||
COMMENT ON COLUMN materials.full_material_grade IS '전체 재질명 (예: ASTM A312 TP304, ASTM A106 GR B 등)';
|
||||
45
backend/scripts/legacy/23_create_support_details_table.sql
Normal file
45
backend/scripts/legacy/23_create_support_details_table.sql
Normal file
@@ -0,0 +1,45 @@
|
||||
-- SUPPORT 카테고리 상세 정보 테이블 생성
|
||||
-- 2025.09.24 - 사용자 피드백 기반 개선사항 #3
|
||||
|
||||
-- support_details 테이블 생성
|
||||
CREATE TABLE IF NOT EXISTS support_details (
|
||||
id SERIAL PRIMARY KEY,
|
||||
material_id INTEGER NOT NULL REFERENCES materials(id) ON DELETE CASCADE,
|
||||
file_id INTEGER NOT NULL REFERENCES files(id) ON DELETE CASCADE,
|
||||
|
||||
-- 서포트 타입 정보
|
||||
support_type VARCHAR(50), -- URETHANE_BLOCK, CLAMP, HANGER, SPRING_HANGER 등
|
||||
support_subtype VARCHAR(100), -- 상세 타입
|
||||
|
||||
-- 하중 정보
|
||||
load_rating VARCHAR(20), -- LIGHT, MEDIUM, HEAVY, CUSTOM
|
||||
load_capacity VARCHAR(20), -- 40T, 50TON 등
|
||||
|
||||
-- 재질 정보
|
||||
material_standard VARCHAR(50), -- 재질 표준
|
||||
material_grade VARCHAR(100), -- 재질 등급
|
||||
|
||||
-- 사이즈 정보
|
||||
pipe_size VARCHAR(20), -- 지지하는 파이프 크기
|
||||
length_mm DECIMAL(10,2), -- 길이 (mm)
|
||||
width_mm DECIMAL(10,2), -- 폭 (mm)
|
||||
height_mm DECIMAL(10,2), -- 높이 (mm)
|
||||
|
||||
-- 분류 신뢰도
|
||||
classification_confidence DECIMAL(3,2), -- 0.00-1.00
|
||||
|
||||
-- 메타데이터
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
-- 인덱스 생성
|
||||
CREATE INDEX IF NOT EXISTS idx_support_details_material_id ON support_details(material_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_support_details_file_id ON support_details(file_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_support_details_support_type ON support_details(support_type);
|
||||
|
||||
-- 코멘트 추가
|
||||
COMMENT ON TABLE support_details IS '배관 지지재 상세 정보 (우레탄 블록, 클램프, 행거 등)';
|
||||
COMMENT ON COLUMN support_details.support_type IS '서포트 타입 (URETHANE_BLOCK, CLAMP, HANGER 등)';
|
||||
COMMENT ON COLUMN support_details.load_capacity IS '하중 용량 (40T, 50TON 등)';
|
||||
COMMENT ON COLUMN support_details.pipe_size IS '지지하는 파이프 크기';
|
||||
130
backend/scripts/legacy/24_add_special_category_support.sql
Normal file
130
backend/scripts/legacy/24_add_special_category_support.sql
Normal file
@@ -0,0 +1,130 @@
|
||||
-- ================================
|
||||
-- SPECIAL 카테고리 지원 추가 마이그레이션
|
||||
-- 생성일: 2025.09.30
|
||||
-- 목적: SPECIAL 카테고리 분류 및 관련 기능 지원
|
||||
-- ================================
|
||||
|
||||
-- 1. materials 테이블 SPECIAL 카테고리 지원 확인
|
||||
-- ================================
|
||||
|
||||
-- classified_category 컬럼이 SPECIAL 값을 지원하는지 확인
|
||||
-- (이미 VARCHAR(50)이므로 추가 작업 불필요, 하지만 명시적으로 체크)
|
||||
|
||||
-- SPECIAL 카테고리 관련 인덱스 추가 (성능 최적화)
|
||||
CREATE INDEX IF NOT EXISTS idx_materials_special_category
|
||||
ON materials(classified_category)
|
||||
WHERE classified_category = 'SPECIAL';
|
||||
|
||||
-- 2. SPECIAL 카테고리 분류 규칙 테이블 생성
|
||||
-- ================================
|
||||
|
||||
-- SPECIAL 키워드 패턴 테이블
|
||||
CREATE TABLE IF NOT EXISTS special_classification_patterns (
|
||||
id SERIAL PRIMARY KEY,
|
||||
pattern_type VARCHAR(20) NOT NULL, -- 'KEYWORD', 'REGEX', 'EXACT'
|
||||
pattern_value VARCHAR(200) NOT NULL,
|
||||
description TEXT,
|
||||
priority INTEGER DEFAULT 1, -- 우선순위 (낮을수록 높은 우선순위)
|
||||
is_active BOOLEAN DEFAULT TRUE,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
-- 기본 SPECIAL 키워드 패턴 삽입
|
||||
INSERT INTO special_classification_patterns (pattern_type, pattern_value, description, priority) VALUES
|
||||
('KEYWORD', 'SPECIAL', '영문 SPECIAL 키워드', 1),
|
||||
('KEYWORD', '스페셜', '한글 스페셜 키워드', 1),
|
||||
('KEYWORD', 'SPEC', '영문 SPEC 축약어', 2),
|
||||
('KEYWORD', 'SPL', '영문 SPL 축약어', 2)
|
||||
ON CONFLICT DO NOTHING;
|
||||
|
||||
-- 3. SPECIAL 자재 추가 정보 테이블
|
||||
-- ================================
|
||||
|
||||
-- SPECIAL 자재 상세 정보 테이블 (도면 업로드 관련)
|
||||
CREATE TABLE IF NOT EXISTS special_material_details (
|
||||
id SERIAL PRIMARY KEY,
|
||||
material_id INTEGER REFERENCES materials(id) ON DELETE CASCADE,
|
||||
file_id INTEGER REFERENCES files(id) ON DELETE CASCADE,
|
||||
|
||||
-- 도면 정보
|
||||
drawing_number VARCHAR(100), -- 도면 번호
|
||||
drawing_revision VARCHAR(20), -- 도면 리비전
|
||||
drawing_uploaded BOOLEAN DEFAULT FALSE, -- 도면 업로드 여부
|
||||
drawing_file_path TEXT, -- 도면 파일 경로
|
||||
|
||||
-- 특수 요구사항
|
||||
special_requirements TEXT, -- 특수 제작 요구사항
|
||||
manufacturing_notes TEXT, -- 제작 참고사항
|
||||
approval_required BOOLEAN DEFAULT TRUE, -- 승인 필요 여부
|
||||
approved_by VARCHAR(100), -- 승인자
|
||||
approved_at TIMESTAMP, -- 승인 일시
|
||||
|
||||
-- 분류 정보
|
||||
classification_confidence FLOAT DEFAULT 1.0,
|
||||
classification_reason TEXT, -- 분류 근거
|
||||
|
||||
-- 관리 정보
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
-- 4. 인덱스 생성 (성능 최적화)
|
||||
-- ================================
|
||||
|
||||
-- special_classification_patterns 인덱스
|
||||
CREATE INDEX IF NOT EXISTS idx_special_patterns_type ON special_classification_patterns(pattern_type);
|
||||
CREATE INDEX IF NOT EXISTS idx_special_patterns_active ON special_classification_patterns(is_active);
|
||||
CREATE INDEX IF NOT EXISTS idx_special_patterns_priority ON special_classification_patterns(priority);
|
||||
|
||||
-- special_material_details 인덱스
|
||||
CREATE INDEX IF NOT EXISTS idx_special_details_material ON special_material_details(material_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_special_details_file ON special_material_details(file_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_special_details_drawing_uploaded ON special_material_details(drawing_uploaded);
|
||||
CREATE INDEX IF NOT EXISTS idx_special_details_approval ON special_material_details(approval_required);
|
||||
|
||||
-- 5. 기존 자재 재분류 (선택적)
|
||||
-- ================================
|
||||
|
||||
-- 기존 자료 중 SPECIAL 키워드가 포함된 자재를 SPECIAL 카테고리로 재분류
|
||||
UPDATE materials
|
||||
SET
|
||||
classified_category = 'SPECIAL',
|
||||
classification_confidence = 1.0,
|
||||
updated_by = 'SYSTEM_MIGRATION',
|
||||
classified_at = CURRENT_TIMESTAMP
|
||||
WHERE
|
||||
(
|
||||
UPPER(original_description) LIKE '%SPECIAL%' OR
|
||||
UPPER(original_description) LIKE '%스페셜%' OR
|
||||
UPPER(original_description) LIKE '%SPEC%' OR
|
||||
UPPER(original_description) LIKE '%SPL%'
|
||||
)
|
||||
AND (classified_category IS NULL OR classified_category != 'SPECIAL');
|
||||
|
||||
-- 6. 통계 및 검증
|
||||
-- ================================
|
||||
|
||||
-- SPECIAL 카테고리 자재 개수 확인
|
||||
DO $$
|
||||
DECLARE
|
||||
special_count INTEGER;
|
||||
BEGIN
|
||||
SELECT COUNT(*) INTO special_count FROM materials WHERE classified_category = 'SPECIAL';
|
||||
RAISE NOTICE 'SPECIAL 카테고리로 분류된 자재 개수: %', special_count;
|
||||
END $$;
|
||||
|
||||
-- 7. 권한 설정 (필요시)
|
||||
-- ================================
|
||||
|
||||
-- SPECIAL 자재 관리 권한 (향후 확장용)
|
||||
-- 현재는 기본 materials 테이블 권한을 따름
|
||||
|
||||
COMMIT;
|
||||
|
||||
-- ================================
|
||||
-- 마이그레이션 완료 로그
|
||||
-- ================================
|
||||
INSERT INTO migration_log (script_name, executed_at, description) VALUES
|
||||
('24_add_special_category_support.sql', CURRENT_TIMESTAMP, 'SPECIAL 카테고리 지원 추가 및 기존 자재 재분류')
|
||||
ON CONFLICT DO NOTHING;
|
||||
151
backend/scripts/legacy/25_execute_special_category_migration.py
Executable file
151
backend/scripts/legacy/25_execute_special_category_migration.py
Executable file
@@ -0,0 +1,151 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
SPECIAL 카테고리 마이그레이션 실행 스크립트
|
||||
생성일: 2025.09.30
|
||||
목적: SPECIAL 카테고리 지원 추가 및 기존 자재 재분류
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
|
||||
from sqlalchemy import create_engine, text
|
||||
from app.database import DATABASE_URL
|
||||
|
||||
def execute_special_migration():
|
||||
"""SPECIAL 카테고리 마이그레이션 실행"""
|
||||
engine = create_engine(DATABASE_URL)
|
||||
|
||||
with engine.connect() as conn:
|
||||
print("🚀 SPECIAL 카테고리 마이그레이션 시작...")
|
||||
print("=" * 60)
|
||||
|
||||
try:
|
||||
# 1. 마이그레이션 스크립트 실행
|
||||
print("📋 1단계: 마이그레이션 스크립트 실행...")
|
||||
script_path = os.path.join(os.path.dirname(__file__), '24_add_special_category_support.sql')
|
||||
with open(script_path, 'r', encoding='utf-8') as f:
|
||||
sql_content = f.read()
|
||||
|
||||
# SQL 명령어들을 분리하여 실행
|
||||
sql_commands = sql_content.split(';')
|
||||
for i, command in enumerate(sql_commands):
|
||||
command = command.strip()
|
||||
if command and not command.startswith('--') and command != 'COMMIT':
|
||||
try:
|
||||
conn.execute(text(command))
|
||||
if i % 10 == 0: # 진행상황 표시
|
||||
print(f" - 명령어 {i+1}/{len(sql_commands)} 실행 중...")
|
||||
except Exception as e:
|
||||
print(f" ⚠️ 명령어 실행 중 오류 (무시됨): {e}")
|
||||
continue
|
||||
|
||||
print("✅ 마이그레이션 스크립트 실행 완료")
|
||||
|
||||
# 2. 기존 자재 재분류 확인
|
||||
print("\n📊 2단계: 기존 자재 재분류 결과 확인...")
|
||||
|
||||
# SPECIAL 키워드가 포함된 자재 개수 확인
|
||||
result = conn.execute(text("""
|
||||
SELECT COUNT(*) as count
|
||||
FROM materials
|
||||
WHERE classified_category = 'SPECIAL'
|
||||
""")).fetchone()
|
||||
|
||||
special_count = result.count if result else 0
|
||||
print(f" - SPECIAL 카테고리로 분류된 자재: {special_count}개")
|
||||
|
||||
# 키워드별 분류 결과 확인
|
||||
keyword_results = conn.execute(text("""
|
||||
SELECT
|
||||
CASE
|
||||
WHEN UPPER(original_description) LIKE '%SPECIAL%' THEN 'SPECIAL'
|
||||
WHEN UPPER(original_description) LIKE '%스페셜%' THEN '스페셜'
|
||||
WHEN UPPER(original_description) LIKE '%SPEC%' THEN 'SPEC'
|
||||
WHEN UPPER(original_description) LIKE '%SPL%' THEN 'SPL'
|
||||
END as keyword_type,
|
||||
COUNT(*) as count
|
||||
FROM materials
|
||||
WHERE classified_category = 'SPECIAL'
|
||||
GROUP BY keyword_type
|
||||
ORDER BY count DESC
|
||||
""")).fetchall()
|
||||
|
||||
if keyword_results:
|
||||
print(" - 키워드별 분류 결과:")
|
||||
for row in keyword_results:
|
||||
print(f" * {row.keyword_type}: {row.count}개")
|
||||
|
||||
# 3. 테이블 생성 확인
|
||||
print("\n🏗️ 3단계: 새 테이블 생성 확인...")
|
||||
|
||||
# special_classification_patterns 테이블 확인
|
||||
patterns_count = conn.execute(text("""
|
||||
SELECT COUNT(*) as count
|
||||
FROM special_classification_patterns
|
||||
""")).fetchone()
|
||||
|
||||
patterns_count = patterns_count.count if patterns_count else 0
|
||||
print(f" - special_classification_patterns 테이블: {patterns_count}개 패턴 등록됨")
|
||||
|
||||
# special_material_details 테이블 확인
|
||||
details_exists = conn.execute(text("""
|
||||
SELECT EXISTS (
|
||||
SELECT 1 FROM information_schema.tables
|
||||
WHERE table_name = 'special_material_details'
|
||||
) as exists
|
||||
""")).fetchone()
|
||||
|
||||
if details_exists.exists:
|
||||
print(" - special_material_details 테이블: 생성 완료")
|
||||
else:
|
||||
print(" - ❌ special_material_details 테이블: 생성 실패")
|
||||
|
||||
# 4. 인덱스 생성 확인
|
||||
print("\n🔍 4단계: 인덱스 생성 확인...")
|
||||
|
||||
special_indexes = conn.execute(text("""
|
||||
SELECT indexname
|
||||
FROM pg_indexes
|
||||
WHERE indexname LIKE '%special%'
|
||||
ORDER BY indexname
|
||||
""")).fetchall()
|
||||
|
||||
if special_indexes:
|
||||
print(" - SPECIAL 관련 인덱스:")
|
||||
for idx in special_indexes:
|
||||
print(f" * {idx.indexname}")
|
||||
else:
|
||||
print(" - ⚠️ SPECIAL 관련 인덱스가 생성되지 않았습니다.")
|
||||
|
||||
# 커밋
|
||||
conn.commit()
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("🎉 SPECIAL 카테고리 마이그레이션 완료!")
|
||||
print(f"📊 총 {special_count}개 자재가 SPECIAL 카테고리로 분류되었습니다.")
|
||||
print("🔧 새로운 기능:")
|
||||
print(" - SPECIAL 키워드 자동 감지")
|
||||
print(" - 도면 업로드 관리")
|
||||
print(" - 특수 제작 요구사항 추적")
|
||||
print(" - 승인 프로세스 지원")
|
||||
|
||||
except Exception as e:
|
||||
print(f"\n❌ 마이그레이션 실행 중 오류 발생: {e}")
|
||||
conn.rollback()
|
||||
raise
|
||||
|
||||
def main():
|
||||
"""메인 실행 함수"""
|
||||
print("SPECIAL 카테고리 마이그레이션 실행")
|
||||
print("TK-MP-Project - 특수 자재 관리 시스템")
|
||||
print("=" * 60)
|
||||
|
||||
try:
|
||||
execute_special_migration()
|
||||
except Exception as e:
|
||||
print(f"\n💥 마이그레이션 실패: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
28
backend/scripts/legacy/26_add_user_status_column.sql
Normal file
28
backend/scripts/legacy/26_add_user_status_column.sql
Normal file
@@ -0,0 +1,28 @@
|
||||
-- users 테이블에 status 컬럼 추가 및 기존 데이터 마이그레이션
|
||||
|
||||
-- 1. status 컬럼 추가 (기본값은 'active')
|
||||
ALTER TABLE users
|
||||
ADD COLUMN IF NOT EXISTS status VARCHAR(20) DEFAULT 'active';
|
||||
|
||||
-- 2. status 컬럼에 CHECK 제약 조건 추가
|
||||
ALTER TABLE users
|
||||
ADD CONSTRAINT users_status_check
|
||||
CHECK (status IN ('pending', 'active', 'suspended', 'deleted'));
|
||||
|
||||
-- 3. 기존 데이터 마이그레이션
|
||||
-- is_active가 false인 사용자는 'pending'으로
|
||||
-- is_active가 true인 사용자는 'active'로
|
||||
UPDATE users
|
||||
SET status = CASE
|
||||
WHEN is_active = FALSE THEN 'pending'
|
||||
WHEN is_active = TRUE THEN 'active'
|
||||
ELSE 'active'
|
||||
END;
|
||||
|
||||
-- 4. status 컬럼에 인덱스 추가 (조회 성능 향상)
|
||||
CREATE INDEX IF NOT EXISTS idx_users_status ON users(status);
|
||||
|
||||
-- 5. 향후 is_active 컬럼은 deprecated로 간주
|
||||
-- 하지만 하위 호환성을 위해 당분간 유지
|
||||
COMMENT ON COLUMN users.status IS 'User account status: pending, active, suspended, deleted';
|
||||
COMMENT ON COLUMN users.is_active IS 'DEPRECATED: Use status column instead. Kept for backward compatibility.';
|
||||
135
backend/scripts/legacy/27_add_purchase_tracking.sql
Normal file
135
backend/scripts/legacy/27_add_purchase_tracking.sql
Normal file
@@ -0,0 +1,135 @@
|
||||
-- 엑셀 내보내기 이력 및 구매 상태 관리 테이블
|
||||
|
||||
-- 1. 엑셀 내보내기 이력 테이블
|
||||
CREATE TABLE IF NOT EXISTS excel_export_history (
|
||||
export_id SERIAL PRIMARY KEY,
|
||||
file_id INTEGER REFERENCES files(id) ON DELETE CASCADE,
|
||||
job_no VARCHAR(50) REFERENCES jobs(job_no),
|
||||
exported_by INTEGER REFERENCES users(user_id),
|
||||
export_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
export_type VARCHAR(50), -- 'full', 'category', 'filtered'
|
||||
category VARCHAR(50), -- PIPE, FLANGE, VALVE 등
|
||||
material_count INTEGER,
|
||||
file_name VARCHAR(255),
|
||||
notes TEXT,
|
||||
-- 메타데이터
|
||||
filters_applied JSONB, -- 적용된 필터 조건들
|
||||
export_options JSONB -- 내보내기 옵션들
|
||||
);
|
||||
|
||||
-- 2. 내보낸 자재 상세 (어떤 자재들이 내보내졌는지 추적)
|
||||
CREATE TABLE IF NOT EXISTS exported_materials (
|
||||
id SERIAL PRIMARY KEY,
|
||||
export_id INTEGER REFERENCES excel_export_history(export_id) ON DELETE CASCADE,
|
||||
material_id INTEGER REFERENCES materials(id),
|
||||
purchase_status VARCHAR(50) DEFAULT 'pending', -- pending, requested, ordered, received, cancelled
|
||||
purchase_request_no VARCHAR(100), -- 구매요청 번호
|
||||
purchase_order_no VARCHAR(100), -- 구매주문 번호
|
||||
requested_date TIMESTAMP,
|
||||
ordered_date TIMESTAMP,
|
||||
expected_date DATE,
|
||||
received_date TIMESTAMP,
|
||||
quantity_exported INTEGER, -- 내보낸 수량
|
||||
quantity_ordered INTEGER, -- 주문 수량
|
||||
quantity_received INTEGER, -- 입고 수량
|
||||
unit_price DECIMAL(15, 2),
|
||||
total_price DECIMAL(15, 2),
|
||||
vendor_name VARCHAR(255),
|
||||
notes TEXT,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_by INTEGER REFERENCES users(user_id)
|
||||
);
|
||||
|
||||
-- 3. 구매 상태 이력 (상태 변경 추적)
|
||||
CREATE TABLE IF NOT EXISTS purchase_status_history (
|
||||
history_id SERIAL PRIMARY KEY,
|
||||
exported_material_id INTEGER REFERENCES exported_materials(id) ON DELETE CASCADE,
|
||||
material_id INTEGER REFERENCES materials(id),
|
||||
previous_status VARCHAR(50),
|
||||
new_status VARCHAR(50),
|
||||
changed_by INTEGER REFERENCES users(user_id),
|
||||
changed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
reason TEXT,
|
||||
metadata JSONB -- 추가 정보 (예: 문서 번호, 승인자 등)
|
||||
);
|
||||
|
||||
-- 4. 구매 문서 관리
|
||||
CREATE TABLE IF NOT EXISTS purchase_documents (
|
||||
document_id SERIAL PRIMARY KEY,
|
||||
export_id INTEGER REFERENCES excel_export_history(export_id),
|
||||
document_type VARCHAR(50), -- 'purchase_request', 'purchase_order', 'invoice', 'receipt'
|
||||
document_no VARCHAR(100),
|
||||
document_date DATE,
|
||||
file_path VARCHAR(500),
|
||||
uploaded_by INTEGER REFERENCES users(user_id),
|
||||
uploaded_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
notes TEXT
|
||||
);
|
||||
|
||||
-- 인덱스 추가
|
||||
CREATE INDEX IF NOT EXISTS idx_export_history_file_id ON excel_export_history(file_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_export_history_job_no ON excel_export_history(job_no);
|
||||
CREATE INDEX IF NOT EXISTS idx_export_history_date ON excel_export_history(export_date);
|
||||
CREATE INDEX IF NOT EXISTS idx_exported_materials_export_id ON exported_materials(export_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_exported_materials_material_id ON exported_materials(material_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_exported_materials_status ON exported_materials(purchase_status);
|
||||
CREATE INDEX IF NOT EXISTS idx_exported_materials_pr_no ON exported_materials(purchase_request_no);
|
||||
CREATE INDEX IF NOT EXISTS idx_exported_materials_po_no ON exported_materials(purchase_order_no);
|
||||
CREATE INDEX IF NOT EXISTS idx_purchase_history_material ON purchase_status_history(material_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_purchase_history_date ON purchase_status_history(changed_at);
|
||||
|
||||
-- 뷰 생성: 구매 상태별 자재 현황
|
||||
CREATE OR REPLACE VIEW v_purchase_status_summary AS
|
||||
SELECT
|
||||
em.purchase_status,
|
||||
COUNT(DISTINCT em.material_id) as material_count,
|
||||
COUNT(DISTINCT em.export_id) as export_count,
|
||||
SUM(em.quantity_exported) as total_quantity_exported,
|
||||
SUM(em.quantity_ordered) as total_quantity_ordered,
|
||||
SUM(em.quantity_received) as total_quantity_received,
|
||||
SUM(em.total_price) as total_amount,
|
||||
MAX(em.updated_at) as last_updated
|
||||
FROM exported_materials em
|
||||
GROUP BY em.purchase_status;
|
||||
|
||||
-- 뷰 생성: 자재별 최신 구매 상태
|
||||
CREATE OR REPLACE VIEW v_material_latest_purchase_status AS
|
||||
SELECT DISTINCT ON (m.id)
|
||||
m.id as material_id,
|
||||
m.original_description,
|
||||
m.classified_category,
|
||||
em.purchase_status,
|
||||
em.purchase_request_no,
|
||||
em.purchase_order_no,
|
||||
em.vendor_name,
|
||||
em.expected_date,
|
||||
em.quantity_ordered,
|
||||
em.quantity_received,
|
||||
em.updated_at as status_updated_at,
|
||||
eeh.export_date as last_exported_date
|
||||
FROM materials m
|
||||
LEFT JOIN exported_materials em ON m.id = em.material_id
|
||||
LEFT JOIN excel_export_history eeh ON em.export_id = eeh.export_id
|
||||
ORDER BY m.id, em.updated_at DESC;
|
||||
|
||||
-- 트리거: updated_at 자동 업데이트
|
||||
CREATE OR REPLACE FUNCTION update_exported_materials_updated_at()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
NEW.updated_at = CURRENT_TIMESTAMP;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
CREATE TRIGGER update_exported_materials_updated_at_trigger
|
||||
BEFORE UPDATE ON exported_materials
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION update_exported_materials_updated_at();
|
||||
|
||||
-- 코멘트 추가
|
||||
COMMENT ON TABLE excel_export_history IS '엑셀 내보내기 이력 관리';
|
||||
COMMENT ON TABLE exported_materials IS '내보낸 자재의 구매 상태 추적';
|
||||
COMMENT ON TABLE purchase_status_history IS '구매 상태 변경 이력';
|
||||
COMMENT ON TABLE purchase_documents IS '구매 관련 문서 관리';
|
||||
COMMENT ON COLUMN exported_materials.purchase_status IS 'pending: 구매신청 전, requested: 구매신청, ordered: 구매주문, received: 입고완료, cancelled: 취소';
|
||||
44
backend/scripts/legacy/28_add_purchase_requests.sql
Normal file
44
backend/scripts/legacy/28_add_purchase_requests.sql
Normal file
@@ -0,0 +1,44 @@
|
||||
-- 구매신청 관리 테이블
|
||||
|
||||
-- 구매신청 그룹 (같이 신청한 항목들의 묶음)
|
||||
CREATE TABLE IF NOT EXISTS purchase_requests (
|
||||
request_id SERIAL PRIMARY KEY,
|
||||
request_no VARCHAR(50) UNIQUE, -- PR-20241014-001 형식
|
||||
file_id INTEGER REFERENCES files(id),
|
||||
job_no VARCHAR(50) REFERENCES jobs(job_no),
|
||||
category VARCHAR(50),
|
||||
material_count INTEGER,
|
||||
excel_file_path VARCHAR(500), -- 저장된 엑셀 파일 경로
|
||||
requested_by INTEGER REFERENCES users(user_id),
|
||||
requested_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
status VARCHAR(20) DEFAULT 'requested', -- requested, ordered, received
|
||||
notes TEXT
|
||||
);
|
||||
|
||||
-- 구매신청 자재 상세
|
||||
CREATE TABLE IF NOT EXISTS purchase_request_items (
|
||||
item_id SERIAL PRIMARY KEY,
|
||||
request_id INTEGER REFERENCES purchase_requests(request_id) ON DELETE CASCADE,
|
||||
material_id INTEGER REFERENCES materials(id),
|
||||
quantity INTEGER,
|
||||
unit VARCHAR(20),
|
||||
user_requirement TEXT,
|
||||
is_ordered BOOLEAN DEFAULT FALSE,
|
||||
is_received BOOLEAN DEFAULT FALSE,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
-- 인덱스
|
||||
CREATE INDEX IF NOT EXISTS idx_purchase_requests_file_id ON purchase_requests(file_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_purchase_requests_job_no ON purchase_requests(job_no);
|
||||
CREATE INDEX IF NOT EXISTS idx_purchase_requests_status ON purchase_requests(status);
|
||||
CREATE INDEX IF NOT EXISTS idx_purchase_request_items_request_id ON purchase_request_items(request_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_purchase_request_items_material_id ON purchase_request_items(material_id);
|
||||
|
||||
-- 뷰: 구매신청된 자재 ID 목록
|
||||
CREATE OR REPLACE VIEW v_requested_material_ids AS
|
||||
SELECT DISTINCT material_id
|
||||
FROM purchase_request_items;
|
||||
|
||||
COMMENT ON TABLE purchase_requests IS '구매신청 그룹 관리';
|
||||
COMMENT ON TABLE purchase_request_items IS '구매신청 자재 상세';
|
||||
20
backend/scripts/legacy/29_add_revision_status.sql
Normal file
20
backend/scripts/legacy/29_add_revision_status.sql
Normal file
@@ -0,0 +1,20 @@
|
||||
-- 리비전 관리 개선: 자재 상태 추적
|
||||
-- 리비전 업로드 시 삭제된 자재의 상태를 추적
|
||||
|
||||
-- materials 테이블에 revision_status 컬럼 추가
|
||||
ALTER TABLE materials ADD COLUMN IF NOT EXISTS revision_status VARCHAR(20) DEFAULT 'active';
|
||||
-- 가능한 값: 'active', 'inventory', 'deleted_not_purchased', 'changed'
|
||||
|
||||
-- revision_status 설명:
|
||||
-- 'active': 정상 활성 자재 (기본값)
|
||||
-- 'inventory': 재고품 (구매신청 후 리비전에서 삭제됨 - 연노랑색 표시)
|
||||
-- 'deleted_not_purchased': 구매신청 전 삭제됨 (숨김 처리)
|
||||
-- 'changed': 변경된 자재 (추가 구매 필요)
|
||||
|
||||
-- 인덱스 추가 (성능 최적화)
|
||||
CREATE INDEX IF NOT EXISTS idx_materials_revision_status ON materials(revision_status);
|
||||
CREATE INDEX IF NOT EXISTS idx_materials_drawing_name ON materials(drawing_name);
|
||||
CREATE INDEX IF NOT EXISTS idx_materials_line_no ON materials(line_no);
|
||||
|
||||
COMMENT ON COLUMN materials.revision_status IS '리비전 자재 상태: active(활성), inventory(재고품), deleted_not_purchased(삭제됨), changed(변경됨)';
|
||||
|
||||
237
backend/scripts/legacy/PRODUCTION_MIGRATION.sql
Normal file
237
backend/scripts/legacy/PRODUCTION_MIGRATION.sql
Normal file
@@ -0,0 +1,237 @@
|
||||
-- ================================
|
||||
-- TK-MP-Project 메인 서버 배포용 마이그레이션
|
||||
-- 생성일: 2025.09.28
|
||||
-- 목적: 개발 중 추가된 필수 컬럼들을 메인 서버에 적용
|
||||
-- ================================
|
||||
|
||||
-- 1. materials 테이블 필수 컬럼 추가
|
||||
-- ================================
|
||||
|
||||
-- 파이프 사이즈 정보
|
||||
ALTER TABLE materials ADD COLUMN IF NOT EXISTS main_nom VARCHAR(50);
|
||||
ALTER TABLE materials ADD COLUMN IF NOT EXISTS red_nom VARCHAR(50);
|
||||
|
||||
-- 전체 재질명
|
||||
ALTER TABLE materials ADD COLUMN IF NOT EXISTS full_material_grade TEXT;
|
||||
|
||||
-- 업로드 시 행 번호 추적
|
||||
ALTER TABLE materials ADD COLUMN IF NOT EXISTS row_number INTEGER;
|
||||
|
||||
-- 해시값 (구매 추적용)
|
||||
ALTER TABLE materials ADD COLUMN IF NOT EXISTS material_hash VARCHAR(64);
|
||||
|
||||
-- 검증 정보
|
||||
ALTER TABLE materials ADD COLUMN IF NOT EXISTS verified_by VARCHAR(100);
|
||||
ALTER TABLE materials ADD COLUMN IF NOT EXISTS verified_at TIMESTAMP;
|
||||
|
||||
-- 분류 상세 정보 (이미 있을 수 있지만 확인)
|
||||
ALTER TABLE materials ADD COLUMN IF NOT EXISTS classified_subcategory VARCHAR(100);
|
||||
ALTER TABLE materials ADD COLUMN IF NOT EXISTS schedule VARCHAR(20);
|
||||
ALTER TABLE materials ADD COLUMN IF NOT EXISTS drawing_name VARCHAR(100);
|
||||
ALTER TABLE materials ADD COLUMN IF NOT EXISTS area_code VARCHAR(20);
|
||||
ALTER TABLE materials ADD COLUMN IF NOT EXISTS line_no VARCHAR(50);
|
||||
|
||||
-- 2. files 테이블 필수 컬럼 추가
|
||||
-- ================================
|
||||
|
||||
-- 프로젝트 연결 정보
|
||||
ALTER TABLE files ADD COLUMN IF NOT EXISTS job_no VARCHAR(50);
|
||||
ALTER TABLE files ADD COLUMN IF NOT EXISTS bom_name VARCHAR(255);
|
||||
ALTER TABLE files ADD COLUMN IF NOT EXISTS description TEXT;
|
||||
ALTER TABLE files ADD COLUMN IF NOT EXISTS parsed_count INTEGER DEFAULT 0;
|
||||
|
||||
-- 3. material_purchase_tracking 테이블 컬럼 추가
|
||||
-- ================================
|
||||
|
||||
-- 구매 상태 및 설명
|
||||
ALTER TABLE material_purchase_tracking
|
||||
ADD COLUMN IF NOT EXISTS purchase_status VARCHAR(20) DEFAULT 'pending',
|
||||
ADD COLUMN IF NOT EXISTS description TEXT;
|
||||
|
||||
-- 4. user_requirements 테이블 컬럼 추가
|
||||
-- ================================
|
||||
|
||||
-- 자재별 요구사항 연결
|
||||
ALTER TABLE user_requirements ADD COLUMN IF NOT EXISTS material_id INTEGER;
|
||||
|
||||
-- 5. 성능 최적화 인덱스 추가
|
||||
-- ================================
|
||||
|
||||
-- materials 테이블 인덱스
|
||||
CREATE INDEX IF NOT EXISTS idx_materials_main_nom ON materials(main_nom);
|
||||
CREATE INDEX IF NOT EXISTS idx_materials_red_nom ON materials(red_nom);
|
||||
CREATE INDEX IF NOT EXISTS idx_materials_main_red_nom ON materials(main_nom, red_nom);
|
||||
CREATE INDEX IF NOT EXISTS idx_materials_full_material_grade ON materials(full_material_grade);
|
||||
CREATE INDEX IF NOT EXISTS idx_materials_material_hash ON materials(material_hash);
|
||||
CREATE INDEX IF NOT EXISTS idx_materials_verified_by ON materials(verified_by);
|
||||
CREATE INDEX IF NOT EXISTS idx_materials_classified_subcategory ON materials(classified_subcategory);
|
||||
CREATE INDEX IF NOT EXISTS idx_materials_schedule ON materials(schedule);
|
||||
|
||||
-- files 테이블 인덱스
|
||||
CREATE INDEX IF NOT EXISTS idx_files_job_no ON files(job_no);
|
||||
|
||||
-- user_requirements 테이블 인덱스
|
||||
CREATE INDEX IF NOT EXISTS idx_user_requirements_material_id ON user_requirements(material_id);
|
||||
|
||||
-- fitting_details 테이블 분리 스케줄 컬럼 추가
|
||||
ALTER TABLE fitting_details ADD COLUMN IF NOT EXISTS main_schedule VARCHAR(20);
|
||||
ALTER TABLE fitting_details ADD COLUMN IF NOT EXISTS red_schedule VARCHAR(20);
|
||||
ALTER TABLE fitting_details ADD COLUMN IF NOT EXISTS has_different_schedules BOOLEAN DEFAULT FALSE;
|
||||
|
||||
-- fitting_details 분리 스케줄 인덱스
|
||||
CREATE INDEX IF NOT EXISTS idx_fitting_details_main_schedule ON fitting_details(main_schedule);
|
||||
CREATE INDEX IF NOT EXISTS idx_fitting_details_red_schedule ON fitting_details(red_schedule);
|
||||
|
||||
-- 3. 컬럼 설명 추가
|
||||
-- ================================
|
||||
|
||||
COMMENT ON COLUMN materials.main_nom IS 'MAIN_NOM 필드 - 주 사이즈 (예: 4", 150A)';
|
||||
COMMENT ON COLUMN materials.red_nom IS 'RED_NOM 필드 - 축소 사이즈 (Reducing 피팅/플랜지용)';
|
||||
COMMENT ON COLUMN materials.full_material_grade IS '전체 재질명 (예: ASTM A312 TP304, ASTM A106 GR B 등)';
|
||||
COMMENT ON COLUMN materials.row_number IS '업로드 파일에서의 행 번호 (디버깅용)';
|
||||
|
||||
-- 6. support_details 테이블 생성 (SUPPORT 카테고리용)
|
||||
-- ================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS support_details (
|
||||
id SERIAL PRIMARY KEY,
|
||||
material_id INTEGER NOT NULL REFERENCES materials(id) ON DELETE CASCADE,
|
||||
file_id INTEGER NOT NULL REFERENCES files(id) ON DELETE CASCADE,
|
||||
|
||||
-- 서포트 타입 정보
|
||||
support_type VARCHAR(50), -- URETHANE_BLOCK, CLAMP, HANGER, SPRING_HANGER 등
|
||||
support_subtype VARCHAR(100), -- 상세 타입
|
||||
|
||||
-- 하중 정보
|
||||
load_rating VARCHAR(20), -- LIGHT, MEDIUM, HEAVY, CUSTOM
|
||||
load_capacity VARCHAR(20), -- 40T, 50TON 등
|
||||
|
||||
-- 재질 정보
|
||||
material_standard VARCHAR(50), -- 재질 표준
|
||||
material_grade VARCHAR(100), -- 재질 등급
|
||||
|
||||
-- 사이즈 정보
|
||||
pipe_size VARCHAR(20), -- 지지하는 파이프 크기
|
||||
length_mm DECIMAL(10,2), -- 길이 (mm)
|
||||
width_mm DECIMAL(10,2), -- 폭 (mm)
|
||||
height_mm DECIMAL(10,2), -- 높이 (mm)
|
||||
|
||||
-- 분류 신뢰도
|
||||
classification_confidence DECIMAL(3,2), -- 0.00-1.00
|
||||
|
||||
-- 메타데이터
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
-- support_details 인덱스
|
||||
CREATE INDEX IF NOT EXISTS idx_support_details_material_id ON support_details(material_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_support_details_file_id ON support_details(file_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_support_details_support_type ON support_details(support_type);
|
||||
|
||||
-- 8. SPECIAL 카테고리 지원 추가
|
||||
-- ================================
|
||||
|
||||
-- SPECIAL 카테고리 관련 인덱스 추가 (성능 최적화)
|
||||
CREATE INDEX IF NOT EXISTS idx_materials_special_category
|
||||
ON materials(classified_category)
|
||||
WHERE classified_category = 'SPECIAL';
|
||||
|
||||
-- SPECIAL 키워드 패턴 테이블
|
||||
CREATE TABLE IF NOT EXISTS special_classification_patterns (
|
||||
id SERIAL PRIMARY KEY,
|
||||
pattern_type VARCHAR(20) NOT NULL, -- 'KEYWORD', 'REGEX', 'EXACT'
|
||||
pattern_value VARCHAR(200) NOT NULL,
|
||||
description TEXT,
|
||||
priority INTEGER DEFAULT 1, -- 우선순위 (낮을수록 높은 우선순위)
|
||||
is_active BOOLEAN DEFAULT TRUE,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
-- 기본 SPECIAL 키워드 패턴 삽입
|
||||
INSERT INTO special_classification_patterns (pattern_type, pattern_value, description, priority) VALUES
|
||||
('KEYWORD', 'SPECIAL', '영문 SPECIAL 키워드', 1),
|
||||
('KEYWORD', '스페셜', '한글 스페셜 키워드', 1),
|
||||
('KEYWORD', 'SPEC', '영문 SPEC 축약어', 2),
|
||||
('KEYWORD', 'SPL', '영문 SPL 축약어', 2)
|
||||
ON CONFLICT DO NOTHING;
|
||||
|
||||
-- SPECIAL 자재 상세 정보 테이블 (도면 업로드 관련)
|
||||
CREATE TABLE IF NOT EXISTS special_material_details (
|
||||
id SERIAL PRIMARY KEY,
|
||||
material_id INTEGER REFERENCES materials(id) ON DELETE CASCADE,
|
||||
file_id INTEGER REFERENCES files(id) ON DELETE CASCADE,
|
||||
|
||||
-- 도면 정보
|
||||
drawing_number VARCHAR(100), -- 도면 번호
|
||||
drawing_revision VARCHAR(20), -- 도면 리비전
|
||||
drawing_uploaded BOOLEAN DEFAULT FALSE, -- 도면 업로드 여부
|
||||
drawing_file_path TEXT, -- 도면 파일 경로
|
||||
|
||||
-- 특수 요구사항
|
||||
special_requirements TEXT, -- 특수 제작 요구사항
|
||||
manufacturing_notes TEXT, -- 제작 참고사항
|
||||
approval_required BOOLEAN DEFAULT TRUE, -- 승인 필요 여부
|
||||
approved_by VARCHAR(100), -- 승인자
|
||||
approved_at TIMESTAMP, -- 승인 일시
|
||||
|
||||
-- 분류 정보
|
||||
classification_confidence FLOAT DEFAULT 1.0,
|
||||
classification_reason TEXT, -- 분류 근거
|
||||
|
||||
-- 관리 정보
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
-- SPECIAL 관련 인덱스
|
||||
CREATE INDEX IF NOT EXISTS idx_special_patterns_type ON special_classification_patterns(pattern_type);
|
||||
CREATE INDEX IF NOT EXISTS idx_special_patterns_active ON special_classification_patterns(is_active);
|
||||
CREATE INDEX IF NOT EXISTS idx_special_details_material ON special_material_details(material_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_special_details_drawing_uploaded ON special_material_details(drawing_uploaded);
|
||||
|
||||
-- 기존 자재 중 SPECIAL 키워드가 포함된 자재를 SPECIAL 카테고리로 재분류
|
||||
UPDATE materials
|
||||
SET
|
||||
classified_category = 'SPECIAL',
|
||||
classification_confidence = 1.0,
|
||||
classified_at = CURRENT_TIMESTAMP
|
||||
WHERE
|
||||
(
|
||||
UPPER(original_description) LIKE '%SPECIAL%' OR
|
||||
UPPER(original_description) LIKE '%스페셜%' OR
|
||||
UPPER(original_description) LIKE '%SPEC%' OR
|
||||
UPPER(original_description) LIKE '%SPL%'
|
||||
)
|
||||
AND (classified_category IS NULL OR classified_category != 'SPECIAL');
|
||||
|
||||
-- 7. 기존 데이터 정리 (선택사항)
|
||||
-- ================================
|
||||
|
||||
-- 기존 데이터에 기본값 설정 (필요시 주석 해제)
|
||||
-- UPDATE materials SET main_nom = '', red_nom = '', full_material_grade = ''
|
||||
-- WHERE main_nom IS NULL OR red_nom IS NULL OR full_material_grade IS NULL;
|
||||
|
||||
-- ================================
|
||||
-- 마이그레이션 완료 확인
|
||||
-- ================================
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
-- 컬럼 존재 확인
|
||||
IF EXISTS (
|
||||
SELECT 1 FROM information_schema.columns
|
||||
WHERE table_name = 'materials'
|
||||
AND column_name IN ('main_nom', 'red_nom', 'full_material_grade', 'row_number')
|
||||
GROUP BY table_name
|
||||
HAVING COUNT(*) = 4
|
||||
) THEN
|
||||
RAISE NOTICE '✅ TK-MP-Project 메인 서버 마이그레이션 완료!';
|
||||
RAISE NOTICE '📋 추가된 컬럼: main_nom, red_nom, full_material_grade, row_number';
|
||||
RAISE NOTICE '🔍 추가된 인덱스: 4개 (성능 최적화)';
|
||||
RAISE NOTICE '🚀 파일 업로드 기능 정상 작동 가능';
|
||||
ELSE
|
||||
RAISE NOTICE '❌ 마이그레이션 실패 - 일부 컬럼이 생성되지 않았습니다.';
|
||||
END IF;
|
||||
END $$;
|
||||
113
backend/scripts/legacy/auto_migrate.py
Normal file
113
backend/scripts/legacy/auto_migrate.py
Normal file
@@ -0,0 +1,113 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
TK-MP-Project 자동 DB 마이그레이션 스크립트
|
||||
배포 시 백엔드 시작 전에 자동으로 실행되어 DB 스키마를 동기화
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
import time
|
||||
from datetime import datetime
|
||||
|
||||
# 백엔드 모듈 경로 추가
|
||||
sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
|
||||
|
||||
def wait_for_db():
|
||||
"""데이터베이스 연결 대기"""
|
||||
max_retries = 30
|
||||
retry_count = 0
|
||||
|
||||
while retry_count < max_retries:
|
||||
try:
|
||||
from app.database import engine
|
||||
# 간단한 연결 테스트
|
||||
with engine.connect() as conn:
|
||||
conn.execute("SELECT 1")
|
||||
print("✅ 데이터베이스 연결 성공")
|
||||
return True
|
||||
except Exception as e:
|
||||
retry_count += 1
|
||||
print(f"⏳ 데이터베이스 연결 대기 중... ({retry_count}/{max_retries})")
|
||||
time.sleep(2)
|
||||
|
||||
print("❌ 데이터베이스 연결 실패")
|
||||
return False
|
||||
|
||||
def sync_database_schema():
|
||||
"""데이터베이스 스키마 동기화"""
|
||||
try:
|
||||
from app.models import Base
|
||||
from app.database import engine
|
||||
|
||||
print("🔄 데이터베이스 스키마 동기화 중...")
|
||||
|
||||
# 모든 테이블 생성/업데이트
|
||||
Base.metadata.create_all(bind=engine)
|
||||
|
||||
print("✅ 데이터베이스 스키마 동기화 완료")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ 스키마 동기화 실패: {str(e)}")
|
||||
return False
|
||||
|
||||
def ensure_required_data():
|
||||
"""필수 데이터 확인 및 생성"""
|
||||
try:
|
||||
from app.database import get_db
|
||||
from app.auth.models import User
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
print("🔄 필수 데이터 확인 중...")
|
||||
|
||||
db = next(get_db())
|
||||
|
||||
# 관리자 계정 확인
|
||||
admin_user = db.query(User).filter(User.username == 'admin').first()
|
||||
if not admin_user:
|
||||
print("📝 기본 관리자 계정 생성 중...")
|
||||
admin_user = User(
|
||||
username='admin',
|
||||
password='$2b$12$ld4LDOW5mxkiRQEkXfMUIep/aIzFleQZ4yoL10ZQkUxGqnkYuhNMW', # admin123
|
||||
name='시스템 관리자',
|
||||
email='admin@tkmp.com',
|
||||
role='admin',
|
||||
access_level='admin',
|
||||
department='IT',
|
||||
position='시스템 관리자',
|
||||
status='active'
|
||||
)
|
||||
db.add(admin_user)
|
||||
db.commit()
|
||||
print("✅ 기본 관리자 계정 생성 완료")
|
||||
|
||||
db.close()
|
||||
print("✅ 필수 데이터 확인 완료")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ 필수 데이터 확인 실패: {str(e)}")
|
||||
return False
|
||||
|
||||
def main():
|
||||
"""메인 실행 함수"""
|
||||
print("🚀 TK-MP-Project 자동 DB 마이그레이션 시작")
|
||||
print(f"⏰ 시작 시간: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
|
||||
|
||||
# 1. 데이터베이스 연결 대기
|
||||
if not wait_for_db():
|
||||
sys.exit(1)
|
||||
|
||||
# 2. 스키마 동기화
|
||||
if not sync_database_schema():
|
||||
sys.exit(1)
|
||||
|
||||
# 3. 필수 데이터 확인
|
||||
if not ensure_required_data():
|
||||
sys.exit(1)
|
||||
|
||||
print("🎉 자동 DB 마이그레이션 완료!")
|
||||
print(f"⏰ 완료 시간: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
19
backend/scripts/legacy/create_jobs.sql
Normal file
19
backend/scripts/legacy/create_jobs.sql
Normal file
@@ -0,0 +1,19 @@
|
||||
CREATE TABLE IF NOT EXISTS jobs (
|
||||
job_no VARCHAR(50) PRIMARY KEY,
|
||||
job_name VARCHAR(200) NOT NULL,
|
||||
client_name VARCHAR(100) NOT NULL,
|
||||
end_user VARCHAR(100),
|
||||
epc_company VARCHAR(100),
|
||||
project_site VARCHAR(200),
|
||||
contract_date DATE,
|
||||
delivery_date DATE,
|
||||
delivery_terms VARCHAR(100),
|
||||
status VARCHAR(20) DEFAULT '진행중',
|
||||
delivery_completed_date DATE,
|
||||
project_closed_date DATE,
|
||||
description TEXT,
|
||||
created_by VARCHAR(50),
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
is_active BOOLEAN DEFAULT true
|
||||
);
|
||||
237
backend/scripts/legacy/create_material_detail_tables.sql
Normal file
237
backend/scripts/legacy/create_material_detail_tables.sql
Normal file
@@ -0,0 +1,237 @@
|
||||
-- 1. FITTING 상세 테이블
|
||||
CREATE TABLE IF NOT EXISTS fitting_details (
|
||||
id SERIAL PRIMARY KEY,
|
||||
material_id INTEGER REFERENCES materials(id) ON DELETE CASCADE,
|
||||
file_id INTEGER REFERENCES files(id) ON DELETE CASCADE,
|
||||
|
||||
-- 피팅 타입 정보
|
||||
fitting_type VARCHAR(50),
|
||||
fitting_subtype VARCHAR(50),
|
||||
|
||||
-- 연결 방식
|
||||
connection_method VARCHAR(50),
|
||||
connection_code VARCHAR(50),
|
||||
|
||||
-- 압력 등급
|
||||
pressure_rating VARCHAR(50),
|
||||
max_pressure VARCHAR(50),
|
||||
|
||||
-- 제작 방법
|
||||
manufacturing_method VARCHAR(50),
|
||||
|
||||
-- 재질 정보
|
||||
material_standard VARCHAR(100),
|
||||
material_grade VARCHAR(100),
|
||||
material_type VARCHAR(50),
|
||||
|
||||
-- 사이즈 정보
|
||||
main_size VARCHAR(50),
|
||||
reduced_size VARCHAR(50),
|
||||
|
||||
-- 신뢰도
|
||||
classification_confidence FLOAT,
|
||||
|
||||
-- 추가 정보
|
||||
additional_info JSONB,
|
||||
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
-- 2. VALVE 상세 테이블
|
||||
CREATE TABLE IF NOT EXISTS valve_details (
|
||||
id SERIAL PRIMARY KEY,
|
||||
material_id INTEGER REFERENCES materials(id) ON DELETE CASCADE,
|
||||
file_id INTEGER REFERENCES files(id) ON DELETE CASCADE,
|
||||
|
||||
-- 밸브 타입 정보
|
||||
valve_type VARCHAR(50),
|
||||
valve_subtype VARCHAR(50),
|
||||
actuator_type VARCHAR(50),
|
||||
|
||||
-- 연결 방식
|
||||
connection_method VARCHAR(50),
|
||||
|
||||
-- 압력 등급
|
||||
pressure_rating VARCHAR(50),
|
||||
pressure_class VARCHAR(50),
|
||||
|
||||
-- 재질 정보
|
||||
body_material VARCHAR(100),
|
||||
trim_material VARCHAR(100),
|
||||
|
||||
-- 사이즈 정보
|
||||
size_inches VARCHAR(50),
|
||||
|
||||
-- 특수 사양
|
||||
fire_safe BOOLEAN,
|
||||
low_temp_service BOOLEAN,
|
||||
special_features JSONB,
|
||||
|
||||
-- 신뢰도
|
||||
classification_confidence FLOAT,
|
||||
|
||||
-- 추가 정보
|
||||
additional_info JSONB,
|
||||
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
-- 3. FLANGE 상세 테이블
|
||||
CREATE TABLE IF NOT EXISTS flange_details (
|
||||
id SERIAL PRIMARY KEY,
|
||||
material_id INTEGER REFERENCES materials(id) ON DELETE CASCADE,
|
||||
file_id INTEGER REFERENCES files(id) ON DELETE CASCADE,
|
||||
|
||||
-- 플랜지 타입
|
||||
flange_type VARCHAR(50),
|
||||
facing_type VARCHAR(50),
|
||||
|
||||
-- 압력 등급
|
||||
pressure_rating VARCHAR(50),
|
||||
|
||||
-- 재질 정보
|
||||
material_standard VARCHAR(100),
|
||||
material_grade VARCHAR(100),
|
||||
|
||||
-- 사이즈 정보
|
||||
size_inches VARCHAR(50),
|
||||
bolt_hole_count INTEGER,
|
||||
bolt_hole_size VARCHAR(50),
|
||||
|
||||
-- 신뢰도
|
||||
classification_confidence FLOAT,
|
||||
|
||||
-- 추가 정보
|
||||
additional_info JSONB,
|
||||
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
-- 4. BOLT 상세 테이블
|
||||
CREATE TABLE IF NOT EXISTS bolt_details (
|
||||
id SERIAL PRIMARY KEY,
|
||||
material_id INTEGER REFERENCES materials(id) ON DELETE CASCADE,
|
||||
file_id INTEGER REFERENCES files(id) ON DELETE CASCADE,
|
||||
|
||||
-- 볼트 타입
|
||||
bolt_type VARCHAR(50),
|
||||
thread_type VARCHAR(50),
|
||||
|
||||
-- 사양 정보
|
||||
diameter VARCHAR(50),
|
||||
length VARCHAR(50),
|
||||
|
||||
-- 재질 정보
|
||||
material_standard VARCHAR(100),
|
||||
material_grade VARCHAR(100),
|
||||
coating_type VARCHAR(100),
|
||||
|
||||
-- 너트/와셔 정보
|
||||
includes_nut BOOLEAN,
|
||||
includes_washer BOOLEAN,
|
||||
nut_type VARCHAR(50),
|
||||
washer_type VARCHAR(50),
|
||||
|
||||
-- 신뢰도
|
||||
classification_confidence FLOAT,
|
||||
|
||||
-- 추가 정보
|
||||
additional_info JSONB,
|
||||
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
-- 5. GASKET 상세 테이블
|
||||
CREATE TABLE IF NOT EXISTS gasket_details (
|
||||
id SERIAL PRIMARY KEY,
|
||||
material_id INTEGER REFERENCES materials(id) ON DELETE CASCADE,
|
||||
file_id INTEGER REFERENCES files(id) ON DELETE CASCADE,
|
||||
|
||||
-- 가스켓 타입
|
||||
gasket_type VARCHAR(50),
|
||||
gasket_subtype VARCHAR(50),
|
||||
|
||||
-- 재질 정보
|
||||
material_type VARCHAR(100),
|
||||
filler_material VARCHAR(100),
|
||||
|
||||
-- 사이즈 및 등급
|
||||
size_inches VARCHAR(50),
|
||||
pressure_rating VARCHAR(50),
|
||||
thickness VARCHAR(50),
|
||||
|
||||
-- 특수 사양
|
||||
temperature_range VARCHAR(100),
|
||||
fire_safe BOOLEAN,
|
||||
|
||||
-- 신뢰도
|
||||
classification_confidence FLOAT,
|
||||
|
||||
-- 추가 정보
|
||||
additional_info JSONB,
|
||||
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
-- 6. INSTRUMENT 상세 테이블
|
||||
CREATE TABLE IF NOT EXISTS instrument_details (
|
||||
id SERIAL PRIMARY KEY,
|
||||
material_id INTEGER REFERENCES materials(id) ON DELETE CASCADE,
|
||||
file_id INTEGER REFERENCES files(id) ON DELETE CASCADE,
|
||||
|
||||
-- 계장품 타입
|
||||
instrument_type VARCHAR(50),
|
||||
instrument_subtype VARCHAR(50),
|
||||
|
||||
-- 측정 사양
|
||||
measurement_type VARCHAR(50),
|
||||
measurement_range VARCHAR(100),
|
||||
accuracy VARCHAR(50),
|
||||
|
||||
-- 연결 정보
|
||||
connection_type VARCHAR(50),
|
||||
connection_size VARCHAR(50),
|
||||
|
||||
-- 재질 정보
|
||||
body_material VARCHAR(100),
|
||||
wetted_parts_material VARCHAR(100),
|
||||
|
||||
-- 전기 사양
|
||||
electrical_rating VARCHAR(100),
|
||||
output_signal VARCHAR(50),
|
||||
|
||||
-- 신뢰도
|
||||
classification_confidence FLOAT,
|
||||
|
||||
-- 추가 정보
|
||||
additional_info JSONB,
|
||||
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
-- 인덱스 생성
|
||||
CREATE INDEX idx_fitting_details_material_id ON fitting_details(material_id);
|
||||
CREATE INDEX idx_fitting_details_file_id ON fitting_details(file_id);
|
||||
CREATE INDEX idx_fitting_details_type ON fitting_details(fitting_type);
|
||||
|
||||
CREATE INDEX idx_valve_details_material_id ON valve_details(material_id);
|
||||
CREATE INDEX idx_valve_details_file_id ON valve_details(file_id);
|
||||
CREATE INDEX idx_valve_details_type ON valve_details(valve_type);
|
||||
|
||||
CREATE INDEX idx_flange_details_material_id ON flange_details(material_id);
|
||||
CREATE INDEX idx_flange_details_file_id ON flange_details(file_id);
|
||||
|
||||
CREATE INDEX idx_bolt_details_material_id ON bolt_details(material_id);
|
||||
CREATE INDEX idx_bolt_details_file_id ON bolt_details(file_id);
|
||||
|
||||
CREATE INDEX idx_gasket_details_material_id ON gasket_details(material_id);
|
||||
CREATE INDEX idx_gasket_details_file_id ON gasket_details(file_id);
|
||||
|
||||
CREATE INDEX idx_instrument_details_material_id ON instrument_details(material_id);
|
||||
CREATE INDEX idx_instrument_details_file_id ON instrument_details(file_id);
|
||||
144
backend/scripts/legacy/generate_complete_schema.py
Normal file
144
backend/scripts/legacy/generate_complete_schema.py
Normal file
@@ -0,0 +1,144 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
TK-MP-Project 완전한 DB 스키마 생성 스크립트
|
||||
백엔드 SQLAlchemy 모델을 기반으로 PostgreSQL 스키마를 자동 생성
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
from datetime import datetime
|
||||
|
||||
# 백엔드 모듈 경로 추가
|
||||
sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
|
||||
|
||||
from sqlalchemy import create_engine, MetaData
|
||||
from sqlalchemy.schema import CreateTable, CreateIndex
|
||||
from app.models import Base
|
||||
from app.auth.models import User, LoginLog, UserSession, Permission, RolePermission
|
||||
|
||||
def generate_schema_sql():
|
||||
"""SQLAlchemy 모델을 기반으로 완전한 PostgreSQL 스키마 생성"""
|
||||
|
||||
# 메모리 내 SQLite 엔진 생성 (스키마 생성용)
|
||||
engine = create_engine('sqlite:///:memory:', echo=False)
|
||||
|
||||
# 모든 테이블 생성
|
||||
Base.metadata.create_all(engine)
|
||||
|
||||
# PostgreSQL 방언으로 변환
|
||||
from sqlalchemy.dialects import postgresql
|
||||
|
||||
schema_lines = []
|
||||
schema_lines.append("-- ================================")
|
||||
schema_lines.append("-- TK-MP-Project 완전한 데이터베이스 스키마")
|
||||
schema_lines.append(f"-- 자동 생성일: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
|
||||
schema_lines.append("-- SQLAlchemy 모델 기반 자동 생성")
|
||||
schema_lines.append("-- ================================")
|
||||
schema_lines.append("")
|
||||
|
||||
# 테이블 생성 SQL 생성
|
||||
for table in Base.metadata.sorted_tables:
|
||||
create_table_sql = str(CreateTable(table).compile(dialect=postgresql.dialect()))
|
||||
|
||||
# SQLite -> PostgreSQL 변환
|
||||
create_table_sql = create_table_sql.replace('DATETIME', 'TIMESTAMP')
|
||||
create_table_sql = create_table_sql.replace('BOOLEAN', 'BOOLEAN')
|
||||
create_table_sql = create_table_sql.replace('TEXT', 'TEXT')
|
||||
create_table_sql = create_table_sql.replace('JSON', 'JSONB')
|
||||
|
||||
schema_lines.append(f"-- {table.name} 테이블")
|
||||
schema_lines.append(f"CREATE TABLE IF NOT EXISTS {create_table_sql[13:]};") # "CREATE TABLE " 제거
|
||||
schema_lines.append("")
|
||||
|
||||
# 인덱스 생성
|
||||
schema_lines.append("-- ================================")
|
||||
schema_lines.append("-- 인덱스 생성")
|
||||
schema_lines.append("-- ================================")
|
||||
schema_lines.append("")
|
||||
|
||||
for table in Base.metadata.sorted_tables:
|
||||
for index in table.indexes:
|
||||
create_index_sql = str(CreateIndex(index).compile(dialect=postgresql.dialect()))
|
||||
schema_lines.append(f"CREATE INDEX IF NOT EXISTS {create_index_sql[13:]};") # "CREATE INDEX " 제거
|
||||
|
||||
# 필수 데이터 삽입
|
||||
schema_lines.append("")
|
||||
schema_lines.append("-- ================================")
|
||||
schema_lines.append("-- 필수 기본 데이터 삽입")
|
||||
schema_lines.append("-- ================================")
|
||||
schema_lines.append("")
|
||||
|
||||
# 기본 관리자 계정
|
||||
schema_lines.append("-- 기본 관리자 계정 (비밀번호: admin123)")
|
||||
schema_lines.append("INSERT INTO users (username, password, name, email, role, access_level, department, position, status) VALUES")
|
||||
schema_lines.append("('admin', '$2b$12$ld4LDOW5mxkiRQEkXfMUIep/aIzFleQZ4yoL10ZQkUxGqnkYuhNMW', '시스템 관리자', 'admin@tkmp.com', 'admin', 'admin', 'IT', '시스템 관리자', 'active'),")
|
||||
schema_lines.append("('system', '$2b$12$ld4LDOW5mxkiRQEkXfMUIep/aIzFleQZ4yoL10ZQkUxGqnkYuhNMW', '시스템 계정', 'system@tkmp.com', 'system', 'system', 'IT', '시스템 계정', 'active')")
|
||||
schema_lines.append("ON CONFLICT (username) DO NOTHING;")
|
||||
schema_lines.append("")
|
||||
|
||||
# 기본 권한 데이터
|
||||
schema_lines.append("-- 기본 권한 데이터")
|
||||
permissions = [
|
||||
('bom.view', 'BOM 조회 권한', 'bom'),
|
||||
('bom.create', 'BOM 생성 권한', 'bom'),
|
||||
('bom.edit', 'BOM 수정 권한', 'bom'),
|
||||
('bom.delete', 'BOM 삭제 권한', 'bom'),
|
||||
('project.view', '프로젝트 조회 권한', 'project'),
|
||||
('project.create', '프로젝트 생성 권한', 'project'),
|
||||
('project.edit', '프로젝트 수정 권한', 'project'),
|
||||
('file.upload', '파일 업로드 권한', 'file'),
|
||||
('file.download', '파일 다운로드 권한', 'file'),
|
||||
('user.view', '사용자 조회 권한', 'user'),
|
||||
('user.create', '사용자 생성 권한', 'user'),
|
||||
('system.admin', '시스템 관리 권한', 'system')
|
||||
]
|
||||
|
||||
schema_lines.append("INSERT INTO permissions (permission_name, description, module) VALUES")
|
||||
for i, (name, desc, module) in enumerate(permissions):
|
||||
comma = "," if i < len(permissions) - 1 else ""
|
||||
schema_lines.append(f"('{name}', '{desc}', '{module}'){comma}")
|
||||
schema_lines.append("ON CONFLICT (permission_name) DO NOTHING;")
|
||||
schema_lines.append("")
|
||||
|
||||
# 완료 메시지
|
||||
schema_lines.append("-- ================================")
|
||||
schema_lines.append("-- 스키마 생성 완료")
|
||||
schema_lines.append("-- ================================")
|
||||
schema_lines.append("")
|
||||
schema_lines.append("DO $$")
|
||||
schema_lines.append("BEGIN")
|
||||
schema_lines.append(" RAISE NOTICE '✅ TK-MP-Project 완전한 데이터베이스 스키마가 성공적으로 생성되었습니다!';")
|
||||
schema_lines.append(" RAISE NOTICE '👤 기본 계정: admin/admin123, system/admin123';")
|
||||
schema_lines.append(" RAISE NOTICE '🔐 권한 시스템: 모듈별 세분화된 권한 적용';")
|
||||
schema_lines.append(" RAISE NOTICE '📊 자동 생성: SQLAlchemy 모델 기반 완전 동기화';")
|
||||
schema_lines.append("END $$;")
|
||||
|
||||
return '\n'.join(schema_lines)
|
||||
|
||||
def main():
|
||||
"""메인 실행 함수"""
|
||||
try:
|
||||
print("🔄 SQLAlchemy 모델 분석 중...")
|
||||
schema_sql = generate_schema_sql()
|
||||
|
||||
# 스키마 파일 저장
|
||||
output_file = os.path.join(os.path.dirname(__file__), '..', '..', 'database', 'init', '00_auto_generated_schema.sql')
|
||||
os.makedirs(os.path.dirname(output_file), exist_ok=True)
|
||||
|
||||
with open(output_file, 'w', encoding='utf-8') as f:
|
||||
f.write(schema_sql)
|
||||
|
||||
print(f"✅ 완전한 DB 스키마가 생성되었습니다: {output_file}")
|
||||
print("📋 포함된 내용:")
|
||||
print(" - 모든 SQLAlchemy 모델 기반 테이블")
|
||||
print(" - 필수 인덱스")
|
||||
print(" - 기본 관리자 계정")
|
||||
print(" - 기본 권한 데이터")
|
||||
print("🚀 배포 시 이 파일이 자동으로 실행됩니다.")
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ 스키마 생성 실패: {str(e)}")
|
||||
sys.exit(1)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
73
backend/scripts/legacy/setup_database.py
Normal file
73
backend/scripts/legacy/setup_database.py
Normal file
@@ -0,0 +1,73 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
전체 데이터베이스 설정 및 더미 데이터 생성
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
from sqlalchemy import create_engine, text
|
||||
|
||||
# 프로젝트 루트를 Python path에 추가
|
||||
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
|
||||
def setup_database():
|
||||
"""데이터베이스 전체 설정"""
|
||||
|
||||
try:
|
||||
from app.database import engine
|
||||
print("✅ 기존 데이터베이스 연결 사용")
|
||||
except ImportError:
|
||||
# 개발용 직접 연결
|
||||
DATABASE_URL = "sqlite:///./test.db"
|
||||
engine = create_engine(DATABASE_URL)
|
||||
print("⚠️ 개발용 SQLite 연결")
|
||||
|
||||
try:
|
||||
with engine.connect() as conn:
|
||||
print("🏗️ 1단계: jobs 테이블 생성...")
|
||||
|
||||
# jobs 테이블 생성 SQL 실행
|
||||
with open('scripts/01_create_jobs_table.sql', 'r', encoding='utf-8') as f:
|
||||
sql_commands = f.read().split(';')
|
||||
for command in sql_commands:
|
||||
command = command.strip()
|
||||
if command:
|
||||
conn.execute(text(command))
|
||||
|
||||
print("✅ jobs 테이블 생성 완료")
|
||||
|
||||
print("🔧 2단계: files 테이블 수정...")
|
||||
|
||||
# files 테이블 수정 (선택적)
|
||||
try:
|
||||
with open('scripts/02_modify_files_table.sql', 'r', encoding='utf-8') as f:
|
||||
sql_commands = f.read().split(';')
|
||||
for command in sql_commands:
|
||||
command = command.strip()
|
||||
if command and not command.startswith('--'):
|
||||
conn.execute(text(command))
|
||||
print("✅ files 테이블 수정 완료")
|
||||
except Exception as e:
|
||||
print(f"⚠️ files 테이블 수정 건너뜀: {e}")
|
||||
|
||||
# 커밋
|
||||
conn.commit()
|
||||
|
||||
print("🎯 3단계: 더미 데이터 생성...")
|
||||
# 더미 데이터 스크립트 실행
|
||||
exec(open('scripts/03_insert_dummy_data.py').read())
|
||||
|
||||
print("\n🎉 전체 설정 완료!")
|
||||
print("\n📋 다음 단계:")
|
||||
print(" 1. API 서버 실행")
|
||||
print(" 2. GET /jobs 엔드포인트 테스트")
|
||||
print(" 3. Job 선택 후 BOM 파일 업로드")
|
||||
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ 데이터베이스 설정 실패: {e}")
|
||||
return False
|
||||
|
||||
if __name__ == "__main__":
|
||||
setup_database()
|
||||
Reference in New Issue
Block a user