dingdanquanliucheng/backend/app/services/supplier_service.py
taiyi 76c3b4af13 feat: 补全产品、供应商、物流、提醒模块 Redis 缓存
产品/供应商列表缓存 120s,物流任务列表缓存 30s,提醒列表缓存 30s,
写操作后自动清除对应缓存。至此所有主要读接口均已接入 Redis。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-31 11:05:50 +08:00

226 lines
9.2 KiB
Python

"""供应商(工厂)管理服务层。
负责供应商的增删改查业务逻辑。
依赖 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()