修复:完善退回并发幂等与信息隐藏
This commit is contained in:
@@ -377,7 +377,7 @@ class ReturnService:
|
||||
cls._raise_pallet_not_accessible(current_user)
|
||||
matching_items = [item for item in return_order.items if item.pallet_id == pallet.pallet_id]
|
||||
if len(matching_items) != 1:
|
||||
raise ServiceException(message='扫码托盘不属于该退回单')
|
||||
cls._raise_pallet_not_accessible(current_user, '扫码托盘不属于该退回单')
|
||||
inventory = await MasterDao.get_pallet_inventory(query_db, pallet.pallet_id)
|
||||
if inventory is None:
|
||||
cls._raise_pallet_not_accessible(current_user, '托盘库存不存在')
|
||||
@@ -445,21 +445,37 @@ class ReturnService:
|
||||
query_db: AsyncSession,
|
||||
pallet_ids: list[int],
|
||||
source_site_id: int,
|
||||
) -> tuple[list[BizPalletInventory], list[BizPallet], dict[int, BizFillingRecord]]:
|
||||
current_user: CurrentUserModel,
|
||||
) -> tuple[
|
||||
list[BizPalletInventory],
|
||||
list[BizPallet],
|
||||
dict[int, BizFillingRecord | None],
|
||||
list[int] | None,
|
||||
]:
|
||||
ordered_ids = cls._validate_pallet_ids(pallet_ids)
|
||||
inventories = await InventoryDao.get_inventories_for_update(query_db, ordered_ids)
|
||||
pallets = await InventoryDao.get_pallets_by_ids(query_db, ordered_ids)
|
||||
inventory_by_id = {inventory.pallet_id: inventory for inventory in inventories}
|
||||
pallet_by_id = {pallet.pallet_id: pallet for pallet in pallets}
|
||||
if set(inventory_by_id) != set(ordered_ids) or set(pallet_by_id) != set(ordered_ids):
|
||||
raise ServiceException(message='存在不存在或库存缺失的托盘')
|
||||
if set(inventory_by_id) != set(ordered_ids):
|
||||
cls._raise_pallet_not_accessible(current_user, '存在不存在或库存缺失的托盘')
|
||||
|
||||
ordered_inventories = [inventory_by_id[pallet_id] for pallet_id in ordered_ids]
|
||||
ordered_pallets = [pallet_by_id[pallet_id] for pallet_id in ordered_ids]
|
||||
for pallet, inventory in zip(ordered_pallets, ordered_inventories, strict=True):
|
||||
cls._validate_request_inventory(pallet, inventory, source_site_id)
|
||||
if any(inventory.site_id != source_site_id for inventory in ordered_inventories):
|
||||
cls._raise_pallet_not_accessible(current_user, '同一退回单托盘必须属于同一现场')
|
||||
try:
|
||||
await AccessService.assert_site_access(query_db, current_user, source_site_id)
|
||||
except PermissionException:
|
||||
cls._raise_pallet_not_accessible(current_user)
|
||||
fresh_site_ids = await cls._site_scope(query_db, current_user)
|
||||
if fresh_site_ids is not None and source_site_id not in fresh_site_ids:
|
||||
cls._raise_pallet_not_accessible(current_user)
|
||||
|
||||
filling_by_id = {}
|
||||
pallets = await InventoryDao.get_pallets_by_ids(query_db, ordered_ids)
|
||||
pallet_by_id = {pallet.pallet_id: pallet for pallet in pallets}
|
||||
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]
|
||||
|
||||
filling_by_id: dict[int, BizFillingRecord | None] = {}
|
||||
filling_ids = sorted(
|
||||
{
|
||||
inventory.active_filling_id
|
||||
@@ -468,14 +484,31 @@ class ReturnService:
|
||||
}
|
||||
)
|
||||
for filling_id in filling_ids:
|
||||
filling = await OperationDao.get_active_filling_for_update(query_db, filling_id)
|
||||
if filling is None:
|
||||
raise ServiceException(message='托盘活动装填周期不存在或已关闭')
|
||||
filling_by_id[filling_id] = filling
|
||||
for inventory in ordered_inventories:
|
||||
filling_by_id[filling_id] = await OperationDao.get_active_filling_for_update(query_db, filling_id)
|
||||
return ordered_inventories, ordered_pallets, filling_by_id, fresh_site_ids
|
||||
|
||||
@classmethod
|
||||
def _validate_locked_request_pallets(
|
||||
cls,
|
||||
inventories: list[BizPalletInventory],
|
||||
pallets: list[BizPallet],
|
||||
filling_by_id: dict[int, BizFillingRecord | None],
|
||||
source_site_id: int,
|
||||
current_user: CurrentUserModel,
|
||||
) -> None:
|
||||
for pallet, inventory in zip(pallets, inventories, strict=True):
|
||||
cls._validate_request_inventory(pallet, inventory, source_site_id)
|
||||
for inventory in inventories:
|
||||
if inventory.active_filling_id is not None:
|
||||
cls._validate_active_filling(filling_by_id[inventory.active_filling_id], inventory)
|
||||
return ordered_inventories, ordered_pallets, filling_by_id
|
||||
filling = filling_by_id[inventory.active_filling_id]
|
||||
if filling is None:
|
||||
cls._raise_pallet_not_accessible(current_user, '托盘活动装填周期不存在或已关闭')
|
||||
try:
|
||||
cls._validate_active_filling(filling, inventory)
|
||||
except ServiceException:
|
||||
if not cls._is_admin(current_user):
|
||||
cls._raise_pallet_not_accessible(current_user)
|
||||
raise
|
||||
|
||||
@staticmethod
|
||||
def _build_return_items(
|
||||
@@ -585,7 +618,7 @@ class ReturnService:
|
||||
await query_db.rollback()
|
||||
|
||||
@classmethod
|
||||
async def request_return_services(
|
||||
async def request_return_services( # noqa: PLR0915
|
||||
cls,
|
||||
query_db: AsyncSession,
|
||||
page_object: ReturnRequestModel,
|
||||
@@ -595,30 +628,34 @@ class ReturnService:
|
||||
user_id, user_name = cls._current_identity(current_user)
|
||||
ordered_ids = cls._validate_pallet_ids(page_object.pallet_ids)
|
||||
request_hash = cls._build_request_hash(page_object)
|
||||
site_ids = await cls._site_scope(query_db, current_user)
|
||||
if site_ids is not None and page_object.source_site_id not in site_ids:
|
||||
raise PermissionException(data='', message='无权访问该客户现场')
|
||||
await AccessService.assert_site_access(
|
||||
query_db,
|
||||
current_user,
|
||||
page_object.source_site_id,
|
||||
)
|
||||
request_warehouse_ids = None if site_ids is None else []
|
||||
preflight_site_ids = await cls._site_scope(query_db, current_user)
|
||||
if preflight_site_ids is not None and page_object.source_site_id not in preflight_site_ids:
|
||||
cls._raise_pallet_not_accessible(current_user)
|
||||
try:
|
||||
await AccessService.assert_site_access(
|
||||
query_db,
|
||||
current_user,
|
||||
page_object.source_site_id,
|
||||
)
|
||||
except PermissionException:
|
||||
cls._raise_pallet_not_accessible(current_user)
|
||||
request_warehouse_ids = None if preflight_site_ids is None else []
|
||||
existing = await OperationDao.get_return_by_request_idempotency_key(
|
||||
query_db,
|
||||
page_object.idempotency_key,
|
||||
site_ids=site_ids,
|
||||
site_ids=preflight_site_ids,
|
||||
warehouse_ids=request_warehouse_ids,
|
||||
)
|
||||
if existing is not None and not cls._is_exact_request_result(existing, page_object, request_hash):
|
||||
raise ServiceException(message='申请退回失败,幂等键已被其他请求使用')
|
||||
preflight_existing_id = int(existing.return_id) if existing is not None else None
|
||||
|
||||
await query_db.rollback()
|
||||
if existing is not None:
|
||||
if preflight_existing_id is not None:
|
||||
locked_existing = await OperationDao.get_return_for_update(
|
||||
query_db,
|
||||
existing.return_id,
|
||||
site_ids=site_ids,
|
||||
preflight_existing_id,
|
||||
site_ids=preflight_site_ids,
|
||||
warehouse_ids=request_warehouse_ids,
|
||||
)
|
||||
if locked_existing is None:
|
||||
@@ -635,12 +672,35 @@ class ReturnService:
|
||||
await query_db.rollback()
|
||||
return cls._success('申请成功', return_id)
|
||||
|
||||
inventories, _, _ = await cls._lock_request_pallets(
|
||||
inventories, pallets, filling_by_id, fresh_site_ids = await cls._lock_request_pallets(
|
||||
query_db,
|
||||
ordered_ids,
|
||||
page_object.source_site_id,
|
||||
current_user,
|
||||
)
|
||||
fresh_warehouse_ids = None if fresh_site_ids is None else []
|
||||
fresh_existing = await OperationDao.get_return_by_request_idempotency_key(
|
||||
query_db,
|
||||
page_object.idempotency_key,
|
||||
site_ids=fresh_site_ids,
|
||||
warehouse_ids=fresh_warehouse_ids,
|
||||
)
|
||||
if fresh_existing is not None:
|
||||
if not cls._is_exact_request_result(fresh_existing, page_object, request_hash):
|
||||
raise ServiceException(message='申请退回失败,幂等键已被其他请求使用')
|
||||
fresh_existing_id = int(fresh_existing.return_id)
|
||||
await cls._lock_and_reauthorize_request_site(query_db, fresh_existing, current_user)
|
||||
await query_db.rollback()
|
||||
return cls._success('申请成功', fresh_existing_id)
|
||||
|
||||
await cls._lock_new_request_route(query_db, page_object, current_user)
|
||||
cls._validate_locked_request_pallets(
|
||||
inventories,
|
||||
pallets,
|
||||
filling_by_id,
|
||||
page_object.source_site_id,
|
||||
current_user,
|
||||
)
|
||||
for inventory in inventories:
|
||||
PalletStateMachine.ensure_transition(
|
||||
PalletFlowState(inventory.flow_state),
|
||||
|
||||
Reference in New Issue
Block a user