329 lines
12 KiB
Python
329 lines
12 KiB
Python
|
|
# 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"<User(id={self.id}, username='{self.username}', email='{self.email}')>"
|
||
|
|
|
||
|
|
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)
|
||
|
|
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"<STPFile(id={self.id}, object_key='{self.object_key}', status='{self.status}')>"
|
||
|
|
|
||
|
|
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"<GeometryData(id={self.id}, stp_file_id={self.stp_file_id})>"
|
||
|
|
|
||
|
|
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"<HTMLFile(id={self.id}, stp_file_id={self.stp_file_id}, object_key='{self.object_key}')>"
|
||
|
|
|
||
|
|
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"<ProcessingTask(id={self.id}, task_id='{self.task_id}', status='{self.status}')>"
|
||
|
|
|
||
|
|
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"<MoldCavityData(stp_file_id={self.stp_file_id}, mold_material='{self.mold_material}')>"
|
||
|
|
|
||
|
|
|
||
|
|
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"<FeatureDetection(id={self.id}, feature_type='{self.feature_type}', confidence={self.confidence})>"
|
||
|
|
|
||
|
|
|
||
|
|
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"<DesignRecommendation(id={self.id}, rec_type='{self.rec_type}', priority='{self.priority}')>"
|
||
|
|
|
||
|
|
|
||
|
|
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"<UserActivity(id={self.id}, user_id={self.user_id}, activity_type='{self.activity_type}')>"
|
||
|
|
|
||
|
|
|
||
|
|
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"<SystemLog(id={self.id}, level='{self.level}', module='{self.module}')>"
|
||
|
|
|