Phase 3 완료: 파일 처리 시스템 구축

 주요 완성 기능:
- 프로젝트 생성 API (project_name 필드 포함)
- 엑셀 파일 업로드 및 파싱 시스템
- 자재 DB 저장 (2837개 자재 성공 저장)
- 자재 조회 및 요약 통계 API
- 외래키 관계 정상 동작 (projects -> files -> materials)

📊 테스트 결과:
- MP7 PIPING PROJECT Rev.2 프로젝트 생성
- 00.MP7 PIPING R.2_BOM.XLS 파일 업로드 성공
- NIPPLE, PIPE 등 자재 분류 및 재질 추출
- ASTM A106, SCH 80, 1인치 사이즈 등 정확 파싱

🛠️ 기술 스택:
- FastAPI + PostgreSQL + SQLAlchemy
- pandas를 활용한 엑셀 파싱
- 외래키 제약조건 적용된 정규화 DB 설계
This commit is contained in:
Hyungi Ahn
2025-07-14 13:19:24 +09:00
parent 2f9107ca55
commit 13c375477a
5 changed files with 499 additions and 68 deletions

View File

@@ -1,23 +1,25 @@
from fastapi import FastAPI, Depends, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from sqlalchemy.orm import Session
from sqlalchemy import text
from typing import List
from datetime import datetime
from . import models, schemas
from .database import SessionLocal, engine, get_db
from .database import get_db, engine
from .models import Base, Project
from .schemas import ProjectCreate, ProjectResponse
from .api import files
# 데이터베이스 테이블 생성 (이미 존재하면 무시)
models.Base.metadata.create_all(bind=engine)
Base.metadata.create_all(bind=engine)
app = FastAPI(
title="TK-MP BOM System API",
description="BOM (Bill of Materials) 시스템 API - 실제 데이터베이스 연동",
version="2.0.0"
title="TK-MP-Project API",
description="BOM 시스템 개발 프로젝트 - Phase 3: 파일 처리 시스템",
version="1.0.0",
docs_url="/docs",
redoc_url="/redoc"
)
# CORS 설정
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
@@ -26,62 +28,101 @@ app.add_middleware(
allow_headers=["*"],
)
app.include_router(files.router, prefix="/api/files", tags=["파일 관리"])
@app.get("/")
async def root():
return {
"message": "TK-MP BOM System API",
"version": "2.0.0",
"message": "TK-MP-Project API Server",
"version": "1.0.0 - Phase 3",
"status": "running",
"database": "PostgreSQL 연결됨"
"timestamp": datetime.now().isoformat(),
"new_features": [
"✅ Phase 1: 기반 시스템 구축",
"✅ Phase 2: 데이터베이스 연동",
"🔄 Phase 3: 파일 처리 시스템 개발 중"
]
}
@app.get("/health")
async def health_check(db: Session = Depends(get_db)):
try:
# 데이터베이스 연결 테스트 (SQLAlchemy 2.0 문법)
result = db.execute(text("SELECT 1"))
result = db.execute(text("SELECT 1 as test"))
test_value = result.fetchone()[0]
return {
"status": "healthy",
"database": "connected",
"api": "operational",
"version": "2.0.0",
"db_test": "SUCCESS"
"status": "healthy",
"database": "connected",
"test_query": test_value == 1,
"timestamp": datetime.now().isoformat(),
"phase": "Phase 3 - 파일 처리 시스템"
}
except Exception as e:
return {
"status": "error",
"database": "disconnected",
"error": str(e)
}
raise HTTPException(status_code=500, detail=f"데이터베이스 연결 실패: {str(e)}")
# 프로젝트 API
@app.get("/api/projects", response_model=schemas.ProjectResponse)
@app.get("/api/projects", response_model=List[ProjectResponse])
async def get_projects(db: Session = Depends(get_db)):
try:
projects = db.query(models.Project).all()
return {
"projects": projects,
"total": len(projects),
"message": f"{len(projects)}개 프로젝트 조회됨"
}
result = db.execute(text("""
SELECT id, official_project_code, project_name, design_project_code,
is_code_matched, status, created_at, updated_at
FROM projects
ORDER BY created_at DESC
"""))
projects = result.fetchall()
return [
ProjectResponse(
id=project.id,
official_project_code=project.official_project_code,
project_name=project.project_name,
design_project_code=project.design_project_code,
is_code_matched=project.is_code_matched,
status=project.status,
created_at=project.created_at,
updated_at=project.updated_at
)
for project in projects
]
except Exception as e:
raise HTTPException(status_code=500, detail=f"데이터베이스 오류: {str(e)}")
raise HTTPException(status_code=500, detail=f"프로젝트 조회 실패: {str(e)}")
@app.post("/api/projects", response_model=schemas.Project)
async def create_project(project: schemas.ProjectCreate, db: Session = Depends(get_db)):
@app.post("/api/projects", response_model=ProjectResponse)
async def create_project(project: ProjectCreate, db: Session = Depends(get_db)):
try:
db_project = models.Project(**project.dict())
db.add(db_project)
insert_query = text("""
INSERT INTO projects (official_project_code, project_name, design_project_code, is_code_matched, status, created_at)
VALUES (:official_code, :project_name, :design_code, :is_matched, :status, :created_at)
RETURNING id, official_project_code, project_name, design_project_code, is_code_matched, status, created_at, updated_at
""")
result = db.execute(insert_query, {
"official_code": project.official_project_code,
"project_name": project.project_name,
"design_code": project.design_project_code,
"is_matched": project.is_code_matched,
"status": project.status,
"created_at": datetime.now()
})
new_project = result.fetchone()
db.commit()
db.refresh(db_project)
return db_project
return ProjectResponse(
id=new_project.id,
official_project_code=new_project.official_project_code,
project_name=new_project.project_name,
design_project_code=new_project.design_project_code,
is_code_matched=new_project.is_code_matched,
status=new_project.status,
created_at=new_project.created_at,
updated_at=new_project.updated_at
)
except Exception as e:
db.rollback()
raise HTTPException(status_code=500, detail=f"프로젝트 생성 오류: {str(e)}")
raise HTTPException(status_code=500, detail=f"프로젝트 생성 실패: {str(e)}")
@app.get("/api/projects/{project_id}", response_model=schemas.Project)
async def get_project(project_id: int, db: Session = Depends(get_db)):
project = db.query(models.Project).filter(models.Project.id == project_id).first()
if project is None:
raise HTTPException(status_code=404, detail="프로젝트를 찾을 수 없습니다")
return project
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000, reload=True)