修复:强化发货权限与幂等一致性
This commit is contained in:
@@ -476,6 +476,15 @@ class ShipmentService:
|
||||
ordered_ids,
|
||||
page_object.source_warehouse_id,
|
||||
)
|
||||
await cls._lock_and_authorize_routes(
|
||||
query_db,
|
||||
current_user,
|
||||
warehouse_ids={page_object.source_warehouse_id},
|
||||
site_ids={page_object.target_site_id},
|
||||
hidden_warehouse_ids=set(),
|
||||
hidden_site_ids=set(),
|
||||
require_active=True,
|
||||
)
|
||||
|
||||
current_time = datetime.now()
|
||||
shipment = BizShipmentOrder(
|
||||
@@ -538,6 +547,21 @@ class ShipmentService:
|
||||
ordered_ids,
|
||||
page_object.source_warehouse_id,
|
||||
)
|
||||
await cls._lock_and_authorize_routes(
|
||||
query_db,
|
||||
current_user,
|
||||
warehouse_ids={
|
||||
shipment.source_warehouse_id,
|
||||
page_object.source_warehouse_id,
|
||||
},
|
||||
site_ids={
|
||||
shipment.target_site_id,
|
||||
page_object.target_site_id,
|
||||
},
|
||||
hidden_warehouse_ids={shipment.source_warehouse_id},
|
||||
hidden_site_ids={shipment.target_site_id},
|
||||
require_active=True,
|
||||
)
|
||||
|
||||
current_time = datetime.now()
|
||||
shipment.source_warehouse_id = page_object.source_warehouse_id
|
||||
@@ -576,6 +600,7 @@ class ShipmentService:
|
||||
|
||||
ordered_ids = sorted(shipment_ids)
|
||||
warehouse_ids, site_ids = await cls._scopes(query_db, current_user)
|
||||
locked_shipments = []
|
||||
for shipment_id in ordered_ids:
|
||||
shipment = await OperationDao.get_shipment_for_update(
|
||||
query_db,
|
||||
@@ -587,6 +612,19 @@ class ShipmentService:
|
||||
cls._raise_shipment_not_accessible(current_user)
|
||||
if shipment.status != DocumentStatus.DRAFT.value:
|
||||
raise ServiceException(message='已确认发货单不允许删除')
|
||||
locked_shipments.append(shipment)
|
||||
|
||||
route_warehouse_ids = {shipment.source_warehouse_id for shipment in locked_shipments}
|
||||
route_site_ids = {shipment.target_site_id for shipment in locked_shipments}
|
||||
await cls._lock_and_authorize_routes(
|
||||
query_db,
|
||||
current_user,
|
||||
warehouse_ids=route_warehouse_ids,
|
||||
site_ids=route_site_ids,
|
||||
hidden_warehouse_ids=route_warehouse_ids,
|
||||
hidden_site_ids=route_site_ids,
|
||||
require_active=False,
|
||||
)
|
||||
|
||||
for shipment_id in ordered_ids:
|
||||
await OperationDao.delete_shipment(query_db, shipment_id)
|
||||
@@ -613,50 +651,80 @@ class ShipmentService:
|
||||
and shipment.confirm_request_hash == request_hash
|
||||
)
|
||||
|
||||
@classmethod
|
||||
async def _lock_and_authorize_routes(
|
||||
cls,
|
||||
query_db: AsyncSession,
|
||||
current_user: CurrentUserModel,
|
||||
*,
|
||||
warehouse_ids: set[int],
|
||||
site_ids: set[int],
|
||||
hidden_warehouse_ids: set[int],
|
||||
hidden_site_ids: set[int],
|
||||
require_active: bool,
|
||||
) -> None:
|
||||
for warehouse_id in sorted(warehouse_ids):
|
||||
warehouse = await MasterDao.get_warehouse(
|
||||
query_db,
|
||||
warehouse_id,
|
||||
for_update=True,
|
||||
)
|
||||
if warehouse is None:
|
||||
raise ServiceException(message='来源仓库不存在')
|
||||
if require_active and warehouse.status != '0':
|
||||
raise ServiceException(message='来源仓库已停用')
|
||||
try:
|
||||
await AccessService.assert_warehouse_access(
|
||||
query_db,
|
||||
current_user,
|
||||
warehouse_id,
|
||||
for_update=True,
|
||||
)
|
||||
except PermissionException:
|
||||
if warehouse_id in hidden_warehouse_ids:
|
||||
cls._raise_shipment_not_accessible(current_user)
|
||||
raise
|
||||
|
||||
for site_id in sorted(site_ids):
|
||||
site = await MasterDao.get_site(
|
||||
query_db,
|
||||
site_id,
|
||||
for_update=True,
|
||||
)
|
||||
if site is None:
|
||||
raise ServiceException(message='目标现场不存在')
|
||||
if require_active and site.status != '0':
|
||||
raise ServiceException(message='目标现场已停用')
|
||||
try:
|
||||
await AccessService.assert_site_access(
|
||||
query_db,
|
||||
current_user,
|
||||
site_id,
|
||||
for_update=True,
|
||||
)
|
||||
except PermissionException:
|
||||
if site_id in hidden_site_ids:
|
||||
cls._raise_shipment_not_accessible(current_user)
|
||||
raise
|
||||
|
||||
@classmethod
|
||||
async def _lock_and_reauthorize_route(
|
||||
cls,
|
||||
query_db: AsyncSession,
|
||||
shipment: BizShipmentOrder,
|
||||
current_user: CurrentUserModel,
|
||||
*,
|
||||
require_active: bool = True,
|
||||
) -> None:
|
||||
warehouse = await MasterDao.get_warehouse(
|
||||
await cls._lock_and_authorize_routes(
|
||||
query_db,
|
||||
shipment.source_warehouse_id,
|
||||
for_update=True,
|
||||
current_user,
|
||||
warehouse_ids={shipment.source_warehouse_id},
|
||||
site_ids={shipment.target_site_id},
|
||||
hidden_warehouse_ids={shipment.source_warehouse_id},
|
||||
hidden_site_ids={shipment.target_site_id},
|
||||
require_active=require_active,
|
||||
)
|
||||
if warehouse is None:
|
||||
raise ServiceException(message='来源仓库不存在')
|
||||
if warehouse.status != '0':
|
||||
raise ServiceException(message='来源仓库已停用')
|
||||
try:
|
||||
await AccessService.assert_warehouse_access(
|
||||
query_db,
|
||||
current_user,
|
||||
shipment.source_warehouse_id,
|
||||
for_update=True,
|
||||
)
|
||||
except PermissionException:
|
||||
cls._raise_shipment_not_accessible(current_user)
|
||||
|
||||
site = await MasterDao.get_site(
|
||||
query_db,
|
||||
shipment.target_site_id,
|
||||
for_update=True,
|
||||
)
|
||||
if site is None:
|
||||
raise ServiceException(message='目标现场不存在')
|
||||
if site.status != '0':
|
||||
raise ServiceException(message='目标现场已停用')
|
||||
try:
|
||||
await AccessService.assert_site_access(
|
||||
query_db,
|
||||
current_user,
|
||||
shipment.target_site_id,
|
||||
for_update=True,
|
||||
)
|
||||
except PermissionException:
|
||||
cls._raise_shipment_not_accessible(current_user)
|
||||
|
||||
@classmethod
|
||||
async def _recover_confirm_integrity_error(
|
||||
@@ -684,11 +752,26 @@ class ShipmentService:
|
||||
request_hash,
|
||||
):
|
||||
raise ServiceException(message='确认发货失败,幂等键已被其他请求使用')
|
||||
await cls._validate_active_route(
|
||||
locked_recovered = await OperationDao.get_shipment_for_update(
|
||||
query_db,
|
||||
recovered.source_warehouse_id,
|
||||
recovered.target_site_id,
|
||||
shipment_id,
|
||||
warehouse_ids=warehouse_ids,
|
||||
site_ids=site_ids,
|
||||
)
|
||||
if locked_recovered is None:
|
||||
cls._raise_shipment_not_accessible(current_user)
|
||||
if not cls._is_exact_confirmed(
|
||||
locked_recovered,
|
||||
shipment_id,
|
||||
idempotency_key,
|
||||
request_hash,
|
||||
):
|
||||
raise ServiceException(message='确认发货失败,幂等键已被其他请求使用')
|
||||
await cls._lock_and_reauthorize_route(
|
||||
query_db,
|
||||
locked_recovered,
|
||||
current_user,
|
||||
require_active=False,
|
||||
)
|
||||
return cls._success('确认成功')
|
||||
finally:
|
||||
@@ -745,7 +828,12 @@ class ShipmentService:
|
||||
idempotency_key,
|
||||
request_hash,
|
||||
):
|
||||
await cls._lock_and_reauthorize_route(query_db, locked_shipment, current_user)
|
||||
await cls._lock_and_reauthorize_route(
|
||||
query_db,
|
||||
locked_shipment,
|
||||
current_user,
|
||||
require_active=False,
|
||||
)
|
||||
await query_db.rollback()
|
||||
return cls._success('确认成功')
|
||||
raise ServiceException(message='发货单已确认,幂等信息不匹配')
|
||||
|
||||
Reference in New Issue
Block a user