新增 logistics_waybill 表存储一个任务关联的多个运单号, 司机在揽货时可通过拍照调用快递100 OCR 自动识别运单号, 支持连续拍多张统一提交、手动输入兜底、编辑删除, 揽货时必填至少一个运单号并自动同步到 logistics_task。 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
179 lines
7.1 KiB
Python
179 lines
7.1 KiB
Python
"""
|
||
物流数据访问层。
|
||
|
||
负责封装物流任务(LogisticsTask)和物流跟踪记录(LogisticsTrace)的数据库查询操作,
|
||
提供物流任务的条件筛选、创建、状态更新,以及物流节点跟踪记录的查询和创建等功能。
|
||
被 OrderService、LogisticsService 调用。
|
||
"""
|
||
|
||
from sqlalchemy import select
|
||
from sqlalchemy.orm import Session
|
||
|
||
from backend.app.models.business import LogisticsTask, LogisticsTrace, LogisticsWaybill
|
||
|
||
|
||
class LogisticsRepository:
|
||
"""物流数据访问层,封装物流任务和物流跟踪表的数据库操作。
|
||
|
||
被 OrderService、LogisticsService 调用。
|
||
"""
|
||
|
||
def list_tasks_by_filters(self, session: Session, filters: dict) -> list[LogisticsTask]:
|
||
"""根据筛选条件查询物流任务列表,支持按订单 ID、任务号、状态、司机、工厂筛选。
|
||
|
||
:param session: 数据库会话
|
||
:param filters: 筛选条件字典,可包含 order_id、task_no(模糊)、
|
||
status、driver_id、factory_id 等键
|
||
:return: 符合条件的物流任务列表,按 id 降序排列
|
||
被 LogisticsService.list_tasks 调用。
|
||
"""
|
||
stmt = select(LogisticsTask).where(LogisticsTask.deleted == 0)
|
||
|
||
if filters.get("order_id") is not None:
|
||
stmt = stmt.where(LogisticsTask.order_id == filters["order_id"])
|
||
if filters.get("task_no"):
|
||
stmt = stmt.where(LogisticsTask.task_no.contains(filters["task_no"]))
|
||
if filters.get("status"):
|
||
stmt = stmt.where(LogisticsTask.status == filters["status"])
|
||
if filters.get("driver_id") is not None:
|
||
stmt = stmt.where(LogisticsTask.driver_id == filters["driver_id"])
|
||
if filters.get("factory_id") is not None:
|
||
stmt = stmt.where(LogisticsTask.factory_id == filters["factory_id"])
|
||
|
||
stmt = stmt.order_by(LogisticsTask.id.desc())
|
||
return list(session.execute(stmt).scalars())
|
||
|
||
def get_task(self, session: Session, task_id: int) -> LogisticsTask | None:
|
||
"""根据 ID 获取单个物流任务详情。
|
||
|
||
:param session: 数据库会话
|
||
:param task_id: 物流任务主键 ID
|
||
:return: 物流任务对象,不存在则返回 None
|
||
被 LogisticsService.get_task 调用。
|
||
"""
|
||
stmt = select(LogisticsTask).where(LogisticsTask.id == task_id, LogisticsTask.deleted == 0)
|
||
return session.execute(stmt).scalar_one_or_none()
|
||
|
||
def get_task_by_order_id(self, session: Session, order_id: int) -> LogisticsTask | None:
|
||
"""根据订单 ID 获取关联的最新物流任务(按 id 降序取第一条)。
|
||
|
||
:param session: 数据库会话
|
||
:param order_id: 订单主键 ID
|
||
:return: 最新的物流任务对象,不存在则返回 None
|
||
被 OrderService 在查询订单详情时关联获取物流信息调用。
|
||
"""
|
||
stmt = (
|
||
select(LogisticsTask)
|
||
.where(LogisticsTask.order_id == order_id, LogisticsTask.deleted == 0)
|
||
.order_by(LogisticsTask.id.desc())
|
||
)
|
||
return session.execute(stmt).scalar_one_or_none()
|
||
|
||
def create_task(self, session: Session, payload: dict) -> LogisticsTask:
|
||
"""创建新的物流任务记录。
|
||
|
||
:param session: 数据库会话
|
||
:param payload: 物流任务字段字典
|
||
:return: 新创建的物流任务对象(含自增 ID)
|
||
被 LogisticsService.create_task 调用。
|
||
"""
|
||
task = LogisticsTask(**payload)
|
||
session.add(task)
|
||
session.flush()
|
||
return task
|
||
|
||
def update_task_status(self, session: Session, task: LogisticsTask, status: str) -> LogisticsTask:
|
||
"""更新物流任务的状态。
|
||
|
||
:param session: 数据库会话
|
||
:param task: 物流任务对象
|
||
:param status: 目标状态值
|
||
:return: 更新后的物流任务对象
|
||
被 LogisticsService 中的状态流转方法调用。
|
||
"""
|
||
task.status = status
|
||
session.add(task)
|
||
session.flush()
|
||
return task
|
||
|
||
def list_traces_by_order_id(self, session: Session, order_id: int) -> list[LogisticsTrace]:
|
||
"""根据订单 ID 查询该订单的所有物流跟踪节点记录,按节点时间和 id 升序排列。
|
||
|
||
:param session: 数据库会话
|
||
:param order_id: 订单主键 ID
|
||
:return: 物流跟踪记录列表
|
||
被 LogisticsService.list_traces、OrderService.get_order 调用,
|
||
用于展示物流轨迹时间线。
|
||
"""
|
||
stmt = (
|
||
select(LogisticsTrace)
|
||
.where(LogisticsTrace.order_id == order_id)
|
||
.order_by(LogisticsTrace.node_time.asc(), LogisticsTrace.id.asc())
|
||
)
|
||
return list(session.execute(stmt).scalars())
|
||
|
||
def create_trace(self, session: Session, payload: dict) -> LogisticsTrace:
|
||
"""创建一条物流跟踪节点记录。
|
||
|
||
:param session: 数据库会话
|
||
:param payload: 物流跟踪记录字段字典
|
||
:return: 新创建的跟踪记录对象
|
||
被 LogisticsService.create_trace 调用,用于记录物流各节点信息。
|
||
"""
|
||
trace = LogisticsTrace(**payload)
|
||
session.add(trace)
|
||
session.flush()
|
||
return trace
|
||
|
||
def list_waybills_by_task_id(self, session: Session, task_id: int) -> list[LogisticsWaybill]:
|
||
"""查询指定任务的所有运单号记录。
|
||
|
||
:param session: 数据库会话
|
||
:param task_id: 物流任务 ID
|
||
:return: 运单号列表,按 id 升序排列
|
||
被 LogisticsService.list_waybills 调用。
|
||
"""
|
||
stmt = (
|
||
select(LogisticsWaybill)
|
||
.where(LogisticsWaybill.task_id == task_id, LogisticsWaybill.deleted == 0)
|
||
.order_by(LogisticsWaybill.id.asc())
|
||
)
|
||
return list(session.execute(stmt).scalars())
|
||
|
||
def create_waybill(self, session: Session, payload: dict) -> LogisticsWaybill:
|
||
"""创建运单号记录。
|
||
|
||
:param session: 数据库会话
|
||
:param payload: 运单号字段字典
|
||
:return: 新创建的运单号对象
|
||
被 LogisticsService.submit_waybills 调用。
|
||
"""
|
||
waybill = LogisticsWaybill(**payload)
|
||
session.add(waybill)
|
||
session.flush()
|
||
return waybill
|
||
|
||
def delete_waybill(self, session: Session, waybill_id: int, task_id: int) -> bool:
|
||
"""软删除单条运单号记录。
|
||
|
||
校验 task_id 防止越权删除其他任务的运单号。
|
||
|
||
:param session: 数据库会话
|
||
:param waybill_id: 运单号记录 ID
|
||
:param task_id: 物流任务 ID(用于校验归属)
|
||
:return: 删除成功返回 True
|
||
被 LogisticsService.delete_waybill 调用。
|
||
"""
|
||
stmt = select(LogisticsWaybill).where(
|
||
LogisticsWaybill.id == waybill_id,
|
||
LogisticsWaybill.task_id == task_id,
|
||
LogisticsWaybill.deleted == 0,
|
||
)
|
||
waybill = session.execute(stmt).scalar_one_or_none()
|
||
if waybill is None:
|
||
return False
|
||
waybill.deleted = 1
|
||
session.add(waybill)
|
||
session.flush()
|
||
return True
|