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 앞으로 스키마 문제가 발생하면 위 명령 하나로 자동 해결!
200 lines
6.4 KiB
Python
200 lines
6.4 KiB
Python
"""
|
|
리비전 자재 처리 API 엔드포인트
|
|
"""
|
|
|
|
from fastapi import APIRouter, Depends, HTTPException, status
|
|
from sqlalchemy.orm import Session
|
|
from typing import List, Dict, Any
|
|
from pydantic import BaseModel
|
|
|
|
from ..database import get_db
|
|
from ..auth.middleware import get_current_user
|
|
from ..services.revision_material_service import RevisionMaterialService
|
|
from ..auth.models import User
|
|
from ..utils.logger import get_logger
|
|
|
|
logger = get_logger(__name__)
|
|
|
|
router = APIRouter(prefix="/revision-material", tags=["Revision Material"])
|
|
|
|
|
|
class ProcessingResultRequest(BaseModel):
|
|
processing_results: List[Dict[str, Any]]
|
|
|
|
|
|
class MaterialProcessRequest(BaseModel):
|
|
action: str
|
|
additional_data: Dict[str, Any] = {}
|
|
|
|
|
|
@router.get("/category/{file_id}/{category}")
|
|
async def get_category_materials(
|
|
file_id: int,
|
|
category: str,
|
|
include_processing_info: bool = True,
|
|
db: Session = Depends(get_db),
|
|
current_user: User = Depends(get_current_user)
|
|
):
|
|
"""
|
|
리비전 페이지용 카테고리별 자재 조회
|
|
"""
|
|
|
|
if category == 'PIPE':
|
|
raise HTTPException(
|
|
status_code=status.HTTP_400_BAD_REQUEST,
|
|
detail="PIPE 카테고리는 별도 처리가 필요합니다."
|
|
)
|
|
|
|
try:
|
|
material_service = RevisionMaterialService(db)
|
|
|
|
materials = material_service.get_category_materials_for_revision(
|
|
file_id=file_id,
|
|
category=category,
|
|
include_processing_info=include_processing_info
|
|
)
|
|
|
|
# 처리 정보 요약
|
|
processing_info = {
|
|
"total_materials": len(materials),
|
|
"by_status": {},
|
|
"by_priority": {"high": 0, "medium": 0, "low": 0}
|
|
}
|
|
|
|
for material in materials:
|
|
proc_info = material.get('processing_info', {})
|
|
status = proc_info.get('display_status', 'UNKNOWN')
|
|
priority = proc_info.get('priority', 'medium')
|
|
|
|
processing_info["by_status"][status] = processing_info["by_status"].get(status, 0) + 1
|
|
processing_info["by_priority"][priority] += 1
|
|
|
|
logger.info(f"Retrieved {len(materials)} materials for category {category} in file {file_id}")
|
|
|
|
return {
|
|
"success": True,
|
|
"data": {
|
|
"materials": materials,
|
|
"processing_info": processing_info
|
|
},
|
|
"message": f"{category} 카테고리 자재 조회 완료"
|
|
}
|
|
|
|
except Exception as e:
|
|
logger.error(f"Failed to get category materials: {e}")
|
|
raise HTTPException(
|
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
detail=f"카테고리 자재 조회 중 오류가 발생했습니다: {str(e)}"
|
|
)
|
|
|
|
|
|
@router.post("/process/{material_id}")
|
|
async def process_material(
|
|
material_id: int,
|
|
request: MaterialProcessRequest,
|
|
db: Session = Depends(get_db),
|
|
current_user: User = Depends(get_current_user)
|
|
):
|
|
"""
|
|
개별 자재 처리
|
|
"""
|
|
|
|
try:
|
|
material_service = RevisionMaterialService(db)
|
|
|
|
# 자재 정보 조회
|
|
material_query = """
|
|
SELECT * FROM materials WHERE id = :material_id
|
|
"""
|
|
|
|
result = material_service.db_service.execute_query(
|
|
material_query, {"material_id": material_id}
|
|
)
|
|
material = result.fetchone()
|
|
|
|
if not material:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_404_NOT_FOUND,
|
|
detail="자재를 찾을 수 없습니다."
|
|
)
|
|
|
|
material_dict = dict(material._mapping)
|
|
|
|
# 액션에 따른 처리
|
|
if request.action == "new_material":
|
|
processing_result = material_service.process_new_material(
|
|
material_dict,
|
|
material_dict.get('classified_category', 'UNKNOWN')
|
|
)
|
|
elif request.action == "remove_material":
|
|
processing_result = material_service.process_removed_material(
|
|
material_dict,
|
|
material_dict.get('classified_category', 'UNKNOWN')
|
|
)
|
|
else:
|
|
# 기본 처리 (구매 상태별)
|
|
# 이전 자재 정보가 필요한 경우 additional_data에서 가져옴
|
|
prev_material = request.additional_data.get('previous_material', material_dict)
|
|
|
|
processing_result = material_service.process_material_by_purchase_status(
|
|
prev_material,
|
|
material_dict,
|
|
material_dict.get('classified_category', 'UNKNOWN')
|
|
)
|
|
|
|
# 처리 결과 적용
|
|
apply_result = material_service.apply_material_processing_results([processing_result])
|
|
|
|
logger.info(f"Processed material {material_id} with action {request.action}")
|
|
|
|
return {
|
|
"success": True,
|
|
"data": {
|
|
"processing_result": processing_result,
|
|
"apply_result": apply_result
|
|
},
|
|
"message": "자재 처리 완료"
|
|
}
|
|
|
|
except HTTPException:
|
|
raise
|
|
except Exception as e:
|
|
logger.error(f"Failed to process material {material_id}: {e}")
|
|
raise HTTPException(
|
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
detail=f"자재 처리 중 오류가 발생했습니다: {str(e)}"
|
|
)
|
|
|
|
|
|
@router.post("/apply-results")
|
|
async def apply_processing_results(
|
|
request: ProcessingResultRequest,
|
|
db: Session = Depends(get_db),
|
|
current_user: User = Depends(get_current_user)
|
|
):
|
|
"""
|
|
자재 처리 결과를 일괄 적용
|
|
"""
|
|
|
|
try:
|
|
material_service = RevisionMaterialService(db)
|
|
|
|
apply_result = material_service.apply_material_processing_results(
|
|
request.processing_results
|
|
)
|
|
|
|
logger.info(f"Applied {len(request.processing_results)} processing results")
|
|
|
|
return {
|
|
"success": True,
|
|
"data": apply_result,
|
|
"message": "처리 결과 적용 완료"
|
|
}
|
|
|
|
except Exception as e:
|
|
logger.error(f"Failed to apply processing results: {e}")
|
|
raise HTTPException(
|
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
detail=f"처리 결과 적용 중 오류가 발생했습니다: {str(e)}"
|
|
)
|