安全:补齐业务批次归属校验

This commit is contained in:
焦龙言
2026-09-23 11:49:35 +08:00
parent 97b4dc8508
commit 5a76fe7521
10 changed files with 509 additions and 58 deletions
@@ -16,7 +16,7 @@ from module_business.dao.master_dao import MasterDao
from module_business.dao.operation_dao import OperationDao
from module_business.domain.enums import PalletEventType, PalletFlowState
from module_business.domain.pallet_state_machine import PalletStateMachine
from module_business.domain.test_batch_scope import TestBatchScope
from module_business.domain.test_batch_scope import TestBatchScope, assert_record_in_scope
from module_business.entity.do.master_do import BizPallet, BizStrain
from module_business.entity.do.operation_do import BizFillingRecord, BizPalletEvent, BizPalletInventory
from module_business.entity.vo.master_vo import StrainPageQueryModel
@@ -260,9 +260,11 @@ class FillingService:
pallet = await InventoryDao.get_pallet_by_qr_token(query_db, qr_token, test_scope)
if pallet is None:
cls._raise_pallet_not_accessible(current_user)
assert_record_in_scope(pallet, test_scope, '托盘')
inventory = await MasterDao.get_pallet_inventory(query_db, pallet.pallet_id, test_scope)
if inventory is None:
cls._raise_pallet_not_accessible(current_user, '托盘库存不存在')
assert_record_in_scope(inventory, test_scope, '库存')
if inventory.warehouse_id is None:
cls._raise_pallet_not_accessible(current_user, '托盘不在仓库')
await cls._assert_pallet_warehouse_access(
@@ -351,6 +353,7 @@ class FillingService:
warehouse_ids=[page_object.warehouse_id],
)
if existing is not None:
assert_record_in_scope(existing, test_scope, '装填记录')
await AccessService.assert_warehouse_access(
query_db,
current_user,
@@ -375,6 +378,7 @@ class FillingService:
)
if scanned_pallet is None:
cls._raise_pallet_not_accessible(current_user)
assert_record_in_scope(scanned_pallet, test_scope, '托盘')
inventory = await InventoryDao.get_inventory_for_update(
query_db,
@@ -383,6 +387,7 @@ class FillingService:
)
if inventory is None:
cls._raise_pallet_not_accessible(current_user, '托盘库存不存在')
assert_record_in_scope(inventory, test_scope, '库存')
if inventory.warehouse_id is None:
cls._raise_pallet_not_accessible(current_user, '托盘不在仓库')
await cls._assert_pallet_warehouse_access(
@@ -398,6 +403,7 @@ class FillingService:
if len(locked_pallets) != 1 or locked_pallets[0].pallet_id != scanned_pallet.pallet_id:
cls._raise_pallet_not_accessible(current_user)
pallet = locked_pallets[0]
assert_record_in_scope(pallet, test_scope, '托盘')
if inventory.active_filling_id is not None:
concurrent = await OperationDao.get_active_filling_for_update(
@@ -406,6 +412,7 @@ class FillingService:
test_scope,
)
if concurrent is not None and concurrent.idempotency_key == page_object.idempotency_key:
assert_record_in_scope(concurrent, test_scope, '活动装填')
# 结束首次非锁定读取建立的 RR 快照,再按最新数据范围恢复并发成功结果。
await query_db.rollback()
recovery_transaction_ended = True
@@ -425,6 +432,7 @@ class FillingService:
)
if warehouse is None:
raise ServiceException(message='仓库不存在')
assert_record_in_scope(warehouse, test_scope, '仓库')
if warehouse.status != '0':
raise ServiceException(message='仓库已停用')
await cls._assert_pallet_warehouse_access(
@@ -441,6 +449,7 @@ class FillingService:
for_update=True,
)
)
assert_record_in_scope(strain, test_scope, '菌种')
cls._validate_empty_warehouse_pallet(
pallet,
@@ -16,7 +16,7 @@ from module_business.dao.master_dao import MasterDao
from module_business.dao.operation_dao import OperationDao
from module_business.domain.enums import DocumentStatus, PalletEventType, PalletFlowState
from module_business.domain.pallet_state_machine import PalletStateMachine
from module_business.domain.test_batch_scope import TestBatchScope
from module_business.domain.test_batch_scope import TestBatchScope, assert_record_in_scope
from module_business.entity.do.master_do import BizPallet
from module_business.entity.do.operation_do import (
BizInboundItem,
@@ -103,6 +103,7 @@ class InboundService:
warehouse = await MasterDao.get_warehouse(query_db, warehouse_id, test_scope)
if warehouse is None:
raise ServiceException(message='目标仓库不存在')
assert_record_in_scope(warehouse, test_scope, '仓库')
if warehouse.status != '0':
raise ServiceException(message='目标仓库已停用')
return warehouse
@@ -128,6 +129,8 @@ class InboundService:
test_scope,
for_update=True,
)
if warehouses[warehouse_id] is not None:
assert_record_in_scope(warehouses[warehouse_id], test_scope, '仓库')
try:
await AccessService.assert_warehouse_access(
query_db,
@@ -195,6 +198,10 @@ class InboundService:
ordered_ids = sorted(pallet_ids)
inventories = await InventoryDao.get_inventories_for_update(query_db, ordered_ids, test_scope)
pallets = await InventoryDao.get_pallets_by_ids(query_db, ordered_ids, test_scope)
for inventory in inventories:
assert_record_in_scope(inventory, test_scope, '库存')
for pallet in pallets:
assert_record_in_scope(pallet, test_scope, '托盘')
expected_ids = set(ordered_ids)
inventory_by_id = {inventory.pallet_id: inventory for inventory in inventories}
pallet_by_id = {pallet.pallet_id: pallet for pallet in pallets}
@@ -327,15 +334,18 @@ class InboundService:
warehouse = await MasterDao.get_warehouse(query_db, query_object.warehouse_id, test_scope)
if warehouse is None:
raise ServiceException(message='目标仓库不存在')
assert_record_in_scope(warehouse, test_scope, '仓库')
if warehouse.status != '0':
raise ServiceException(message='目标仓库已停用')
pallet = await InventoryDao.get_pallet_by_qr_token(query_db, qr_token, test_scope)
if pallet is None:
raise ServiceException(message='托盘不存在')
assert_record_in_scope(pallet, test_scope, '托盘')
inventory = await MasterDao.get_pallet_inventory(query_db, pallet.pallet_id, test_scope)
if inventory is None:
raise ServiceException(message='托盘库存不存在')
assert_record_in_scope(inventory, test_scope, '库存')
cls._validate_pallet_pair(pallet, inventory)
return PalletScanResponseModel(
pallet_id=pallet.pallet_id,
@@ -18,7 +18,7 @@ from module_business.dao.master_dao import MasterDao
from module_business.dao.operation_dao import OperationDao
from module_business.domain.enums import PalletEventType, PalletFlowState, ReturnStatus
from module_business.domain.pallet_state_machine import PalletStateMachine
from module_business.domain.test_batch_scope import TestBatchScope
from module_business.domain.test_batch_scope import TestBatchScope, assert_record_in_scope
from module_business.entity.do.master_do import BizPallet
from module_business.entity.do.operation_do import (
BizFillingRecord,
@@ -256,6 +256,7 @@ class ReturnService:
)
if return_order is None:
cls._raise_return_not_accessible(current_user)
assert_record_in_scope(return_order, test_scope, '退回单')
return ReturnModel.model_validate(return_order)
@classmethod
@@ -346,9 +347,11 @@ class ReturnService:
pallet = await InventoryDao.get_pallet_by_qr_token(query_db, qr_token, test_scope)
if pallet is None:
cls._raise_pallet_not_accessible(current_user)
assert_record_in_scope(pallet, test_scope, '托盘')
inventory = await MasterDao.get_pallet_inventory(query_db, pallet.pallet_id, test_scope)
if inventory is None or inventory.site_id is None:
cls._raise_pallet_not_accessible(current_user, '托盘库存不存在或不在客户现场')
assert_record_in_scope(inventory, test_scope, '库存')
await cls._assert_pallet_site_access(query_db, current_user, inventory.site_id)
cls._validate_request_inventory(pallet, inventory, inventory.site_id)
@@ -357,6 +360,7 @@ class ReturnService:
filling = await OperationDao.get_filling_by_id(query_db, inventory.active_filling_id, test_scope)
if filling is None:
raise ServiceException(message='托盘活动装填周期不存在')
assert_record_in_scope(filling, test_scope, '活动装填')
cls._validate_active_filling(filling, inventory)
return cls._scan_response(pallet, inventory, filling, ['request'])
@@ -380,18 +384,22 @@ class ReturnService:
)
if return_order is None:
cls._raise_return_not_accessible(current_user)
assert_record_in_scope(return_order, test_scope, '退回单')
if return_order.status != ReturnStatus.REQUESTED.value:
raise ServiceException(message='退回单当前状态不允许确认扫码')
pallet = await InventoryDao.get_pallet_by_qr_token(query_db, qr_token, test_scope)
if pallet is None:
cls._raise_pallet_not_accessible(current_user)
assert_record_in_scope(pallet, test_scope, '托盘')
matching_items = [item for item in return_order.items if item.pallet_id == pallet.pallet_id]
if len(matching_items) != 1:
cls._raise_pallet_not_accessible(current_user, '扫码托盘不属于该退回单')
assert_record_in_scope(matching_items[0], test_scope, '退回单明细')
inventory = await MasterDao.get_pallet_inventory(query_db, pallet.pallet_id, test_scope)
if inventory is None:
cls._raise_pallet_not_accessible(current_user, '托盘库存不存在')
assert_record_in_scope(inventory, test_scope, '库存')
item = matching_items[0]
cls._validate_confirm_snapshot(return_order, item, pallet, inventory)
@@ -400,6 +408,7 @@ class ReturnService:
filling = await OperationDao.get_filling_by_id(query_db, inventory.active_filling_id, test_scope)
if filling is None:
raise ServiceException(message='托盘活动装填周期不存在')
assert_record_in_scope(filling, test_scope, '活动装填')
cls._validate_active_filling(filling, inventory)
return cls._scan_response(pallet, inventory, filling, ['confirm'])
@@ -472,6 +481,8 @@ class ReturnService:
cls._raise_pallet_not_accessible(current_user, '存在不存在或库存缺失的托盘')
ordered_inventories = [inventory_by_id[pallet_id] for pallet_id in ordered_ids]
for inventory in ordered_inventories:
assert_record_in_scope(inventory, test_scope, '库存')
if any(inventory.site_id != source_site_id for inventory in ordered_inventories):
cls._raise_pallet_not_accessible(current_user, '同一退回单托盘必须属于同一现场')
if any(
@@ -485,6 +496,8 @@ class ReturnService:
if set(pallet_by_id) != set(ordered_ids):
cls._raise_pallet_not_accessible(current_user, '存在不存在或库存缺失的托盘')
ordered_pallets = [pallet_by_id[pallet_id] for pallet_id in ordered_ids]
for pallet in ordered_pallets:
assert_record_in_scope(pallet, test_scope, '托盘')
filling_by_id: dict[int, BizFillingRecord | None] = {}
filling_ids = sorted(
@@ -560,6 +573,7 @@ class ReturnService:
)
if warehouse is None:
raise ServiceException(message='目标仓库不存在')
assert_record_in_scope(warehouse, test_scope, '仓库')
if warehouse.status != '0':
raise ServiceException(message='目标仓库已停用')
@@ -572,6 +586,7 @@ class ReturnService:
)
if site is None:
raise ServiceException(message='来源客户现场不存在')
assert_record_in_scope(site, test_scope, '现场')
if site.status != '0':
raise ServiceException(message='来源客户现场已停用')
@@ -595,6 +610,7 @@ class ReturnService:
cls._raise_return_not_accessible(current_user)
if site is None:
raise ServiceException(message='来源客户现场不存在')
assert_record_in_scope(site, test_scope, '现场')
@classmethod
async def _recover_request_integrity_error(
@@ -620,6 +636,7 @@ class ReturnService:
)
if recovered is None or not cls._is_exact_request_result(recovered, page_object, request_hash):
raise ServiceException(message='申请退回失败,幂等冲突')
assert_record_in_scope(recovered, test_scope, '退回单')
locked = await OperationDao.get_return_for_update(
query_db,
recovered.return_id,
@@ -629,6 +646,7 @@ class ReturnService:
)
if locked is None:
cls._raise_return_not_accessible(current_user)
assert_record_in_scope(locked, test_scope, '退回单')
if not cls._is_exact_request_result(
locked,
page_object,
@@ -689,6 +707,7 @@ class ReturnService:
)
if locked_existing is None:
cls._raise_return_not_accessible(current_user)
assert_record_in_scope(locked_existing, test_scope, '退回单')
if not cls._is_exact_request_result(
locked_existing,
page_object,
@@ -721,6 +740,7 @@ class ReturnService:
scope=test_scope,
)
if fresh_existing is not None:
assert_record_in_scope(fresh_existing, test_scope, '退回单')
if not cls._is_exact_request_result(fresh_existing, page_object, request_hash):
raise ServiceException(message='申请退回失败,幂等键已被其他请求使用')
fresh_existing_id = int(fresh_existing.return_id)
@@ -863,6 +883,9 @@ class ReturnService:
test_scope: TestBatchScope,
) -> tuple[list[BizPalletInventory], dict[int, BizFillingRecord]]:
ordered_ids = sorted(item.pallet_id for item in items)
assert_record_in_scope(return_order, test_scope, '退回单')
for item in items:
assert_record_in_scope(item, test_scope, '退回单明细')
inventories = await InventoryDao.get_inventories_for_update(query_db, ordered_ids, test_scope)
pallets = await InventoryDao.get_pallets_by_ids(query_db, ordered_ids, test_scope)
inventory_by_id = {inventory.pallet_id: inventory for inventory in inventories}
@@ -876,6 +899,10 @@ class ReturnService:
raise ServiceException(message='退回单存在不存在或库存缺失的托盘')
ordered_inventories = [inventory_by_id[pallet_id] for pallet_id in ordered_ids]
for inventory in ordered_inventories:
assert_record_in_scope(inventory, test_scope, '库存')
for pallet in pallet_by_id.values():
assert_record_in_scope(pallet, test_scope, '托盘')
for pallet_id in ordered_ids:
cls._validate_confirm_snapshot(
return_order,
@@ -896,6 +923,7 @@ class ReturnService:
filling = await OperationDao.get_active_filling_for_update(query_db, filling_id, test_scope)
if filling is None:
raise ServiceException(message='托盘活动装填周期不存在或已关闭')
assert_record_in_scope(filling, test_scope, '活动装填')
filling_by_id[filling_id] = filling
for inventory in ordered_inventories:
if inventory.active_filling_id is not None:
@@ -929,6 +957,7 @@ class ReturnService:
cls._raise_return_not_accessible(current_user)
if warehouse is None:
raise ServiceException(message='目标仓库不存在')
assert_record_in_scope(warehouse, test_scope, '仓库')
if require_active and warehouse.status != '0':
raise ServiceException(message='目标仓库已停用')
@@ -955,6 +984,7 @@ class ReturnService:
)
if route is None:
cls._raise_return_not_accessible(current_user)
assert_record_in_scope(route, test_scope, '退回单')
recovered = await OperationDao.get_return_by_confirm_idempotency_key(
query_db,
page_object.idempotency_key,
@@ -964,6 +994,7 @@ class ReturnService:
)
if recovered is None:
raise ServiceException(message='确认退回失败,幂等冲突')
assert_record_in_scope(recovered, test_scope, '退回单')
if not cls._is_exact_confirm_result(recovered, return_id, page_object, request_hash):
raise ServiceException(message='确认退回失败,幂等键已被其他请求使用')
locked = await OperationDao.get_return_for_update(
@@ -975,6 +1006,7 @@ class ReturnService:
)
if locked is None:
cls._raise_return_not_accessible(current_user)
assert_record_in_scope(locked, test_scope, '退回单')
if not cls._is_exact_confirm_result(
locked,
return_id,
@@ -1019,6 +1051,7 @@ class ReturnService:
)
if return_order is None:
cls._raise_return_not_accessible(current_user)
assert_record_in_scope(return_order, test_scope, '退回单')
try:
await AccessService.assert_warehouse_access(
query_db,
@@ -1041,6 +1074,8 @@ class ReturnService:
request_hash,
):
raise ServiceException(message='确认退回失败,幂等键已被其他请求使用')
if existing is not None:
assert_record_in_scope(existing, test_scope, '退回单')
await query_db.rollback()
await TestBatchAccessService.lock_active_batch(query_db, test_scope)
@@ -1053,6 +1088,7 @@ class ReturnService:
)
if locked is None:
cls._raise_return_not_accessible(current_user)
assert_record_in_scope(locked, test_scope, '退回单')
if locked.status == ReturnStatus.CONFIRMED.value:
if cls._is_exact_confirm_result(
locked,
@@ -16,7 +16,7 @@ from module_business.dao.master_dao import MasterDao
from module_business.dao.operation_dao import OperationDao
from module_business.domain.enums import DocumentStatus, PalletEventType, PalletFlowState
from module_business.domain.pallet_state_machine import PalletStateMachine
from module_business.domain.test_batch_scope import TestBatchScope
from module_business.domain.test_batch_scope import TestBatchScope, assert_record_in_scope
from module_business.entity.do.master_do import BizPallet
from module_business.entity.do.operation_do import (
BizFillingRecord,
@@ -105,12 +105,14 @@ class ShipmentService:
warehouse = await MasterDao.get_warehouse(query_db, source_warehouse_id, test_scope)
if warehouse is None:
raise ServiceException(message='来源仓库不存在')
assert_record_in_scope(warehouse, test_scope, '仓库')
if warehouse.status != '0':
raise ServiceException(message='来源仓库已停用')
await AccessService.assert_site_access(query_db, current_user, target_site_id)
site = await MasterDao.get_site(query_db, target_site_id, test_scope)
if site is None:
raise ServiceException(message='目标现场不存在')
assert_record_in_scope(site, test_scope, '现场')
if site.status != '0':
raise ServiceException(message='目标现场已停用')
return warehouse, site
@@ -166,6 +168,10 @@ class ShipmentService:
ordered_ids = cls._validate_pallet_ids(pallet_ids)
inventories = await InventoryDao.get_inventories_for_update(query_db, ordered_ids, test_scope)
pallets = await InventoryDao.get_pallets_by_ids(query_db, ordered_ids, test_scope)
for inventory in inventories:
assert_record_in_scope(inventory, test_scope, '库存')
for pallet in pallets:
assert_record_in_scope(pallet, test_scope, '托盘')
if (
len(inventories) != len(ordered_ids)
or len(pallets) != len(ordered_ids)
@@ -188,6 +194,7 @@ class ShipmentService:
filling = await OperationDao.get_active_filling_for_update(query_db, filling_id, test_scope)
if filling is None:
raise ServiceException(message='托盘活动装填周期不存在或已关闭')
assert_record_in_scope(filling, test_scope, '活动装填')
fillings.append(filling)
if len(fillings) != len(ordered_ids):
raise ServiceException(message='托盘活动装填周期不完整')
@@ -412,6 +419,7 @@ class ShipmentService:
warehouse = await MasterDao.get_warehouse(query_db, query_object.source_warehouse_id, test_scope)
if warehouse is None:
raise ServiceException(message='来源仓库不存在')
assert_record_in_scope(warehouse, test_scope, '仓库')
if warehouse.status != '0':
raise ServiceException(message='来源仓库已停用')
await AccessService.assert_site_access(
@@ -422,15 +430,18 @@ class ShipmentService:
site = await MasterDao.get_site(query_db, query_object.target_site_id, test_scope)
if site is None:
raise ServiceException(message='目标现场不存在')
assert_record_in_scope(site, test_scope, '现场')
if site.status != '0':
raise ServiceException(message='目标现场已停用')
pallet = await InventoryDao.get_pallet_by_qr_token(query_db, qr_token, test_scope)
if pallet is None:
cls._raise_pallet_not_accessible(current_user)
assert_record_in_scope(pallet, test_scope, '托盘')
inventory = await MasterDao.get_pallet_inventory(query_db, pallet.pallet_id, test_scope)
if inventory is None:
cls._raise_pallet_not_accessible(current_user, '托盘库存不存在')
assert_record_in_scope(inventory, test_scope, '库存')
if inventory.pallet_id != pallet.pallet_id or inventory.warehouse_id != query_object.source_warehouse_id:
cls._raise_pallet_not_accessible(current_user, '托盘不在来源仓库')
if inventory.site_id is not None or inventory.current_line_id is not None:
@@ -440,6 +451,7 @@ class ShipmentService:
filling = await OperationDao.get_filling_by_id(query_db, inventory.active_filling_id, test_scope)
if filling is None:
raise ServiceException(message='托盘活动装填周期不存在')
assert_record_in_scope(filling, test_scope, '活动装填')
cls._validate_scan_pallet(
pallet,
inventory,
@@ -629,7 +641,7 @@ class ShipmentService:
)
@classmethod
async def _lock_and_authorize_routes(
async def _lock_and_authorize_routes( # noqa: PLR0912
cls,
query_db: AsyncSession,
current_user: CurrentUserModel,
@@ -651,6 +663,8 @@ class ShipmentService:
for_update=True,
)
warehouses[warehouse_id] = warehouse
if warehouse is not None:
assert_record_in_scope(warehouse, test_scope, '仓库')
try:
await AccessService.assert_warehouse_access(
query_db,
@@ -671,6 +685,8 @@ class ShipmentService:
for_update=True,
)
sites[site_id] = site
if site is not None:
assert_record_in_scope(site, test_scope, '现场')
try:
await AccessService.assert_site_access(
query_db,
@@ -749,6 +765,7 @@ class ShipmentService:
)
if current is None:
cls._raise_shipment_not_accessible(current_user)
assert_record_in_scope(current, test_scope, '发货单')
recovered = await OperationDao.get_shipment_by_confirm_idempotency_key(
query_db,
idempotency_key,
@@ -758,6 +775,7 @@ class ShipmentService:
)
if recovered is None:
raise ServiceException(message='确认发货失败,幂等冲突')
assert_record_in_scope(recovered, test_scope, '发货单')
if not cls._is_exact_confirmed(
recovered,
shipment_id,
@@ -774,6 +792,7 @@ class ShipmentService:
)
if locked_recovered is None:
cls._raise_shipment_not_accessible(current_user)
assert_record_in_scope(locked_recovered, test_scope, '发货单')
if not cls._is_exact_confirmed(
locked_recovered,
shipment_id,
@@ -814,6 +833,7 @@ class ShipmentService:
)
if shipment is None:
cls._raise_shipment_not_accessible(current_user)
assert_record_in_scope(shipment, test_scope, '发货单')
request_hash = cls._build_confirm_request_hash(shipment_id)
idempotency_key = page_object.idempotency_key
@@ -841,6 +861,7 @@ class ShipmentService:
)
if locked_shipment is None:
cls._raise_shipment_not_accessible(current_user)
assert_record_in_scope(locked_shipment, test_scope, '发货单')
if locked_shipment.status == DocumentStatus.CONFIRMED.value:
if cls._is_exact_confirmed(
locked_shipment,
@@ -18,7 +18,7 @@ from module_business.dao.master_dao import MasterDao
from module_business.dao.operation_dao import OperationDao
from module_business.domain.enums import PalletEventType, PalletFlowState
from module_business.domain.pallet_state_machine import PalletStateMachine
from module_business.domain.test_batch_scope import TestBatchScope
from module_business.domain.test_batch_scope import TestBatchScope, assert_record_in_scope
from module_business.entity.do.operation_do import BizPalletEvent, BizUsageRecord
from module_business.entity.vo.master_vo import LinePageQueryModel
from module_business.entity.vo.operation_vo import (
@@ -165,6 +165,7 @@ class UsageService:
cls._raise_usage_not_accessible(current_user)
if site is None:
raise ServiceException(message='客户现场不存在')
assert_record_in_scope(site, test_scope, '现场')
@classmethod
async def _recover_integrity_error(
@@ -189,6 +190,7 @@ class UsageService:
)
if recovered is None:
raise ServiceException(message='使用失败,数据冲突')
assert_record_in_scope(recovered, test_scope, '使用记录')
if recovered.site_id != expected_site_id or not cls._is_exact_idempotent_result(
recovered, page_object, request_hash
):
@@ -255,6 +257,7 @@ class UsageService:
)
if usage is None:
cls._raise_usage_not_accessible(current_user)
assert_record_in_scope(usage, test_scope, '使用记录')
return UsageModel.model_validate(usage)
@classmethod
@@ -270,6 +273,7 @@ class UsageService:
site = await MasterDao.get_site(query_db, site_id, test_scope)
if site is None:
raise ServiceException(message='客户现场不存在')
assert_record_in_scope(site, test_scope, '现场')
if site.status != '0':
raise ServiceException(message='客户现场已停用')
lines = await MasterDao.list_line(
@@ -301,9 +305,11 @@ class UsageService:
pallet = await InventoryDao.get_pallet_by_qr_token(query_db, normalized_qr, test_scope)
if pallet is None:
cls._raise_pallet_not_accessible(current_user)
assert_record_in_scope(pallet, test_scope, '托盘')
inventory = await MasterDao.get_pallet_inventory(query_db, pallet.pallet_id, test_scope)
if inventory is None:
cls._raise_pallet_not_accessible(current_user, '托盘库存不存在')
assert_record_in_scope(inventory, test_scope, '库存')
if inventory.site_id is None:
cls._raise_pallet_not_accessible(current_user, '托盘不在客户现场')
await cls._assert_pallet_site_access(query_db, current_user, inventory.site_id)
@@ -328,6 +334,7 @@ class UsageService:
filling = await OperationDao.get_filling_by_id(query_db, inventory.active_filling_id, test_scope)
if filling is None:
raise ServiceException(message='托盘活动装填周期不存在')
assert_record_in_scope(filling, test_scope, '活动装填')
if (
filling.filling_id != inventory.active_filling_id
or filling.pallet_id != pallet.pallet_id
@@ -369,6 +376,8 @@ class UsageService:
preflight_site_ids = await cls._site_scope(query_db, current_user)
# 产线归属是请求内稳定路由;锁事务仍以库存中的实际现场复核。
preflight_line = await MasterDao.get_line(query_db, page_object.line_id, test_scope)
if preflight_line is not None:
assert_record_in_scope(preflight_line, test_scope, '产线')
preflight_site_id = preflight_line.site_id if preflight_line is not None else None
existing = None
if preflight_site_id is not None and (
@@ -381,6 +390,7 @@ class UsageService:
site_ids=[preflight_site_id],
)
if existing is not None:
assert_record_in_scope(existing, test_scope, '使用记录')
if not cls._is_exact_idempotent_result(existing, page_object, request_hash):
raise ServiceException(message='使用失败,幂等键已被其他请求使用')
if preflight_site_ids is not None and existing.site_id not in preflight_site_ids:
@@ -402,6 +412,7 @@ class UsageService:
)
if scanned_pallet is None:
cls._raise_pallet_not_accessible(current_user)
assert_record_in_scope(scanned_pallet, test_scope, '托盘')
scanned_pallet_id = int(scanned_pallet.pallet_id)
await query_db.rollback()
await TestBatchAccessService.lock_active_batch(query_db, test_scope)
@@ -413,6 +424,7 @@ class UsageService:
)
if inventory is None:
cls._raise_pallet_not_accessible(current_user, '托盘库存不存在')
assert_record_in_scope(inventory, test_scope, '库存')
if inventory.site_id is None:
cls._raise_pallet_not_accessible(current_user, '托盘不在客户现场')
expected_site_id = inventory.site_id
@@ -429,6 +441,7 @@ class UsageService:
if len(locked_pallets) != 1 or locked_pallets[0].pallet_id != inventory.pallet_id:
cls._raise_pallet_not_accessible(current_user)
pallet = locked_pallets[0]
assert_record_in_scope(pallet, test_scope, '托盘')
filling = (
await OperationDao.get_active_filling_for_update(
@@ -446,6 +459,7 @@ class UsageService:
site_ids=[inventory.site_id],
)
if locked_existing is not None:
assert_record_in_scope(locked_existing, test_scope, '使用记录')
if locked_existing.site_id != inventory.site_id or not cls._is_exact_idempotent_result(
locked_existing,
page_object,
@@ -466,6 +480,7 @@ class UsageService:
raise ServiceException(message='托盘不存在活动装填周期')
if filling is None:
raise ServiceException(message='托盘活动装填周期不存在或已关闭')
assert_record_in_scope(filling, test_scope, '活动装填')
site = await MasterDao.get_site(query_db, inventory.site_id, test_scope, for_update=True)
await cls._assert_pallet_site_access(
@@ -476,11 +491,13 @@ class UsageService:
)
if site is None:
raise ServiceException(message='客户现场不存在')
assert_record_in_scope(site, test_scope, '现场')
if site.status != '0':
raise ServiceException(message='客户现场已停用')
line = await MasterDao.get_line(query_db, page_object.line_id, test_scope, for_update=True)
if line is None:
raise ServiceException(message='产线不存在')
assert_record_in_scope(line, test_scope, '产线')
if line.status != '0':
raise ServiceException(message='产线已停用')
if line.site_id != inventory.site_id:
@@ -540,6 +557,7 @@ class UsageService:
test_batch_id=test_scope.batch_id,
)
usage = await OperationDao.add_usage_record(query_db, usage)
assert_record_in_scope(usage, test_scope, '使用记录')
usage_id = usage.usage_id
result = cls._success(usage)