94 lines
3.7 KiB
Python
94 lines
3.7 KiB
Python
"""运输计划 - 安排运输时车辆/人员/证件资源快照 Schema"""
|
|
from __future__ import annotations
|
|
|
|
from datetime import date, datetime
|
|
from decimal import Decimal
|
|
from typing import Any, Optional
|
|
|
|
from pydantic import BaseModel, Field, field_validator
|
|
|
|
|
|
class SnapshotCertRead(BaseModel):
|
|
"""证书快照(车辆主车/挂车、人员证件)"""
|
|
|
|
id: str
|
|
cert_id: Optional[str] = Field(default=None, alias="certId")
|
|
vehicle_part: Optional[str] = Field(default=None, alias="vehiclePart")
|
|
cert_name: str = Field(alias="certName")
|
|
cert_no: Optional[str] = Field(default=None, alias="certNo")
|
|
expire_date: Optional[date] = Field(default=None, alias="expireDate")
|
|
remind_days: int = Field(default=30, alias="remindDays")
|
|
attachment_urls: Optional[str] = Field(default=None, alias="attachmentUrls")
|
|
owner_type: Optional[str] = Field(default=None, alias="ownerType")
|
|
owner_id: Optional[str] = Field(default=None, alias="ownerId")
|
|
status: Optional[str] = None
|
|
|
|
model_config = {"populate_by_name": True}
|
|
|
|
|
|
class SnapshotPersonnelRow(BaseModel):
|
|
"""车辆关联人员快照(含人员证件)"""
|
|
|
|
personnel_id: Optional[str] = Field(default=None, alias="personnelId")
|
|
personnel_name: str = Field(alias="personnelName")
|
|
phone_no: Optional[str] = Field(default=None, alias="phoneNo")
|
|
age: Optional[int] = None
|
|
id_card_no: Optional[str] = Field(default=None, alias="idCardNo")
|
|
role: str
|
|
is_default: int = Field(default=0, alias="isDefault")
|
|
remark: Optional[str] = None
|
|
cert_list: list[SnapshotCertRead] = Field(default_factory=list, alias="certList")
|
|
|
|
model_config = {"populate_by_name": True}
|
|
|
|
|
|
class TransportResourceSnapshot(BaseModel):
|
|
"""安排运输时的车辆+人员+证件整包快照"""
|
|
|
|
snapshotted_at: datetime = Field(alias="snapshottedAt")
|
|
vehicle_id: str = Field(alias="vehicleId")
|
|
vehicle_type: str = Field(alias="vehicleType")
|
|
carrier_id: Optional[str] = Field(default=None, alias="carrierId")
|
|
carrier_name: Optional[str] = Field(default=None, alias="carrierName")
|
|
license_plate: str = Field(alias="licensePlate")
|
|
trailer_no: Optional[str] = Field(default=None, alias="trailerNo")
|
|
load_capacity: Optional[Decimal] = Field(default=None, alias="loadCapacity")
|
|
vehicle_status: str = Field(alias="vehicleStatus")
|
|
cert_list: list[SnapshotCertRead] = Field(default_factory=list, alias="certList")
|
|
personnel_list: list[SnapshotPersonnelRow] = Field(default_factory=list, alias="personnelList")
|
|
personnel_summary: Optional[str] = Field(default=None, alias="personnelSummary")
|
|
selected_driver_id: Optional[str] = Field(default=None, alias="selectedDriverId")
|
|
|
|
model_config = {"populate_by_name": True}
|
|
|
|
|
|
def parse_resource_snapshot(raw: Any) -> Optional[TransportResourceSnapshot]:
|
|
"""从 ORM 文本列或 dict 解析快照。"""
|
|
if raw is None:
|
|
return None
|
|
if isinstance(raw, TransportResourceSnapshot):
|
|
return raw
|
|
if isinstance(raw, str):
|
|
text = raw.strip()
|
|
if not text:
|
|
return None
|
|
return TransportResourceSnapshot.model_validate_json(text)
|
|
if isinstance(raw, dict):
|
|
return TransportResourceSnapshot.model_validate(raw)
|
|
return None
|
|
|
|
|
|
class PlanResourceSnapshotMixin(BaseModel):
|
|
trailer_no: Optional[str] = Field(default=None, alias="trailerNo")
|
|
resource_snapshot: Optional[TransportResourceSnapshot] = Field(
|
|
default=None, alias="resourceSnapshot"
|
|
)
|
|
|
|
model_config = {"populate_by_name": True}
|
|
|
|
@field_validator("resource_snapshot", mode="before")
|
|
@classmethod
|
|
def _coerce_resource_snapshot(cls, value: Any) -> Any:
|
|
parsed = parse_resource_snapshot(value)
|
|
return parsed.model_dump(by_alias=True) if parsed else None
|