修复:强化发货权限与幂等一致性

This commit is contained in:
焦龙言
2026-07-26 20:06:35 +08:00
parent 7317631a13
commit dc28cf6df0
2 changed files with 860 additions and 41 deletions
@@ -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='发货单已确认,幂等信息不匹配')