fix(workbench): serialize events and target work order sync

This commit is contained in:
2026-10-08 15:41:49 +08:00
parent b70283c2ff
commit 4a63fcd7ff
20 changed files with 673 additions and 90 deletions
+28 -18
View File
@@ -200,10 +200,13 @@ class OperationalTaskService:
return await self.detail(task_id)
async def list(
self, *, task_type: str | None, status: str | None, limit: int, offset: int
self, *, task_type: str | None, status: str | None, limit: int, offset: int,
peripheral_work_order_id: UUID | None = None,
) -> tuple[list[OperationalTaskSummary], int | None]:
rows = await self.repo.list_tasks(
task_type=task_type, status=status, limit=limit + 1, offset=offset
task_type=task_type, status=status, limit=limit + 1, offset=offset,
**({"peripheral_work_order_id": str(peripheral_work_order_id)}
if peripheral_work_order_id is not None else {}),
)
items: list[OperationalTaskSummary] = []
for row in rows[:limit]:
@@ -922,9 +925,13 @@ class OperationalTaskService:
require(OPERATIONAL_ACTION_WORK_ORDER_APPROVE in self.permissions
and OPERATIONAL_ACTION_WORK_ORDER_EXECUTE in self.permissions,
"PERMISSION_DENIED", "Current user cannot confirm and dispatch work orders", status=403)
current = await self._action(task_id, action_id)
require(current.kind == "create_work_order", "INVALID_ACTION_KIND",
"Only work orders support one-step confirmation", status=422)
# A SELECT on the default non-autocommit connection starts an outer
# transaction. End it explicitly, or approval/submit become savepoints
# whose event lock survives into the peripheral HTTP call.
async with self.repo.transaction():
current = await self._action(task_id, action_id)
require(current.kind == "create_work_order", "INVALID_ACTION_KIND",
"Only work orders support one-step confirmation", status=422)
approval = await self.decide_action(task_id, action_id, ActionDecisionCommand(
request_id=uuid5(NAMESPACE_URL, f"confirm:{command.request_id}:approve"),
expected_version=command.expected_version, decision="approved",
@@ -1024,7 +1031,8 @@ class OperationalTaskService:
scope, command.request_id, body, {"action": _json(action)}
)
task = await self.detail(task_id)
async with self.repo.transaction():
task = await self.detail(task_id)
try:
outcome = await dispatcher.dispatch(
context=self.context, task=task, action=action, credential=credential
@@ -1099,19 +1107,21 @@ class OperationalTaskService:
) -> HumanApprovalAction:
body = _json(command)
scope = f"task:{task_id}:action:{action_id}:reconcile"
replay = await self.repo.get_request(scope, str(command.request_id))
if replay:
self._require_same_request(replay, body)
return HumanApprovalAction.model_validate(replay["response"]["action"])
action = await self._action(task_id, action_id)
require(action.version == command.expected_version, "VERSION_CONFLICT",
"Action version changed", status=409)
require(action.execution_status in {"submitted", "unknown"}, "INVALID_TRANSITION",
"Only submitted or unknown actions can be reconciled", status=409)
require(action.required_execution_permission in self.permissions,
"PERMISSION_DENIED", "Current user cannot reconcile this action", status=403)
async with self.repo.transaction():
replay = await self.repo.get_request(scope, str(command.request_id))
if replay:
self._require_same_request(replay, body)
return HumanApprovalAction.model_validate(replay["response"]["action"])
action = await self._action(task_id, action_id)
require(action.version == command.expected_version, "VERSION_CONFLICT",
"Action version changed", status=409)
require(action.execution_status in {"submitted", "unknown"}, "INVALID_TRANSITION",
"Only submitted or unknown actions can be reconciled", status=409)
require(action.required_execution_permission in self.permissions,
"PERMISSION_DENIED", "Current user cannot reconcile this action", status=403)
task = await self.detail(task_id)
outcome = await dispatcher.reconcile(
context=self.context, task=await self.detail(task_id), action=action,
context=self.context, task=task, action=action,
credential=credential,
)
self._validate_dispatch_outcome(outcome)