dingdanquanliucheng/backend/app/api/orders.py

349 lines
17 KiB
Python
Raw Normal View History

"""
订单管理路由模块
职责
处理订单的全生命周期管理接口URL 前缀为 /api/orders
包括订单列表查询创建更新详情查看提交取消
状态变更审批撤销审批供应商文本发送与确认
"""
2026-05-14 13:51:06 +08:00
from fastapi import APIRouter, Depends, Query
from sqlalchemy.orm import Session
from backend.app.api.deps import get_order_service, require_permissions, require_roles
2026-05-14 13:51:06 +08:00
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,
2026-05-14 13:51:06 +08:00
ConfirmSupplierTextRequest,
CreateOrderRequest,
SupplierTextRequest,
2026-05-19 17:30:35 +08:00
UpdateOrderRequest,
2026-05-14 13:51:06 +08:00
)
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")), # 权限鉴权:订单查看
2026-05-14 13:51:06 +08:00
) -> dict:
"""分页查询订单列表
用途根据多维度筛选条件获取订单列表支持按订单号状态客户业务员工厂来源时间范围筛选
请求参数Query 参数组合筛选 + 分页参数
返回值分页订单列表包含 totalpage_nopage_sizelist
权限要求业务员/经理/管理员且需 order:list 权限
"""
2026-06-19 23:03:14 +08:00
result = order_service.list_orders(
2026-05-14 13:51:06 +08:00
session,
order_service.normalize_list_filters(
{
"order_no": order_no,
2026-05-14 13:51:06 +08:00
"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,
2026-05-28 22:59:04 +08:00
"page_no": page_no,
"page_size": page_size,
},
current_user,
),
2026-05-28 22:59:04 +08:00
current_user=current_user,
2026-05-14 13:51:06 +08:00
)
return success_payload(
{
2026-06-19 23:03:14 +08:00
"total": result["total"],
2026-05-14 13:51:06 +08:00
"page_no": page_no,
"page_size": page_size,
2026-06-19 23:03:14 +08:00
"list": result["list"],
2026-05-14 13:51:06 +08:00
}
)
2026-06-19 23:03:14 +08:00
@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),
})
2026-05-14 13:51:06 +08:00
@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")), # 权限鉴权:订单创建
2026-05-14 13:51:06 +08:00
) -> dict:
"""创建新订单
用途新建一个订单需包含至少一条订单明细
请求参数CreateOrderRequest客户信息明细列表 items
返回值创建成功后的订单信息
权限要求业务员salesman或管理员admin且需 order:create 权限
"""
2026-05-14 13:51:06 +08:00
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)
2026-05-14 13:51:06 +08:00
return success_payload(order)
2026-05-19 17:30:35 +08:00
@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")), # 权限鉴权:订单更新
2026-05-19 17:30:35 +08:00
) -> dict:
"""更新订单信息
用途修改已有订单的基本信息和明细需包含至少一条订单明细
请求参数order_id路径参数+ UpdateOrderRequest更新字段
返回值更新后的订单信息
权限要求业务员salesman或管理员admin且需 order:update 权限
"""
2026-05-19 17:30:35 +08:00
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)
2026-05-14 13:51:06 +08:00
@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")), # 权限鉴权:订单查看
2026-05-14 13:51:06 +08:00
) -> dict:
"""查询订单详情
用途根据订单 ID 获取单个订单的完整信息含明细状态历史等
请求参数order_id - 订单 ID路径参数
返回值订单详情信息
权限要求业务员/经理/管理员且需 order:list 权限
"""
order = order_service.get_order(order_id, session, current_user)
2026-05-14 13:51:06 +08:00
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")), # 权限鉴权:订单提交
2026-05-14 13:51:06 +08:00
) -> dict:
"""提交订单
用途将草稿状态的订单提交审批进入审批流程
请求参数order_id - 订单 ID路径参数
返回值提交后的订单信息
权限要求业务员salesman或管理员admin且需 order:submit 权限
"""
order = order_service.submit_order(order_id, session, current_user)
2026-05-14 13:51:06 +08:00
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")), # 权限鉴权:订单查看
2026-05-14 13:51:06 +08:00
) -> dict:
"""取消订单
用途取消一个未完成的订单需提供取消原因
请求参数order_id路径参数+ CancelOrderRequest取消原因
返回值取消后的订单信息
权限要求业务员salesman或管理员admin且需 order:list 权限
"""
order = order_service.cancel_order(order_id, payload.model_dump(), session, current_user)
2026-05-14 13:51:06 +08:00
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:
2026-05-14 13:51:06 +08:00
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")), # 权限鉴权:订单审批
2026-05-14 13:51:06 +08:00
) -> dict:
"""审批订单
用途对已提交的订单进行审批通过操作
请求参数order_id路径参数+ ApproveOrderRequest审批意见
返回值审批后的订单信息
权限要求经理manager或管理员admin且需 order:approve 权限
"""
order = order_service.approve_order(order_id, payload.model_dump(), session, current_user)
2026-05-14 13:51:06 +08:00
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")), # 权限鉴权:撤销审批
2026-05-14 13:51:06 +08:00
) -> dict:
"""撤销订单审批
用途撤销已通过的订单审批将订单回退到待审批状态
请求参数order_id路径参数+ ApproveOrderRequest撤销原因
返回值撤销审批后的订单信息
权限要求经理manager或管理员admin且需 order:cancel-approve 权限
"""
order = order_service.cancel_approve_order(order_id, payload.model_dump(), session, current_user)
2026-05-14 13:51:06 +08:00
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")), # 权限鉴权:供应商文本
2026-05-14 13:51:06 +08:00
) -> dict:
"""生成/发送供应商文本
用途根据订单信息生成供应商沟通文本供内部确认后发送给供应商
请求参数order_id路径参数+ SupplierTextRequest文本参数
返回值包含供应商文本内容的订单信息
权限要求经理manager或管理员admin且需 order:supplier-text 权限
"""
order = order_service.get_supplier_text(order_id, payload.model_dump(), session, current_user)
2026-05-14 13:51:06 +08:00
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")), # 权限鉴权:供应商文本
2026-05-14 13:51:06 +08:00
) -> dict:
"""确认供应商文本
用途确认已生成的供应商沟通文本触发实际发送流程
请求参数order_id路径参数+ ConfirmSupplierTextRequest确认内容
返回值确认后的订单信息
权限要求经理manager或管理员admin且需 order:supplier-text 权限
"""
order = order_service.confirm_supplier_text(order_id, payload.model_dump(), session, current_user)
2026-05-14 13:51:06 +08:00
if not order:
raise AppException(code=ErrorCode.NOT_FOUND, message="订单不存在", status_code=404)
return success_payload(order)