From 57ca252f8162032fe124505fff91c3fffacf90e8 Mon Sep 17 00:00:00 2001 From: wuzilong <704541015@qq.com> Date: Sun, 13 Sep 2026 11:44:24 +0800 Subject: [PATCH] feat(pl): add JWT driver-plan routes for miniprogram Expose history/detail/feedback/attachments under /transport/driver-plan and exclude the path from API encryption middleware. Co-authored-by: Cursor --- backend/app/api/pl/__init__.py | 17 +++- backend/app/api/pl/driver_plan.py | 138 ++++++++++++++++++++++++++++++ backend/app/core/config.py | 2 + 3 files changed, 156 insertions(+), 1 deletion(-) create mode 100644 backend/app/api/pl/driver_plan.py diff --git a/backend/app/api/pl/__init__.py b/backend/app/api/pl/__init__.py index 66459be07..37f43d444 100644 --- a/backend/app/api/pl/__init__.py +++ b/backend/app/api/pl/__init__.py @@ -2,9 +2,18 @@ from fastapi import APIRouter from app.api.pl import plan, transport, freight, inventory, menu_badges, ocr -from app.api.pl import base_data, vehicle_recharge, vehicle_maintenance, vehicle_urea, transport_address +from app.api.pl import ( + base_data, + vehicle_fuel, + vehicle_recharge, + vehicle_maintenance, + vehicle_urea, + transport_address, +) from app.api.pl import customer_stop_pref from app.api.pl import io_order +from app.api.pl import site_plan +from app.api.pl import driver_plan pl_router = APIRouter(prefix="/transport", tags=["运输计划管理"]) @@ -30,6 +39,10 @@ pl_router.include_router(io_order.outbound_router) # 运输管理 pl_router.include_router(transport.router) +# 现场计划单(登录态) +pl_router.include_router(site_plan.router) +pl_router.include_router(driver_plan.router) + # 运费结算 pl_router.include_router(freight.router) @@ -43,6 +56,8 @@ pl_router.include_router(menu_badges.router) pl_router.include_router(vehicle_recharge.apply_router) pl_router.include_router(vehicle_recharge.record_router) +pl_router.include_router(vehicle_fuel.router) + # 车辆维保 pl_router.include_router(vehicle_maintenance.apply_router) pl_router.include_router(vehicle_maintenance.process_router) diff --git a/backend/app/api/pl/driver_plan.py b/backend/app/api/pl/driver_plan.py new file mode 100644 index 000000000..e4c8c5a27 --- /dev/null +++ b/backend/app/api/pl/driver_plan.py @@ -0,0 +1,138 @@ +"""小程序/门户登录态 · 司机运输计划单""" +from __future__ import annotations + +from typing import Optional + +from fastapi import APIRouter, Depends, File, Form, HTTPException, Query, UploadFile +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.response import error_response, success_response +from app.core.tenant_context import get_tenant_no +from app.schemas.pl.transport import ( + PublicTransportDriverStopFeedback, + PublicTransportFeedback, +) +from app.services import pl_driver_plan_service as drv_svc + +router = APIRouter(prefix="/driver-plan", tags=["运输计划-司机计划单"]) + + +def _resolve_personnel_or_empty(db: Session, user: DbUserContext, tenant_no: str): + return drv_svc.resolve_personnel_for_user( + db, tenant_no=tenant_no, user_id=user.id + ) + + +@router.get("/history") +def get_driver_plan_history( + date: str = Query(..., description="筛选日 YYYY-MM-DD"), + db: Session = Depends(get_db), + user: DbUserContext = Depends(get_db_current_user), +): + tenant_no = get_tenant_no(db, user.to_tenant_user_dict()) + page, err = drv_svc.build_driver_history_page( + db, + tenant_no=tenant_no, + user_id=user.id, + date_str=date, + ) + if err: + return error_response(400, err) + return success_response(page.model_dump(by_alias=True)) + + +@router.get("/{plan_id}") +def get_driver_plan_detail( + plan_id: str, + db: Session = Depends(get_db), + user: DbUserContext = Depends(get_db_current_user), +): + tenant_no = get_tenant_no(db, user.to_tenant_user_dict()) + personnel = _resolve_personnel_or_empty(db, user, tenant_no) + if not personnel: + raise HTTPException(status_code=404, detail="未关联驾押人员,无法查看") + page, err = drv_svc.build_driver_page_for_plan( + db, + tenant_no=tenant_no, + plan_id=plan_id, + personnel_id=personnel.id, + ) + if err or page is None: + raise HTTPException(status_code=404, detail=err or "计划不存在") + return success_response(page.model_dump(by_alias=True)) + + +@router.post("/{plan_id}/feedback") +def save_driver_plan_feedback( + plan_id: str, + payload: PublicTransportFeedback, + db: Session = Depends(get_db), + user: DbUserContext = Depends(get_db_current_user), +): + tenant_no = get_tenant_no(db, user.to_tenant_user_dict()) + personnel = _resolve_personnel_or_empty(db, user, tenant_no) + if not personnel: + return error_response(400, "未关联驾押人员,无法提交回执") + ok, msg = drv_svc.save_driver_feedback_for_plan( + db, + tenant_no=tenant_no, + plan_id=plan_id, + personnel_id=personnel.id, + payload=payload, + ) + if not ok: + return error_response(400, msg or "保存失败") + db.commit() + return success_response({"success": True, "message": "保存成功"}) + + +@router.post("/{plan_id}/driver-feedback") +def save_driver_plan_stop_feedback( + plan_id: str, + payload: PublicTransportDriverStopFeedback, + db: Session = Depends(get_db), + user: DbUserContext = Depends(get_db_current_user), +): + tenant_no = get_tenant_no(db, user.to_tenant_user_dict()) + personnel = _resolve_personnel_or_empty(db, user, tenant_no) + if not personnel: + return error_response(400, "未关联驾押人员,无法提交回执") + ok, msg = drv_svc.save_driver_stop_feedback_for_plan( + db, + tenant_no=tenant_no, + plan_id=plan_id, + personnel_id=personnel.id, + payload=payload, + ) + if not ok: + return error_response(400, msg or "保存失败") + db.commit() + return success_response({"success": True, "message": "保存成功"}) + + +@router.post("/{plan_id}/attachments") +def upload_driver_plan_attachment( + plan_id: str, + file: UploadFile = File(...), + folder: Optional[str] = Form(""), + db: Session = Depends(get_db), + user: DbUserContext = Depends(get_db_current_user), +): + tenant_no = get_tenant_no(db, user.to_tenant_user_dict()) + personnel = _resolve_personnel_or_empty(db, user, tenant_no) + if not personnel: + return error_response(400, "未关联驾押人员,无法上传附件") + data, err = drv_svc.upload_driver_attachment_for_plan( + db, + tenant_no=tenant_no, + plan_id=plan_id, + personnel_id=personnel.id, + file=file, + folder=folder or "", + ) + if err: + code, msg = err + return error_response(code, msg) + return success_response(data) diff --git a/backend/app/core/config.py b/backend/app/core/config.py index 0507bd5e2..00b92bd5f 100644 --- a/backend/app/core/config.py +++ b/backend/app/core/config.py @@ -178,6 +178,8 @@ class Settings(BaseSettings): "/api/public/pl/site," "/api/public/pl/inbound," "/api/public/pl/outbound," + "/api/transport/site-plan," + "/api/transport/driver-plan," "/api/public/db/inventory," "/api/public/db/vehicle-plates," "/api/public/duty"