feat: 安全领取并分派采购任务 (#72)
This commit is contained in:
@@ -147,16 +147,17 @@ class HttpAdminGateway(AdminGateway):
|
||||
client: ClientInfo,
|
||||
capabilities: ClaimCapabilities,
|
||||
) -> Optional[AdminTask]:
|
||||
"""领取一个采集任务;Admin 返回 204 时返回 ``None``。"""
|
||||
"""按调用方已安全确认的能力领取一个任务。"""
|
||||
|
||||
request_id = str(uuid4())
|
||||
self._client_id = client.client_id.strip()
|
||||
payload = {
|
||||
"client": {"name": client.name.strip()},
|
||||
# #30 只允许领取采集任务。采购能力必须由安全门禁工单开启。
|
||||
"supported_types": [TaskType.COLLECT.value],
|
||||
"supported_types": [
|
||||
task_type.value for task_type in capabilities.supported_types
|
||||
],
|
||||
"capabilities": {
|
||||
"purchase_mode": "dry_run",
|
||||
"purchase_mode": capabilities.purchase_mode,
|
||||
"schema_versions": list(capabilities.schema_versions),
|
||||
},
|
||||
}
|
||||
@@ -394,6 +395,8 @@ class HttpAdminGateway(AdminGateway):
|
||||
request_id,
|
||||
) from exc
|
||||
|
||||
cls._validate_claim_payload(task_type, task_payload, request_id)
|
||||
|
||||
return AdminTask(
|
||||
task_id=task_id,
|
||||
task_type=task_type,
|
||||
@@ -404,6 +407,61 @@ class HttpAdminGateway(AdminGateway):
|
||||
updated_at=updated_at,
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _validate_claim_payload(
|
||||
task_type: TaskType,
|
||||
payload: Mapping[str, object],
|
||||
request_id: str,
|
||||
) -> None:
|
||||
"""在采购任务进入本地执行前校验全部安全字段。"""
|
||||
|
||||
goods_url = payload.get("goods_url")
|
||||
if not isinstance(goods_url, str) or not goods_url.strip():
|
||||
raise AdminGatewayError(
|
||||
"ADMIN_INVALID_RESPONSE",
|
||||
"Admin 任务 payload.goods_url 不能为空",
|
||||
False,
|
||||
request_id,
|
||||
)
|
||||
if task_type is not TaskType.PURCHASE:
|
||||
return
|
||||
goods_id = payload.get("goods_id")
|
||||
options = payload.get("options")
|
||||
quantity = payload.get("quantity")
|
||||
max_price_cent = payload.get("max_price_cent")
|
||||
valid_options = (
|
||||
isinstance(options, Mapping)
|
||||
and bool(options)
|
||||
and all(
|
||||
isinstance(key, str)
|
||||
and bool(key.strip())
|
||||
and isinstance(value, str)
|
||||
and bool(value.strip())
|
||||
for key, value in options.items()
|
||||
)
|
||||
)
|
||||
valid = (
|
||||
isinstance(goods_id, str)
|
||||
and bool(goods_id.strip())
|
||||
and valid_options
|
||||
and isinstance(quantity, int)
|
||||
and not isinstance(quantity, bool)
|
||||
and quantity > 0
|
||||
and isinstance(max_price_cent, int)
|
||||
and not isinstance(max_price_cent, bool)
|
||||
and max_price_cent > 0
|
||||
)
|
||||
if not valid:
|
||||
raise AdminGatewayError(
|
||||
"ADMIN_INVALID_RESPONSE",
|
||||
(
|
||||
"Admin 采购任务缺少有效的 goods_id、动态 options、"
|
||||
"quantity 或 max_price_cent"
|
||||
),
|
||||
False,
|
||||
request_id,
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _decode_json(body: bytes, request_id: str) -> dict:
|
||||
try:
|
||||
|
||||
Reference in New Issue
Block a user