调整部分前端布局和后端增删改逻辑

This commit is contained in:
焦龙言
2026-09-28 18:29:10 +08:00
parent 17d991a74a
commit a539b4eb71
57 changed files with 87382 additions and 895 deletions
@@ -0,0 +1,37 @@
"""首页运营驾驶舱接口。"""
from typing import Annotated
from fastapi import Request, Response
from sqlalchemy.ext.asyncio import AsyncSession
from common.aspect.db_seesion import DBSessionDependency
from common.aspect.pre_auth import CurrentUserDependency, PreAuthDependency
from common.router import APIRouterPro
from common.vo import PageResponseModel
from module_admin.entity.vo.user_vo import CurrentUserModel
from module_business.entity.vo.dashboard_vo import ScanExceptionModel
from module_business.service.dashboard_service import DashboardService
from utils.response_util import ResponseUtil
dashboard_controller = APIRouterPro(
prefix='/business/dashboard',
tags=['业务管理-运营驾驶舱'],
dependencies=[PreAuthDependency()],
)
@dashboard_controller.get(
'/scan-exceptions',
summary='获取首页扫码异常分页列表接口',
description='普通用户仅查看本人扫码异常,超级管理员查看全部扫码异常',
response_model=PageResponseModel[ScanExceptionModel],
)
async def get_business_dashboard_scan_exceptions(
request: Request,
query_db: Annotated[AsyncSession, DBSessionDependency()],
current_user: Annotated[CurrentUserModel, CurrentUserDependency()],
) -> Response:
result = await DashboardService.get_scan_exception_list_services(query_db, current_user)
return ResponseUtil.success(model_content=result)
@@ -78,6 +78,13 @@ async def get_business_filling_strain_options(
response_model=DataResponseModel[PalletScanResponseModel],
dependencies=[UserInterfaceAuthDependency('business:filling:query')],
)
@Log(
title='扫码操作',
business_type=BusinessType.OTHER,
request_log_mode='none',
response_log_mode='summary',
oper_url_mode='route',
)
async def scan_business_filling_pallet(
request: Request,
qr_token: Annotated[
@@ -81,6 +81,13 @@ async def get_business_inbound_warehouse_options(
response_model=DataResponseModel[PalletScanResponseModel],
dependencies=[UserInterfaceAuthDependency('business:inbound:query')],
)
@Log(
title='扫码操作',
business_type=BusinessType.OTHER,
request_log_mode='none',
response_log_mode='summary',
oper_url_mode='route',
)
async def scan_business_inbound_pallet(
request: Request,
qr_token: Annotated[
@@ -82,6 +82,13 @@ async def get_business_return_warehouse_options(
response_model=DataResponseModel[PalletScanResponseModel],
dependencies=[UserInterfaceAuthDependency('business:return:query')],
)
@Log(
title='扫码操作',
business_type=BusinessType.OTHER,
request_log_mode='none',
response_log_mode='summary',
oper_url_mode='route',
)
async def scan_business_return_pallet(
request: Request,
qr_token: Annotated[
@@ -98,6 +98,13 @@ async def get_business_shipment_site_options(
response_model=DataResponseModel[PalletScanResponseModel],
dependencies=[UserInterfaceAuthDependency('business:shipment:query')],
)
@Log(
title='扫码操作',
business_type=BusinessType.OTHER,
request_log_mode='none',
response_log_mode='summary',
oper_url_mode='route',
)
async def scan_business_shipment_pallet(
request: Request,
qr_token: Annotated[
@@ -82,6 +82,13 @@ async def get_business_usage_line_options(
response_model=DataResponseModel[PalletScanResponseModel],
dependencies=[UserInterfaceAuthDependency('business:usage:query')],
)
@Log(
title='扫码操作',
business_type=BusinessType.OTHER,
request_log_mode='none',
response_log_mode='summary',
oper_url_mode='route',
)
async def scan_business_usage_pallet(
request: Request,
qr_token: Annotated[
@@ -0,0 +1,16 @@
from datetime import datetime
from pydantic import BaseModel, ConfigDict, Field
from pydantic.alias_generators import to_camel
class ScanExceptionModel(BaseModel):
"""首页扫码异常记录。"""
model_config = ConfigDict(alias_generator=to_camel, from_attributes=True, populate_by_name=True)
oper_id: int = Field(gt=0, description='异常日志ID')
source: str = Field(min_length=1, max_length=50, description='扫码来源')
error_message: str = Field(min_length=1, max_length=2000, description='异常消息')
operator_name: str = Field(min_length=1, max_length=50, description='操作人员')
occurred_at: datetime | None = Field(default=None, description='异常时间')
@@ -0,0 +1,73 @@
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,
)