리비전 페이지 제거 및 트랜잭션 오류 임시 수정
Some checks failed
SonarQube Analysis / SonarQube Scan (push) Has been cancelled
Some checks failed
SonarQube Analysis / SonarQube Scan (push) Has been cancelled
- frontend/src/pages/revision/ 폴더 완전 삭제 - EnhancedRevisionPage.css 제거 - support_details 저장 시 트랜잭션 오류로 인해 임시로 상세 정보 저장 비활성화 - 리비전 기능 재설계 예정
This commit is contained in:
@@ -367,12 +367,14 @@ async def upload_file(
|
||||
)
|
||||
|
||||
# 4. 자재 데이터 처리
|
||||
is_revision_upload = parent_file_id is not None or revision != 'Rev.0'
|
||||
processing_result = upload_service.process_materials_data(
|
||||
file_path=file_path,
|
||||
file_id=file_record.id,
|
||||
job_no=job_no,
|
||||
revision=revision,
|
||||
parent_file_id=parent_file_id
|
||||
parent_file_id=parent_file_id,
|
||||
is_revision=is_revision_upload
|
||||
)
|
||||
|
||||
# 5. 파일 레코드 업데이트 (파싱된 자재 수)
|
||||
|
||||
278
backend/app/routers/simple_revision.py
Normal file
278
backend/app/routers/simple_revision.py
Normal file
@@ -0,0 +1,278 @@
|
||||
"""
|
||||
간단한 리비전 관리 API
|
||||
복잡한 dependency 없이 핵심 기능만 제공
|
||||
"""
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, UploadFile, File as FastAPIFile
|
||||
from sqlalchemy.orm import Session
|
||||
from typing import Dict, List, Any, Optional
|
||||
import json
|
||||
|
||||
from ..database import get_db
|
||||
from ..services.simple_revision_service import SimpleRevisionService
|
||||
from ..services.file_upload_service import FileUploadService
|
||||
from ..models import File, Material
|
||||
from ..utils.logger import get_logger
|
||||
|
||||
logger = get_logger(__name__)
|
||||
|
||||
router = APIRouter(prefix="/simple-revision", tags=["Simple Revision"])
|
||||
|
||||
@router.post("/compare")
|
||||
async def compare_revisions(
|
||||
current_file_id: int,
|
||||
previous_file_id: int,
|
||||
category: str,
|
||||
username: str = "system",
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""
|
||||
두 리비전 간의 자재 비교
|
||||
|
||||
Args:
|
||||
current_file_id: 현재 파일 ID
|
||||
previous_file_id: 이전 파일 ID
|
||||
category: 비교할 카테고리 (PIPE, FITTING, FLANGE, VALVE, GASKET, BOLT, SUPPORT, SPECIAL, UNCLASSIFIED)
|
||||
username: 비교 수행자
|
||||
"""
|
||||
try:
|
||||
service = SimpleRevisionService(db)
|
||||
result = service.compare_revisions(
|
||||
current_file_id, previous_file_id, category, username
|
||||
)
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"message": f"{category} 카테고리 리비전 비교 완료",
|
||||
"data": result
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"리비전 비교 실패: {e}")
|
||||
raise HTTPException(status_code=500, detail=f"리비전 비교 실패: {str(e)}")
|
||||
|
||||
@router.get("/comparison/{comparison_id}")
|
||||
async def get_comparison_result(
|
||||
comparison_id: int,
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""저장된 비교 결과 조회"""
|
||||
try:
|
||||
service = SimpleRevisionService(db)
|
||||
result = service.get_comparison_result(comparison_id)
|
||||
|
||||
if not result:
|
||||
raise HTTPException(status_code=404, detail="비교 결과를 찾을 수 없습니다")
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"data": result
|
||||
}
|
||||
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"비교 결과 조회 실패: {e}")
|
||||
raise HTTPException(status_code=500, detail=f"비교 결과 조회 실패: {str(e)}")
|
||||
|
||||
@router.post("/upload-revision")
|
||||
async def upload_revision_file(
|
||||
file: UploadFile = FastAPIFile(...),
|
||||
job_no: str = "default",
|
||||
previous_file_id: int = None,
|
||||
username: str = "system",
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""
|
||||
리비전 파일 업로드 및 자동 비교
|
||||
|
||||
Args:
|
||||
file: 업로드할 BOM 파일
|
||||
job_no: 작업 번호
|
||||
previous_file_id: 비교할 이전 파일 ID
|
||||
username: 업로드 사용자
|
||||
"""
|
||||
try:
|
||||
# 1. 파일 업로드
|
||||
upload_service = FileUploadService(db)
|
||||
upload_result = await upload_service.process_upload(
|
||||
file=file,
|
||||
job_no=job_no,
|
||||
username=username,
|
||||
is_revision=True
|
||||
)
|
||||
|
||||
current_file_id = upload_result['file_id']
|
||||
|
||||
# 2. 이전 파일이 지정되지 않은 경우 최신 파일 찾기
|
||||
if not previous_file_id:
|
||||
previous_file = db.query(File).filter(
|
||||
File.job_no == job_no,
|
||||
File.id != current_file_id,
|
||||
File.is_active == True
|
||||
).order_by(File.upload_date.desc()).first()
|
||||
|
||||
if previous_file:
|
||||
previous_file_id = previous_file.id
|
||||
else:
|
||||
return {
|
||||
"success": True,
|
||||
"message": "첫 번째 파일이므로 비교할 이전 파일이 없습니다",
|
||||
"data": {
|
||||
"file_id": current_file_id,
|
||||
"comparison_results": []
|
||||
}
|
||||
}
|
||||
|
||||
# 3. 각 카테고리별로 자동 비교 수행
|
||||
categories = [
|
||||
'PIPE', 'FITTING', 'FLANGE', 'VALVE',
|
||||
'GASKET', 'BOLT', 'SUPPORT', 'SPECIAL', 'UNCLASSIFIED'
|
||||
]
|
||||
|
||||
revision_service = SimpleRevisionService(db)
|
||||
comparison_results = []
|
||||
|
||||
for category in categories:
|
||||
try:
|
||||
# 해당 카테고리에 자재가 있는지 확인
|
||||
current_materials = db.query(Material).filter(
|
||||
Material.file_id == current_file_id,
|
||||
Material.classified_category == category
|
||||
).count()
|
||||
|
||||
previous_materials = db.query(Material).filter(
|
||||
Material.file_id == previous_file_id,
|
||||
Material.classified_category == category
|
||||
).count()
|
||||
|
||||
if current_materials > 0 or previous_materials > 0:
|
||||
# 비교 수행
|
||||
comparison_result = revision_service.compare_revisions(
|
||||
current_file_id, previous_file_id, category, username
|
||||
)
|
||||
comparison_results.append(comparison_result)
|
||||
|
||||
except Exception as e:
|
||||
logger.warning(f"{category} 카테고리 비교 실패: {e}")
|
||||
continue
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"message": f"리비전 파일 업로드 및 비교 완료 ({len(comparison_results)}개 카테고리)",
|
||||
"data": {
|
||||
"file_id": current_file_id,
|
||||
"previous_file_id": previous_file_id,
|
||||
"comparison_results": comparison_results
|
||||
}
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"리비전 업로드 실패: {e}")
|
||||
raise HTTPException(status_code=500, detail=f"리비전 업로드 실패: {str(e)}")
|
||||
|
||||
@router.get("/categories/{file_id}")
|
||||
async def get_available_categories(
|
||||
file_id: int,
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""파일의 사용 가능한 카테고리 목록 조회"""
|
||||
try:
|
||||
# 파일에 포함된 카테고리별 자재 수 조회
|
||||
categories_query = db.query(
|
||||
Material.classified_category,
|
||||
db.func.count(Material.id).label('count')
|
||||
).filter(
|
||||
Material.file_id == file_id
|
||||
).group_by(Material.classified_category).all()
|
||||
|
||||
categories = []
|
||||
for category, count in categories_query:
|
||||
if category and count > 0:
|
||||
categories.append({
|
||||
'category': category,
|
||||
'count': count
|
||||
})
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"data": {
|
||||
"file_id": file_id,
|
||||
"categories": categories
|
||||
}
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"카테고리 조회 실패: {e}")
|
||||
raise HTTPException(status_code=500, detail=f"카테고리 조회 실패: {str(e)}")
|
||||
|
||||
@router.get("/changed-materials/{current_file_id}/{previous_file_id}")
|
||||
async def get_changed_materials_only(
|
||||
current_file_id: int,
|
||||
previous_file_id: int,
|
||||
categories: str = None, # 쉼표로 구분된 카테고리 목록
|
||||
username: str = "system",
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""변경된 자재만 필터링하여 반환"""
|
||||
try:
|
||||
service = SimpleRevisionService(db)
|
||||
|
||||
# 카테고리 파싱
|
||||
category_list = None
|
||||
if categories:
|
||||
category_list = [cat.strip().upper() for cat in categories.split(',')]
|
||||
|
||||
result = service.get_changed_materials_only(
|
||||
current_file_id, previous_file_id, category_list, username
|
||||
)
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"message": f"변경된 자재 조회 완료 ({result['total_changed_materials']}개 변경)",
|
||||
"data": result
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"변경된 자재 조회 실패: {e}")
|
||||
raise HTTPException(status_code=500, detail=f"변경된 자재 조회 실패: {str(e)}")
|
||||
|
||||
@router.get("/history/{job_no}")
|
||||
async def get_revision_history(
|
||||
job_no: str,
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""작업 번호별 리비전 히스토리 조회"""
|
||||
try:
|
||||
from ..models import SimpleRevisionComparison
|
||||
|
||||
comparisons = db.query(SimpleRevisionComparison).filter(
|
||||
SimpleRevisionComparison.job_no == job_no
|
||||
).order_by(SimpleRevisionComparison.created_at.desc()).all()
|
||||
|
||||
history = []
|
||||
for comp in comparisons:
|
||||
history.append({
|
||||
'comparison_id': comp.id,
|
||||
'category': comp.category,
|
||||
'created_at': comp.created_at.isoformat(),
|
||||
'created_by': comp.created_by_username,
|
||||
'summary': {
|
||||
'added': comp.added_count,
|
||||
'removed': comp.removed_count,
|
||||
'changed': comp.changed_count,
|
||||
'unchanged': comp.unchanged_count
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"data": {
|
||||
"job_no": job_no,
|
||||
"history": history
|
||||
}
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"리비전 히스토리 조회 실패: {e}")
|
||||
raise HTTPException(status_code=500, detail=f"리비전 히스토리 조회 실패: {str(e)}")
|
||||
Reference in New Issue
Block a user