📸 Completion Photo Upload: - 수신함에서 '완료됨' 상태 선택 시 완료 사진 업로드 기능 추가 (1장 제한) - Base64 인코딩으로 사진 업로드 및 미리보기 기능 - 완료 상태 변경 시 actual_completion_date 자동 설정 🗄️ Final Issue DB Structure: - 최종 부적합 사항을 위한 포괄적인 DB 스키마 설계 및 구현 - 프로젝트별 순번 (project_sequence_no) 자동 생성 시스템 📋 New Database Fields: - completion_photo_path: 완료 사진 경로 - solution: 해결방안 (관리함에서 입력) - responsible_department: 담당부서 (department_type ENUM) - responsible_person: 담당자 (VARCHAR 100) - expected_completion_date: 조치 예상일 (DATE) - actual_completion_date: 완료 확인일 (DATE, 자동 설정) - cause_department: 원인부서 (department_type ENUM) - management_comment: ISSUE에 대한 의견 (TEXT) - final_description: 최종 내용 (수정본 또는 원본) - final_category: 최종 카테고리 (수정본 또는 원본) 🔧 Backend Implementation: - Issue 모델에 11개 새 필드 추가 - IssueStatusUpdateRequest에 completion_photo 필드 추가 - ManagementUpdateRequest 스키마 신규 생성 - update_issue_status API에 완료 사진 처리 로직 추가 - generate_project_sequence_no() 함수로 프로젝트별 순번 자동 생성 🎨 Frontend Implementation: - 상태 결정 모달에 완료 사진 업로드 섹션 추가 - 완료 상태 선택 시에만 사진 업로드 UI 표시 - 파일 크기 제한 (5MB), 이미지 파일 검증 - 사진 미리보기 및 Base64 변환 처리 - 완료 사진 없이 완료 처리 시 확인 다이얼로그 🚀 User Experience: - 직관적인 완료 사진 업로드 워크플로우 - 실시간 사진 미리보기로 업로드 확인 가능 - 완료 처리 시 자동으로 완료 확인일 기록 - 프로젝트별 순번으로 체계적인 이슈 관리 🔍 Database Migration: - 016_add_management_fields.sql 마이그레이션 성공적으로 실행 - 멱등성 보장 및 기존 데이터 보존 - 인덱스 최적화 (project_sequence, responsible_department, expected_completion) - 기존 이슈들의 final_description/final_category 자동 초기화 Expected Result: ✅ 수신함에서 완료 상태 선택 시 완료 사진 업로드 가능 ✅ 완료 처리 시 완료 확인일 자동 기록 ✅ 프로젝트별 순번으로 체계적인 이슈 번호 관리 ✅ 관리함에서 사용할 모든 필드 준비 완료 ✅ 최종 부적합 사항 리포트 생성을 위한 DB 구조 완성
224 lines
11 KiB
PL/PgSQL
224 lines
11 KiB
PL/PgSQL
-- 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;
|