| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325 |
- """运输计划 - 三方粘贴识别批量提交"""
- from __future__ import annotations
- from typing import Optional
- from fastapi import HTTPException
- from sqlalchemy.orm import Session
- from app.core.utils import generate_uuid
- from app.models.pl_models import PlCarrier, PlPersonnel, PlVehicle
- from app.schemas.pl.base_data import VehiclePersonnelRow
- from app.schemas.pl.paste_recognize import (
- ConflictAction,
- PasteCommitBody,
- PasteCommitResult,
- PastePersonnelIn,
- PasteVehicleIn,
- )
- from app.services.pl_personnel_service import save_vehicle_personnel
- def _conflict_map(conflicts: list[ConflictAction]) -> dict[str, ConflictAction]:
- result: dict[str, ConflictAction] = {}
- for item in conflicts:
- key = (item.key or "").strip()
- if not key:
- continue
- if key in result:
- raise HTTPException(status_code=400, detail=f"冲突项「{key}」重复")
- result[key] = item
- return result
- def _find_personnel_by_id_card(
- db: Session, tenant_no: str, id_card_no: Optional[str]
- ) -> Optional[PlPersonnel]:
- card = (id_card_no or "").strip().upper()
- if not card:
- return None
- return (
- db.query(PlPersonnel)
- .filter(
- PlPersonnel.tenant_no == tenant_no,
- PlPersonnel.id_card_no == card,
- PlPersonnel.del_flag == 0,
- )
- .first()
- )
- def _find_vehicle_by_plate(
- db: Session, tenant_no: str, license_plate: str
- ) -> Optional[PlVehicle]:
- plate = (license_plate or "").strip()
- if not plate:
- return None
- return (
- db.query(PlVehicle)
- .filter(
- PlVehicle.tenant_no == tenant_no,
- PlVehicle.license_plate == plate,
- PlVehicle.del_flag == 0,
- )
- .first()
- )
- def _get_personnel(
- db: Session, tenant_no: str, personnel_id: str
- ) -> PlPersonnel:
- row = (
- db.query(PlPersonnel)
- .filter(
- PlPersonnel.id == personnel_id,
- PlPersonnel.tenant_no == tenant_no,
- PlPersonnel.del_flag == 0,
- )
- .first()
- )
- if not row:
- raise HTTPException(status_code=400, detail=f"人员不存在:{personnel_id}")
- return row
- def _get_vehicle(db: Session, tenant_no: str, vehicle_id: str) -> PlVehicle:
- row = (
- db.query(PlVehicle)
- .filter(
- PlVehicle.id == vehicle_id,
- PlVehicle.tenant_no == tenant_no,
- PlVehicle.del_flag == 0,
- )
- .first()
- )
- if not row:
- raise HTTPException(status_code=400, detail=f"车辆不存在:{vehicle_id}")
- return row
- def _resolve_personnel(
- db: Session,
- tenant_no: str,
- *,
- key: str,
- draft: PastePersonnelIn,
- conflict: Optional[ConflictAction],
- user_id: str,
- dept_id: str,
- ) -> str:
- name = (draft.personnel_name or "").strip()
- if not name:
- raise HTTPException(status_code=400, detail=f"{key}姓名不能为空")
- existing = _find_personnel_by_id_card(db, tenant_no, draft.id_card_no)
- if existing and conflict is None:
- raise HTTPException(
- status_code=400,
- detail=f"人员「{name}」身份证已存在,请先选择复用/覆盖/取消",
- )
- if conflict is not None:
- if conflict.action == "cancel":
- raise HTTPException(status_code=400, detail="已取消")
- target = _get_personnel(db, tenant_no, conflict.existing_id)
- if conflict.action == "reuse":
- return target.id
- # overwrite
- target.personnel_name = name
- target.phone_no = (draft.phone_no or "").strip() or None
- card = (draft.id_card_no or "").strip().upper() or None
- if card:
- target.id_card_no = card
- target.personnel_type = draft.personnel_type
- target.personnel_affiliation = "carrier"
- target.update_by = user_id
- return target.id
- personnel = PlPersonnel(
- id=generate_uuid(),
- tenant_no=tenant_no,
- personnel_name=name,
- phone_no=(draft.phone_no or "").strip() or None,
- id_card_no=(draft.id_card_no or "").strip().upper() or None,
- personnel_type=draft.personnel_type,
- personnel_affiliation="carrier",
- status="1",
- create_by=user_id,
- create_dept_id=dept_id,
- )
- db.add(personnel)
- db.flush()
- return personnel.id
- def _resolve_vehicle(
- db: Session,
- tenant_no: str,
- *,
- draft: PasteVehicleIn,
- conflict: Optional[ConflictAction],
- user_id: str,
- dept_id: str,
- ) -> str:
- plate = (draft.license_plate or "").strip()
- if not plate:
- raise HTTPException(status_code=400, detail="车牌号不能为空")
- carrier_id = (draft.carrier_id or "").strip() or None
- carrier_name = (draft.carrier_name or "").strip() or None
- if carrier_id:
- carrier = (
- db.query(PlCarrier)
- .filter(
- PlCarrier.id == carrier_id,
- PlCarrier.tenant_no == tenant_no,
- PlCarrier.del_flag == 0,
- )
- .first()
- )
- if not carrier:
- raise HTTPException(status_code=400, detail="承运商不存在")
- carrier_name = carrier_name or carrier.carrier_name
- else:
- carrier_name = None
- existing = _find_vehicle_by_plate(db, tenant_no, plate)
- if existing and conflict is None:
- raise HTTPException(
- status_code=400,
- detail=f"车牌「{plate}」已存在,请先选择复用/覆盖/取消",
- )
- if conflict is not None:
- if conflict.action == "cancel":
- raise HTTPException(status_code=400, detail="已取消")
- target = _get_vehicle(db, tenant_no, conflict.existing_id)
- if conflict.action == "reuse":
- return target.id
- trailer_no = (draft.trailer_no or "").strip()
- if not trailer_no:
- raise HTTPException(status_code=400, detail="挂车号不能为空")
- target.trailer_no = trailer_no
- target.load_capacity = draft.load_capacity
- target.carrier_id = carrier_id
- target.carrier_name = carrier_name
- target.vehicle_type = "carrier"
- target.update_by = user_id
- return target.id
- trailer_no = (draft.trailer_no or "").strip()
- if not trailer_no:
- raise HTTPException(status_code=400, detail="挂车号不能为空")
- vehicle = PlVehicle(
- id=generate_uuid(),
- tenant_no=tenant_no,
- vehicle_type="carrier",
- carrier_id=carrier_id,
- carrier_name=carrier_name,
- license_plate=plate,
- trailer_no=trailer_no,
- load_capacity=draft.load_capacity,
- vehicle_status="available",
- create_by=user_id,
- create_dept_id=dept_id,
- )
- db.add(vehicle)
- db.flush()
- return vehicle.id
- def commit_paste_recognize(
- db: Session,
- tenant_no: str,
- body: PasteCommitBody,
- *,
- user_id: str,
- dept_id: str,
- ) -> PasteCommitResult:
- cmap = _conflict_map(body.conflicts or [])
- for item in cmap.values():
- if item.action == "cancel":
- raise HTTPException(status_code=400, detail="已取消")
- need_vehicle = body.mode == "vehicle_bundle" or (
- body.mode == "personnel_first" and body.create_vehicle
- )
- if need_vehicle and body.vehicle is None:
- raise HTTPException(status_code=400, detail="缺少车辆信息")
- driver_id: Optional[str] = None
- escort_id: Optional[str] = None
- vehicle_id: Optional[str] = None
- if body.driver is not None:
- driver_id = _resolve_personnel(
- db,
- tenant_no,
- key="驾驶员",
- draft=body.driver,
- conflict=cmap.get("driver"),
- user_id=user_id,
- dept_id=dept_id,
- )
- if body.escort is not None:
- escort_id = _resolve_personnel(
- db,
- tenant_no,
- key="押运员",
- draft=body.escort,
- conflict=cmap.get("escort"),
- user_id=user_id,
- dept_id=dept_id,
- )
- if need_vehicle and body.vehicle is not None:
- vehicle_id = _resolve_vehicle(
- db,
- tenant_no,
- draft=body.vehicle,
- conflict=cmap.get("vehicle"),
- user_id=user_id,
- dept_id=dept_id,
- )
- rows: list[VehiclePersonnelRow] = []
- if driver_id and body.driver is not None:
- rows.append(
- VehiclePersonnelRow(
- personnelId=driver_id,
- personnelName=body.driver.personnel_name.strip(),
- phoneNo=(body.driver.phone_no or "").strip() or None,
- role="driver",
- isDefault=1,
- )
- )
- if escort_id and body.escort is not None:
- rows.append(
- VehiclePersonnelRow(
- personnelId=escort_id,
- personnelName=body.escort.personnel_name.strip(),
- phoneNo=(body.escort.phone_no or "").strip() or None,
- role="escort",
- isDefault=0,
- )
- )
- if rows:
- save_vehicle_personnel(
- db,
- vehicle_id,
- rows,
- tenant_no,
- user_id=user_id,
- dept_id=dept_id,
- vehicle_type="carrier",
- )
- if body.mode == "personnel_first" and not need_vehicle:
- if not driver_id and not escort_id:
- raise HTTPException(status_code=400, detail="请至少识别一名人员")
- if body.mode == "vehicle_bundle" and not vehicle_id:
- raise HTTPException(status_code=400, detail="车辆创建失败")
- return PasteCommitResult(
- vehicleId=vehicle_id,
- driverId=driver_id,
- escortId=escort_id,
- )
|