feat: SWG 가스켓 전체 구성 정보 표시 개선
Some checks failed
SonarQube Analysis / SonarQube Scan (push) Has been cancelled
Some checks failed
SonarQube Analysis / SonarQube Scan (push) Has been cancelled
- H/F/I/O SS304/GRAPHITE/CS/CS 패턴에서 4개 구성요소 모두 표시 - 기존 SS304 + GRAPHITE → SS304/GRAPHITE/CS/CS로 완전한 구성 표시 - 외부링/필러/내부링/추가구성 모든 정보 포함 - 구매수량 계산 모달에서 정확한 재질 정보 확인 가능
This commit is contained in:
354
backend/app/schemas/response_models.py
Normal file
354
backend/app/schemas/response_models.py
Normal file
@@ -0,0 +1,354 @@
|
||||
"""
|
||||
API 응답 모델 정의
|
||||
타입 안정성 및 API 문서화를 위한 Pydantic 모델들
|
||||
"""
|
||||
from pydantic import BaseModel, Field, ConfigDict
|
||||
from typing import List, Optional, Dict, Any, Union
|
||||
from datetime import datetime
|
||||
from enum import Enum
|
||||
|
||||
|
||||
# ================================
|
||||
# 기본 응답 모델
|
||||
# ================================
|
||||
|
||||
class BaseResponse(BaseModel):
|
||||
"""기본 응답 모델"""
|
||||
success: bool = Field(description="요청 성공 여부")
|
||||
message: Optional[str] = Field(None, description="응답 메시지")
|
||||
timestamp: datetime = Field(default_factory=datetime.now, description="응답 시간")
|
||||
|
||||
|
||||
class ErrorResponse(BaseResponse):
|
||||
"""에러 응답 모델"""
|
||||
success: bool = Field(False, description="요청 성공 여부")
|
||||
error: Dict[str, Any] = Field(description="에러 정보")
|
||||
|
||||
model_config = ConfigDict(
|
||||
json_schema_extra={
|
||||
"example": {
|
||||
"success": False,
|
||||
"message": "요청 처리 중 오류가 발생했습니다",
|
||||
"error": {
|
||||
"code": "VALIDATION_ERROR",
|
||||
"details": "입력 데이터가 올바르지 않습니다"
|
||||
},
|
||||
"timestamp": "2025-01-01T12:00:00"
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
class SuccessResponse(BaseResponse):
|
||||
"""성공 응답 모델"""
|
||||
success: bool = Field(True, description="요청 성공 여부")
|
||||
data: Optional[Any] = Field(None, description="응답 데이터")
|
||||
|
||||
|
||||
# ================================
|
||||
# 열거형 정의
|
||||
# ================================
|
||||
|
||||
class FileStatus(str, Enum):
|
||||
"""파일 상태"""
|
||||
ACTIVE = "active"
|
||||
INACTIVE = "inactive"
|
||||
PROCESSING = "processing"
|
||||
ERROR = "error"
|
||||
|
||||
|
||||
class MaterialCategory(str, Enum):
|
||||
"""자재 카테고리"""
|
||||
PIPE = "PIPE"
|
||||
FITTING = "FITTING"
|
||||
VALVE = "VALVE"
|
||||
FLANGE = "FLANGE"
|
||||
BOLT = "BOLT"
|
||||
GASKET = "GASKET"
|
||||
INSTRUMENT = "INSTRUMENT"
|
||||
EXCLUDE = "EXCLUDE"
|
||||
|
||||
|
||||
class JobStatus(str, Enum):
|
||||
"""작업 상태"""
|
||||
ACTIVE = "active"
|
||||
COMPLETED = "completed"
|
||||
ON_HOLD = "on_hold"
|
||||
CANCELLED = "cancelled"
|
||||
|
||||
|
||||
# ================================
|
||||
# 파일 관련 모델
|
||||
# ================================
|
||||
|
||||
class FileInfo(BaseModel):
|
||||
"""파일 정보 모델"""
|
||||
id: int = Field(description="파일 ID")
|
||||
filename: str = Field(description="파일명")
|
||||
original_filename: str = Field(description="원본 파일명")
|
||||
job_no: Optional[str] = Field(None, description="작업 번호")
|
||||
bom_name: Optional[str] = Field(None, description="BOM 이름")
|
||||
revision: str = Field(default="Rev.0", description="리비전")
|
||||
parsed_count: int = Field(default=0, description="파싱된 자재 수")
|
||||
bom_type: str = Field(default="unknown", description="BOM 타입")
|
||||
status: FileStatus = Field(description="파일 상태")
|
||||
file_size: Optional[int] = Field(None, description="파일 크기 (bytes)")
|
||||
upload_date: datetime = Field(description="업로드 일시")
|
||||
description: Optional[str] = Field(None, description="파일 설명")
|
||||
|
||||
model_config = ConfigDict(
|
||||
json_schema_extra={
|
||||
"example": {
|
||||
"id": 1,
|
||||
"filename": "BOM_Rev1.xlsx",
|
||||
"original_filename": "BOM_Rev1.xlsx",
|
||||
"job_no": "TK-2025-001",
|
||||
"bom_name": "메인 BOM",
|
||||
"revision": "Rev.1",
|
||||
"parsed_count": 150,
|
||||
"bom_type": "excel",
|
||||
"status": "active",
|
||||
"file_size": 2048576,
|
||||
"upload_date": "2025-01-01T12:00:00",
|
||||
"description": "파일: BOM_Rev1.xlsx"
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
class FileListResponse(BaseResponse):
|
||||
"""파일 목록 응답 모델"""
|
||||
success: bool = Field(True, description="요청 성공 여부")
|
||||
data: List[FileInfo] = Field(description="파일 목록")
|
||||
total_count: int = Field(description="전체 파일 수")
|
||||
cache_hit: bool = Field(default=False, description="캐시 히트 여부")
|
||||
|
||||
|
||||
class FileDeleteResponse(BaseResponse):
|
||||
"""파일 삭제 응답 모델"""
|
||||
success: bool = Field(True, description="삭제 성공 여부")
|
||||
message: str = Field(description="삭제 결과 메시지")
|
||||
deleted_file_id: int = Field(description="삭제된 파일 ID")
|
||||
|
||||
|
||||
# ================================
|
||||
# 자재 관련 모델
|
||||
# ================================
|
||||
|
||||
class MaterialInfo(BaseModel):
|
||||
"""자재 정보 모델"""
|
||||
id: int = Field(description="자재 ID")
|
||||
file_id: int = Field(description="파일 ID")
|
||||
line_number: Optional[int] = Field(None, description="엑셀 행 번호")
|
||||
original_description: str = Field(description="원본 품명")
|
||||
classified_category: Optional[MaterialCategory] = Field(None, description="분류된 카테고리")
|
||||
classified_subcategory: Optional[str] = Field(None, description="세부 분류")
|
||||
material_grade: Optional[str] = Field(None, description="재질 등급")
|
||||
schedule: Optional[str] = Field(None, description="스케줄")
|
||||
size_spec: Optional[str] = Field(None, description="사이즈 규격")
|
||||
quantity: float = Field(description="수량")
|
||||
unit: str = Field(description="단위")
|
||||
classification_confidence: Optional[float] = Field(None, description="분류 신뢰도")
|
||||
is_verified: bool = Field(default=False, description="검증 여부")
|
||||
|
||||
model_config = ConfigDict(
|
||||
json_schema_extra={
|
||||
"example": {
|
||||
"id": 1,
|
||||
"file_id": 1,
|
||||
"line_number": 5,
|
||||
"original_description": "PIPE, SEAMLESS, A333-6, 6\", SCH40",
|
||||
"classified_category": "PIPE",
|
||||
"classified_subcategory": "SEAMLESS",
|
||||
"material_grade": "A333-6",
|
||||
"schedule": "SCH40",
|
||||
"size_spec": "6\"",
|
||||
"quantity": 12.5,
|
||||
"unit": "EA",
|
||||
"classification_confidence": 0.95,
|
||||
"is_verified": False
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
class MaterialListResponse(BaseResponse):
|
||||
"""자재 목록 응답 모델"""
|
||||
success: bool = Field(True, description="요청 성공 여부")
|
||||
data: List[MaterialInfo] = Field(description="자재 목록")
|
||||
total_count: int = Field(description="전체 자재 수")
|
||||
file_info: Optional[FileInfo] = Field(None, description="파일 정보")
|
||||
cache_hit: bool = Field(default=False, description="캐시 히트 여부")
|
||||
|
||||
|
||||
# ================================
|
||||
# 작업 관련 모델
|
||||
# ================================
|
||||
|
||||
class JobInfo(BaseModel):
|
||||
"""작업 정보 모델"""
|
||||
job_no: str = Field(description="작업 번호")
|
||||
job_name: str = Field(description="작업명")
|
||||
client_name: Optional[str] = Field(None, description="고객사명")
|
||||
end_user: Optional[str] = Field(None, description="최종 사용자")
|
||||
epc_company: Optional[str] = Field(None, description="EPC 회사")
|
||||
status: JobStatus = Field(description="작업 상태")
|
||||
created_at: datetime = Field(description="생성 일시")
|
||||
file_count: int = Field(default=0, description="파일 수")
|
||||
material_count: int = Field(default=0, description="자재 수")
|
||||
|
||||
model_config = ConfigDict(
|
||||
json_schema_extra={
|
||||
"example": {
|
||||
"job_no": "TK-2025-001",
|
||||
"job_name": "석유화학 플랜트 배관 프로젝트",
|
||||
"client_name": "한국석유화학",
|
||||
"end_user": "울산공장",
|
||||
"epc_company": "현대엔지니어링",
|
||||
"status": "active",
|
||||
"created_at": "2025-01-01T09:00:00",
|
||||
"file_count": 3,
|
||||
"material_count": 450
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
class JobListResponse(BaseResponse):
|
||||
"""작업 목록 응답 모델"""
|
||||
success: bool = Field(True, description="요청 성공 여부")
|
||||
data: List[JobInfo] = Field(description="작업 목록")
|
||||
total_count: int = Field(description="전체 작업 수")
|
||||
cache_hit: bool = Field(default=False, description="캐시 히트 여부")
|
||||
|
||||
|
||||
# ================================
|
||||
# 분류 관련 모델
|
||||
# ================================
|
||||
|
||||
class ClassificationResult(BaseModel):
|
||||
"""분류 결과 모델"""
|
||||
category: MaterialCategory = Field(description="분류된 카테고리")
|
||||
subcategory: Optional[str] = Field(None, description="세부 분류")
|
||||
confidence: float = Field(description="분류 신뢰도 (0.0-1.0)")
|
||||
material_grade: Optional[str] = Field(None, description="재질 등급")
|
||||
size_spec: Optional[str] = Field(None, description="사이즈 규격")
|
||||
schedule: Optional[str] = Field(None, description="스케줄")
|
||||
details: Optional[Dict[str, Any]] = Field(None, description="분류 상세 정보")
|
||||
|
||||
model_config = ConfigDict(
|
||||
json_schema_extra={
|
||||
"example": {
|
||||
"category": "PIPE",
|
||||
"subcategory": "SEAMLESS",
|
||||
"confidence": 0.95,
|
||||
"material_grade": "A333-6",
|
||||
"size_spec": "6\"",
|
||||
"schedule": "SCH40",
|
||||
"details": {
|
||||
"matched_keywords": ["PIPE", "SEAMLESS", "A333-6"],
|
||||
"size_detected": True,
|
||||
"material_detected": True
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
class ClassificationResponse(BaseResponse):
|
||||
"""분류 응답 모델"""
|
||||
success: bool = Field(True, description="분류 성공 여부")
|
||||
data: ClassificationResult = Field(description="분류 결과")
|
||||
processing_time: float = Field(description="처리 시간 (초)")
|
||||
cache_hit: bool = Field(default=False, description="캐시 히트 여부")
|
||||
|
||||
|
||||
# ================================
|
||||
# 통계 관련 모델
|
||||
# ================================
|
||||
|
||||
class MaterialStatistics(BaseModel):
|
||||
"""자재 통계 모델"""
|
||||
category: MaterialCategory = Field(description="자재 카테고리")
|
||||
count: int = Field(description="개수")
|
||||
percentage: float = Field(description="비율 (%)")
|
||||
total_quantity: float = Field(description="총 수량")
|
||||
unique_items: int = Field(description="고유 항목 수")
|
||||
|
||||
|
||||
class ProjectStatistics(BaseModel):
|
||||
"""프로젝트 통계 모델"""
|
||||
job_no: str = Field(description="작업 번호")
|
||||
total_materials: int = Field(description="총 자재 수")
|
||||
total_files: int = Field(description="총 파일 수")
|
||||
category_breakdown: List[MaterialStatistics] = Field(description="카테고리별 분석")
|
||||
classification_accuracy: float = Field(description="분류 정확도")
|
||||
verified_percentage: float = Field(description="검증 완료율")
|
||||
|
||||
model_config = ConfigDict(
|
||||
json_schema_extra={
|
||||
"example": {
|
||||
"job_no": "TK-2025-001",
|
||||
"total_materials": 450,
|
||||
"total_files": 3,
|
||||
"category_breakdown": [
|
||||
{
|
||||
"category": "PIPE",
|
||||
"count": 180,
|
||||
"percentage": 40.0,
|
||||
"total_quantity": 1250.5,
|
||||
"unique_items": 45
|
||||
}
|
||||
],
|
||||
"classification_accuracy": 0.92,
|
||||
"verified_percentage": 0.75
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
class StatisticsResponse(BaseResponse):
|
||||
"""통계 응답 모델"""
|
||||
success: bool = Field(True, description="요청 성공 여부")
|
||||
data: ProjectStatistics = Field(description="통계 데이터")
|
||||
cache_hit: bool = Field(default=False, description="캐시 히트 여부")
|
||||
|
||||
|
||||
# ================================
|
||||
# 시스템 관련 모델
|
||||
# ================================
|
||||
|
||||
class CacheInfo(BaseModel):
|
||||
"""캐시 정보 모델"""
|
||||
status: str = Field(description="캐시 상태")
|
||||
used_memory: str = Field(description="사용 메모리")
|
||||
connected_clients: int = Field(description="연결된 클라이언트 수")
|
||||
hit_rate: float = Field(description="캐시 히트율 (%)")
|
||||
total_commands: int = Field(description="총 명령 수")
|
||||
|
||||
|
||||
class SystemHealthResponse(BaseResponse):
|
||||
"""시스템 상태 응답 모델"""
|
||||
success: bool = Field(True, description="요청 성공 여부")
|
||||
data: Dict[str, Any] = Field(description="시스템 상태 정보")
|
||||
cache_info: Optional[CacheInfo] = Field(None, description="캐시 정보")
|
||||
database_status: str = Field(description="데이터베이스 상태")
|
||||
api_version: str = Field(description="API 버전")
|
||||
|
||||
|
||||
# ================================
|
||||
# 유니온 타입 (여러 응답 타입)
|
||||
# ================================
|
||||
|
||||
# API 응답으로 사용할 수 있는 모든 타입
|
||||
APIResponse = Union[
|
||||
SuccessResponse,
|
||||
ErrorResponse,
|
||||
FileListResponse,
|
||||
FileDeleteResponse,
|
||||
MaterialListResponse,
|
||||
JobListResponse,
|
||||
ClassificationResponse,
|
||||
StatisticsResponse,
|
||||
SystemHealthResponse
|
||||
]
|
||||
Reference in New Issue
Block a user