from typing import Any from sqlalchemy.ext.asyncio import AsyncSession from common.vo import PageModel from exceptions.exception import ServiceException from module_admin.dao.log_dao import OperationLogDao from module_admin.entity.vo.user_vo import CurrentUserModel from module_business.entity.vo.dashboard_vo import ScanExceptionModel class DashboardService: """首页运营驾驶舱数据服务。""" _SCAN_EXCEPTION_LIMIT = 5 _SCAN_SOURCE_LABELS = { '/business/inbound/scan/{qr_token}': '首次入库扫码', '/business/filling/scan/{qr_token}': '托盘装填扫码', '/business/shipment/scan/{qr_token}': '整托发货扫码', '/business/usage/scan/{qr_token}': '托盘使用扫码', '/business/return/scan/{qr_token}': '托盘退回扫码', } @staticmethod def _row_value(row: Any, *keys: str) -> Any: if isinstance(row, dict): for key in keys: if key in row: return row[key] return None for key in keys: value = getattr(row, key, None) if value is not None: return value return None @classmethod def _scan_source(cls, oper_url: str | None) -> str: if not oper_url: return '扫码接口' for route_path, label in cls._SCAN_SOURCE_LABELS.items(): route_prefix = route_path.split('/{', 1)[0] if oper_url == route_path or oper_url.startswith(f'{route_prefix}/'): return label return '扫码接口' @classmethod async def get_scan_exception_list_services( cls, query_db: AsyncSession, current_user: CurrentUserModel ) -> PageModel[ScanExceptionModel]: """按用户范围获取首页最近扫码异常。""" if current_user.user is None or not current_user.user.user_name: raise ServiceException(message='当前用户信息无效') oper_name = None if current_user.user.admin else current_user.user.user_name page = await OperationLogDao.get_scan_exception_log_list(query_db, oper_name, cls._SCAN_EXCEPTION_LIMIT) rows = [ ScanExceptionModel( oper_id=cls._row_value(row, 'operId', 'oper_id'), source=cls._scan_source(cls._row_value(row, 'operUrl', 'oper_url')), error_message=cls._row_value(row, 'errorMsg', 'error_msg') or '扫码请求失败', operator_name=cls._row_value(row, 'operName', 'oper_name') or '未知用户', occurred_at=cls._row_value(row, 'operTime', 'oper_time'), ) for row in page.rows ] return PageModel[ScanExceptionModel]( rows=rows, pageNum=page.page_num, pageSize=page.page_size, total=page.total, hasNext=page.has_next, )