This commit is contained in:
zlwu committed 2026-06-29 11:15:39 +08:00
1 parent 493f8d6756
commit d84f4b944c
406 files changed
+58954 -10376

No files matched your search

+122 -5
View File
@@ -1,4 +1,5 @@
"""运输计划管理 - 基础数据 API(承运商、车辆、人员)"""
from datetime import datetime
from typing import Optional
from fastapi import APIRouter, Depends, HTTPException, Query, status
@@ -7,6 +8,7 @@ from sqlalchemy.orm import Session
from app.core.database import get_db
from app.core.db_auth import DbUserContext, get_db_current_user
from app.core.delete_guard import assert_row_deletable_by_creator, user_info_from_context
from app.services.pl_data_scope import apply_pl_scope, assert_pl_row_in_scope
from app.core.tenant_context import get_tenant_no
from app.core.utils import generate_uuid
@@ -18,9 +20,11 @@ from app.models.pl_models import (
PlVehiclePersonnel,
)
from app.schemas.pl import (
CarrierAuditReject,
CarrierCreate,
CarrierRead,
CarrierUpdate,
carrier_to_read,
DriverCreate,
DriverRead,
DriverUpdate,
@@ -40,6 +44,10 @@ from app.services.pl_cert_service import (
nearest_expire_date,
save_owner_certs,
)
from app.services.pl_carrier_service import (
carrier_eligible_for_transport,
carrier_qualification_complete,
)
from app.services.pl_personnel_service import (
load_vehicle_personnel_rows,
load_vehicle_personnel_summaries,
@@ -58,6 +66,7 @@ router = APIRouter(prefix="", tags=["运输计划-基础数据"])
def list_carriers(
q: Optional[str] = Query(default=None, description="模糊搜索"),
status: Optional[str] = Query(default=None, description="状态"),
audit_status: Optional[str] = Query(default=None, alias="auditStatus", description="审核状态"),
db: Session = Depends(get_db),
user: DbUserContext = Depends(get_db_current_user),
) -> list[CarrierRead]:
@@ -70,6 +79,9 @@ def list_carriers(
if status:
query = query.filter(PlCarrier.status == status)
if audit_status:
query = query.filter(PlCarrier.audit_status == audit_status)
if q:
query = query.filter(
or_(
@@ -84,7 +96,7 @@ def list_carriers(
case((PlCarrier.order_num.is_(None), 1), else_=0).asc(),
PlCarrier.create_time.desc(),
)
return query.all()
return [carrier_to_read(row) for row in query.all()]
@router.post("/carrier", response_model=CarrierRead, status_code=status.HTTP_201_CREATED)
@@ -102,6 +114,11 @@ def create_carrier(
contact_name=payload.contact_name,
contact_phone=payload.contact_phone,
status=payload.status,
business_license_urls=payload.business_license_urls.strip(),
road_transport_license_urls=payload.road_transport_license_urls.strip(),
transport_agreement_urls=payload.transport_agreement_urls.strip(),
transport_agreement_expire_date=payload.transport_agreement_expire_date,
audit_status="pending",
remark=payload.remark,
create_by=user.id,
create_dept_id=user.dept_id,
@@ -109,7 +126,7 @@ def create_carrier(
db.add(carrier)
db.commit()
db.refresh(carrier)
return carrier
return carrier_to_read(carrier)
@router.get("/carrier/{id}", response_model=CarrierRead)
@@ -120,7 +137,7 @@ def get_carrier(
) -> CarrierRead:
tenant_no = get_tenant_no(db, user.to_tenant_user_dict())
carrier = _get_carrier(db, tenant_no, id, user)
return carrier
return carrier_to_read(carrier)
@router.put("/carrier/{id}", response_model=CarrierRead)
@@ -133,13 +150,28 @@ def update_carrier(
tenant_no = get_tenant_no(db, user.to_tenant_user_dict())
carrier = _get_carrier(db, tenant_no, id, user)
for key, value in payload.model_dump(exclude_unset=True).items():
payload_data = payload.model_dump(exclude_unset=True)
qual_keys = (
"business_license_urls",
"road_transport_license_urls",
"transport_agreement_urls",
"transport_agreement_expire_date",
)
qual_changed = any(key in payload_data for key in qual_keys)
for key, value in payload_data.items():
setattr(carrier, key, value)
if qual_changed and carrier.audit_status == "approved":
carrier.audit_status = "pending"
carrier.audit_by = None
carrier.audit_time = None
carrier.audit_remark = None
carrier.update_by = user.id
db.commit()
db.refresh(carrier)
return carrier
return carrier_to_read(carrier)
@router.delete("/carrier/{id}", status_code=status.HTTP_204_NO_CONTENT)
@@ -150,11 +182,59 @@ def delete_carrier(
) -> None:
tenant_no = get_tenant_no(db, user.to_tenant_user_dict())
carrier = _get_carrier(db, tenant_no, id, user)
assert_row_deletable_by_creator(carrier, user_info_from_context(user))
carrier.del_flag = 1
carrier.update_by = user.id
db.commit()
@router.post("/carrier/{id}/approve", response_model=CarrierRead)
def approve_carrier(
id: str,
db: Session = Depends(get_db),
user: DbUserContext = Depends(get_db_current_user),
) -> CarrierRead:
tenant_no = get_tenant_no(db, user.to_tenant_user_dict())
carrier = _get_carrier(db, tenant_no, id, user)
if carrier.audit_status == "approved":
raise HTTPException(status_code=400, detail="承运商已审核通过")
if not carrier_qualification_complete(carrier):
raise HTTPException(status_code=400, detail="承运商资质附件不完整,无法审核通过")
carrier.audit_status = "approved"
carrier.audit_by = user.id
carrier.audit_time = datetime.now()
carrier.audit_remark = None
carrier.update_by = user.id
db.commit()
db.refresh(carrier)
return carrier_to_read(carrier)
@router.post("/carrier/{id}/reject", response_model=CarrierRead)
def reject_carrier(
id: str,
payload: CarrierAuditReject,
db: Session = Depends(get_db),
user: DbUserContext = Depends(get_db_current_user),
) -> CarrierRead:
tenant_no = get_tenant_no(db, user.to_tenant_user_dict())
carrier = _get_carrier(db, tenant_no, id, user)
if carrier.audit_status == "approved":
raise HTTPException(status_code=400, detail="承运商已审核通过,无法驳回")
carrier.audit_status = "rejected"
carrier.audit_by = user.id
carrier.audit_time = datetime.now()
carrier.audit_remark = (payload.remark or "").strip() or None
carrier.update_by = user.id
db.commit()
db.refresh(carrier)
return carrier_to_read(carrier)
def _get_carrier(db: Session, tenant_no: str, carrier_id: str, user: DbUserContext) -> PlCarrier:
carrier = (
db.query(PlCarrier)
@@ -212,6 +292,11 @@ def list_vehicles(
vehicle_type: Optional[str] = Query(default=None, alias="vehicleType", description="车辆类型"),
carrier_id: Optional[str] = Query(default=None, alias="carrierId", description="承运商ID"),
vehicle_status: Optional[str] = Query(default=None, alias="vehicleStatus", description="车辆状态"),
exclude_ineligible_carrier: Optional[bool] = Query(
default=False,
alias="excludeIneligibleCarrier",
description="排除资质不全或协议过期的承运商车辆",
),
db: Session = Depends(get_db),
user: DbUserContext = Depends(get_db_current_user),
) -> list[VehicleRead]:
@@ -240,6 +325,34 @@ def list_vehicles(
PlVehicle.create_time.desc(),
)
vehicles = query.all()
if exclude_ineligible_carrier:
carrier_ids = {
v.carrier_id for v in vehicles if v.vehicle_type == "carrier" and v.carrier_id
}
carrier_map: dict[str, PlCarrier] = {}
if carrier_ids:
carriers = (
db.query(PlCarrier)
.filter(
PlCarrier.id.in_(carrier_ids),
PlCarrier.tenant_no == tenant_no,
PlCarrier.del_flag == 0,
)
.all()
)
carrier_map = {c.id: c for c in carriers}
vehicles = [
v
for v in vehicles
if v.vehicle_type != "carrier"
or not v.carrier_id
or (
v.carrier_id in carrier_map
and carrier_eligible_for_transport(carrier_map[v.carrier_id])
)
]
summaries = load_vehicle_personnel_summaries(
db, tenant_no, [v.id for v in vehicles]
)
@@ -387,6 +500,7 @@ def delete_vehicle(
) -> None:
tenant_no = get_tenant_no(db, user.to_tenant_user_dict())
vehicle = _get_vehicle(db, tenant_no, id, user)
assert_row_deletable_by_creator(vehicle, user_info_from_context(user))
vehicle.del_flag = 1
vehicle.update_by = user.id
db.commit()
@@ -568,6 +682,7 @@ def delete_personnel(
) -> None:
tenant_no = get_tenant_no(db, user.to_tenant_user_dict())
personnel = _get_personnel(db, tenant_no, id, user)
assert_row_deletable_by_creator(personnel, user_info_from_context(user))
personnel.del_flag = 1
personnel.update_by = user.id
db.commit()
@@ -710,6 +825,7 @@ def delete_driver(
) -> None:
tenant_no = get_tenant_no(db, user.to_tenant_user_dict())
personnel = _get_personnel(db, tenant_no, id, user)
assert_row_deletable_by_creator(personnel, user_info_from_context(user))
personnel.del_flag = 1
personnel.update_by = user.id
db.commit()
@@ -783,5 +899,6 @@ def unbind_vehicle_driver(
assert_pl_row_in_scope(db, vd, PlVehiclePersonnel, user, tenant_no)
else:
assert_pl_row_in_scope(db, vd, PlVehicleDriver, user, tenant_no)
assert_row_deletable_by_creator(vd, user_info_from_context(user))
vd.del_flag = 1
db.commit()