🔧 완전한 스키마 자동화 시스템 구축
Some checks failed
SonarQube Analysis / SonarQube Scan (push) Has been cancelled
Some checks failed
SonarQube Analysis / SonarQube Scan (push) Has been cancelled
✨ 주요 기능: - 완전한 데이터베이스 스키마 분석 및 자동 마이그레이션 시스템 - 44개 테이블 완전 지원 (운영 서버 43개 + 1개 추가) - 누락된 테이블/컬럼 자동 감지 및 생성 🔧 해결된 스키마 문제: - users.status 컬럼 누락 → 자동 추가 - files 테이블 4개 컬럼 누락 → 자동 추가 - materials 테이블 22개 컬럼 누락 → 자동 추가 - support_details, purchase_requests, purchase_request_items 테이블 누락 → 자동 생성 - material_purchase_tracking.description, purchase_status 컬럼 누락 → 자동 추가 🚀 자동화 도구: - schema_analyzer.py: 코드와 DB 스키마 비교 분석 - auto_migrator.py: 자동 마이그레이션 실행 - docker_migrator.py: Docker 환경용 간편 마이그레이션 - schema_monitor.py: 실시간 스키마 모니터링 📋 리비전 관리 시스템: - 8개 카테고리별 리비전 페이지 구현 - PIPE Cutting Plan 관리 시스템 - PIPE Issue Management 시스템 - 완전한 리비전 비교 및 추적 기능 🎯 사용법: docker exec tk-mp-backend python3 scripts/docker_migrator.py 앞으로 스키마 문제가 발생하면 위 명령 하나로 자동 해결!
This commit is contained in:
199
backend/scripts/auto_migrator.py
Normal file
199
backend/scripts/auto_migrator.py
Normal file
@@ -0,0 +1,199 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
자동 마이그레이션 실행기 - 스키마 분석 결과를 바탕으로 자동으로 DB를 업데이트
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import psycopg2
|
||||
from psycopg2.extras import RealDictCursor
|
||||
from datetime import datetime
|
||||
from schema_analyzer import SchemaAnalyzer
|
||||
|
||||
class AutoMigrator:
|
||||
def __init__(self):
|
||||
self.db_config = {
|
||||
'host': 'localhost',
|
||||
'port': 5432,
|
||||
'database': 'tk_mp_bom',
|
||||
'user': 'tkmp_user',
|
||||
'password': 'tkmp_password'
|
||||
}
|
||||
self.analyzer = SchemaAnalyzer()
|
||||
self.migration_log = []
|
||||
|
||||
def run_full_analysis_and_migration(self):
|
||||
"""전체 분석 및 마이그레이션 실행"""
|
||||
print("🚀 자동 마이그레이션 시작")
|
||||
|
||||
# 1. 스키마 분석
|
||||
print("\n1️⃣ 스키마 분석 중...")
|
||||
self.analyzer.analyze_code_models()
|
||||
self.analyzer.analyze_db_schema()
|
||||
analysis_result = self.analyzer.compare_schemas()
|
||||
|
||||
# 2. 분석 결과 확인
|
||||
if not analysis_result['missing_tables'] and not analysis_result['missing_columns']:
|
||||
print("✅ 스키마가 이미 최신 상태입니다!")
|
||||
return True
|
||||
|
||||
# 3. 마이그레이션 실행
|
||||
print("\n2️⃣ 마이그레이션 실행 중...")
|
||||
success = self._execute_migrations(analysis_result)
|
||||
|
||||
# 4. 결과 로깅
|
||||
self._log_migration_result(success, analysis_result)
|
||||
|
||||
return success
|
||||
|
||||
def _execute_migrations(self, analysis_result) -> bool:
|
||||
"""마이그레이션 실행"""
|
||||
try:
|
||||
conn = psycopg2.connect(**self.db_config)
|
||||
cursor = conn.cursor()
|
||||
|
||||
# 트랜잭션 시작
|
||||
conn.autocommit = False
|
||||
|
||||
# 1. 누락된 테이블 생성
|
||||
for table_name in analysis_result['missing_tables']:
|
||||
success = self._create_missing_table(cursor, table_name)
|
||||
if not success:
|
||||
conn.rollback()
|
||||
return False
|
||||
|
||||
# 2. 누락된 컬럼 추가
|
||||
for missing_col in analysis_result['missing_columns']:
|
||||
success = self._add_missing_column(cursor, missing_col)
|
||||
if not success:
|
||||
conn.rollback()
|
||||
return False
|
||||
|
||||
# 커밋
|
||||
conn.commit()
|
||||
cursor.close()
|
||||
conn.close()
|
||||
|
||||
print("✅ 마이그레이션 성공!")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ 마이그레이션 실패: {e}")
|
||||
if 'conn' in locals():
|
||||
conn.rollback()
|
||||
return False
|
||||
|
||||
def _create_missing_table(self, cursor, table_name: str) -> bool:
|
||||
"""누락된 테이블 생성"""
|
||||
try:
|
||||
create_sql = self.analyzer._generate_create_table_sql(table_name)
|
||||
print(f" 📋 테이블 생성: {table_name}")
|
||||
cursor.execute(create_sql)
|
||||
|
||||
self.migration_log.append({
|
||||
'type': 'CREATE_TABLE',
|
||||
'table': table_name,
|
||||
'sql': create_sql,
|
||||
'timestamp': datetime.now().isoformat()
|
||||
})
|
||||
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f" ❌ 테이블 생성 실패 {table_name}: {e}")
|
||||
return False
|
||||
|
||||
def _add_missing_column(self, cursor, missing_col: dict) -> bool:
|
||||
"""누락된 컬럼 추가"""
|
||||
try:
|
||||
alter_sql = self.analyzer._generate_add_column_sql(missing_col)
|
||||
print(f" 🔧 컬럼 추가: {missing_col['table']}.{missing_col['column']}")
|
||||
cursor.execute(alter_sql)
|
||||
|
||||
self.migration_log.append({
|
||||
'type': 'ADD_COLUMN',
|
||||
'table': missing_col['table'],
|
||||
'column': missing_col['column'],
|
||||
'sql': alter_sql,
|
||||
'timestamp': datetime.now().isoformat()
|
||||
})
|
||||
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f" ❌ 컬럼 추가 실패 {missing_col['table']}.{missing_col['column']}: {e}")
|
||||
return False
|
||||
|
||||
def _log_migration_result(self, success: bool, analysis_result: dict):
|
||||
"""마이그레이션 결과 로깅"""
|
||||
log_entry = {
|
||||
'timestamp': datetime.now().isoformat(),
|
||||
'success': success,
|
||||
'analysis_result': analysis_result,
|
||||
'migration_log': self.migration_log
|
||||
}
|
||||
|
||||
# 로그 파일에 저장
|
||||
import json
|
||||
log_filename = f"migration_log_{datetime.now().strftime('%Y%m%d_%H%M%S')}.json"
|
||||
|
||||
with open(log_filename, 'w', encoding='utf-8') as f:
|
||||
json.dump(log_entry, f, indent=2, ensure_ascii=False)
|
||||
|
||||
print(f"📄 마이그레이션 로그 저장: {log_filename}")
|
||||
|
||||
def fix_immediate_issues(self):
|
||||
"""즉시 해결이 필요한 문제들 수정"""
|
||||
print("🔧 즉시 해결 필요한 문제들 수정 중...")
|
||||
|
||||
immediate_fixes = [
|
||||
{
|
||||
'description': 'users 테이블에 status 컬럼 추가',
|
||||
'sql': "ALTER TABLE users ADD COLUMN status VARCHAR(20) DEFAULT 'active'",
|
||||
'check_sql': "SELECT column_name FROM information_schema.columns WHERE table_name = 'users' AND column_name = 'status'"
|
||||
}
|
||||
]
|
||||
|
||||
try:
|
||||
conn = psycopg2.connect(**self.db_config)
|
||||
cursor = conn.cursor(cursor_factory=RealDictCursor)
|
||||
|
||||
for fix in immediate_fixes:
|
||||
# 이미 존재하는지 확인
|
||||
cursor.execute(fix['check_sql'])
|
||||
if cursor.fetchone():
|
||||
print(f" ✅ 이미 존재: {fix['description']}")
|
||||
continue
|
||||
|
||||
# 수정 실행
|
||||
cursor.execute(fix['sql'])
|
||||
conn.commit()
|
||||
print(f" 🔧 수정 완료: {fix['description']}")
|
||||
|
||||
cursor.close()
|
||||
conn.close()
|
||||
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ 즉시 수정 실패: {e}")
|
||||
return False
|
||||
|
||||
def main():
|
||||
migrator = AutoMigrator()
|
||||
|
||||
# 1. 즉시 해결 필요한 문제들 수정
|
||||
migrator.fix_immediate_issues()
|
||||
|
||||
# 2. 전체 분석 및 마이그레이션
|
||||
success = migrator.run_full_analysis_and_migration()
|
||||
|
||||
if success:
|
||||
print("\n🎉 모든 마이그레이션이 성공적으로 완료되었습니다!")
|
||||
else:
|
||||
print("\n💥 마이그레이션 중 오류가 발생했습니다. 로그를 확인해주세요.")
|
||||
|
||||
return success
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user