新增 logistics_waybill 表存储一个任务关联的多个运单号, 司机在揽货时可通过拍照调用快递100 OCR 自动识别运单号, 支持连续拍多张统一提交、手动输入兜底、编辑删除, 揽货时必填至少一个运单号并自动同步到 logistics_task。 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
388 lines
15 KiB
Python
388 lines
15 KiB
Python
"""
|
||
物流任务管理路由模块
|
||
|
||
提供物流运输任务的全生命周期管理接口,包括:
|
||
- 物流任务的创建、查询、取消(管理端)
|
||
- 司机端任务的接单、提货、送达操作
|
||
- 物流轨迹的查询与新增
|
||
|
||
URL 前缀:/api/logistics、/api/driver
|
||
权限要求:manager/admin 管理端操作,driver 司机端操作
|
||
"""
|
||
|
||
from fastapi import APIRouter, Depends, Query
|
||
from sqlalchemy.orm import Session
|
||
|
||
from backend.app.api.deps import get_logistics_service, require_permissions, require_roles
|
||
from backend.app.db import get_db_session
|
||
from backend.app.schemas.common import success_payload
|
||
from backend.app.schemas.logistics import CreateLogisticsTaskRequest, CreateLogisticsTraceRequest, DriverTaskOperateRequest, RecognizeWaybillRequest, SubmitWaybillsRequest, UpdateTrackingRequest
|
||
from backend.app.services.logistics_service import LogisticsService
|
||
|
||
router = APIRouter(tags=["logistics"])
|
||
|
||
|
||
@router.get("/api/logistics/tasks")
|
||
def list_logistics_tasks(
|
||
order_id: int | None = Query(default=None), # 按订单ID筛选
|
||
task_no: str | None = Query(default=None), # 按任务编号精确搜索
|
||
status: str | None = Query(default=None), # 按任务状态筛选(如 pending/in_transit/completed)
|
||
driver_id: int | None = Query(default=None), # 按司机ID筛选
|
||
factory_id: int | None = Query(default=None), # 按工厂ID筛选
|
||
logistics_service: LogisticsService = Depends(get_logistics_service), # 物流服务实例
|
||
session: Session = Depends(get_db_session), # 数据库会话
|
||
current_user: dict = Depends(require_roles("manager", "admin")), # 当前登录用户,仅manager和admin可访问
|
||
_permission_user: dict = Depends(require_permissions("logistics:task:list")), # 权限校验:物流任务查看权限
|
||
) -> dict:
|
||
"""
|
||
获取物流任务列表(管理端)
|
||
|
||
支持按订单ID、任务编号、状态、司机ID、工厂ID进行筛选查询。
|
||
仅管理员和经理角色可访问。
|
||
|
||
请求参数:
|
||
order_id: 订单ID(可选)
|
||
task_no: 任务编号(可选)
|
||
status: 任务状态(可选)
|
||
driver_id: 司机ID(可选)
|
||
factory_id: 工厂ID(可选)
|
||
|
||
返回值:
|
||
包含物流任务列表的分页数据
|
||
"""
|
||
return success_payload(
|
||
logistics_service.list_tasks(
|
||
session=session,
|
||
filters={
|
||
"order_id": order_id,
|
||
"task_no": task_no,
|
||
"status": status,
|
||
"driver_id": driver_id,
|
||
"factory_id": factory_id,
|
||
},
|
||
current_user=current_user,
|
||
)
|
||
)
|
||
|
||
|
||
@router.post("/api/logistics/tasks")
|
||
def create_logistics_task(
|
||
payload: CreateLogisticsTaskRequest, # 创建物流任务请求体,包含订单ID、司机ID等信息
|
||
logistics_service: LogisticsService = Depends(get_logistics_service), # 物流服务实例
|
||
session: Session = Depends(get_db_session), # 数据库会话
|
||
current_user: dict = Depends(require_roles("manager", "admin")), # 当前登录用户
|
||
_permission_user: dict = Depends(require_permissions("logistics:task:create")), # 权限校验:物流任务创建权限
|
||
) -> dict:
|
||
"""
|
||
创建物流任务
|
||
|
||
为指定订单创建新的物流运输任务,分配司机并启动物流流程。
|
||
|
||
请求参数:
|
||
payload: CreateLogisticsTaskRequest 请求体,包含订单关联信息
|
||
|
||
返回值:
|
||
新创建的物流任务详情
|
||
|
||
权限要求:manager、admin 角色,需 logistics:task:create 权限
|
||
"""
|
||
return success_payload(logistics_service.create_task(payload.model_dump(), session, current_user))
|
||
|
||
|
||
@router.get("/api/logistics/tasks/{task_id}")
|
||
def get_logistics_task(
|
||
task_id: int, # 物流任务ID
|
||
logistics_service: LogisticsService = Depends(get_logistics_service), # 物流服务实例
|
||
session: Session = Depends(get_db_session), # 数据库会话
|
||
current_user: dict = Depends(require_roles("manager", "admin")), # 当前登录用户
|
||
_permission_user: dict = Depends(require_permissions("logistics:task:create")), # 权限校验
|
||
) -> dict:
|
||
"""
|
||
获取物流任务详情
|
||
|
||
根据任务ID查询物流任务的完整信息,包括关联的订单和司机信息。
|
||
|
||
请求参数:
|
||
task_id: 路径参数,物流任务ID
|
||
|
||
返回值:
|
||
物流任务详情信息
|
||
|
||
权限要求:manager、admin 角色
|
||
"""
|
||
return success_payload(logistics_service.get_task(task_id, session, current_user))
|
||
|
||
|
||
@router.put("/api/logistics/tasks/{task_id}/tracking")
|
||
def update_tracking_number(
|
||
task_id: int, # 物流任务ID
|
||
payload: UpdateTrackingRequest, # 修改物流单号请求体
|
||
logistics_service: LogisticsService = Depends(get_logistics_service),
|
||
session: Session = Depends(get_db_session),
|
||
current_user: dict = Depends(require_roles("salesman", "manager", "admin")),
|
||
) -> dict:
|
||
"""
|
||
修改物流单号
|
||
|
||
业务员或管理员修改已有物流任务的快递单号和快递公司。
|
||
业务员仅可修改自己订单关联的物流任务。
|
||
|
||
请求参数:
|
||
task_id: 路径参数,物流任务ID
|
||
payload: UpdateTrackingRequest 请求体
|
||
|
||
返回值:
|
||
更新后的物流任务信息
|
||
|
||
权限要求:salesman、manager、admin 角色
|
||
"""
|
||
return success_payload(logistics_service.update_tracking(task_id, payload.model_dump(), session, current_user))
|
||
|
||
|
||
@router.get("/api/driver/tasks")
|
||
def list_driver_tasks(
|
||
logistics_service: LogisticsService = Depends(get_logistics_service), # 物流服务实例
|
||
session: Session = Depends(get_db_session), # 数据库会话
|
||
current_user: dict = Depends(require_roles("driver")), # 当前登录用户,仅司机角色
|
||
_permission_user: dict = Depends(require_permissions("driver:task:list")), # 权限校验:司机任务列表权限
|
||
) -> dict:
|
||
"""
|
||
获取司机任务列表(司机端)
|
||
|
||
返回当前登录司机的待处理任务列表。
|
||
|
||
返回值:
|
||
当前司机的物流任务列表
|
||
|
||
权限要求:driver 角色,需 driver:task:list 权限
|
||
"""
|
||
return success_payload(logistics_service.list_driver_tasks(session, current_user))
|
||
|
||
|
||
@router.get("/api/driver/tasks/{task_id}")
|
||
def get_driver_task(
|
||
task_id: int, # 物流任务ID
|
||
logistics_service: LogisticsService = Depends(get_logistics_service), # 物流服务实例
|
||
session: Session = Depends(get_db_session), # 数据库会话
|
||
current_user: dict = Depends(require_roles("driver")), # 当前登录用户,仅司机角色
|
||
_permission_user: dict = Depends(require_permissions("driver:task:list")), # 权限校验
|
||
) -> dict:
|
||
"""
|
||
获取司机任务详情(司机端)
|
||
|
||
根据任务ID查询当前司机的物流任务详情。
|
||
|
||
请求参数:
|
||
task_id: 路径参数,物流任务ID
|
||
|
||
返回值:
|
||
物流任务详情信息
|
||
|
||
权限要求:driver 角色,需 driver:task:list 权限
|
||
"""
|
||
return success_payload(logistics_service.get_driver_task(task_id, session, current_user))
|
||
|
||
|
||
@router.post("/api/driver/tasks/{task_id}/accept")
|
||
def accept_driver_task(
|
||
task_id: int, # 物流任务ID
|
||
payload: DriverTaskOperateRequest, # 司机操作请求体,包含操作备注等信息
|
||
logistics_service: LogisticsService = Depends(get_logistics_service), # 物流服务实例
|
||
session: Session = Depends(get_db_session), # 数据库会话
|
||
current_user: dict = Depends(require_roles("driver")), # 当前登录用户,仅司机角色
|
||
_permission_user: dict = Depends(require_permissions("driver:task:accept")), # 权限校验:接单权限
|
||
) -> dict:
|
||
"""
|
||
司机接单操作
|
||
|
||
司机接受指定的物流任务,任务状态变更为已接单。
|
||
|
||
请求参数:
|
||
task_id: 路径参数,物流任务ID
|
||
payload: DriverTaskOperateRequest 请求体,包含操作备注
|
||
|
||
返回值:
|
||
接单后的任务详情
|
||
|
||
权限要求:driver 角色,需 driver:task:accept 权限
|
||
"""
|
||
return success_payload(logistics_service.accept_task(task_id, payload.model_dump(), session, current_user))
|
||
|
||
|
||
@router.post("/api/driver/tasks/{task_id}/pickup")
|
||
def pickup_driver_task(
|
||
task_id: int, # 物流任务ID
|
||
payload: DriverTaskOperateRequest, # 司机操作请求体,包含提货备注等信息
|
||
logistics_service: LogisticsService = Depends(get_logistics_service), # 物流服务实例
|
||
session: Session = Depends(get_db_session), # 数据库会话
|
||
current_user: dict = Depends(require_roles("driver")), # 当前登录用户,仅司机角色
|
||
_permission_user: dict = Depends(require_permissions("driver:task:pickup")), # 权限校验:提货权限
|
||
) -> dict:
|
||
"""
|
||
司机提货操作
|
||
|
||
司机确认提货,任务状态变更为运输中。
|
||
|
||
请求参数:
|
||
task_id: 路径参数,物流任务ID
|
||
payload: DriverTaskOperateRequest 请求体,包含提货备注
|
||
|
||
返回值:
|
||
提货后的任务详情
|
||
|
||
权限要求:driver 角色,需 driver:task:pickup 权限
|
||
"""
|
||
return success_payload(logistics_service.pickup_task(task_id, payload.model_dump(), session, current_user))
|
||
|
||
|
||
@router.post("/api/driver/tasks/{task_id}/deliver")
|
||
def deliver_driver_task(
|
||
task_id: int, # 物流任务ID
|
||
payload: DriverTaskOperateRequest, # 司机操作请求体,包含送达备注等信息
|
||
logistics_service: LogisticsService = Depends(get_logistics_service), # 物流服务实例
|
||
session: Session = Depends(get_db_session), # 数据库会话
|
||
current_user: dict = Depends(require_roles("driver")), # 当前登录用户,仅司机角色
|
||
_permission_user: dict = Depends(require_permissions("driver:task:deliver")), # 权限校验:送达权限
|
||
) -> dict:
|
||
"""
|
||
司机送达操作
|
||
|
||
司机确认货物已送达目的地,任务状态变更为已完成。
|
||
|
||
请求参数:
|
||
task_id: 路径参数,物流任务ID
|
||
payload: DriverTaskOperateRequest 请求体,包含送达备注
|
||
|
||
返回值:
|
||
送达后的任务详情
|
||
|
||
权限要求:driver 角色,需 driver:task:deliver 权限
|
||
"""
|
||
return success_payload(logistics_service.deliver_task(task_id, payload.model_dump(), session, current_user))
|
||
|
||
|
||
@router.post("/api/driver/recognize-waybill")
|
||
def recognize_waybill(
|
||
payload: RecognizeWaybillRequest,
|
||
logistics_service: LogisticsService = Depends(get_logistics_service),
|
||
session: Session = Depends(get_db_session),
|
||
current_user: dict = Depends(require_roles("driver")),
|
||
_permission_user: dict = Depends(require_permissions("driver:task:accept")),
|
||
) -> dict:
|
||
"""快递100面单OCR识别运单号。"""
|
||
return success_payload(logistics_service.recognize_waybill(payload.model_dump(), session, current_user))
|
||
|
||
|
||
@router.post("/api/driver/tasks/{task_id}/waybills")
|
||
def submit_waybills(
|
||
task_id: int,
|
||
payload: SubmitWaybillsRequest,
|
||
logistics_service: LogisticsService = Depends(get_logistics_service),
|
||
session: Session = Depends(get_db_session),
|
||
current_user: dict = Depends(require_roles("driver")),
|
||
_permission_user: dict = Depends(require_permissions("driver:task:accept")),
|
||
) -> dict:
|
||
"""批量提交运单号。"""
|
||
return success_payload(logistics_service.submit_waybills(task_id, payload.model_dump(), session, current_user))
|
||
|
||
|
||
@router.get("/api/driver/tasks/{task_id}/waybills")
|
||
def list_waybills(
|
||
task_id: int,
|
||
logistics_service: LogisticsService = Depends(get_logistics_service),
|
||
session: Session = Depends(get_db_session),
|
||
current_user: dict = Depends(require_roles("driver")),
|
||
_permission_user: dict = Depends(require_permissions("driver:task:list")),
|
||
) -> dict:
|
||
"""查询任务关联的运单号列表。"""
|
||
return success_payload(logistics_service.list_waybills(task_id, session, current_user))
|
||
|
||
|
||
@router.delete("/api/driver/tasks/{task_id}/waybills/{waybill_id}")
|
||
def delete_waybill(
|
||
task_id: int,
|
||
waybill_id: int,
|
||
logistics_service: LogisticsService = Depends(get_logistics_service),
|
||
session: Session = Depends(get_db_session),
|
||
current_user: dict = Depends(require_roles("driver")),
|
||
_permission_user: dict = Depends(require_permissions("driver:task:accept")),
|
||
) -> dict:
|
||
"""删除单条运单号。"""
|
||
return success_payload(logistics_service.delete_waybill(task_id, waybill_id, session, current_user))
|
||
|
||
|
||
@router.post("/api/logistics/tasks/{task_id}/cancel")
|
||
def cancel_logistics_task(
|
||
task_id: int, # 物流任务ID
|
||
payload: DriverTaskOperateRequest, # 操作请求体,包含取消原因等信息
|
||
logistics_service: LogisticsService = Depends(get_logistics_service), # 物流服务实例
|
||
session: Session = Depends(get_db_session), # 数据库会话
|
||
current_user: dict = Depends(require_roles("manager", "admin")), # 当前登录用户,仅manager和admin
|
||
_permission_user: dict = Depends(require_permissions("logistics:task:cancel")), # 权限校验:任务取消权限
|
||
) -> dict:
|
||
"""
|
||
取消物流任务(管理端)
|
||
|
||
管理员取消指定的物流任务,任务状态变更为已取消。
|
||
|
||
请求参数:
|
||
task_id: 路径参数,物流任务ID
|
||
payload: DriverTaskOperateRequest 请求体,包含取消原因
|
||
|
||
返回值:
|
||
取消后的任务详情
|
||
|
||
权限要求:manager、admin 角色,需 logistics:task:cancel 权限
|
||
"""
|
||
return success_payload(logistics_service.cancel_task(task_id, payload.model_dump(), session, current_user))
|
||
|
||
|
||
@router.get("/api/logistics/{order_id}/trace")
|
||
def get_logistics_trace(
|
||
order_id: int, # 订单ID
|
||
logistics_service: LogisticsService = Depends(get_logistics_service), # 物流服务实例
|
||
session: Session = Depends(get_db_session), # 数据库会话
|
||
current_user: dict = Depends(require_roles("manager", "admin", "driver")), # 当前登录用户
|
||
_permission_user: dict = Depends(require_permissions("logistics:trace:list")), # 权限校验:轨迹查看权限
|
||
) -> dict:
|
||
"""
|
||
查询物流轨迹
|
||
|
||
根据订单ID查询该订单的完整物流运输轨迹记录。
|
||
|
||
请求参数:
|
||
order_id: 路径参数,订单ID
|
||
|
||
返回值:
|
||
物流轨迹列表,按时间倒序排列
|
||
|
||
权限要求:manager、admin、driver 角色,需 logistics:trace:list 权限
|
||
"""
|
||
return success_payload(logistics_service.get_trace(order_id, session, current_user))
|
||
|
||
|
||
@router.post("/api/logistics/{order_id}/trace")
|
||
def create_logistics_trace(
|
||
order_id: int, # 订单ID
|
||
payload: CreateLogisticsTraceRequest, # 创建轨迹请求体,包含轨迹状态、备注等信息
|
||
logistics_service: LogisticsService = Depends(get_logistics_service), # 物流服务实例
|
||
session: Session = Depends(get_db_session), # 数据库会话
|
||
current_user: dict = Depends(require_roles("manager", "admin", "driver")), # 当前登录用户
|
||
_permission_user: dict = Depends(require_permissions("logistics:trace:create")), # 权限校验:轨迹创建权限
|
||
) -> dict:
|
||
"""
|
||
新增物流轨迹记录
|
||
|
||
为指定订单添加一条新的物流轨迹记录,如更新运输状态、添加备注等。
|
||
|
||
请求参数:
|
||
order_id: 路径参数,订单ID
|
||
payload: CreateLogisticsTraceRequest 请求体,包含轨迹详情
|
||
|
||
返回值:
|
||
新创建的轨迹记录信息
|
||
|
||
权限要求:manager、admin、driver 角色,需 logistics:trace:create 权限
|
||
"""
|
||
return success_payload(logistics_service.create_trace(order_id, payload.model_dump(), session, current_user))
|