Files
TK-BOM-Project/backend/app/routers/revision_status.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

259 lines
7.7 KiB
Python

"""
리비전 상태 관리 API 엔드포인트
"""
from fastapi import APIRouter, Depends, HTTPException, status, Query
from sqlalchemy.orm import Session
from typing import Optional
from pydantic import BaseModel
from ..database import get_db
from ..auth.middleware import get_current_user
from ..services.revision_status_service import RevisionStatusService
from ..auth.models import User
from ..utils.logger import get_logger
logger = get_logger(__name__)
router = APIRouter(prefix="/revision-status", tags=["Revision Status"])
class CreateComparisonRequest(BaseModel):
job_no: str
current_file_id: int
previous_file_id: int
comparison_result: dict
class RejectComparisonRequest(BaseModel):
reason: str = ""
@router.get("/{job_no}/{file_id}")
async def get_revision_status(
job_no: str,
file_id: int,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user)
):
"""
리비전 상태 조회
"""
try:
status_service = RevisionStatusService(db)
revision_status = status_service.get_revision_status(job_no, file_id)
if "error" in revision_status:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=revision_status["error"]
)
logger.info(f"Retrieved revision status for {job_no}/{file_id}")
return {
"success": True,
"data": revision_status,
"message": "리비전 상태 조회 완료"
}
except HTTPException:
raise
except Exception as e:
logger.error(f"Failed to get revision status: {e}")
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"리비전 상태 조회 중 오류가 발생했습니다: {str(e)}"
)
@router.get("/history/{job_no}")
async def get_revision_history(
job_no: str,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user)
):
"""
작업의 전체 리비전 히스토리 조회
"""
try:
status_service = RevisionStatusService(db)
history = status_service.get_revision_history(job_no)
logger.info(f"Retrieved revision history for {job_no}: {len(history)} revisions")
return {
"success": True,
"data": history,
"message": "리비전 히스토리 조회 완료"
}
except Exception as e:
logger.error(f"Failed to get revision history: {e}")
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"리비전 히스토리 조회 중 오류가 발생했습니다: {str(e)}"
)
@router.post("/create-comparison")
async def create_comparison_record(
request: CreateComparisonRequest,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user)
):
"""
리비전 비교 기록 생성
"""
try:
status_service = RevisionStatusService(db)
comparison_id = status_service.create_revision_comparison_record(
job_no=request.job_no,
current_file_id=request.current_file_id,
previous_file_id=request.previous_file_id,
comparison_result=request.comparison_result,
created_by=current_user.username
)
logger.info(f"Created revision comparison record: {comparison_id}")
return {
"success": True,
"data": {"comparison_id": comparison_id},
"message": "리비전 비교 기록 생성 완료"
}
except Exception as e:
logger.error(f"Failed to create comparison record: {e}")
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"비교 기록 생성 중 오류가 발생했습니다: {str(e)}"
)
@router.post("/apply-comparison/{comparison_id}")
async def apply_comparison(
comparison_id: int,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user)
):
"""
리비전 비교 결과 적용
"""
try:
status_service = RevisionStatusService(db)
apply_result = status_service.apply_revision_comparison(
comparison_id=comparison_id,
applied_by=current_user.username
)
if not apply_result["success"]:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=apply_result["error"]
)
logger.info(f"Applied revision comparison: {comparison_id}")
return {
"success": True,
"data": apply_result,
"message": "리비전 비교 결과 적용 완료"
}
except HTTPException:
raise
except Exception as e:
logger.error(f"Failed to apply comparison {comparison_id}: {e}")
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"비교 결과 적용 중 오류가 발생했습니다: {str(e)}"
)
@router.get("/pending")
async def get_pending_revisions(
job_no: Optional[str] = Query(None, description="특정 작업의 대기 중인 리비전만 조회"),
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user)
):
"""
대기 중인 리비전 목록 조회
"""
try:
status_service = RevisionStatusService(db)
pending_revisions = status_service.get_pending_revisions(job_no)
logger.info(f"Retrieved {len(pending_revisions)} pending revisions")
return {
"success": True,
"data": pending_revisions,
"message": "대기 중인 리비전 조회 완료"
}
except Exception as e:
logger.error(f"Failed to get pending revisions: {e}")
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"대기 중인 리비전 조회 중 오류가 발생했습니다: {str(e)}"
)
@router.post("/reject-comparison/{comparison_id}")
async def reject_comparison(
comparison_id: int,
request: RejectComparisonRequest,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user)
):
"""
리비전 비교 결과 거부
"""
try:
# 비교 기록을 거부 상태로 업데이트
update_query = """
UPDATE revision_comparisons
SET is_applied = false,
notes = CONCAT(COALESCE(notes, ''), '\n거부됨: ', :reason, ' (by ', :rejected_by, ')')
WHERE id = :comparison_id
"""
from ..services.database_service import DatabaseService
db_service = DatabaseService(db)
db_service.execute_query(update_query, {
"comparison_id": comparison_id,
"reason": request.reason or "사유 없음",
"rejected_by": current_user.username
})
db.commit()
logger.info(f"Rejected revision comparison: {comparison_id}")
return {
"success": True,
"data": {"comparison_id": comparison_id, "reason": request.reason},
"message": "리비전 비교 결과 거부 완료"
}
except Exception as e:
logger.error(f"Failed to reject comparison {comparison_id}: {e}")
db.rollback()
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"비교 결과 거부 중 오류가 발생했습니다: {str(e)}"
)