Files
shanxia2.0/backend/app/api/pl/driver_plan.py
T
wuzilongandCursor 811e6a129f feat(pl): allocate copy_seq_no on first driver plan share-to-group
Add ensure-copy-seq API and miniprogram clipboard prefix so each plan
gets a tenant-scoped fixed serial on first copy; cover allocation with
unit tests and refresh the backend graph.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-09 12:36:58 +08:00

179 lines
5.8 KiB
Python

"""小程序/门户登录态 · 司机运输计划单"""
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("/calendar-counts")
def get_driver_plan_calendar_counts(
month: str = Query(..., description="月份 YYYY-MM"),
db: Session = Depends(get_db),
user: DbUserContext = Depends(get_db_current_user),
):
tenant_no = get_tenant_no(db, user.to_tenant_user_dict())
counts, err = drv_svc.build_driver_calendar_counts(
db,
tenant_no=tenant_no,
user_id=user.id,
month_str=month,
)
if err:
return error_response(400, err)
return success_response({"month": month, "counts": counts or {}})
@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}/ensure-copy-seq")
def ensure_driver_plan_copy_seq(
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="未关联驾押人员,无法查看")
seq, err = drv_svc.ensure_copy_seq_for_driver(
db,
tenant_no=tenant_no,
plan_id=plan_id,
personnel_id=personnel.id,
)
if err or seq is None:
raise HTTPException(status_code=404, detail=err or "计划不存在")
db.commit()
return success_response({"copySeqNo": seq})
@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)