产品/供应商列表缓存 120s,物流任务列表缓存 30s,提醒列表缓存 30s, 写操作后自动清除对应缓存。至此所有主要读接口均已接入 Redis。 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
226 lines
9.2 KiB
Python
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()
|