为 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>
90 lines
3.7 KiB
Python
90 lines
3.7 KiB
Python
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]
|