feat(models): Task/Product 模型扩充 + 4个Alembic迁移

Task 模型:
- 新增 received_at, completed_at, reject_reason, is_rework
- 新增 TaskRecord 模型 (remark + images JSON)
- 状态常量 PENDING/WIP/COMPLETED/REJECTED/ARCHIVED
- 全部时间字段改用北京时间 (get_beijing_time)

Product 模型:
- order_id → 可选; 新增 external_serial, current_location_id
- 新增 material_name/spec_model/category/material_type 快照字段
- 新增 overall_status (备货/生产/测试/维修/在库)

迁移:
- b2c3: HEX计数器Sequence + external_serial + order_id nullable
- c3d4: material快照4列
- d4e5: overall_status
- e5f6: task_records表
This commit is contained in:
2026-08-05 14:00:23 +08:00
parent 82f474f71f
commit e0c01a562f
9 changed files with 216 additions and 12 deletions

View File

@ -2,12 +2,13 @@
from app.models.base import Base
from app.models.production_order import ProductionOrder
from app.models.product import Product
from app.models.task import Task
from app.models.task import Task, TaskRecord
from app.models.task_log import TaskLog
__all__ = [
"Base",
"ProductionOrder",
"Product",
"Task",
"TaskRecord",
"TaskLog",
]

View File

@ -1,11 +1,12 @@
"""产品模型"""
import uuid
from datetime import datetime, timezone
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
class Product(Base):
@ -18,15 +19,33 @@ class Product(Base):
String(16), unique=True, index=True, nullable=False, comment="产品序列号(16位)",
)
# ---- 物理外键(关联本库 production_orders ----
order_id: Mapped[uuid.UUID] = mapped_column(
UUID(as_uuid=True), ForeignKey("production_orders.id"), nullable=False, comment="所属订单ID",
# ---- 物理外键(关联本库 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(
@ -41,8 +60,12 @@ class Product(Base):
status: Mapped[str] = mapped_column(
String(50), nullable=False, default="pending", comment="产品状态",
)
# 宏观流转状态 — 首次扫码时强制设定:备货/生产/测试/维修/在库
overall_status: Mapped[str | None] = mapped_column(
String(20), nullable=True, comment="宏观状态: 备货/生产/测试/维修/在库",
)
created_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), default=lambda: datetime.now(timezone.utc), comment="创建时间",
DateTime(timezone=True), default=get_beijing_time, comment="创建时间",
)
# ---- 关系 ----

View File

@ -1,11 +1,12 @@
"""生产订单模型"""
import uuid
from datetime import datetime, timezone
from datetime import datetime
from sqlalchemy import String, DateTime
from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.orm import Mapped, mapped_column
from app.models.base import Base
from app.core.time_utils import get_beijing_time
class ProductionOrder(Base):
@ -24,7 +25,7 @@ class ProductionOrder(Base):
String(50), nullable=False, default="pending", comment="订单状态",
)
created_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), default=lambda: datetime.now(timezone.utc), comment="创建时间",
DateTime(timezone=True), default=get_beijing_time, comment="创建时间",
)
def __repr__(self) -> str:

View File

@ -1,11 +1,12 @@
"""任务模型 — 支持无限嵌套、任务裂变分支、返工闭环"""
import uuid
from datetime import datetime, timezone
from datetime import datetime
from sqlalchemy import String, DateTime, Boolean, 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
# 任务状态常量
TASK_STATUS_PENDING = "PENDING" # 待接收
@ -52,7 +53,7 @@ class Task(Base):
# ---- 时间追踪 ----
created_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), default=lambda: datetime.now(timezone.utc), comment="创建时间",
DateTime(timezone=True), default=get_beijing_time, comment="创建时间",
)
received_at: Mapped[datetime | None] = mapped_column(
DateTime(timezone=True), nullable=True, comment="操作员确认接收时间",
@ -77,6 +78,39 @@ class Task(Base):
child_tasks: Mapped[list["Task"]] = relationship(
"Task", back_populates="parent_task", lazy="selectin",
)
records: Mapped[list["TaskRecord"]] = relationship(
"TaskRecord", back_populates="task", lazy="selectin", cascade="all, delete-orphan",
)
def __repr__(self) -> str:
return f"<Task {self.task_name}>"
# ============================================================
# 任务进度记录 — 随时备注/传图
# ============================================================
class TaskRecord(Base):
__tablename__ = "task_records"
id: Mapped[int] = mapped_column(
primary_key=True, autoincrement=True,
)
task_id: Mapped[uuid.UUID] = mapped_column(
UUID(as_uuid=True), ForeignKey("tasks.id"), nullable=False, index=True, comment="所属任务ID",
)
remark: Mapped[str | None] = mapped_column(
String(2000), nullable=True, comment="备注文本",
)
images: Mapped[str | None] = mapped_column(
String(4000), nullable=True, comment="图片URL列表(JSON字符串)",
)
created_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), default=get_beijing_time, comment="记录时间",
)
# ---- 关系 ----
task: Mapped["Task"] = relationship("Task", back_populates="records")
def __repr__(self) -> str:
return f"<TaskRecord {self.id} @ {self.created_at}>"

View File

@ -1,11 +1,12 @@
"""任务操作日志模型"""
import uuid
from datetime import datetime, timezone
from datetime import datetime
from sqlalchemy import String, DateTime, ForeignKey, Text
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
class TaskLog(Base):
@ -34,7 +35,7 @@ class TaskLog(Base):
)
created_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), default=lambda: datetime.now(timezone.utc), comment="创建时间",
DateTime(timezone=True), default=get_beijing_time, comment="创建时间",
)
# ---- 关系 ----