"""供应商(工厂)管理服务层。 负责供应商的增删改查业务逻辑。 依赖 SupplierRepository 进行数据持久化,依赖 audit_service 记录操作审计日志。 供应商同时承担"工厂"角色,用于订单的生产下单。 """ from sqlalchemy.exc import SQLAlchemyError from sqlalchemy.orm import Session from backend.app.core.cache import cache_delete_pattern, cache_get, cache_set, make_cache_key from backend.app.core.error_codes import ErrorCode from backend.app.core.exceptions import AppException from backend.app.repositories.supplier_repository import SupplierRepository from backend.app.services.audit_service import audit_service class SupplierService: """供应商服务,封装供应商信息的 CRUD 业务逻辑。 依赖: SupplierRepository - 供应商数据访问 audit_service - 操作审计日志 """ def __init__(self) -> None: self.repository = SupplierRepository() def list_suppliers(self, filters: dict | None = None, session: Session | None = None) -> dict: """查询供应商列表。 Args: filters: 查询过滤条件 session: 数据库会话 Returns: 包含 total、page_no、page_size、list 的分页字典 被调用路由: suppliers.py - GET /suppliers """ if session is None: raise AppException(code=ErrorCode.PARAM_ERROR, message="数据库会话未初始化", status_code=500) cache_key = make_cache_key("supplier:list", **(filters or {})) cached = cache_get(cache_key) if cached is not None: return cached try: suppliers = self.repository.list_suppliers(session, filters or {}) result = { "total": len(suppliers), "page_no": 1, "page_size": len(suppliers) or 20, "list": [ { "supplier_id": supplier.id, "supplier_name": supplier.supplier_name, "supplier_type": supplier.supplier_type, "contact_name": supplier.contact_name, "contact_mobile": supplier.contact_mobile, "address": supplier.address, "template_type": supplier.template_type, "status": supplier.status, "remark": supplier.remark, } for supplier in suppliers ], } cache_set(cache_key, result, ttl=120) return result except SQLAlchemyError as exc: raise AppException(code=ErrorCode.SYSTEM_ERROR, message="数据库操作失败", status_code=500) from exc def create_supplier(self, payload: dict, session: Session | None = None) -> dict: """创建新供应商。 校验名称和类型非空,通过名称+类型判断是否重复。自动记录审计日志。 Args: payload: 包含 supplier_name、supplier_type 等的请求体 session: 数据库会话 Returns: 包含 supplier_id、supplier_name、supplier_type、status 的结果字典 被调用路由: suppliers.py - POST /suppliers """ if session is None: raise AppException(code=ErrorCode.PARAM_ERROR, message="数据库会话未初始化", status_code=500) try: if not payload["supplier_name"].strip(): raise AppException(code=ErrorCode.PARAM_ERROR, message="供应商名称不能为空", status_code=400) if not payload["supplier_type"].strip(): raise AppException(code=ErrorCode.PARAM_ERROR, message="供应商类型不能为空", status_code=400) existed = self.repository.get_supplier_by_name_and_type( session, payload["supplier_name"], payload["supplier_type"], ) if existed is not None: raise AppException(code=ErrorCode.DUPLICATE, message="供应商已存在", status_code=400) supplier = self.repository.create_supplier(session, payload) audit_service.write_log( session, { "operate_type": "supplier_create", "biz_type": "supplier", "biz_id": supplier.id, "before_value": None, "after_value": { "supplier_name": supplier.supplier_name, "supplier_type": supplier.supplier_type, }, "remark": f"新增供应商 {supplier.supplier_name}", }, ) session.commit() cache_delete_pattern("supplier:*") return { "supplier_id": supplier.id, "supplier_name": supplier.supplier_name, "supplier_type": supplier.supplier_type, "status": supplier.status, } except AppException: session.rollback() raise except SQLAlchemyError as exc: session.rollback() raise AppException(code=ErrorCode.SYSTEM_ERROR, message="数据库操作失败", status_code=500) from exc def get_supplier(self, supplier_id: int, session: Session | None = None) -> dict: """查询单个供应商详情。 Args: supplier_id: 供应商 ID session: 数据库会话 Returns: 供应商详情字典 被调用路由: suppliers.py - GET /suppliers/{supplier_id} """ if session is None: raise AppException(code=ErrorCode.PARAM_ERROR, message="数据库会话未初始化", status_code=500) try: supplier = self.repository.get_supplier(session, supplier_id) if supplier is None: raise AppException(code=ErrorCode.NOT_FOUND, message="供应商不存在", status_code=404) return { "supplier_id": supplier.id, "supplier_name": supplier.supplier_name, "supplier_type": supplier.supplier_type, "contact_name": supplier.contact_name, "contact_mobile": supplier.contact_mobile, "address": supplier.address, "template_type": supplier.template_type, "status": supplier.status, "remark": supplier.remark, } except AppException: raise except SQLAlchemyError as exc: raise AppException(code=ErrorCode.SYSTEM_ERROR, message="数据库操作失败", status_code=500) from exc def update_supplier(self, supplier_id: int, payload: dict, session: Session | None = None) -> dict | None: """更新供应商信息。 自动记录审计日志。 Args: supplier_id: 供应商 ID payload: 包含更新字段的请求体 session: 数据库会话 Returns: 更新结果字典,供应商不存在时返回 None 被调用路由: suppliers.py - PUT /suppliers/{supplier_id} """ if session is None: raise AppException(code=ErrorCode.PARAM_ERROR, message="数据库会话未初始化", status_code=500) try: supplier = self.repository.get_supplier(session, supplier_id) if supplier is None: return None if not payload["supplier_name"].strip(): raise AppException(code=ErrorCode.PARAM_ERROR, message="供应商名称不能为空", status_code=400) self.repository.update_supplier( session, supplier, { "supplier_name": payload["supplier_name"], "supplier_type": payload["supplier_type"], "contact_name": payload.get("contact_name"), "contact_mobile": payload.get("contact_mobile"), "address": payload.get("address"), "template_type": payload.get("template_type"), "status": payload.get("status", 1), "remark": payload.get("remark"), }, ) audit_service.write_log( session, { "operate_type": "supplier_update", "biz_type": "supplier", "biz_id": supplier.id, "before_value": None, "after_value": {"supplier_name": supplier.supplier_name, "supplier_type": supplier.supplier_type}, "remark": f"更新供应商 {supplier.supplier_name}", }, ) session.commit() cache_delete_pattern("supplier:*") return {"supplier_id": supplier.id, "supplier_name": supplier.supplier_name} except AppException: session.rollback() raise except SQLAlchemyError as exc: session.rollback() raise AppException(code=ErrorCode.SYSTEM_ERROR, message="数据库操作失败", status_code=500) from exc supplier_service = SupplierService()