Files
track-LICA/backend/app/models/product.py
duxingchen 3286a11bc7 chore: fork from IRIS track 供 LICA 部门独立运行
- 复制来源: /home/yueli/track @ 192c8ee (feature/ai-audit-update)
- 组织隔离目标: LICA
- 端口规划: 前端 8030 / 后端 8031 / 数据库 8032
- 已排除 deploy.sh、deploy_full.sh、docker-compose.prod.yml(IRIS 生产发布脚本)
- 已排除工作区未提交改动,取干净的 192c8ee 状态
2026-09-21 15:56:52 +08:00

87 lines
3.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""产品模型"""
import uuid
from datetime import datetime
from sqlalchemy import String, DateTime, ForeignKey
from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.orm import Mapped, mapped_column, relationship
from app.models.base import Base
from app.core.time_utils import get_beijing_time
from app.core.lifecycle import LIFECYCLE_PRODUCTION
class Product(Base):
__tablename__ = "products"
id: Mapped[uuid.UUID] = mapped_column(
UUID(as_uuid=True), primary_key=True, default=uuid.uuid4,
)
serial_number: Mapped[str] = mapped_column(
String(16), unique=True, index=True, nullable=False, comment="产品序列号(16位)",
)
# ---- 物理外键(关联本库 production_orders— 可选 ----
order_id: Mapped[uuid.UUID | None] = mapped_column(
UUID(as_uuid=True), ForeignKey("production_orders.id"), nullable=True, comment="所属订单ID(可选)",
)
# ---- 外部序列号(用户自定义,可选) ----
external_serial: Mapped[str | None] = mapped_column(
String(64), nullable=True, comment="用户自定义产品序列号(可选)",
)
# ---- 逻辑外键(关联老系统物料表,仅存储 ID无物理约束 ----
material_id: Mapped[str | None] = mapped_column(
String(64), nullable=True, comment="物料ID(逻辑外键→老系统)",
)
# ---- MOM 物料快照字段(创产品时写入,防止老系统数据变动影响标签) ----
material_name: Mapped[str | None] = mapped_column(
String(255), nullable=True, comment="物料名称快照",
)
spec_model: Mapped[str | None] = mapped_column(
String(255), nullable=True, comment="规格型号快照",
)
category: Mapped[str | None] = mapped_column(
String(255), nullable=True, comment="物料分类快照",
)
material_type: Mapped[str | None] = mapped_column(
String(100), nullable=True, comment="物料类型快照",
)
# ---- 物理外键(自引用:父产品) ----
parent_product_id: Mapped[uuid.UUID | None] = mapped_column(
UUID(as_uuid=True), ForeignKey("products.id"), nullable=True, comment="父产品ID",
)
# ---- 当前位置追踪逻辑外键→用户ID 或 'virtual_warehouse' ----
current_location_id: Mapped[str | None] = mapped_column(
String(64), nullable=True, comment="当前持有者ID 或 'virtual_warehouse'(仓库)",
)
status: Mapped[str] = mapped_column(
String(50), nullable=False, default="pending", comment="产品状态",
)
# 宏观流转状态 — 首次扫码时强制设定:备货/生产/测试/维修/待仓库收货/已入库/已出库
overall_status: Mapped[str | None] = mapped_column(
String(20), nullable=True, comment="宏观状态: 备货/生产/测试/维修/待仓库收货/已入库/已出库",
)
# 生命周期阶段 — 生产制造 vs 出库后返厂售后。
# 设备出库后再次被派发任务 = 回流返厂,此处切为 AFTER_SALES 且不再回退。
lifecycle_phase: Mapped[str] = mapped_column(
String(20), nullable=False, default=LIFECYCLE_PRODUCTION,
server_default=LIFECYCLE_PRODUCTION, index=True,
comment="生命周期阶段: PRODUCTION(生产制造) | AFTER_SALES(出库后返厂售后)",
)
created_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), default=get_beijing_time, comment="创建时间",
)
# ---- 关系 ----
order: Mapped["ProductionOrder"] = relationship("ProductionOrder", lazy="selectin")
parent_product: Mapped["Product | None"] = relationship(
"Product", remote_side="Product.id", lazy="selectin",
)
def __repr__(self) -> str:
return f"<Product {self.serial_number}>"