dingdanquanliucheng/backend/app/api/orders.py
2026-06-19 23:03:14 +08:00

349 lines
17 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
订单管理路由模块
职责:
处理订单的全生命周期管理接口URL 前缀为 /api/orders。
包括:订单列表查询、创建、更新、详情查看、提交、取消、
状态变更、审批、撤销审批、供应商文本发送与确认。
"""
from fastapi import APIRouter, Depends, Query
from sqlalchemy.orm import Session
from backend.app.api.deps import get_order_service, require_permissions, require_roles
from backend.app.core.error_codes import ErrorCode
from backend.app.core.exceptions import AppException
from backend.app.db import get_db_session
from backend.app.schemas.common import success_payload
from backend.app.schemas.orders import (
ApproveOrderRequest,
CancelOrderRequest,
ChangeOrderStatusRequest,
ConfirmSupplierTextRequest,
CreateOrderRequest,
SupplierTextRequest,
UpdateOrderRequest,
)
from backend.app.services.order_service import OrderService
router = APIRouter(prefix="/api/orders", tags=["orders"])
@router.get("")
def list_orders(
order_no: str | None = Query(default=None), # 订单编号模糊搜索
order_status: str | None = Query(default=None), # 订单状态筛选
customer_name: str | None = Query(default=None), # 客户名称模糊搜索
customer_mobile: str | None = Query(default=None), # 客户手机号搜索
salesman_id: int | None = Query(default=None), # 业务员 ID 筛选
factory_id: int | None = Query(default=None), # 工厂 ID 筛选
order_source: str | None = Query(default=None), # 订单来源筛选
start_time: str | None = Query(default=None), # 查询起始时间
end_time: str | None = Query(default=None), # 查询结束时间
page_no: int = Query(default=1), # 页码,默认第 1 页
page_size: int = Query(default=20), # 每页条数,默认 20 条
order_service: OrderService = Depends(get_order_service), # 注入订单服务
session: Session = Depends(get_db_session), # 注入数据库会话
current_user: dict = Depends(require_roles("salesman", "manager", "admin")), # 角色鉴权
_permission_user: dict = Depends(require_permissions("order:list")), # 权限鉴权:订单查看
) -> dict:
"""分页查询订单列表
用途:根据多维度筛选条件获取订单列表,支持按订单号、状态、客户、业务员、工厂、来源、时间范围筛选。
请求参数Query 参数组合筛选 + 分页参数。
返回值:分页订单列表,包含 total、page_no、page_size、list。
权限要求:业务员/经理/管理员,且需 order:list 权限。
"""
result = order_service.list_orders(
session,
order_service.normalize_list_filters(
{
"order_no": order_no,
"order_status": order_status,
"customer_name": customer_name,
"customer_mobile": customer_mobile,
"salesman_id": salesman_id,
"factory_id": factory_id,
"order_source": order_source,
"start_time": start_time,
"end_time": end_time,
"page_no": page_no,
"page_size": page_size,
},
current_user,
),
current_user=current_user,
)
return success_payload(
{
"total": result["total"],
"page_no": page_no,
"page_size": page_size,
"list": result["list"],
}
)
@router.get("/stats")
def order_stats(
order_service: OrderService = Depends(get_order_service),
session: Session = Depends(get_db_session),
current_user: dict = Depends(require_roles("salesman", "manager", "admin")),
_permission_user: dict = Depends(require_permissions("order:list")),
) -> dict:
"""获取订单统计数据
返回各状态的订单数量统计,用于工作台展示。
返回值:各状态订单数量的字典
"""
from sqlalchemy import func, select
from backend.app.models.business import SalesOrder
# 统计各状态的订单数量
stmt = (
select(SalesOrder.order_status, func.count(SalesOrder.id))
.where(SalesOrder.deleted == 0)
.group_by(SalesOrder.order_status)
)
rows = session.execute(stmt).all()
stats = {row[0]: row[1] for row in rows}
return success_payload({
"total": sum(stats.values()),
"draft": stats.get("draft", 0),
"pending_approve": stats.get("pending_approve", 0),
"approved": stats.get("approved", 0),
"rejected": stats.get("rejected", 0),
"pending_factory": stats.get("pending_factory", 0),
"pending_driver": stats.get("pending_driver", 0),
"accepted": stats.get("accepted", 0),
"picked_up": stats.get("picked_up", 0),
"delivered": stats.get("delivered", 0),
"production": stats.get("production", 0),
"shipped": stats.get("shipped", 0),
"completed": stats.get("completed", 0),
"settled": stats.get("settled", 0),
"canceled": stats.get("canceled", 0),
"cancel_pending": stats.get("cancel_pending", 0),
"cancel_fulfillment_pending": stats.get("cancel_fulfillment_pending", 0),
})
@router.post("")
def create_order(
payload: CreateOrderRequest, # 创建订单的请求体
order_service: OrderService = Depends(get_order_service), # 注入订单服务
session: Session = Depends(get_db_session), # 注入数据库会话
current_user: dict = Depends(require_roles("salesman", "admin")), # 角色鉴权:业务员/管理员
_permission_user: dict = Depends(require_permissions("order:create")), # 权限鉴权:订单创建
) -> dict:
"""创建新订单
用途:新建一个订单,需包含至少一条订单明细。
请求参数CreateOrderRequest客户信息、明细列表 items 等)。
返回值:创建成功后的订单信息。
权限要求业务员salesman或管理员admin且需 order:create 权限。
"""
if not payload.items:
raise AppException(code=ErrorCode.PARAM_ERROR, message="订单明细不能为空", status_code=400)
order = order_service.create_order(payload.model_dump(), session, current_user)
return success_payload(order)
@router.put("/{order_id}")
def update_order(
order_id: int, # 订单 ID路径参数
payload: UpdateOrderRequest, # 更新订单的请求体
order_service: OrderService = Depends(get_order_service), # 注入订单服务
session: Session = Depends(get_db_session), # 注入数据库会话
current_user: dict = Depends(require_roles("salesman", "admin")), # 角色鉴权:业务员/管理员
_permission_user: dict = Depends(require_permissions("order:update")), # 权限鉴权:订单更新
) -> dict:
"""更新订单信息
用途:修改已有订单的基本信息和明细,需包含至少一条订单明细。
请求参数order_id路径参数+ UpdateOrderRequest更新字段
返回值:更新后的订单信息。
权限要求业务员salesman或管理员admin且需 order:update 权限。
"""
if not payload.items:
raise AppException(code=ErrorCode.PARAM_ERROR, message="订单明细不能为空", status_code=400)
order = order_service.update_order(order_id, payload.model_dump(), session, current_user)
if not order:
raise AppException(code=ErrorCode.NOT_FOUND, message="订单不存在", status_code=404)
return success_payload(order)
@router.get("/{order_id}")
def get_order(
order_id: int, # 订单 ID路径参数
order_service: OrderService = Depends(get_order_service), # 注入订单服务
session: Session = Depends(get_db_session), # 注入数据库会话
current_user: dict = Depends(require_roles("salesman", "manager", "admin")), # 角色鉴权
_permission_user: dict = Depends(require_permissions("order:list")), # 权限鉴权:订单查看
) -> dict:
"""查询订单详情
用途:根据订单 ID 获取单个订单的完整信息(含明细、状态历史等)。
请求参数order_id - 订单 ID路径参数
返回值:订单详情信息。
权限要求:业务员/经理/管理员,且需 order:list 权限。
"""
order = order_service.get_order(order_id, session, current_user)
if not order:
raise AppException(code=ErrorCode.NOT_FOUND, message="订单不存在", status_code=404)
return success_payload(order)
@router.post("/{order_id}/submit")
def submit_order(
order_id: int, # 订单 ID路径参数
order_service: OrderService = Depends(get_order_service), # 注入订单服务
session: Session = Depends(get_db_session), # 注入数据库会话
current_user: dict = Depends(require_roles("salesman", "admin")), # 角色鉴权:业务员/管理员
_permission_user: dict = Depends(require_permissions("order:submit")), # 权限鉴权:订单提交
) -> dict:
"""提交订单
用途:将草稿状态的订单提交审批,进入审批流程。
请求参数order_id - 订单 ID路径参数
返回值:提交后的订单信息。
权限要求业务员salesman或管理员admin且需 order:submit 权限。
"""
order = order_service.submit_order(order_id, session, current_user)
if not order:
raise AppException(code=ErrorCode.NOT_FOUND, message="订单不存在", status_code=404)
return success_payload(order)
@router.post("/{order_id}/cancel")
def cancel_order(
order_id: int, # 订单 ID路径参数
payload: CancelOrderRequest, # 取消原因请求体
order_service: OrderService = Depends(get_order_service), # 注入订单服务
session: Session = Depends(get_db_session), # 注入数据库会话
current_user: dict = Depends(require_roles("salesman", "admin")), # 角色鉴权:业务员/管理员
_permission_user: dict = Depends(require_permissions("order:list")), # 权限鉴权:订单查看
) -> dict:
"""取消订单
用途:取消一个未完成的订单,需提供取消原因。
请求参数order_id路径参数+ CancelOrderRequest取消原因
返回值:取消后的订单信息。
权限要求业务员salesman或管理员admin且需 order:list 权限。
"""
order = order_service.cancel_order(order_id, payload.model_dump(), session, current_user)
if not order:
raise AppException(code=ErrorCode.NOT_FOUND, message="订单不存在", status_code=404)
return success_payload(order)
@router.post("/{order_id}/status")
def change_order_status(
order_id: int, # 订单 ID路径参数
payload: ChangeOrderStatusRequest, # 状态变更请求体
order_service: OrderService = Depends(get_order_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:
"""变更订单状态
用途:手动变更订单的当前状态(如发货、完成等),需经理或管理员操作。
请求参数order_id路径参数+ ChangeOrderStatusRequest目标状态
返回值:状态变更后的订单信息。
权限要求经理manager或管理员admin且需 logistics:task:create 权限。
"""
order = order_service.change_order_status(order_id, payload.model_dump(), session, current_user)
if not order:
raise AppException(code=ErrorCode.NOT_FOUND, message="订单不存在", status_code=404)
return success_payload(order)
@router.post("/{order_id}/approve")
def approve_order(
order_id: int, # 订单 ID路径参数
payload: ApproveOrderRequest, # 审批意见请求体
order_service: OrderService = Depends(get_order_service), # 注入订单服务
session: Session = Depends(get_db_session), # 注入数据库会话
current_user: dict = Depends(require_roles("manager", "admin")), # 角色鉴权:经理/管理员
_permission_user: dict = Depends(require_permissions("order:approve")), # 权限鉴权:订单审批
) -> dict:
"""审批订单
用途:对已提交的订单进行审批通过操作。
请求参数order_id路径参数+ ApproveOrderRequest审批意见
返回值:审批后的订单信息。
权限要求经理manager或管理员admin且需 order:approve 权限。
"""
order = order_service.approve_order(order_id, payload.model_dump(), session, current_user)
if not order:
raise AppException(code=ErrorCode.NOT_FOUND, message="订单不存在", status_code=404)
return success_payload(order)
@router.post("/{order_id}/cancel-approve")
def cancel_approve_order(
order_id: int, # 订单 ID路径参数
payload: ApproveOrderRequest, # 撤销审批意见请求体
order_service: OrderService = Depends(get_order_service), # 注入订单服务
session: Session = Depends(get_db_session), # 注入数据库会话
current_user: dict = Depends(require_roles("manager", "admin")), # 角色鉴权:经理/管理员
_permission_user: dict = Depends(require_permissions("order:cancel-approve")), # 权限鉴权:撤销审批
) -> dict:
"""撤销订单审批
用途:撤销已通过的订单审批,将订单回退到待审批状态。
请求参数order_id路径参数+ ApproveOrderRequest撤销原因
返回值:撤销审批后的订单信息。
权限要求经理manager或管理员admin且需 order:cancel-approve 权限。
"""
order = order_service.cancel_approve_order(order_id, payload.model_dump(), session, current_user)
if not order:
raise AppException(code=ErrorCode.NOT_FOUND, message="订单不存在", status_code=404)
return success_payload(order)
@router.post("/{order_id}/supplier-text")
def supplier_text(
order_id: int, # 订单 ID路径参数
payload: SupplierTextRequest, # 供应商文本请求体
order_service: OrderService = Depends(get_order_service), # 注入订单服务
session: Session = Depends(get_db_session), # 注入数据库会话
current_user: dict = Depends(require_roles("manager", "admin")), # 角色鉴权:经理/管理员
_permission_user: dict = Depends(require_permissions("order:supplier-text")), # 权限鉴权:供应商文本
) -> dict:
"""生成/发送供应商文本
用途:根据订单信息生成供应商沟通文本,供内部确认后发送给供应商。
请求参数order_id路径参数+ SupplierTextRequest文本参数
返回值:包含供应商文本内容的订单信息。
权限要求经理manager或管理员admin且需 order:supplier-text 权限。
"""
order = order_service.get_supplier_text(order_id, payload.model_dump(), session, current_user)
if not order:
raise AppException(code=ErrorCode.NOT_FOUND, message="订单不存在", status_code=404)
return success_payload(order)
@router.post("/{order_id}/supplier-text/confirm")
def confirm_supplier_text(
order_id: int, # 订单 ID路径参数
payload: ConfirmSupplierTextRequest, # 确认文本请求体
order_service: OrderService = Depends(get_order_service), # 注入订单服务
session: Session = Depends(get_db_session), # 注入数据库会话
current_user: dict = Depends(require_roles("manager", "admin")), # 角色鉴权:经理/管理员
_permission_user: dict = Depends(require_permissions("order:supplier-text")), # 权限鉴权:供应商文本
) -> dict:
"""确认供应商文本
用途:确认已生成的供应商沟通文本,触发实际发送流程。
请求参数order_id路径参数+ ConfirmSupplierTextRequest确认内容
返回值:确认后的订单信息。
权限要求经理manager或管理员admin且需 order:supplier-text 权限。
"""
order = order_service.confirm_supplier_text(order_id, payload.model_dump(), session, current_user)
if not order:
raise AppException(code=ErrorCode.NOT_FOUND, message="订单不存在", status_code=404)
return success_payload(order)