| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394 |
- """运输计划管理 - 基础数据 Schema(承运商、车辆、人员、证书)"""
- from datetime import date, datetime
- from decimal import Decimal
- from typing import Optional
- from pydantic import BaseModel, Field
- from app.services.pl_carrier_service import (
- carrier_agreement_expired,
- carrier_qualification_complete,
- )
- # =============================================================================
- # 承运商 Schema
- # =============================================================================
- class CarrierBase(BaseModel):
- carrier_name: str = Field(..., max_length=200, alias="carrierName", description="承运商名称")
- contact_name: Optional[str] = Field(default=None, max_length=100, alias="contactName", description="联系人")
- contact_phone: Optional[str] = Field(default=None, max_length=32, alias="contactPhone", description="联系电话")
- status: str = Field(default="1", max_length=1, description="合作状态 1-合作中 0-已终止")
- remark: Optional[str] = Field(default=None, description="备注")
- class CarrierQualificationBase(BaseModel):
- business_license_urls: Optional[str] = Field(
- default=None, max_length=2000, alias="businessLicenseUrls", description="营业执照附件URL"
- )
- road_transport_license_urls: Optional[str] = Field(
- default=None,
- max_length=2000,
- alias="roadTransportLicenseUrls",
- description="道路经营许可证附件URL",
- )
- transport_agreement_urls: Optional[str] = Field(
- default=None, max_length=2000, alias="transportAgreementUrls", description="运输协议附件URL"
- )
- transport_agreement_expire_date: Optional[date] = Field(
- default=None, alias="transportAgreementExpireDate", description="运输协议有效期"
- )
- class CarrierCreate(CarrierBase, CarrierQualificationBase):
- model_config = {"populate_by_name": True}
- class CarrierUpdate(BaseModel):
- carrier_name: Optional[str] = Field(default=None, max_length=200, alias="carrierName")
- contact_name: Optional[str] = Field(default=None, max_length=100, alias="contactName")
- contact_phone: Optional[str] = Field(default=None, max_length=32, alias="contactPhone")
- status: Optional[str] = Field(default=None, max_length=1)
- remark: Optional[str] = None
- business_license_urls: Optional[str] = Field(
- default=None, max_length=2000, alias="businessLicenseUrls"
- )
- road_transport_license_urls: Optional[str] = Field(
- default=None, max_length=2000, alias="roadTransportLicenseUrls"
- )
- transport_agreement_urls: Optional[str] = Field(
- default=None, max_length=2000, alias="transportAgreementUrls"
- )
- transport_agreement_expire_date: Optional[date] = Field(
- default=None, alias="transportAgreementExpireDate"
- )
- model_config = {"populate_by_name": True}
- class CarrierAuditReject(BaseModel):
- remark: Optional[str] = Field(default=None, max_length=512, description="驳回原因")
- model_config = {"populate_by_name": True}
- class CarrierRead(CarrierBase, CarrierQualificationBase):
- id: str
- audit_status: str = Field(default="pending", alias="auditStatus")
- audit_by: Optional[str] = Field(default=None, alias="auditBy")
- audit_time: Optional[datetime] = Field(default=None, alias="auditTime")
- audit_remark: Optional[str] = Field(default=None, alias="auditRemark")
- qualification_complete: bool = Field(default=False, alias="qualificationComplete")
- transport_agreement_expired: bool = Field(default=False, alias="transportAgreementExpired")
- tenant_no: str = Field(alias="tenantNo")
- order_num: Optional[int] = Field(default=None, alias="orderNum")
- create_by: Optional[str] = Field(default=None, alias="createBy")
- create_dept_id: Optional[str] = Field(default=None, alias="createDeptId")
- create_time: datetime = Field(alias="createTime")
- update_by: Optional[str] = Field(default=None, alias="updateBy")
- update_time: datetime = Field(alias="updateTime")
- del_flag: int = Field(default=0, alias="delFlag")
- model_config = {"from_attributes": True, "populate_by_name": True}
- def carrier_to_read(carrier, today: Optional[date] = None) -> CarrierRead:
- complete = carrier_qualification_complete(carrier)
- expired = carrier_agreement_expired(carrier, today) if complete else False
- return CarrierRead(
- id=carrier.id,
- carrier_name=carrier.carrier_name,
- contact_name=carrier.contact_name,
- contact_phone=carrier.contact_phone,
- status=carrier.status,
- remark=carrier.remark,
- business_license_urls=carrier.business_license_urls,
- road_transport_license_urls=carrier.road_transport_license_urls,
- transport_agreement_urls=carrier.transport_agreement_urls,
- transport_agreement_expire_date=carrier.transport_agreement_expire_date,
- audit_status=carrier.audit_status or "pending",
- audit_by=carrier.audit_by,
- audit_time=carrier.audit_time,
- audit_remark=carrier.audit_remark,
- qualification_complete=complete,
- transport_agreement_expired=expired,
- tenant_no=carrier.tenant_no,
- order_num=carrier.order_num,
- create_by=carrier.create_by,
- create_dept_id=carrier.create_dept_id,
- create_time=carrier.create_time,
- update_by=carrier.update_by,
- update_time=carrier.update_time,
- del_flag=carrier.del_flag,
- )
- # =============================================================================
- # 子表行 Schema
- # =============================================================================
- class VehiclePersonnelRow(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
- role: str = Field(..., description="driver|escort")
- is_default: int = Field(default=0, alias="isDefault")
- remark: Optional[str] = None
- model_config = {"populate_by_name": True}
- class CertRow(BaseModel):
- cert_id: Optional[str] = Field(default=None, alias="certId")
- vehicle_part: Optional[str] = Field(
- default=None,
- alias="vehiclePart",
- description="车辆部位 head-主车 trailer-挂车,人员证书为空",
- )
- 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")
- model_config = {"populate_by_name": True}
- class CertRead(CertRow):
- id: str
- owner_type: str = Field(alias="ownerType")
- owner_id: str = Field(alias="ownerId")
- status: str
- tenant_no: str = Field(alias="tenantNo")
- create_time: datetime = Field(alias="createTime")
- update_time: datetime = Field(alias="updateTime")
- model_config = {"from_attributes": True, "populate_by_name": True}
- # =============================================================================
- # 车辆 Schema
- # =============================================================================
- class VehicleBase(BaseModel):
- vehicle_type: str = Field(..., alias="vehicleType", description="车辆类型 self-自有 carrier-承运商")
- carrier_id: Optional[str] = Field(default=None, alias="carrierId", description="承运商ID")
- carrier_name: Optional[str] = Field(default=None, max_length=200, alias="carrierName", description="承运商名称")
- license_plate: str = Field(..., max_length=20, alias="licensePlate", description="车牌号")
- trailer_no: Optional[str] = Field(default=None, max_length=20, alias="trailerNo", description="挂车号")
- default_driver_id: Optional[str] = Field(default=None, alias="defaultDriverId", description="默认司机ID(只读兼容)")
- default_driver_name: Optional[str] = Field(default=None, max_length=100, alias="defaultDriverName", description="默认司机姓名(只读兼容)")
- load_capacity: Optional[Decimal] = Field(default=None, alias="loadCapacity", description="载重(吨)")
- vehicle_status: str = Field(default="available", max_length=20, alias="vehicleStatus", description="车辆状态")
- photo_urls: Optional[str] = Field(
- default=None, max_length=2000, alias="photoUrls", description="车辆照片URL,逗号分隔(默认1张)"
- )
- remark: Optional[str] = None
- class VehicleCreate(VehicleBase):
- personnel_list: list[VehiclePersonnelRow] = Field(default_factory=list, alias="personnelList")
- cert_list: list[CertRow] = Field(default_factory=list, alias="certList")
- model_config = {"populate_by_name": True}
- class VehicleUpdate(BaseModel):
- vehicle_type: Optional[str] = Field(default=None, max_length=20, alias="vehicleType")
- carrier_id: Optional[str] = Field(default=None, alias="carrierId")
- carrier_name: Optional[str] = Field(default=None, max_length=200, alias="carrierName")
- license_plate: Optional[str] = Field(default=None, max_length=20, alias="licensePlate")
- trailer_no: Optional[str] = Field(default=None, max_length=20, alias="trailerNo")
- load_capacity: Optional[Decimal] = Field(default=None, alias="loadCapacity")
- vehicle_status: Optional[str] = Field(default=None, max_length=20, alias="vehicleStatus")
- photo_urls: Optional[str] = Field(
- default=None, max_length=2000, alias="photoUrls", description="车辆照片URL,逗号分隔(默认1张)"
- )
- remark: Optional[str] = None
- personnel_list: Optional[list[VehiclePersonnelRow]] = Field(default=None, alias="personnelList")
- cert_list: Optional[list[CertRow]] = Field(default=None, alias="certList")
- model_config = {"populate_by_name": True}
- class VehicleRead(VehicleBase):
- id: str
- tenant_no: str = Field(alias="tenantNo")
- order_num: Optional[int] = Field(default=None, alias="orderNum")
- personnel_summary: Optional[str] = Field(default=None, alias="personnelSummary")
- cert_list: Optional[list[CertRead]] = Field(default=None, alias="certList")
- create_by: Optional[str] = Field(default=None, alias="createBy")
- create_dept_id: Optional[str] = Field(default=None, alias="createDeptId")
- create_time: datetime = Field(alias="createTime")
- update_by: Optional[str] = Field(default=None, alias="updateBy")
- update_time: datetime = Field(alias="updateTime")
- del_flag: int = Field(default=0, alias="delFlag")
- model_config = {"from_attributes": True, "populate_by_name": True}
- class VehicleDetailRead(VehicleRead):
- personnel_list: list[VehiclePersonnelRow] = Field(default_factory=list, alias="personnelList")
- cert_list: list[CertRead] = Field(default_factory=list, alias="certList")
- model_config = {"from_attributes": True, "populate_by_name": True}
- # =============================================================================
- # 人员 Schema
- # =============================================================================
- class PersonnelBase(BaseModel):
- personnel_name: str = Field(..., max_length=100, alias="personnelName", description="姓名")
- phone_no: Optional[str] = Field(default=None, max_length=32, alias="phoneNo", description="手机号")
- age: Optional[int] = Field(default=None, description="年龄")
- id_card_no: Optional[str] = Field(default=None, max_length=32, alias="idCardNo", description="身份证号")
- personnel_type: str = Field(
- ...,
- max_length=20,
- alias="personnelType",
- description="人员类型 driver/escort/both",
- )
- personnel_affiliation: str = Field(
- default="self",
- max_length=20,
- alias="personnelAffiliation",
- description="人员归属 self-山峡 carrier-非山峡",
- )
- status: str = Field(default="1", max_length=1, description="状态 1-在职 0-停用")
- photo_urls: Optional[str] = Field(
- default=None, max_length=2000, alias="photoUrls", description="人员照片URL,逗号分隔(默认1张)"
- )
- remark: Optional[str] = None
- class PersonnelCreate(PersonnelBase):
- cert_list: list[CertRow] = Field(default_factory=list, alias="certList")
- model_config = {"populate_by_name": True}
- class PersonnelUpdate(BaseModel):
- personnel_name: Optional[str] = Field(default=None, max_length=100, alias="personnelName")
- phone_no: Optional[str] = Field(default=None, max_length=32, alias="phoneNo")
- age: Optional[int] = None
- id_card_no: Optional[str] = Field(default=None, max_length=32, alias="idCardNo")
- personnel_type: Optional[str] = Field(default=None, max_length=20, alias="personnelType")
- personnel_affiliation: Optional[str] = Field(
- default=None, max_length=20, alias="personnelAffiliation"
- )
- status: Optional[str] = Field(default=None, max_length=1)
- photo_urls: Optional[str] = Field(
- default=None, max_length=2000, alias="photoUrls", description="人员照片URL,逗号分隔(默认1张)"
- )
- remark: Optional[str] = None
- cert_list: Optional[list[CertRow]] = Field(default=None, alias="certList")
- model_config = {"populate_by_name": True}
- class PersonnelRead(PersonnelBase):
- id: str
- tenant_no: str = Field(alias="tenantNo")
- order_num: Optional[int] = Field(default=None, alias="orderNum")
- nearest_expire_date: Optional[date] = Field(default=None, alias="nearestExpireDate")
- cert_list: Optional[list[CertRead]] = Field(default=None, alias="certList")
- create_by: Optional[str] = Field(default=None, alias="createBy")
- create_dept_id: Optional[str] = Field(default=None, alias="createDeptId")
- create_time: datetime = Field(alias="createTime")
- update_by: Optional[str] = Field(default=None, alias="updateBy")
- update_time: datetime = Field(alias="updateTime")
- del_flag: int = Field(default=0, alias="delFlag")
- model_config = {"from_attributes": True, "populate_by_name": True}
- class PersonnelDetailRead(PersonnelRead):
- cert_list: list[CertRead] = Field(default_factory=list, alias="certList")
- model_config = {"from_attributes": True, "populate_by_name": True}
- # =============================================================================
- # 司机 Schema(兼容旧 API)
- # =============================================================================
- class DriverBase(BaseModel):
- driver_name: str = Field(..., max_length=100, alias="driverName", description="姓名")
- phone_no: Optional[str] = Field(default=None, max_length=32, alias="phoneNo", description="手机号")
- id_card_no: Optional[str] = Field(default=None, max_length=32, alias="idCardNo", description="身份证号")
- driving_license_no: Optional[str] = Field(default=None, max_length=32, alias="drivingLicenseNo", description="驾驶证号")
- driving_license_expire: Optional[date] = Field(default=None, alias="drivingLicenseExpire", description="驾驶证有效期")
- vehicle_license_no: Optional[str] = Field(default=None, max_length=32, alias="vehicleLicenseNo", description="行驶证号")
- vehicle_license_expire: Optional[date] = Field(default=None, alias="vehicleLicenseExpire", description="行驶证有效期")
- hazmat_cert_no: Optional[str] = Field(default=None, max_length=32, alias="hazmatCertNo", description="危化品证号")
- hazmat_cert_expire: Optional[date] = Field(default=None, alias="hazmatCertExpire", description="危化品证有效期")
- remark: Optional[str] = None
- class DriverCreate(DriverBase):
- model_config = {"populate_by_name": True}
- class DriverUpdate(BaseModel):
- driver_name: Optional[str] = Field(default=None, max_length=100, alias="driverName")
- phone_no: Optional[str] = Field(default=None, max_length=32, alias="phoneNo")
- id_card_no: Optional[str] = Field(default=None, max_length=32, alias="idCardNo")
- driving_license_no: Optional[str] = Field(default=None, max_length=32, alias="drivingLicenseNo")
- driving_license_expire: Optional[date] = Field(default=None, alias="drivingLicenseExpire")
- vehicle_license_no: Optional[str] = Field(default=None, max_length=32, alias="vehicleLicenseNo")
- vehicle_license_expire: Optional[date] = Field(default=None, alias="vehicleLicenseExpire")
- hazmat_cert_no: Optional[str] = Field(default=None, max_length=32, alias="hazmatCertNo")
- hazmat_cert_expire: Optional[date] = Field(default=None, alias="hazmatCertExpire")
- remark: Optional[str] = None
- model_config = {"populate_by_name": True}
- class DriverRead(DriverBase):
- id: str
- tenant_no: str = Field(alias="tenantNo")
- order_num: Optional[int] = Field(default=None, alias="orderNum")
- create_by: Optional[str] = Field(default=None, alias="createBy")
- create_dept_id: Optional[str] = Field(default=None, alias="createDeptId")
- create_time: datetime = Field(alias="createTime")
- update_by: Optional[str] = Field(default=None, alias="updateBy")
- update_time: datetime = Field(alias="updateTime")
- del_flag: int = Field(default=0, alias="delFlag")
- model_config = {"from_attributes": True, "populate_by_name": True}
- class DriverListResponse(BaseModel):
- total: int
- list: list[DriverRead]
- # =============================================================================
- # 车辆-司机关联 Schema(兼容)
- # =============================================================================
- class VehicleDriverCreate(BaseModel):
- vehicle_id: str = Field(..., alias="vehicleId", description="车辆ID")
- driver_id: str = Field(..., alias="driverId", description="司机ID")
- is_default: int = Field(default=0, alias="isDefault", description="是否默认 0-否 1-是")
- model_config = {"populate_by_name": True}
- class VehicleDriverRead(BaseModel):
- id: str
- vehicle_id: str = Field(alias="vehicleId")
- driver_id: str = Field(alias="driverId")
- is_default: int = Field(alias="isDefault")
- tenant_no: str = Field(alias="tenantNo")
- create_time: datetime = Field(alias="createTime")
- model_config = {"from_attributes": True, "populate_by_name": True}
- class CarrierListResponse(BaseModel):
- total: int
- list: list[CarrierRead]
- class VehicleListResponse(BaseModel):
- total: int
- list: list[VehicleRead]
- class PersonnelListResponse(BaseModel):
- total: int
- list: list[PersonnelRead]
|