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

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
@@ -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)