Files
TK-BOM-Project/backend/scripts/auto_migrator.py
Hyungi Ahn 8f42a1054e
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

앞으로 스키마 문제가 발생하면 위 명령 하나로 자동 해결!
2025-10-21 10:34:45 +09:00

200 lines
6.8 KiB
Python
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/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()