修复:收紧入库数据范围校验

This commit is contained in:
焦龙言
2026-07-26 16:14:27 +08:00
parent 6f22cf0282
commit 2e616ab6e0
5 changed files with 416 additions and 74 deletions
@@ -51,6 +51,15 @@ class OperationDao:
conditions.append(column < end_at)
return conditions
@staticmethod
def _inbound_scope_conditions(
warehouse_ids: list[int] | None,
) -> list[ColumnElement[bool]]:
if warehouse_ids is None:
return []
scoped_warehouse_ids = sorted(set(warehouse_ids))
return [BizInboundOrder.warehouse_id.in_(scoped_warehouse_ids) if scoped_warehouse_ids else false()]
@staticmethod
async def _paginate(
db: AsyncSession,
@@ -98,8 +107,9 @@ class OperationDao:
model: type[Any],
id_column: InstrumentedAttribute,
record_id: int,
*extra_conditions: ColumnElement[bool],
) -> Any | None:
result = await db.execute(select(model).where(id_column == record_id))
result = await db.execute(select(model).where(id_column == record_id, *extra_conditions))
return result.scalar_one_or_none()
@classmethod
@@ -112,8 +122,15 @@ class OperationDao:
item_model: type[Any],
item_order_id_column: InstrumentedAttribute,
item_id_column: InstrumentedAttribute,
*order_conditions: ColumnElement[bool],
) -> Any | None:
order = await cls._get_by_id(db, order_model, order_id_column, order_id)
order = await cls._get_by_id(
db,
order_model,
order_id_column,
order_id,
*order_conditions,
)
if order is None:
return None
@@ -185,12 +202,14 @@ class OperationDao:
cls,
db: AsyncSession,
inbound_id: int,
warehouse_ids: list[int] | None = None,
) -> BizInboundOrder | None:
inbound = await cls._get_for_update(
db,
BizInboundOrder,
BizInboundOrder.inbound_id,
inbound_id,
*cls._inbound_scope_conditions(warehouse_ids),
)
if inbound is None:
return None
@@ -336,12 +355,7 @@ class OperationDao:
is_page: bool = True,
warehouse_ids: list[int] | None = None,
) -> PageModel | list[dict[str, Any]]:
conditions = []
if warehouse_ids is not None:
scoped_warehouse_ids = sorted(set(warehouse_ids))
conditions.append(
BizInboundOrder.warehouse_id.in_(scoped_warehouse_ids) if scoped_warehouse_ids else false()
)
conditions = cls._inbound_scope_conditions(warehouse_ids)
if query_object.inbound_no:
conditions.append(BizInboundOrder.inbound_no.like(f'%{query_object.inbound_no}%'))
if query_object.warehouse_id:
@@ -363,6 +377,7 @@ class OperationDao:
cls,
db: AsyncSession,
inbound_id: int,
warehouse_ids: list[int] | None = None,
) -> BizInboundOrder | None:
return await cls._get_order_detail(
db,
@@ -372,6 +387,7 @@ class OperationDao:
BizInboundItem,
BizInboundItem.inbound_id,
BizInboundItem.inbound_item_id,
*cls._inbound_scope_conditions(warehouse_ids),
)
@classmethod
@@ -2,7 +2,7 @@ import hashlib
import json
import secrets
from datetime import datetime
from typing import Any
from typing import Any, NoReturn
from sqlalchemy.exc import IntegrityError
from sqlalchemy.ext.asyncio import AsyncSession
@@ -81,18 +81,27 @@ class InboundService:
user_id, _ = cls._current_identity(current_user)
return sorted(set(await AccessDao.list_user_warehouse_ids(query_db, user_id)))
@classmethod
def _raise_inbound_not_accessible(
cls,
current_user: CurrentUserModel,
) -> NoReturn:
if cls._is_admin(current_user):
raise ServiceException(message='入库单不存在')
raise PermissionException(data='', message='无权访问该入库单')
@staticmethod
async def _validate_active_warehouse(
query_db: AsyncSession,
warehouse_id: int,
current_user: CurrentUserModel,
) -> Any:
await AccessService.assert_warehouse_access(query_db, current_user, warehouse_id)
warehouse = await MasterDao.get_warehouse(query_db, warehouse_id)
if warehouse is None:
raise ServiceException(message='目标仓库不存在')
if warehouse.status != '0':
raise ServiceException(message='目标仓库已停用')
await AccessService.assert_warehouse_access(query_db, current_user, warehouse_id)
return warehouse
@staticmethod
@@ -188,10 +197,14 @@ class InboundService:
inbound_id: int,
current_user: CurrentUserModel,
) -> InboundModel:
inbound = await OperationDao.get_inbound_by_id(query_db, inbound_id)
warehouse_ids = await cls._warehouse_scope(query_db, current_user)
inbound = await OperationDao.get_inbound_by_id(
query_db,
inbound_id,
warehouse_ids=warehouse_ids,
)
if inbound is None:
raise ServiceException(message='入库单不存在')
await AccessService.assert_warehouse_access(query_db, current_user, inbound.warehouse_id)
cls._raise_inbound_not_accessible(current_user)
return InboundModel.model_validate(inbound)
@classmethod
@@ -318,14 +331,14 @@ class InboundService:
) -> CrudResponseModel:
try:
_, user_name = cls._current_identity(current_user)
inbound = await OperationDao.get_inbound_for_update(query_db, page_object.inbound_id)
if inbound is None:
raise ServiceException(message='入库单不存在')
await AccessService.assert_warehouse_access(
warehouse_ids = await cls._warehouse_scope(query_db, current_user)
inbound = await OperationDao.get_inbound_for_update(
query_db,
current_user,
inbound.warehouse_id,
page_object.inbound_id,
warehouse_ids=warehouse_ids,
)
if inbound is None:
cls._raise_inbound_not_accessible(current_user)
if inbound.status != DocumentStatus.DRAFT.value:
raise ServiceException(message='已确认入库单不允许编辑')
await cls._validate_active_warehouse(
@@ -369,15 +382,15 @@ class InboundService:
raise ServiceException(message='入库单ID不能重复')
ordered_ids = sorted(inbound_ids)
warehouse_ids = await cls._warehouse_scope(query_db, current_user)
for inbound_id in ordered_ids:
inbound = await OperationDao.get_inbound_for_update(query_db, inbound_id)
if inbound is None:
raise ServiceException(message='入库单不存在')
await AccessService.assert_warehouse_access(
inbound = await OperationDao.get_inbound_for_update(
query_db,
current_user,
inbound.warehouse_id,
inbound_id,
warehouse_ids=warehouse_ids,
)
if inbound is None:
cls._raise_inbound_not_accessible(current_user)
if inbound.status != DocumentStatus.DRAFT.value:
raise ServiceException(message='已确认入库单不允许删除')
@@ -448,14 +461,14 @@ class InboundService:
) -> CrudResponseModel:
try:
user_id, user_name = cls._current_identity(current_user)
inbound = await OperationDao.get_inbound_by_id(query_db, inbound_id)
if inbound is None:
raise ServiceException(message='入库单不存在')
await AccessService.assert_warehouse_access(
warehouse_ids = await cls._warehouse_scope(query_db, current_user)
inbound = await OperationDao.get_inbound_by_id(
query_db,
current_user,
inbound.warehouse_id,
inbound_id,
warehouse_ids=warehouse_ids,
)
if inbound is None:
cls._raise_inbound_not_accessible(current_user)
request_hash = cls._build_confirm_request_hash(inbound_id)
idempotency_key = page_object.idempotency_key
@@ -477,14 +490,13 @@ class InboundService:
):
raise ServiceException(message='确认入库失败,幂等键已被其他请求使用')
locked_inbound = await OperationDao.get_inbound_for_update(query_db, inbound_id)
if locked_inbound is None:
raise ServiceException(message='入库单不存在')
await AccessService.assert_warehouse_access(
locked_inbound = await OperationDao.get_inbound_for_update(
query_db,
current_user,
locked_inbound.warehouse_id,
inbound_id,
warehouse_ids=warehouse_ids,
)
if locked_inbound is None:
cls._raise_inbound_not_accessible(current_user)
if locked_inbound.status == DocumentStatus.CONFIRMED.value:
if cls._is_exact_confirmed(
locked_inbound,