修复:限定幂等键精确业务域
This commit is contained in:
@@ -300,7 +300,7 @@ class FillingService:
|
||||
recovered = await OperationDao.get_filling_by_idempotency_key(
|
||||
query_db,
|
||||
page_object.idempotency_key,
|
||||
warehouse_ids=warehouse_ids,
|
||||
warehouse_ids=[page_object.warehouse_id],
|
||||
)
|
||||
if recovered is None:
|
||||
raise ServiceException(message='装填失败,数据冲突')
|
||||
@@ -333,7 +333,7 @@ class FillingService:
|
||||
existing = await OperationDao.get_filling_by_idempotency_key(
|
||||
query_db,
|
||||
page_object.idempotency_key,
|
||||
warehouse_ids=warehouse_ids,
|
||||
warehouse_ids=[page_object.warehouse_id],
|
||||
)
|
||||
if existing is not None:
|
||||
await AccessService.assert_warehouse_access(
|
||||
|
||||
@@ -513,10 +513,17 @@ class InboundService:
|
||||
) -> CrudResponseModel:
|
||||
try:
|
||||
warehouse_ids = await cls._warehouse_scope(query_db, current_user)
|
||||
current = await OperationDao.get_inbound_by_id(
|
||||
query_db,
|
||||
inbound_id,
|
||||
warehouse_ids=warehouse_ids,
|
||||
)
|
||||
if current is None:
|
||||
cls._raise_inbound_not_accessible(current_user)
|
||||
recovered = await OperationDao.get_inbound_by_confirm_idempotency_key(
|
||||
query_db,
|
||||
idempotency_key,
|
||||
warehouse_ids=warehouse_ids,
|
||||
warehouse_ids=[current.warehouse_id],
|
||||
)
|
||||
if recovered is None:
|
||||
raise ServiceException(message='确认入库失败,幂等冲突')
|
||||
@@ -575,7 +582,7 @@ class InboundService:
|
||||
existing = await OperationDao.get_inbound_by_confirm_idempotency_key(
|
||||
query_db,
|
||||
idempotency_key,
|
||||
warehouse_ids=warehouse_ids,
|
||||
warehouse_ids=[inbound.warehouse_id],
|
||||
)
|
||||
if existing is not None:
|
||||
await AccessService.assert_warehouse_access(
|
||||
|
||||
@@ -586,12 +586,14 @@ class ReturnService:
|
||||
try:
|
||||
site_ids = await cls._site_scope(query_db, current_user)
|
||||
request_warehouse_ids = None if site_ids is None else []
|
||||
recovered = await OperationDao.get_return_by_request_idempotency_key(
|
||||
query_db,
|
||||
page_object.idempotency_key,
|
||||
site_ids=site_ids,
|
||||
warehouse_ids=request_warehouse_ids,
|
||||
)
|
||||
recovered = None
|
||||
if site_ids is None or page_object.source_site_id in site_ids:
|
||||
recovered = await OperationDao.get_return_by_request_idempotency_key(
|
||||
query_db,
|
||||
page_object.idempotency_key,
|
||||
source_site_id=page_object.source_site_id,
|
||||
target_warehouse_id=page_object.target_warehouse_id,
|
||||
)
|
||||
if recovered is None or not cls._is_exact_request_result(recovered, page_object, request_hash):
|
||||
raise ServiceException(message='申请退回失败,幂等冲突')
|
||||
locked = await OperationDao.get_return_for_update(
|
||||
@@ -640,8 +642,8 @@ class ReturnService:
|
||||
existing = await OperationDao.get_return_by_request_idempotency_key(
|
||||
query_db,
|
||||
page_object.idempotency_key,
|
||||
site_ids=preflight_site_ids,
|
||||
warehouse_ids=request_warehouse_ids,
|
||||
source_site_id=page_object.source_site_id,
|
||||
target_warehouse_id=page_object.target_warehouse_id,
|
||||
)
|
||||
if existing is not None and not cls._is_exact_request_result(existing, page_object, request_hash):
|
||||
raise ServiceException(message='申请退回失败,幂等键已被其他请求使用')
|
||||
@@ -681,12 +683,11 @@ class ReturnService:
|
||||
fresh_site_ids = await cls._site_scope(query_db, current_user)
|
||||
if fresh_site_ids is not None and page_object.source_site_id not in fresh_site_ids:
|
||||
cls._raise_pallet_not_accessible(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,
|
||||
source_site_id=page_object.source_site_id,
|
||||
target_warehouse_id=page_object.target_warehouse_id,
|
||||
)
|
||||
if fresh_existing is not None:
|
||||
if not cls._is_exact_request_result(fresh_existing, page_object, request_hash):
|
||||
@@ -905,11 +906,19 @@ class ReturnService:
|
||||
try:
|
||||
warehouse_ids = await cls._warehouse_scope(query_db, current_user)
|
||||
confirm_site_ids = None if warehouse_ids is None else []
|
||||
route = await OperationDao.get_return_by_id(
|
||||
query_db,
|
||||
return_id,
|
||||
site_ids=confirm_site_ids,
|
||||
warehouse_ids=warehouse_ids,
|
||||
)
|
||||
if route is None:
|
||||
cls._raise_return_not_accessible(current_user)
|
||||
recovered = await OperationDao.get_return_by_confirm_idempotency_key(
|
||||
query_db,
|
||||
page_object.idempotency_key,
|
||||
site_ids=confirm_site_ids,
|
||||
warehouse_ids=warehouse_ids,
|
||||
source_site_id=route.source_site_id,
|
||||
target_warehouse_id=route.target_warehouse_id,
|
||||
)
|
||||
if recovered is None:
|
||||
raise ServiceException(message='确认退回失败,幂等冲突')
|
||||
@@ -955,20 +964,6 @@ class ReturnService:
|
||||
request_hash = cls._build_confirm_request_hash(return_id, page_object)
|
||||
warehouse_ids = await cls._warehouse_scope(query_db, current_user)
|
||||
confirm_site_ids = None if warehouse_ids is None else []
|
||||
existing = await OperationDao.get_return_by_confirm_idempotency_key(
|
||||
query_db,
|
||||
page_object.idempotency_key,
|
||||
site_ids=confirm_site_ids,
|
||||
warehouse_ids=warehouse_ids,
|
||||
)
|
||||
if existing is not None and not cls._is_exact_confirm_result(
|
||||
existing,
|
||||
return_id,
|
||||
page_object,
|
||||
request_hash,
|
||||
):
|
||||
raise ServiceException(message='确认退回失败,幂等键已被其他请求使用')
|
||||
|
||||
return_order = await OperationDao.get_return_by_id(
|
||||
query_db,
|
||||
return_id,
|
||||
@@ -985,6 +980,19 @@ class ReturnService:
|
||||
)
|
||||
except PermissionException:
|
||||
cls._raise_return_not_accessible(current_user)
|
||||
existing = await OperationDao.get_return_by_confirm_idempotency_key(
|
||||
query_db,
|
||||
page_object.idempotency_key,
|
||||
source_site_id=return_order.source_site_id,
|
||||
target_warehouse_id=return_order.target_warehouse_id,
|
||||
)
|
||||
if existing is not None and not cls._is_exact_confirm_result(
|
||||
existing,
|
||||
return_id,
|
||||
page_object,
|
||||
request_hash,
|
||||
):
|
||||
raise ServiceException(message='确认退回失败,幂等键已被其他请求使用')
|
||||
|
||||
await query_db.rollback()
|
||||
locked = await OperationDao.get_return_for_update(
|
||||
|
||||
@@ -750,11 +750,19 @@ class ShipmentService:
|
||||
) -> CrudResponseModel:
|
||||
try:
|
||||
warehouse_ids, site_ids = await cls._scopes(query_db, current_user)
|
||||
current = await OperationDao.get_shipment_by_id(
|
||||
query_db,
|
||||
shipment_id,
|
||||
warehouse_ids=warehouse_ids,
|
||||
site_ids=site_ids,
|
||||
)
|
||||
if current is None:
|
||||
cls._raise_shipment_not_accessible(current_user)
|
||||
recovered = await OperationDao.get_shipment_by_confirm_idempotency_key(
|
||||
query_db,
|
||||
idempotency_key,
|
||||
warehouse_ids=warehouse_ids,
|
||||
site_ids=site_ids,
|
||||
warehouse_ids=[current.source_warehouse_id],
|
||||
site_ids=[current.target_site_id],
|
||||
)
|
||||
if recovered is None:
|
||||
raise ServiceException(message='确认发货失败,幂等冲突')
|
||||
@@ -815,8 +823,8 @@ class ShipmentService:
|
||||
existing = await OperationDao.get_shipment_by_confirm_idempotency_key(
|
||||
query_db,
|
||||
idempotency_key,
|
||||
warehouse_ids=warehouse_ids,
|
||||
site_ids=site_ids,
|
||||
warehouse_ids=[shipment.source_warehouse_id],
|
||||
site_ids=[shipment.target_site_id],
|
||||
)
|
||||
if existing is not None and not cls._is_exact_confirmed(
|
||||
existing,
|
||||
|
||||
@@ -179,7 +179,7 @@ class UsageService:
|
||||
recovered = await OperationDao.get_usage_by_idempotency_key(
|
||||
query_db,
|
||||
page_object.idempotency_key,
|
||||
site_ids=site_ids,
|
||||
site_ids=[expected_site_id],
|
||||
)
|
||||
if recovered is None:
|
||||
raise ServiceException(message='使用失败,数据冲突')
|
||||
@@ -351,11 +351,18 @@ class UsageService:
|
||||
request_hash = cls._build_request_hash(page_object)
|
||||
user_id, user_name = cls._current_identity(current_user)
|
||||
preflight_site_ids = await cls._site_scope(query_db, current_user)
|
||||
existing = await OperationDao.get_usage_by_idempotency_key(
|
||||
query_db,
|
||||
page_object.idempotency_key,
|
||||
site_ids=preflight_site_ids,
|
||||
)
|
||||
# 产线归属是请求内稳定路由;锁事务仍以库存中的实际现场复核。
|
||||
preflight_line = await MasterDao.get_line(query_db, page_object.line_id)
|
||||
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 (
|
||||
preflight_site_ids is None or preflight_site_id in preflight_site_ids
|
||||
):
|
||||
existing = await OperationDao.get_usage_by_idempotency_key(
|
||||
query_db,
|
||||
page_object.idempotency_key,
|
||||
site_ids=[preflight_site_id],
|
||||
)
|
||||
if existing is not None:
|
||||
if not cls._is_exact_idempotent_result(existing, page_object, request_hash):
|
||||
raise ServiceException(message='使用失败,幂等键已被其他请求使用')
|
||||
@@ -390,6 +397,8 @@ class UsageService:
|
||||
expected_site_id = inventory.site_id
|
||||
await cls._assert_pallet_site_access(query_db, current_user, inventory.site_id)
|
||||
locked_site_ids = await cls._site_scope(query_db, current_user)
|
||||
if locked_site_ids is not None and inventory.site_id not in locked_site_ids:
|
||||
cls._raise_pallet_not_accessible(current_user)
|
||||
|
||||
locked_pallets = await InventoryDao.get_pallets_by_ids(
|
||||
query_db,
|
||||
@@ -410,7 +419,7 @@ class UsageService:
|
||||
locked_existing = await OperationDao.get_usage_by_idempotency_key(
|
||||
query_db,
|
||||
page_object.idempotency_key,
|
||||
site_ids=locked_site_ids,
|
||||
site_ids=[inventory.site_id],
|
||||
)
|
||||
if locked_existing is not None:
|
||||
if locked_existing.site_id != inventory.site_id or not cls._is_exact_idempotent_result(
|
||||
|
||||
Reference in New Issue
Block a user