# models/database.py from sqlalchemy import Column, Integer, String, Text, DateTime, JSON, LargeBinary, Boolean, Float, ForeignKey from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.sql import func from sqlalchemy.orm import relationship from datetime import datetime Base = declarative_base() class User(Base): """用户表""" __tablename__ = "users" id = Column(Integer, primary_key=True, index=True) username = Column(String(50), unique=True, index=True, nullable=False) email = Column(String(255), unique=True, index=True, nullable=False) hashed_password = Column(String(255), nullable=False) full_name = Column(String(100)) is_active = Column(Boolean, default=True) is_superuser = Column(Boolean, default=False) created_at = Column(DateTime, default=func.now()) last_login = Column(DateTime, nullable=True) # 关联关系 stp_files = relationship("STPFile", back_populates="user") def __repr__(self): return f"" class STPFile(Base): """STP源文件元数据表""" __tablename__ = "stp_files" id = Column(Integer, primary_key=True, index=True) user_id = Column(Integer, ForeignKey("users.id"), nullable=True, index=True) # 对象存储信息 object_key = Column(String(500), nullable=False, index=True) # MinIO对象键 storage_bucket = Column(String(100), nullable=False) # 存储桶名称 object_url = Column(String(1000), nullable=True) # 预签名URL(可选) # 文件信息 original_filename = Column(String(255), nullable=False) file_size = Column(Integer, nullable=False) file_hash = Column(String(64), unique=True, index=True) # SHA256 hash mime_type = Column(String(50), default="application/octet-stream") # 时间戳 upload_time = Column(DateTime, default=func.now()) processed_time = Column(DateTime, nullable=True) # 状态 status = Column(String(20), default="pending") # pending, processing, completed, failed error_message = Column(Text, nullable=True) # 保留旧字段以兼容 file_path = Column(String(500), nullable=True) # 本地路径(已弃用) file_content = Column(LargeBinary, nullable=True) # 本地存储(已弃用) filename = Column(String(255), nullable=True) # 已弃用 # 关联关系 user = relationship("User", back_populates="stp_files") geometry_data = relationship("GeometryData", back_populates="stp_file", uselist=False) # 网格数据:一条 STP 记录对应一条网格摘要记录(详细 JSON 在 RustFS) mesh_data = relationship("MeshData", back_populates="stp_file", uselist=False) mold_cavity_data = relationship("MoldCavityData", back_populates="stp_file", uselist=False) html_file = relationship("HTMLFile", back_populates="stp_file", uselist=False) def __repr__(self): return f"" class GeometryData(Base): """几何数据JSON元数据表""" __tablename__ = "geometry_data" id = Column(Integer, primary_key=True, index=True) stp_file_id = Column(Integer, ForeignKey("stp_files.id"), nullable=False, index=True) # 对象存储信息 object_key = Column(String(500), nullable=False) storage_bucket = Column(String(100), nullable=False) object_url = Column(String(1000), nullable=True) # 分析方法 analysis_method = Column(String(50), default="pythonocc") # pythonocc, simulated # 时间戳 created_time = Column(DateTime, default=func.now()) # 几何属性摘要(便于快速查询) volume = Column(Float, nullable=True) surface_area = Column(Float, nullable=True) bounding_box_min = Column(JSON, nullable=True) bounding_box_max = Column(JSON, nullable=True) center_of_mass = Column(JSON, nullable=True) # 拓扑信息 topology_faces = Column(Integer, nullable=True) topology_edges = Column(Integer, nullable=True) topology_vertices = Column(Integer, nullable=True) # 关联关系 stp_file = relationship("STPFile", back_populates="geometry_data") def __repr__(self): return f"" class MeshData(Base): """网格数据JSON元数据表(详细网格存 RustFS,PostgreSQL 存摘要)""" __tablename__ = "mesh_data" id = Column(Integer, primary_key=True, index=True) stp_file_id = Column(Integer, ForeignKey("stp_files.id"), nullable=False, index=True) # 对象存储信息 object_key = Column(String(500), nullable=False) storage_bucket = Column(String(100), nullable=False) object_url = Column(String(1000), nullable=True) # 生成设置 quality = Column(String(20), default="medium") # low / medium / high # 网格规模信息 vertex_count = Column(Integer, nullable=True) face_count = Column(Integer, nullable=True) point_count = Column(Integer, nullable=True) # 采样点云数量 # 网格边界框(便于快速查询) bounding_box_min = Column(JSON, nullable=True) bounding_box_max = Column(JSON, nullable=True) # 时间戳 created_time = Column(DateTime, default=func.now()) # 关联关系 stp_file = relationship("STPFile", back_populates="mesh_data") def __repr__(self): return f"" class HTMLFile(Base): """网页文件元数据表""" __tablename__ = "html_files" id = Column(Integer, primary_key=True, index=True) stp_file_id = Column(Integer, ForeignKey("stp_files.id"), nullable=False, index=True) # 对象存储信息 object_key = Column(String(500), nullable=False) storage_bucket = Column(String(100), nullable=False) object_url = Column(String(1000), nullable=True) # 文件信息 filename = Column(String(255), nullable=False) generated_time = Column(DateTime, default=func.now()) # 可视化相关元数据 visualization_type = Column(String(50), default="3d_viewer") has_interactive_elements = Column(Boolean, default=True) # 保留旧字段以兼容 file_path = Column(String(500), nullable=True) html_content = Column(Text, nullable=True) # 关联关系 stp_file = relationship("STPFile", back_populates="html_file") def __repr__(self): return f"" class ProcessingTask(Base): """处理任务记录表""" __tablename__ = "processing_tasks" id = Column(Integer, primary_key=True, index=True) task_id = Column(String(36), unique=True, index=True, nullable=False) stp_file_id = Column(Integer, ForeignKey("stp_files.id"), nullable=False, index=True) # 任务类型和状态 task_type = Column(String(50), default="stp_parsing") # stp_parsing, geometry_analysis, mold_generation status = Column(String(20), default="pending") # pending, processing, completed, failed # 时间戳 created_time = Column(DateTime, default=func.now()) started_time = Column(DateTime, nullable=True) completed_time = Column(DateTime, nullable=True) # 处理进度 progress = Column(Integer, default=0) # 0-100 current_step = Column(String(100), nullable=True) # 错误信息 error_message = Column(Text, nullable=True) error_stack = Column(Text, nullable=True) # 处理参数 parameters = Column(JSON, nullable=True) # 任务参数 def __repr__(self): return f"" class MoldCavityData(Base): """模具型腔数据元数据表""" __tablename__ = "mold_cavity_data" id = Column(Integer, primary_key=True, index=True) stp_file_id = Column(Integer, ForeignKey("stp_files.id"), nullable=False, index=True) # 对象存储信息 detailed_object_key = Column(String(500), nullable=False) # 完整三维数据 storage_bucket = Column(String(100), nullable=False) # 模具类型和材料 mold_material = Column(String(100), default="Aluminum Alloy 7075") mold_type = Column(String(50), default="single_cavity") # single_cavity, multi_cavity # 工艺参数 shrinkage_rate = Column(Float, nullable=False) draft_angle = Column(Float, nullable=False) parting_line_length = Column(Float, nullable=True) # 生成时间 generated_time = Column(DateTime, default=func.now()) # 关键信息摘要(快速查询字段) cavity_key_info = Column(JSON, nullable=True) # 完整关键信息 # 提取的字段(便于查询和排序) mold_size_length = Column(Float, nullable=True) mold_size_width = Column(Float, nullable=True) mold_size_height = Column(Float, nullable=True) estimated_clamping_force = Column(String(50), nullable=True) product_weight = Column(String(50), nullable=True) product_volume = Column(Float, nullable=True) wall_thickness_range = Column(String(50), nullable=True) complexity_score = Column(Float, nullable=True) # 质量评估 weld_line_risk = Column(String(50), nullable=True) # 熔接痕风险 sink_mark_risk = Column(String(50), nullable=True) # 缩痕风险 warpage_risk = Column(String(50), nullable=True) # 翘曲风险 # 关联关系 stp_file = relationship("STPFile", back_populates="mold_cavity_data") def __repr__(self): return f"" class FeatureDetection(Base): """特征检测结果表""" __tablename__ = "feature_detections" id = Column(Integer, primary_key=True, index=True) stp_file_id = Column(Integer, ForeignKey("stp_files.id"), nullable=False, index=True) # 特征信息 feature_type = Column(String(50), nullable=False, index=True) # thin_wall, thick_wall, rib, boss, draft_angle confidence = Column(Float, nullable=False) # 0.0 - 1.0 # 位置和尺寸 location = Column(JSON, nullable=True) # [x, y, z] dimensions = Column(JSON, nullable=True) # [length, width, height] # 特征参数 parameters = Column(JSON, nullable=True) # 自定义参数 # 检测时间 detected_at = Column(DateTime, default=func.now()) # 关联的几何数据 geometry_data_id = Column(Integer, ForeignKey("geometry_data.id"), nullable=True) def __repr__(self): return f"" class DesignRecommendation(Base): """设计建议表""" __tablename__ = "design_recommendations" id = Column(Integer, primary_key=True, index=True) stp_file_id = Column(Integer, ForeignKey("stp_files.id"), nullable=False, index=True) # 建议信息 rec_type = Column(String(50), nullable=False) # wall_thickness, draft_angle, etc. priority = Column(String(20), nullable=False) # high, medium, low description = Column(String(500), nullable=False) reason = Column(Text, nullable=True) # 建议参数 parameters = Column(JSON, nullable=True) # 状态 status = Column(String(20), default="pending") # pending, accepted, rejected user_notes = Column(Text, nullable=True) # 时间戳 created_at = Column(DateTime, default=func.now()) updated_at = Column(DateTime, nullable=True) def __repr__(self): return f"" class UserActivity(Base): """用户活动日志表""" __tablename__ = "user_activities" id = Column(Integer, primary_key=True, index=True) user_id = Column(Integer, ForeignKey("users.id"), nullable=False, index=True) # 活动信息 activity_type = Column(String(50), nullable=False, index=True) # upload, view, download, delete, export resource_type = Column(String(50), nullable=True) # stp_file, geometry_data, mold_cavity resource_id = Column(Integer, nullable=True) # 活动详情 description = Column(Text, nullable=True) meta_data = Column(JSON, nullable=True) # 时间戳 created_at = Column(DateTime, default=func.now(), index=True) # IP和设备信息 ip_address = Column(String(45), nullable=True) user_agent = Column(String(500), nullable=True) def __repr__(self): return f"" class SystemLog(Base): """系统日志表(重要操作和错误)""" __tablename__ = "system_logs" id = Column(Integer, primary_key=True, index=True) # 日志级别 level = Column(String(20), nullable=False, index=True) # INFO, WARNING, ERROR, CRITICAL # 日志信息 message = Column(Text, nullable=False) module = Column(String(100), nullable=True) # 模块名 function_name = Column(String(100), nullable=True) # 时间戳 created_at = Column(DateTime, default=func.now(), index=True) # 用户信息(如果有关联用户) user_id = Column(Integer, ForeignKey("users.id"), nullable=True) # 额外信息 request_id = Column(String(100), nullable=True) # 关联的请求ID execution_time_ms = Column(Integer, nullable=True) # 执行时间 # 关联数据 resource_type = Column(String(50), nullable=True) resource_id = Column(Integer, nullable=True) def __repr__(self): return f""