# models/database.py from sqlalchemy import Column, Integer, String, Text, DateTime, Date, JSON, LargeBinary, Boolean, Float, ForeignKey, UniqueConstraint, Numeric from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.sql import func from sqlalchemy.orm import relationship from datetime import datetime, date Base = declarative_base() class User(Base): """用户表""" __tablename__ = "users" __excluded_fields__ = {'hashed_password'} 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) created_at = Column(DateTime, default=func.now()) last_login = Column(DateTime, nullable=True) stp_files = relationship("STPFile", back_populates="user") user_roles = relationship("UserRole", back_populates="user", cascade="all, delete-orphan") @property def roles(self): return [ur.role for ur in self.user_roles] @property def is_superuser(self): return any(r.code == 'admin' for r in self.roles) def has_permission(self, permission_code: str) -> bool: if self.is_superuser: return True for role in self.roles: for perm in role.permissions: if perm.code == permission_code: return True return False def safe_dict(self): return {k: v for k, v in self.__dict__.items() if not k.startswith('_') and k not in self.__excluded_fields__} def __repr__(self): return f"" class Role(Base): """角色表""" __tablename__ = "roles" id = Column(Integer, primary_key=True, index=True) code = Column(String(50), unique=True, index=True, nullable=False) name = Column(String(100), nullable=False) description = Column(Text, nullable=True) is_system = Column(Boolean, default=False) created_at = Column(DateTime, default=func.now()) user_roles = relationship("UserRole", back_populates="role", cascade="all, delete-orphan") role_permissions = relationship("RolePermission", back_populates="role", cascade="all, delete-orphan") @property def permissions(self): return [rp.permission for rp in self.role_permissions] def __repr__(self): return f"" class Permission(Base): """权限表""" __tablename__ = "permissions" id = Column(Integer, primary_key=True, index=True) code = Column(String(100), unique=True, index=True, nullable=False) name = Column(String(100), nullable=False) module = Column(String(50), nullable=True) description = Column(Text, nullable=True) created_at = Column(DateTime, default=func.now()) role_permissions = relationship("RolePermission", back_populates="permission", cascade="all, delete-orphan") def __repr__(self): return f"" class UserRole(Base): """用户角色关联表""" __tablename__ = "user_roles" id = Column(Integer, primary_key=True, index=True) user_id = Column(Integer, ForeignKey("users.id"), nullable=False, index=True) role_id = Column(Integer, ForeignKey("roles.id"), nullable=False, index=True) created_at = Column(DateTime, default=func.now()) user = relationship("User", back_populates="user_roles") role = relationship("Role", back_populates="user_roles") def __repr__(self): return f"" class RolePermission(Base): """角色权限关联表""" __tablename__ = "role_permissions" id = Column(Integer, primary_key=True, index=True) role_id = Column(Integer, ForeignKey("roles.id"), nullable=False, index=True) permission_id = Column(Integer, ForeignKey("permissions.id"), nullable=False, index=True) created_at = Column(DateTime, default=func.now()) role = relationship("Role", back_populates="role_permissions") permission = relationship("Permission", back_populates="role_permissions") 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, index=True) # 添加索引支持按文件名查询 file_size = Column(Integer, nullable=False) file_hash = Column(String(64), index=True) # 移除unique约束,允许同一文件多次上传 mime_type = Column(String(50), default="application/octet-stream") # 上传批次标识 - 用于区分同一文件的多次上传 upload_batch = Column(String(36), index=True) # UUID批次号 # 时间戳 upload_time = Column(DateTime, default=func.now()) processed_time = Column(DateTime, nullable=True) # 状态 status = Column(String(20), default="pending", index=True) # pending, processing, completed, failed error_message = Column(Text, nullable=True) # 分析摘要 - 快速查询字段 volume = Column(Float, nullable=True) # 体积 mm³ surface_area = Column(Float, nullable=True) # 表面积 mm² product_weight = Column(Float, nullable=True) # 产品重量 g # 保留旧字段以兼容 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) 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) analysis_metrics = relationship("AnalysisMetrics", back_populates="stp_file", uselist=False) feature_detections = relationship("FeatureDetection", back_populates="stp_file") design_recommendations = relationship("DesignRecommendation", back_populates="stp_file") processing_tasks = relationship("ProcessingTask", back_populates="stp_file") 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) # 任务参数 # 关联关系 stp_file = relationship("STPFile", back_populates="processing_tasks") 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) # 翘曲风险 # 多方案可信化摘要(第1周阶段1) best_scheme_id = Column(String(64), nullable=True, index=True) confidence_score = Column(Float, nullable=True) is_fallback = Column(Boolean, nullable=True, index=True) fallback_reason = Column(Text, 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, wall_non_uniform, rib, boss, draft_angle, high_curvature, fillet 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) # 关联关系 stp_file = relationship("STPFile", back_populates="feature_detections") 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) # 关联关系 stp_file = relationship("STPFile", back_populates="design_recommendations") 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"" class Product(Base): """产品表""" __tablename__ = "products" id = Column(Integer, primary_key=True, index=True) sku = Column(String(50), unique=True, index=True, nullable=False) name = Column(String(200), nullable=False) description = Column(Text, nullable=True) category = Column(String(100), nullable=True) unit = Column(String(20), default="件") item_type = Column(String(20), default="finished", index=True) cost_price = Column(Numeric(12, 2), default=0) sale_price = Column(Numeric(12, 2), default=0) min_stock = Column(Integer, default=0) max_stock = Column(Integer, default=1000) is_active = Column(Boolean, default=True) created_at = Column(DateTime, default=func.now()) updated_at = Column(DateTime, default=func.now(), onupdate=func.now()) inventory = relationship("Inventory", back_populates="product", uselist=False) stock_movements = relationship("StockMovement", back_populates="product") bom_materials = relationship( "ProductMaterial", foreign_keys="ProductMaterial.finished_product_id", back_populates="finished_product", cascade="all, delete-orphan" ) used_in_products = relationship( "ProductMaterial", foreign_keys="ProductMaterial.material_product_id", back_populates="material_product" ) def __repr__(self): return f"" class ProductMaterial(Base): __tablename__ = "product_materials" __table_args__ = ( UniqueConstraint("finished_product_id", "material_product_id", name="uq_product_material_unique"), ) id = Column(Integer, primary_key=True, index=True) finished_product_id = Column(Integer, ForeignKey("products.id"), nullable=False, index=True) material_product_id = Column(Integer, ForeignKey("products.id"), nullable=False, index=True) quantity = Column(Numeric(12, 4), nullable=False) loss_rate = Column(Numeric(5, 4), default=0) created_at = Column(DateTime, default=func.now()) updated_at = Column(DateTime, default=func.now(), onupdate=func.now()) finished_product = relationship( "Product", foreign_keys=[finished_product_id], back_populates="bom_materials" ) material_product = relationship( "Product", foreign_keys=[material_product_id], back_populates="used_in_products" ) def __repr__(self): return f"" class MaterialPriceHistory(Base): """物料价格历史表""" __tablename__ = "material_price_history" id = Column(Integer, primary_key=True, index=True) product_id = Column(Integer, ForeignKey("products.id"), nullable=False, index=True) price = Column(Numeric(12, 2), nullable=False) effective_date = Column(DateTime, default=func.now(), index=True) supplier_id = Column(Integer, ForeignKey("suppliers.id"), nullable=True, index=True) remark = Column(Text, nullable=True) created_at = Column(DateTime, default=func.now()) product = relationship("Product", backref="price_history") supplier = relationship("Supplier", backref="price_history") def __repr__(self): return f"" class MaterialSupplier(Base): """物料供应商关联表""" __tablename__ = "material_suppliers" id = Column(Integer, primary_key=True, index=True) product_id = Column(Integer, ForeignKey("products.id"), nullable=False, index=True) supplier_id = Column(Integer, ForeignKey("suppliers.id"), nullable=False, index=True) is_primary = Column(Boolean, default=False) contact_person = Column(String(100), nullable=True) contact_phone = Column(String(50), nullable=True) lead_time = Column(Integer, nullable=True) # 交货周期(天) min_order_quantity = Column(Integer, nullable=True) created_at = Column(DateTime, default=func.now()) updated_at = Column(DateTime, default=func.now(), onupdate=func.now()) product = relationship("Product", backref="suppliers") supplier = relationship("Supplier", backref="materials") def __repr__(self): return f"" class Supplier(Base): """供应商表""" __tablename__ = "suppliers" id = Column(Integer, primary_key=True, index=True) code = Column(String(50), unique=True, index=True) name = Column(String(200), nullable=False) contact_person = Column(String(100), nullable=True) phone = Column(String(50), nullable=True) email = Column(String(100), nullable=True) address = Column(Text, nullable=True) bank_name = Column(String(100), nullable=True) bank_account = Column(String(50), nullable=True) tax_number = Column(String(50), nullable=True) is_active = Column(Boolean, default=True) created_at = Column(DateTime, default=func.now()) updated_at = Column(DateTime, default=func.now(), onupdate=func.now()) purchase_orders = relationship("PurchaseOrder", back_populates="supplier") def __repr__(self): return f"" class Customer(Base): """客户表""" __tablename__ = "customers" id = Column(Integer, primary_key=True, index=True) code = Column(String(50), unique=True, index=True) name = Column(String(200), nullable=False) contact_person = Column(String(100), nullable=True) phone = Column(String(50), nullable=True) email = Column(String(100), nullable=True) address = Column(Text, nullable=True) bank_name = Column(String(100), nullable=True) bank_account = Column(String(50), nullable=True) tax_number = Column(String(50), nullable=True) credit_limit = Column(Numeric(12, 2), default=0) is_active = Column(Boolean, default=True) created_at = Column(DateTime, default=func.now()) updated_at = Column(DateTime, default=func.now(), onupdate=func.now()) sales_orders = relationship("SalesOrder", back_populates="customer") def __repr__(self): return f"" class Warehouse(Base): """仓库表""" __tablename__ = "warehouses" id = Column(Integer, primary_key=True, index=True) code = Column(String(50), unique=True, index=True) name = Column(String(200), nullable=False) address = Column(Text, nullable=True) manager = Column(String(100), nullable=True) phone = Column(String(50), nullable=True) is_active = Column(Boolean, default=True) is_default = Column(Boolean, default=False) created_at = Column(DateTime, default=func.now()) inventories = relationship("Inventory", back_populates="warehouse") def __repr__(self): return f"" class Inventory(Base): """库存表""" __tablename__ = "inventory" id = Column(Integer, primary_key=True, index=True) product_id = Column(Integer, ForeignKey("products.id"), nullable=False, index=True) warehouse_id = Column(Integer, ForeignKey("warehouses.id"), nullable=False, index=True) quantity = Column(Numeric(12, 4), default=0) locked_quantity = Column(Numeric(12, 4), default=0) batch_number = Column(String(50), nullable=True) location = Column(String(100), nullable=True) updated_at = Column(DateTime, default=func.now(), onupdate=func.now()) product = relationship("Product", back_populates="inventory") warehouse = relationship("Warehouse", back_populates="inventories") def __repr__(self): return f"" @property def available_quantity(self): return self.quantity - self.locked_quantity class StockMovement(Base): """库存变动记录表""" __tablename__ = "stock_movements" id = Column(Integer, primary_key=True, index=True) product_id = Column(Integer, ForeignKey("products.id"), nullable=False, index=True) warehouse_id = Column(Integer, ForeignKey("warehouses.id"), nullable=False) movement_type = Column(String(20), nullable=False) quantity = Column(Numeric(12, 4), nullable=False) before_quantity = Column(Numeric(12, 4), default=0) after_quantity = Column(Numeric(12, 4), default=0) reference_type = Column(String(50), nullable=True) reference_id = Column(Integer, nullable=True) reference_no = Column(String(50), nullable=True) unit_price = Column(Numeric(12, 2), nullable=True) total_amount = Column(Numeric(12, 2), nullable=True) remark = Column(Text, nullable=True) operator_id = Column(Integer, ForeignKey("users.id"), nullable=True) created_at = Column(DateTime, default=func.now(), index=True) product = relationship("Product", back_populates="stock_movements") def __repr__(self): return f"" class PurchaseOrder(Base): """采购订单表""" __tablename__ = "purchase_orders" id = Column(Integer, primary_key=True, index=True) order_no = Column(String(50), unique=True, index=True, nullable=False) supplier_id = Column(Integer, ForeignKey("suppliers.id"), nullable=False, index=True) order_date = Column(DateTime, default=func.now()) expected_date = Column(Date, nullable=True) status = Column(String(20), default="draft") total_amount = Column(Numeric(12, 2), default=0) paid_amount = Column(Numeric(12, 2), default=0) remark = Column(Text, nullable=True) operator_id = Column(Integer, ForeignKey("users.id"), nullable=True) created_at = Column(DateTime, default=func.now()) updated_at = Column(DateTime, default=func.now(), onupdate=func.now()) # 状态变更时间 received_date = Column(DateTime, nullable=True) # 已收货时间 paid_date = Column(DateTime, nullable=True) # 已付款时间 supplier = relationship("Supplier", back_populates="purchase_orders") items = relationship("PurchaseOrderItem", back_populates="order", cascade="all, delete-orphan") def __repr__(self): return f"" class PurchaseOrderItem(Base): """采购订单明细表""" __tablename__ = "purchase_order_items" id = Column(Integer, primary_key=True, index=True) order_id = Column(Integer, ForeignKey("purchase_orders.id"), nullable=False) product_id = Column(Integer, ForeignKey("products.id"), nullable=False) quantity = Column(Integer, nullable=False) received_quantity = Column(Integer, default=0) unit_price = Column(Numeric(12, 2), nullable=False) amount = Column(Numeric(12, 2), nullable=False) remark = Column(Text, nullable=True) order = relationship("PurchaseOrder", back_populates="items") def __repr__(self): return f"" class SalesOrder(Base): """销售订单表""" __tablename__ = "sales_orders" id = Column(Integer, primary_key=True, index=True) order_no = Column(String(50), unique=True, index=True, nullable=False) customer_id = Column(Integer, ForeignKey("customers.id"), nullable=False, index=True) order_date = Column(DateTime, default=func.now()) delivery_date = Column(Date, nullable=True) manufacturing_date = Column(DateTime, nullable=True) actual_delivery_date = Column(DateTime, nullable=True) actual_payment_date = Column(DateTime, nullable=True) status = Column(String(20), default="draft") production_status = Column(String(20), default="not_started", index=True) production_no = Column(String(50), nullable=True, index=True) planned_material_cost = Column(Numeric(12, 2), default=0) actual_material_cost = Column(Numeric(12, 2), default=0) total_amount = Column(Numeric(12, 2), default=0) received_amount = Column(Numeric(12, 2), default=0) remark = Column(Text, nullable=True) operator_id = Column(Integer, ForeignKey("users.id"), nullable=True) created_at = Column(DateTime, default=func.now()) updated_at = Column(DateTime, default=func.now(), onupdate=func.now()) customer = relationship("Customer", back_populates="sales_orders") items = relationship("SalesOrderItem", back_populates="order", cascade="all, delete-orphan") def __repr__(self): return f"" class FinanceTransaction(Base): __tablename__ = "finance_transactions" id = Column(Integer, primary_key=True, index=True) txn_no = Column(String(50), unique=True, index=True, nullable=False) txn_type = Column(String(20), nullable=False, index=True) partner_type = Column(String(20), nullable=False, index=True) partner_id = Column(Integer, nullable=False, index=True) amount = Column(Numeric(12, 2), nullable=False) txn_date = Column(DateTime, default=func.now(), index=True) method = Column(String(30), default="bank") account_name = Column(String(100), nullable=True) status = Column(String(20), default="confirmed", index=True) remark = Column(Text, nullable=True) operator_id = Column(Integer, ForeignKey("users.id"), nullable=True) created_at = Column(DateTime, default=func.now(), index=True) allocations = relationship("FinanceAllocation", back_populates="transaction", cascade="all, delete-orphan") def __repr__(self): return f"" class FinanceAllocation(Base): __tablename__ = "finance_allocations" id = Column(Integer, primary_key=True, index=True) transaction_id = Column(Integer, ForeignKey("finance_transactions.id"), nullable=False, index=True) order_type = Column(String(20), nullable=False, index=True) order_id = Column(Integer, nullable=False, index=True) allocated_amount = Column(Numeric(12, 2), nullable=False) created_at = Column(DateTime, default=func.now(), index=True) transaction = relationship("FinanceTransaction", back_populates="allocations") def __repr__(self): return f"" class AnalysisMetrics(Base): """分析指标表""" __tablename__ = "analysis_metrics" id = Column(Integer, primary_key=True, index=True) stp_file_id = Column(Integer, ForeignKey("stp_files.id"), nullable=False, index=True) # 质量指标 volume_utilization = Column(Float, default=0) # 体积利用率 topology_complexity = Column(Float, default=0) # 拓扑复杂度 wall_uniformity = Column(Float, default=0) # 壁厚均匀性 # 分析摘要 analysis_summary = Column(Text, nullable=True) # FreeCAD 验证结果 verification_status = Column(String(20), nullable=True) # passed, failed, pending, error verification_volume_diff = Column(Float, nullable=True) # 体积差异百分比 verification_area_diff = Column(Float, nullable=True) # 表面积差异百分比 verification_details = Column(JSON, nullable=True) # 完整验证结果 # 时间戳 created_at = Column(DateTime, default=func.now()) # 关联关系 stp_file = relationship("STPFile", back_populates="analysis_metrics") def __repr__(self): return f"" class SalesOrderItem(Base): """销售订单明细表""" __tablename__ = "sales_order_items" id = Column(Integer, primary_key=True, index=True) order_id = Column(Integer, ForeignKey("sales_orders.id"), nullable=False) product_id = Column(Integer, ForeignKey("products.id"), nullable=False) quantity = Column(Integer, nullable=False) delivered_quantity = Column(Integer, default=0) unit_price = Column(Numeric(12, 2), nullable=False) amount = Column(Numeric(12, 2), nullable=False) remark = Column(Text, nullable=True) order = relationship("SalesOrder", back_populates="items") def __repr__(self): return f""