""" 供应商管理路由模块 职责: 处理供应商信息的增删改查接口,URL 前缀为 /api/suppliers。 包括:供应商列表查询、创建供应商、查看详情、更新供应商信息、删除供应商。 """ from fastapi import APIRouter, Body, Depends, Query from sqlalchemy import func, select from sqlalchemy.orm import Session from backend.app.api.deps import get_supplier_service, 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.models.business import Supplier, SalesOrder from backend.app.schemas.common import success_payload from backend.app.schemas.suppliers import CreateSupplierRequest, UpdateSupplierRequest from backend.app.services.supplier_service import SupplierService router = APIRouter(prefix="/api/suppliers", tags=["suppliers"]) @router.get("") def list_suppliers( supplier_name: str | None = Query(default=None), # 供应商名称模糊搜索 supplier_type: str | None = Query(default=None), # 供应商类型筛选 status: int | None = Query(default=None), # 供应商状态筛选(启用/停用) page_no: int = Query(default=1), # 页码,默认第 1 页 page_size: int = Query(default=20), # 每页条数,默认 20 条 supplier_service: SupplierService = Depends(get_supplier_service), # 注入供应商服务 session: Session = Depends(get_db_session), # 注入数据库会话 current_user: dict = Depends(require_roles("salesman", "manager", "admin")), # 角色鉴权 ) -> dict: """分页查询供应商列表 用途:获取所有供应商信息,支持按名称、类型、状态筛选。 请求参数:Query 参数筛选 + 分页参数。 返回值:分页供应商列表,包含 total、page_no、page_size、list。 权限要求:业务员(salesman)、经理(manager)、管理员(admin)。 """ result = supplier_service.list_suppliers( {"supplier_name": supplier_name, "supplier_type": supplier_type, "status": status}, session, ) start = max(page_no - 1, 0) * page_size result["list"] = result["list"][start : start + page_size] result["page_no"] = page_no result["page_size"] = page_size return success_payload(result) @router.post("") def create_supplier( payload: CreateSupplierRequest, # 创建供应商的请求体 supplier_service: SupplierService = Depends(get_supplier_service), # 注入供应商服务 session: Session = Depends(get_db_session), # 注入数据库会话 current_user: dict = Depends(require_roles("manager", "admin")), # 角色鉴权:仅经理/管理员 ) -> dict: """创建新供应商 用途:新增一个供应商记录。 请求参数:CreateSupplierRequest(供应商名称、类型、联系方式等)。 返回值:创建成功后的供应商信息。 权限要求:经理(manager)或管理员(admin)。 """ return success_payload(supplier_service.create_supplier(payload.model_dump(), session)) @router.get("/{supplier_id}") def get_supplier( supplier_id: int, # 供应商 ID(路径参数) supplier_service: SupplierService = Depends(get_supplier_service), # 注入供应商服务 session: Session = Depends(get_db_session), # 注入数据库会话 current_user: dict = Depends(require_roles("salesman", "manager", "admin")), # 角色鉴权 ) -> dict: """查询供应商详情 用途:根据供应商 ID 获取单个供应商的详细信息。 请求参数:supplier_id - 供应商 ID(路径参数)。 返回值:供应商详情信息。 权限要求:业务员(salesman)、经理(manager)、管理员(admin)。 """ supplier = supplier_service.get_supplier(supplier_id, session) if not supplier: raise AppException(code=ErrorCode.NOT_FOUND, message="供应商不存在", status_code=404) return success_payload(supplier) @router.put("/{supplier_id}") def update_supplier( supplier_id: int, # 供应商 ID(路径参数) payload: UpdateSupplierRequest, # 更新供应商的请求体 supplier_service: SupplierService = Depends(get_supplier_service), # 注入供应商服务 session: Session = Depends(get_db_session), # 注入数据库会话 current_user: dict = Depends(require_roles("manager", "admin")), # 角色鉴权:仅经理/管理员 ) -> dict: """更新供应商信息 用途:修改已有供应商的基本信息。 请求参数:supplier_id(路径参数)+ UpdateSupplierRequest(更新字段)。 返回值:更新后的供应商信息。 权限要求:经理(manager)或管理员(admin)。 """ result = supplier_service.update_supplier(supplier_id, payload.model_dump(), session) if not result: raise AppException(code=ErrorCode.NOT_FOUND, message="供应商不存在", status_code=404) return success_payload(result) @router.delete("/{supplier_id}") def delete_supplier( supplier_id: int, # 供应商 ID(路径参数) supplier_service: SupplierService = Depends(get_supplier_service), # 注入供应商服务 session: Session = Depends(get_db_session), # 注入数据库会话 current_user: dict = Depends(require_roles("manager", "admin")), # 角色鉴权:仅经理/管理员 ) -> dict: """软删除供应商 用途:将供应商标记为已删除(deleted=1),不实际删除数据。 请求参数:supplier_id - 供应商 ID(路径参数)。 返回值:删除结果 {"deleted": true}。 权限要求:经理(manager)或管理员(admin)。 """ supplier = session.execute( select(Supplier).where(Supplier.id == supplier_id, Supplier.deleted == 0) ).scalar_one_or_none() if not supplier: raise AppException(code=ErrorCode.NOT_FOUND, message="供应商不存在", status_code=404) supplier.deleted = 1 session.commit() return success_payload({"deleted": True}) @router.delete("/{supplier_id}/permanent") def permanent_delete_supplier( supplier_id: int, # 供应商 ID(路径参数) supplier_service: SupplierService = Depends(get_supplier_service), # 注入供应商服务 session: Session = Depends(get_db_session), # 注入数据库会话 current_user: dict = Depends(require_roles("manager", "admin")), # 角色鉴权:仅经理/管理员 ) -> dict: """永久删除供应商 用途:彻底删除供应商记录。若存在关联的有效订单则禁止删除。 请求参数:supplier_id - 供应商 ID(路径参数)。 返回值:删除结果 {"deleted": true}。 权限要求:经理(manager)或管理员(admin)。 """ supplier = session.execute( select(Supplier).where(Supplier.id == supplier_id, Supplier.deleted == 0) ).scalar_one_or_none() if not supplier: raise AppException(code=ErrorCode.NOT_FOUND, message="供应商不存在", status_code=404) order_count = session.execute( select(func.count(SalesOrder.id)).where( SalesOrder.factory_id == supplier_id, SalesOrder.order_status != "cancelled", ) ).scalar() if order_count > 0: raise AppException( code=ErrorCode.PARAM_ERROR, message="该供应商还有关联订单,无法删除", status_code=400, ) session.delete(supplier) session.commit() return success_payload({"deleted": True}) @router.post("/batch-delete") def batch_delete_suppliers( ids: list[int] = Body(..., embed=True), permanent: bool = Body(default=False, embed=True), session: Session = Depends(get_db_session), current_user: dict = Depends(require_roles("manager", "admin")), ) -> dict: """批量删除供应商""" suppliers = session.execute( select(Supplier).where(Supplier.id.in_(ids), Supplier.deleted == 0) ).scalars().all() if not suppliers: raise AppException(code=ErrorCode.NOT_FOUND, message="未找到可删除的供应商", status_code=404) if permanent: sup_ids = [s.id for s in suppliers] count = session.execute( select(func.count(SalesOrder.id)).where( SalesOrder.factory_id.in_(sup_ids), SalesOrder.order_status != "cancelled" ) ).scalar() if count > 0: raise AppException(code=ErrorCode.PARAM_ERROR, message="所选供应商还有关联订单,无法删除", status_code=400) for s in suppliers: session.delete(s) else: for s in suppliers: s.deleted = 1 session.commit() return success_payload({"deleted": len(suppliers)})