Feat: 后端新增基础资料与业务管理只读汇总接口
为 Web 端改版提供名称、统计和看板数据。只新增只读查询、扩展列表与详情 返回字段;写接口、托盘状态机和幂等键规则均未改动,未新增权限标识。 - 基础资料:列表补充名称与汇总,客户详情返回现场树,托盘详情返回最近流转, 托盘列表支持按流转阶段筛选和二维码精确匹配;新增现场、产线、隧道的上级 资料选项接口;Excel 导入支持 preview 只校验不写入 - 发货出库:列表支持 keyword、receiptState 筛选并补充名称、托盘数、包数和 菌种构成;详情明细补充托盘号、菌种、批次 - 首次入库:列表与详情补充名称、托盘数、托盘当前状态;新增 /summary - 托盘退回:列表支持 keyword 并补充名称与托盘汇总,详情补充托盘号和隧道; 新增 /summary、/site-options - 工厂装填:列表支持 keyword 并补充托盘号、仓库、操作人;新增 /summary、 /batches、/batches/detail - 客户现场使用:列表支持 keyword、fifoSkipped 并补充名称和被跳过的先到托盘号; 新增 /site-options、/site-board 以及 usage-fifo 的 /queue - 先到先用 DAO 抽出公共候选条件与排序,建议查询生成的 SQL 与改动前一致 - 新增各汇总 DAO 的单元测试,同步更新路由权限与模型字段契约测试 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,89 @@
|
||||
from collections.abc import Awaitable, Callable
|
||||
from typing import Any, Literal
|
||||
|
||||
from pydantic import BaseModel
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from common.vo import PageModel
|
||||
from module_business.dao.master_summary_dao import MasterSummaryDao
|
||||
from module_business.domain.test_batch_scope import TestBatchScope
|
||||
from module_business.entity.vo.master_summary_vo import (
|
||||
CustomerDetailModel,
|
||||
CustomerListItemModel,
|
||||
LineListItemModel,
|
||||
MasterParentOptionModel,
|
||||
PalletDetailModel,
|
||||
PalletListItemModel,
|
||||
SiteListItemModel,
|
||||
StrainListItemModel,
|
||||
TunnelListItemModel,
|
||||
WarehouseListItemModel,
|
||||
)
|
||||
from module_business.entity.vo.master_vo import CustomerModel, PalletModel
|
||||
|
||||
SummaryResource = Literal['strain', 'customer', 'site', 'line', 'tunnel', 'warehouse', 'pallet']
|
||||
_SummaryLoader = Callable[[AsyncSession, list[int], TestBatchScope], Awaitable[dict[int, dict[str, Any]]]]
|
||||
|
||||
_ENRICHERS: dict[SummaryResource, tuple[str, _SummaryLoader, type[BaseModel]]] = {
|
||||
'strain': ('strain_id', MasterSummaryDao.get_strain_summaries, StrainListItemModel),
|
||||
'customer': ('customer_id', MasterSummaryDao.get_customer_summaries, CustomerListItemModel),
|
||||
'site': ('site_id', MasterSummaryDao.get_site_summaries, SiteListItemModel),
|
||||
'line': ('line_id', MasterSummaryDao.get_line_summaries, LineListItemModel),
|
||||
'tunnel': ('tunnel_id', MasterSummaryDao.get_tunnel_summaries, TunnelListItemModel),
|
||||
'warehouse': ('warehouse_id', MasterSummaryDao.get_warehouse_summaries, WarehouseListItemModel),
|
||||
'pallet': ('pallet_id', MasterSummaryDao.get_pallet_summaries, PalletListItemModel),
|
||||
}
|
||||
|
||||
|
||||
class MasterSummaryService:
|
||||
"""为基础资料页面补充只读汇总。导出与写入仍使用原主数据模型。"""
|
||||
|
||||
@classmethod
|
||||
async def enrich_page(
|
||||
cls,
|
||||
db: AsyncSession,
|
||||
resource: SummaryResource,
|
||||
page: PageModel,
|
||||
scope: TestBatchScope,
|
||||
) -> PageModel:
|
||||
id_field, loader, item_model = _ENRICHERS[resource]
|
||||
rows = list(page.rows)
|
||||
summaries = await loader(db, [getattr(row, id_field) for row in rows], scope)
|
||||
enriched = [
|
||||
item_model.model_validate({**row.model_dump(), **summaries.get(getattr(row, id_field), {})})
|
||||
for row in rows
|
||||
]
|
||||
return page.model_copy(update={'rows': enriched})
|
||||
|
||||
@classmethod
|
||||
async def customer_detail(
|
||||
cls,
|
||||
db: AsyncSession,
|
||||
customer: CustomerModel,
|
||||
scope: TestBatchScope,
|
||||
) -> CustomerDetailModel:
|
||||
sites = await MasterSummaryDao.get_customer_site_tree(db, customer.customer_id, scope)
|
||||
return CustomerDetailModel.model_validate({**customer.model_dump(), 'sites': sites})
|
||||
|
||||
@classmethod
|
||||
async def pallet_detail(
|
||||
cls,
|
||||
db: AsyncSession,
|
||||
pallet: PalletModel,
|
||||
scope: TestBatchScope,
|
||||
) -> PalletDetailModel:
|
||||
summaries = await MasterSummaryDao.get_pallet_summaries(db, [pallet.pallet_id], scope)
|
||||
events = await MasterSummaryDao.get_pallet_recent_events(db, pallet.pallet_id, scope)
|
||||
return PalletDetailModel.model_validate(
|
||||
{**pallet.model_dump(), **summaries.get(pallet.pallet_id, {}), 'recent_events': events}
|
||||
)
|
||||
|
||||
@classmethod
|
||||
async def customer_options(cls, db: AsyncSession, scope: TestBatchScope) -> list[MasterParentOptionModel]:
|
||||
rows = await MasterSummaryDao.list_customer_options(db, scope)
|
||||
return [MasterParentOptionModel.model_validate(row) for row in rows]
|
||||
|
||||
@classmethod
|
||||
async def site_options(cls, db: AsyncSession, scope: TestBatchScope) -> list[MasterParentOptionModel]:
|
||||
rows = await MasterSummaryDao.list_site_options(db, scope)
|
||||
return [MasterParentOptionModel.model_validate(row) for row in rows]
|
||||
Reference in New Issue
Block a user