✅ 백엔드 구조 개선: - DatabaseService: 공통 DB 쿼리 로직 통합 - FileUploadService: 파일 업로드 로직 모듈화 및 트랜잭션 관리 개선 - 서비스 레이어 패턴 도입으로 코드 재사용성 향상 ✅ 프론트엔드 컴포넌트 개선: - LoadingSpinner, ErrorMessage, ConfirmDialog 공통 컴포넌트 생성 - 재사용 가능한 컴포넌트 라이브러리 구축 - deprecated/backup 파일들 완전 제거 ✅ 성능 최적화: - optimize_database.py: 핵심 DB 인덱스 자동 생성 - 쿼리 최적화 및 통계 업데이트 자동화 - VACUUM ANALYZE 자동 실행 ✅ 코드 정리: - 개별 SQL 마이그레이션 파일들을 legacy/ 폴더로 정리 - 중복된 마이그레이션 스크립트 정리 - 깔끔하고 체계적인 프로젝트 구조 완성 ✅ 자동 마이그레이션 시스템 강화: - complete_migrate.py: SQLAlchemy 기반 완전한 마이그레이션 - analyze_and_fix_schema.py: 백엔드 코드 분석 기반 스키마 수정 - fix_missing_tables.py: 누락된 테이블/컬럼 자동 생성 - start.sh: 배포 시 자동 실행 순서 최적화
This commit is contained in:
@@ -34,11 +34,15 @@ class File(Base):
|
||||
filename = Column(String(255), nullable=False)
|
||||
original_filename = Column(String(255), nullable=False)
|
||||
file_path = Column(String(500), nullable=False)
|
||||
job_no = Column(String(50)) # 작업 번호
|
||||
revision = Column(String(20), default='Rev.0')
|
||||
bom_name = Column(String(200)) # BOM 이름
|
||||
description = Column(Text) # 파일 설명
|
||||
upload_date = Column(DateTime, default=datetime.utcnow)
|
||||
uploaded_by = Column(String(100))
|
||||
file_type = Column(String(10))
|
||||
file_size = Column(Integer)
|
||||
parsed_count = Column(Integer) # 파싱된 자재 수
|
||||
is_active = Column(Boolean, default=True)
|
||||
|
||||
# 관계 설정
|
||||
@@ -51,22 +55,40 @@ class Material(Base):
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
file_id = Column(Integer, ForeignKey("files.id"))
|
||||
line_number = Column(Integer)
|
||||
row_number = Column(Integer) # 업로드 시 행 번호
|
||||
original_description = Column(Text, nullable=False)
|
||||
classified_category = Column(String(50))
|
||||
classified_subcategory = Column(String(100))
|
||||
material_grade = Column(String(50))
|
||||
full_material_grade = Column(Text) # 전체 재질명 (ASTM A312 TP304 등)
|
||||
schedule = Column(String(20))
|
||||
size_spec = Column(String(50))
|
||||
main_nom = Column(String(50)) # 주 사이즈 (4", 150A 등)
|
||||
red_nom = Column(String(50)) # 축소 사이즈 (Reducing 피팅/플랜지용)
|
||||
quantity = Column(Numeric(10, 3), nullable=False)
|
||||
unit = Column(String(10), nullable=False)
|
||||
# length = Column(Numeric(10, 3)) # 임시로 주석 처리
|
||||
length = Column(Numeric(10, 3)) # 길이 정보
|
||||
drawing_name = Column(String(100))
|
||||
area_code = Column(String(20))
|
||||
line_no = Column(String(50))
|
||||
classification_confidence = Column(Numeric(3, 2))
|
||||
classification_details = Column(JSON) # 분류 상세 정보 (JSON)
|
||||
is_verified = Column(Boolean, default=False)
|
||||
verified_by = Column(String(50))
|
||||
verified_at = Column(DateTime)
|
||||
|
||||
# 구매 관련 필드
|
||||
purchase_confirmed = Column(Boolean, default=False)
|
||||
confirmed_quantity = Column(Numeric(10, 3))
|
||||
purchase_status = Column(String(20))
|
||||
purchase_confirmed_by = Column(String(100))
|
||||
purchase_confirmed_at = Column(DateTime)
|
||||
|
||||
# 리비전 관리 필드
|
||||
revision_status = Column(String(20)) # 'new', 'changed', 'inventory', 'deleted_not_purchased'
|
||||
material_hash = Column(String(64)) # 자재 비교용 해시
|
||||
normalized_description = Column(Text) # 정규화된 설명
|
||||
|
||||
drawing_reference = Column(String(100))
|
||||
notes = Column(Text)
|
||||
created_at = Column(DateTime, default=datetime.utcnow)
|
||||
@@ -450,3 +472,349 @@ class MaterialTubingMapping(Base):
|
||||
# 관계 설정
|
||||
material = relationship("Material", backref="tubing_mappings")
|
||||
tubing_product = relationship("TubingProduct", back_populates="material_mappings")
|
||||
|
||||
class SupportDetails(Base):
|
||||
__tablename__ = "support_details"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
material_id = Column(Integer, ForeignKey("materials.id"), nullable=False)
|
||||
file_id = Column(Integer, ForeignKey("files.id"), nullable=False)
|
||||
|
||||
# 서포트 정보
|
||||
support_type = Column(String(50))
|
||||
support_subtype = Column(String(100))
|
||||
load_rating = Column(String(50))
|
||||
load_capacity = Column(String(50))
|
||||
material_standard = Column(String(50))
|
||||
material_grade = Column(String(50))
|
||||
pipe_size = Column(String(50))
|
||||
|
||||
# 치수 정보
|
||||
length_mm = Column(Numeric(10, 2))
|
||||
width_mm = Column(Numeric(10, 2))
|
||||
height_mm = Column(Numeric(10, 2))
|
||||
|
||||
# 분류 신뢰도
|
||||
classification_confidence = Column(Numeric(3, 2))
|
||||
|
||||
# 시간 정보
|
||||
created_at = Column(DateTime, default=datetime.utcnow)
|
||||
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
|
||||
|
||||
# 관계 설정
|
||||
material = relationship("Material")
|
||||
file = relationship("File")
|
||||
|
||||
class PurchaseRequestItems(Base):
|
||||
__tablename__ = "purchase_request_items"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
request_id = Column(String(50), nullable=False) # 구매신청 ID
|
||||
material_id = Column(Integer, ForeignKey("materials.id"), nullable=False)
|
||||
|
||||
# 수량 정보
|
||||
quantity = Column(Integer, nullable=False)
|
||||
unit = Column(String(10), nullable=False)
|
||||
user_requirement = Column(Text)
|
||||
|
||||
# 시간 정보
|
||||
created_at = Column(DateTime, default=datetime.utcnow)
|
||||
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
|
||||
|
||||
# 관계 설정
|
||||
material = relationship("Material")
|
||||
|
||||
class FittingDetails(Base):
|
||||
__tablename__ = "fitting_details"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
material_id = Column(Integer, ForeignKey("materials.id"), nullable=False)
|
||||
file_id = Column(Integer, ForeignKey("files.id"), nullable=False)
|
||||
|
||||
# 피팅 정보
|
||||
fitting_type = Column(String(50))
|
||||
fitting_subtype = Column(String(100))
|
||||
connection_type = Column(String(50))
|
||||
material_standard = Column(String(50))
|
||||
material_grade = Column(String(50))
|
||||
nominal_size = Column(String(50))
|
||||
wall_thickness = Column(String(50))
|
||||
|
||||
# 치수 정보
|
||||
length_mm = Column(Numeric(10, 2))
|
||||
width_mm = Column(Numeric(10, 2))
|
||||
height_mm = Column(Numeric(10, 2))
|
||||
|
||||
# 분류 신뢰도
|
||||
classification_confidence = Column(Numeric(3, 2))
|
||||
|
||||
# 시간 정보
|
||||
created_at = Column(DateTime, default=datetime.utcnow)
|
||||
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
|
||||
|
||||
# 관계 설정
|
||||
material = relationship("Material")
|
||||
file = relationship("File")
|
||||
|
||||
class FlangeDetails(Base):
|
||||
__tablename__ = "flange_details"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
material_id = Column(Integer, ForeignKey("materials.id"), nullable=False)
|
||||
file_id = Column(Integer, ForeignKey("files.id"), nullable=False)
|
||||
|
||||
# 플랜지 정보
|
||||
flange_type = Column(String(50))
|
||||
flange_subtype = Column(String(100))
|
||||
pressure_rating = Column(String(50))
|
||||
material_standard = Column(String(50))
|
||||
material_grade = Column(String(50))
|
||||
nominal_size = Column(String(50))
|
||||
|
||||
# 치수 정보
|
||||
outer_diameter = Column(Numeric(10, 2))
|
||||
thickness = Column(Numeric(10, 2))
|
||||
|
||||
# 분류 신뢰도
|
||||
classification_confidence = Column(Numeric(3, 2))
|
||||
|
||||
# 시간 정보
|
||||
created_at = Column(DateTime, default=datetime.utcnow)
|
||||
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
|
||||
|
||||
# 관계 설정
|
||||
material = relationship("Material")
|
||||
file = relationship("File")
|
||||
|
||||
class ValveDetails(Base):
|
||||
__tablename__ = "valve_details"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
material_id = Column(Integer, ForeignKey("materials.id"), nullable=False)
|
||||
file_id = Column(Integer, ForeignKey("files.id"), nullable=False)
|
||||
|
||||
# 밸브 정보
|
||||
valve_type = Column(String(50))
|
||||
valve_subtype = Column(String(100))
|
||||
connection_type = Column(String(50))
|
||||
actuation_type = Column(String(50))
|
||||
material_standard = Column(String(50))
|
||||
material_grade = Column(String(50))
|
||||
nominal_size = Column(String(50))
|
||||
pressure_rating = Column(String(50))
|
||||
|
||||
# 분류 신뢰도
|
||||
classification_confidence = Column(Numeric(3, 2))
|
||||
|
||||
# 시간 정보
|
||||
created_at = Column(DateTime, default=datetime.utcnow)
|
||||
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
|
||||
|
||||
# 관계 설정
|
||||
material = relationship("Material")
|
||||
file = relationship("File")
|
||||
|
||||
class GasketDetails(Base):
|
||||
__tablename__ = "gasket_details"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
material_id = Column(Integer, ForeignKey("materials.id"), nullable=False)
|
||||
file_id = Column(Integer, ForeignKey("files.id"), nullable=False)
|
||||
|
||||
# 가스켓 정보
|
||||
gasket_type = Column(String(50))
|
||||
material_standard = Column(String(50))
|
||||
material_grade = Column(String(50))
|
||||
nominal_size = Column(String(50))
|
||||
pressure_rating = Column(String(50))
|
||||
filler_material = Column(String(50))
|
||||
thickness = Column(Numeric(10, 2))
|
||||
|
||||
# 분류 신뢰도
|
||||
classification_confidence = Column(Numeric(3, 2))
|
||||
|
||||
# 시간 정보
|
||||
created_at = Column(DateTime, default=datetime.utcnow)
|
||||
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
|
||||
|
||||
# 관계 설정
|
||||
material = relationship("Material")
|
||||
file = relationship("File")
|
||||
|
||||
class BoltDetails(Base):
|
||||
__tablename__ = "bolt_details"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
material_id = Column(Integer, ForeignKey("materials.id"), nullable=False)
|
||||
file_id = Column(Integer, ForeignKey("files.id"), nullable=False)
|
||||
|
||||
# 볼트 정보
|
||||
bolt_type = Column(String(50))
|
||||
material_standard = Column(String(50))
|
||||
material_grade = Column(String(50))
|
||||
thread_size = Column(String(50))
|
||||
length = Column(Numeric(10, 2))
|
||||
pressure_rating = Column(String(50))
|
||||
|
||||
# 분류 신뢰도
|
||||
classification_confidence = Column(Numeric(3, 2))
|
||||
|
||||
# 시간 정보
|
||||
created_at = Column(DateTime, default=datetime.utcnow)
|
||||
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
|
||||
|
||||
# 관계 설정
|
||||
material = relationship("Material")
|
||||
file = relationship("File")
|
||||
|
||||
class InstrumentDetails(Base):
|
||||
__tablename__ = "instrument_details"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
material_id = Column(Integer, ForeignKey("materials.id"), nullable=False)
|
||||
file_id = Column(Integer, ForeignKey("files.id"), nullable=False)
|
||||
|
||||
# 계기 정보
|
||||
instrument_type = Column(String(50))
|
||||
instrument_subtype = Column(String(100))
|
||||
connection_type = Column(String(50))
|
||||
material_standard = Column(String(50))
|
||||
material_grade = Column(String(50))
|
||||
nominal_size = Column(String(50))
|
||||
|
||||
# 분류 신뢰도
|
||||
classification_confidence = Column(Numeric(3, 2))
|
||||
|
||||
# 시간 정보
|
||||
created_at = Column(DateTime, default=datetime.utcnow)
|
||||
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
|
||||
|
||||
# 관계 설정
|
||||
material = relationship("Material")
|
||||
file = relationship("File")
|
||||
|
||||
class PurchaseRequests(Base):
|
||||
__tablename__ = "purchase_requests"
|
||||
|
||||
request_id = Column(String(50), primary_key=True, index=True)
|
||||
request_no = Column(String(100), nullable=False)
|
||||
file_id = Column(Integer, ForeignKey("files.id"), nullable=False)
|
||||
job_no = Column(String(50), nullable=False)
|
||||
category = Column(String(50))
|
||||
material_count = Column(Integer)
|
||||
excel_file_path = Column(String(500))
|
||||
requested_by = Column(Integer, ForeignKey("users.user_id"))
|
||||
|
||||
# 시간 정보
|
||||
created_at = Column(DateTime, default=datetime.utcnow)
|
||||
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
|
||||
|
||||
# 관계 설정
|
||||
file = relationship("File")
|
||||
requested_by_user = relationship("User", foreign_keys=[requested_by])
|
||||
|
||||
class Jobs(Base):
|
||||
__tablename__ = "jobs"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
job_no = Column(String(50), unique=True, nullable=False)
|
||||
job_name = Column(String(200))
|
||||
status = Column(String(20), default='active')
|
||||
created_at = Column(DateTime, default=datetime.utcnow)
|
||||
|
||||
class PipeEndPreparations(Base):
|
||||
__tablename__ = "pipe_end_preparations"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
material_id = Column(Integer, ForeignKey("materials.id"), nullable=False)
|
||||
file_id = Column(Integer, ForeignKey("files.id"), nullable=False)
|
||||
end_prep_type = Column(String(50))
|
||||
end_prep_standard = Column(String(50))
|
||||
classification_confidence = Column(Numeric(3, 2))
|
||||
created_at = Column(DateTime, default=datetime.utcnow)
|
||||
|
||||
# 관계 설정
|
||||
material = relationship("Material")
|
||||
file = relationship("File")
|
||||
|
||||
class MaterialPurchaseTracking(Base):
|
||||
__tablename__ = "material_purchase_tracking"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
material_id = Column(Integer, ForeignKey("materials.id"), nullable=False)
|
||||
file_id = Column(Integer, ForeignKey("files.id"), nullable=False)
|
||||
purchase_status = Column(String(20))
|
||||
requested_quantity = Column(Integer)
|
||||
confirmed_quantity = Column(Integer)
|
||||
purchase_date = Column(DateTime)
|
||||
created_at = Column(DateTime, default=datetime.utcnow)
|
||||
|
||||
# 관계 설정
|
||||
material = relationship("Material")
|
||||
file = relationship("File")
|
||||
|
||||
class ExcelExports(Base):
|
||||
__tablename__ = "excel_exports"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
file_id = Column(Integer, ForeignKey("files.id"), nullable=False)
|
||||
export_type = Column(String(50))
|
||||
file_path = Column(String(500))
|
||||
exported_by = Column(Integer, ForeignKey("users.user_id"))
|
||||
created_at = Column(DateTime, default=datetime.utcnow)
|
||||
|
||||
# 관계 설정
|
||||
file = relationship("File")
|
||||
exported_by_user = relationship("User", foreign_keys=[exported_by])
|
||||
|
||||
class UserActivityLogs(Base):
|
||||
__tablename__ = "user_activity_logs"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
user_id = Column(Integer, ForeignKey("users.user_id"))
|
||||
activity_type = Column(String(50))
|
||||
activity_description = Column(Text)
|
||||
created_at = Column(DateTime, default=datetime.utcnow)
|
||||
|
||||
# 관계 설정
|
||||
user = relationship("User")
|
||||
|
||||
class ExcelExportHistory(Base):
|
||||
__tablename__ = "excel_export_history"
|
||||
|
||||
export_id = Column(String(50), primary_key=True, index=True)
|
||||
file_id = Column(Integer, ForeignKey("files.id"))
|
||||
job_no = Column(String(50))
|
||||
exported_by = Column(Integer, ForeignKey("users.user_id"))
|
||||
export_date = Column(DateTime, default=datetime.utcnow)
|
||||
|
||||
# 관계 설정
|
||||
file = relationship("File")
|
||||
exported_by_user = relationship("User", foreign_keys=[exported_by])
|
||||
|
||||
class ExportedMaterials(Base):
|
||||
__tablename__ = "exported_materials"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
export_id = Column(String(50), ForeignKey("excel_export_history.export_id"))
|
||||
material_id = Column(Integer, ForeignKey("materials.id"))
|
||||
quantity = Column(Integer)
|
||||
status = Column(String(20))
|
||||
|
||||
# 관계 설정
|
||||
export_history = relationship("ExcelExportHistory")
|
||||
material = relationship("Material")
|
||||
|
||||
class PurchaseStatusHistory(Base):
|
||||
__tablename__ = "purchase_status_history"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
material_id = Column(Integer, ForeignKey("materials.id"))
|
||||
old_status = Column(String(20))
|
||||
new_status = Column(String(20))
|
||||
changed_by = Column(Integer, ForeignKey("users.user_id"))
|
||||
changed_at = Column(DateTime, default=datetime.utcnow)
|
||||
|
||||
# 관계 설정
|
||||
material = relationship("Material")
|
||||
changed_by_user = relationship("User", foreign_keys=[changed_by])
|
||||
|
||||
Reference in New Issue
Block a user