feat: 3-System 분리 프로젝트 초기 코드 작성

TK-FB(공장관리+신고)와 M-Project(부적합관리)를 3개 독립 시스템으로
분리하기 위한 전체 코드 구조 작성.
- SSO 인증 서비스 (bcrypt + pbkdf2 이중 해시 지원)
- System 1: 공장관리 (TK-FB 기반, 신고 코드 제거)
- System 2: 신고 (TK-FB에서 workIssue 코드 추출)
- System 3: 부적합관리 (M-Project 기반)
- Gateway 포털 (path-based 라우팅)
- 통합 docker-compose.yml 및 배포 스크립트

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Hyungi Ahn
2026-02-09 14:40:11 +09:00
commit 550633b89d
824 changed files with 1071683 additions and 0 deletions

View File

@@ -0,0 +1,223 @@
-- 016_add_management_fields.sql
-- 관리함에서 사용할 추가 필드들과 완료 사진 업로드 기능 추가
BEGIN;
DO $$
DECLARE
migration_name VARCHAR(255) := '016_add_management_fields.sql';
migration_notes TEXT := '관리함 필드 추가: 원인/해결방안, 담당부서/담당자, 조치예상일, 완료확인일, 원인부서, 의견, 완료사진, 프로젝트별 No 등';
current_status VARCHAR(50);
BEGIN
-- migration_log 테이블이 없으면 생성 (멱등성)
CREATE TABLE IF NOT EXISTS migration_log (
id SERIAL PRIMARY KEY,
migration_file VARCHAR(255) NOT NULL UNIQUE,
executed_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
status VARCHAR(50),
notes TEXT,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
SELECT status INTO current_status FROM migration_log WHERE migration_file = migration_name;
IF current_status IS NULL THEN
RAISE NOTICE '--- 마이그레이션 % 시작 ---', migration_name;
-- issues 테이블에 관리함 관련 컬럼들 추가
-- 완료 사진 경로
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'issues' AND column_name = 'completion_photo_path') THEN
ALTER TABLE issues ADD COLUMN completion_photo_path VARCHAR(255);
RAISE NOTICE '✅ issues.completion_photo_path 컬럼이 추가되었습니다.';
ELSE
RAISE NOTICE ' issues.completion_photo_path 컬럼이 이미 존재합니다.';
END IF;
-- 해결방안 (관리함에서 입력)
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'issues' AND column_name = 'solution') THEN
ALTER TABLE issues ADD COLUMN solution TEXT;
RAISE NOTICE '✅ issues.solution 컬럼이 추가되었습니다.';
ELSE
RAISE NOTICE ' issues.solution 컬럼이 이미 존재합니다.';
END IF;
-- 담당부서 (관리함에서 선택)
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'issues' AND column_name = 'responsible_department') THEN
ALTER TABLE issues ADD COLUMN responsible_department department_type;
RAISE NOTICE '✅ issues.responsible_department 컬럼이 추가되었습니다.';
ELSE
RAISE NOTICE ' issues.responsible_department 컬럼이 이미 존재합니다.';
END IF;
-- 담당자 (관리함에서 입력)
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'issues' AND column_name = 'responsible_person') THEN
ALTER TABLE issues ADD COLUMN responsible_person VARCHAR(100);
RAISE NOTICE '✅ issues.responsible_person 컬럼이 추가되었습니다.';
ELSE
RAISE NOTICE ' issues.responsible_person 컬럼이 이미 존재합니다.';
END IF;
-- 조치 예상일 (관리함에서 입력)
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'issues' AND column_name = 'expected_completion_date') THEN
ALTER TABLE issues ADD COLUMN expected_completion_date DATE;
RAISE NOTICE '✅ issues.expected_completion_date 컬럼이 추가되었습니다.';
ELSE
RAISE NOTICE ' issues.expected_completion_date 컬럼이 이미 존재합니다.';
END IF;
-- 완료 확인일 (완료 상태로 변경 시 자동 입력)
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'issues' AND column_name = 'actual_completion_date') THEN
ALTER TABLE issues ADD COLUMN actual_completion_date DATE;
RAISE NOTICE '✅ issues.actual_completion_date 컬럼이 추가되었습니다.';
ELSE
RAISE NOTICE ' issues.actual_completion_date 컬럼이 이미 존재합니다.';
END IF;
-- 원인부서 (관리함에서 입력)
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'issues' AND column_name = 'cause_department') THEN
ALTER TABLE issues ADD COLUMN cause_department department_type;
RAISE NOTICE '✅ issues.cause_department 컬럼이 추가되었습니다.';
ELSE
RAISE NOTICE ' issues.cause_department 컬럼이 이미 존재합니다.';
END IF;
-- ISSUE에 대한 의견 (관리함에서 입력)
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'issues' AND column_name = 'management_comment') THEN
ALTER TABLE issues ADD COLUMN management_comment TEXT;
RAISE NOTICE '✅ issues.management_comment 컬럼이 추가되었습니다.';
ELSE
RAISE NOTICE ' issues.management_comment 컬럼이 이미 존재합니다.';
END IF;
-- 프로젝트별 순번 (No) - 프로젝트 내에서 1, 2, 3... 순으로 증가
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'issues' AND column_name = 'project_sequence_no') THEN
ALTER TABLE issues ADD COLUMN project_sequence_no INTEGER;
RAISE NOTICE '✅ issues.project_sequence_no 컬럼이 추가되었습니다.';
ELSE
RAISE NOTICE ' issues.project_sequence_no 컬럼이 이미 존재합니다.';
END IF;
-- 최종 내용 (수정된 내용이 있으면 수정본, 없으면 원본)
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'issues' AND column_name = 'final_description') THEN
ALTER TABLE issues ADD COLUMN final_description TEXT;
RAISE NOTICE '✅ issues.final_description 컬럼이 추가되었습니다.';
ELSE
RAISE NOTICE ' issues.final_description 컬럼이 이미 존재합니다.';
END IF;
-- 최종 카테고리 (수정된 카테고리가 있으면 수정본, 없으면 원본)
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'issues' AND column_name = 'final_category') THEN
ALTER TABLE issues ADD COLUMN final_category issuecategory;
RAISE NOTICE '✅ issues.final_category 컬럼이 추가되었습니다.';
ELSE
RAISE NOTICE ' issues.final_category 컬럼이 이미 존재합니다.';
END IF;
-- 인덱스 추가
IF NOT EXISTS (SELECT 1 FROM pg_indexes WHERE tablename = 'issues' AND indexname = 'idx_issues_project_sequence') THEN
CREATE INDEX idx_issues_project_sequence ON issues (project_id, project_sequence_no);
RAISE NOTICE '✅ idx_issues_project_sequence 인덱스가 생성되었습니다.';
ELSE
RAISE NOTICE ' idx_issues_project_sequence 인덱스가 이미 존재합니다.';
END IF;
IF NOT EXISTS (SELECT 1 FROM pg_indexes WHERE tablename = 'issues' AND indexname = 'idx_issues_responsible_department') THEN
CREATE INDEX idx_issues_responsible_department ON issues (responsible_department) WHERE responsible_department IS NOT NULL;
RAISE NOTICE '✅ idx_issues_responsible_department 인덱스가 생성되었습니다.';
ELSE
RAISE NOTICE ' idx_issues_responsible_department 인덱스가 이미 존재합니다.';
END IF;
IF NOT EXISTS (SELECT 1 FROM pg_indexes WHERE tablename = 'issues' AND indexname = 'idx_issues_expected_completion') THEN
CREATE INDEX idx_issues_expected_completion ON issues (expected_completion_date) WHERE expected_completion_date IS NOT NULL;
RAISE NOTICE '✅ idx_issues_expected_completion 인덱스가 생성되었습니다.';
ELSE
RAISE NOTICE ' idx_issues_expected_completion 인덱스가 이미 존재합니다.';
END IF;
-- 프로젝트별 순번 자동 생성 함수
CREATE OR REPLACE FUNCTION generate_project_sequence_no(p_project_id BIGINT) RETURNS INTEGER AS $func$
DECLARE
next_no INTEGER;
BEGIN
-- 해당 프로젝트의 최대 순번 + 1
SELECT COALESCE(MAX(project_sequence_no), 0) + 1
INTO next_no
FROM issues
WHERE project_id = p_project_id;
RETURN next_no;
END;
$func$ LANGUAGE plpgsql;
RAISE NOTICE '✅ generate_project_sequence_no 함수가 생성되었습니다.';
-- 기존 이슈들에 대해 프로젝트별 순번 설정
DO $update_sequence$
DECLARE
issue_record RECORD;
seq_no INTEGER;
BEGIN
FOR issue_record IN
SELECT id, project_id
FROM issues
WHERE project_sequence_no IS NULL
ORDER BY project_id, report_date
LOOP
SELECT generate_project_sequence_no(issue_record.project_id) INTO seq_no;
UPDATE issues
SET project_sequence_no = seq_no
WHERE id = issue_record.id;
END LOOP;
END $update_sequence$;
RAISE NOTICE '✅ 기존 이슈들의 프로젝트별 순번이 설정되었습니다.';
-- 기존 이슈들의 final_description과 final_category 초기화
UPDATE issues
SET
final_description = description,
final_category = category
WHERE final_description IS NULL OR final_category IS NULL;
RAISE NOTICE '✅ 기존 이슈들의 final_description과 final_category가 초기화되었습니다.';
-- 마이그레이션 검증
DECLARE
col_count INTEGER;
idx_count INTEGER;
func_count INTEGER;
BEGIN
SELECT COUNT(*) INTO col_count FROM information_schema.columns
WHERE table_name = 'issues' AND column_name IN (
'completion_photo_path', 'solution', 'responsible_department', 'responsible_person',
'expected_completion_date', 'actual_completion_date', 'cause_department',
'management_comment', 'project_sequence_no', 'final_description', 'final_category'
);
SELECT COUNT(*) INTO idx_count FROM pg_indexes
WHERE tablename = 'issues' AND indexname IN (
'idx_issues_project_sequence', 'idx_issues_responsible_department', 'idx_issues_expected_completion'
);
SELECT COUNT(*) INTO func_count FROM pg_proc WHERE proname = 'generate_project_sequence_no';
RAISE NOTICE '=== 마이그레이션 검증 결과 ===';
RAISE NOTICE '추가된 컬럼: %/11개', col_count;
RAISE NOTICE '생성된 인덱스: %/3개', idx_count;
RAISE NOTICE '생성된 함수: %/1개', func_count;
IF col_count = 11 AND idx_count = 3 AND func_count = 1 THEN
RAISE NOTICE '✅ 마이그레이션이 성공적으로 완료되었습니다!';
INSERT INTO migration_log (migration_file, status, notes) VALUES (migration_name, 'SUCCESS', migration_notes);
ELSE
RAISE EXCEPTION '❌ 마이그레이션 검증 실패!';
END IF;
END;
ELSIF current_status = 'SUCCESS' THEN
RAISE NOTICE ' 마이그레이션 %는 이미 성공적으로 실행되었습니다. 스킵합니다.', migration_name;
ELSE
RAISE NOTICE '⚠️ 마이그레이션 %는 이전에 실패했습니다. 수동 확인이 필요합니다.', migration_name;
END IF;
END $$;
COMMIT;