feat(scada): require complete authorized investigation evidence
This commit is contained in:
@@ -73,6 +73,7 @@ from app.domain.schemas.operational_data import TaskDataSnapshot, TaskDataSource
|
||||
from app.services.content_digest import digest
|
||||
from app.services.operational_presentation import resolve_overlay
|
||||
from app.services.operational_recovery import accept_completion, track_work_order
|
||||
from app.services.operational_scada_coverage import require_complete_scada_evidence
|
||||
|
||||
|
||||
LEASE_DURATION = timedelta(seconds=60)
|
||||
@@ -929,6 +930,10 @@ class OperationalTaskService:
|
||||
current_plan = snapshot
|
||||
if command.report:
|
||||
self._require_evidence_refs(command.report.evidence_refs, known_evidence)
|
||||
if command.report.stage == "final":
|
||||
require_complete_scada_evidence(
|
||||
task, await self.repo.list_evidence(str(task_id)), command.report.evidence_refs,
|
||||
)
|
||||
for gap in command.report.data_gaps:
|
||||
require(set(gap.evidence_refs) <= set(command.report.evidence_refs),
|
||||
"REPORT_GAP_EVIDENCE_INVALID",
|
||||
@@ -1132,7 +1137,7 @@ class OperationalTaskService:
|
||||
await self.repo.lock(f"task:{task_id}")
|
||||
task = await self._task(task_id)
|
||||
self._require_actionable_task(task)
|
||||
self._require_real_action(task)
|
||||
self._require_real_action(task, command.kind)
|
||||
self._validate_action_payload(command)
|
||||
if task["task_type"] == "event_investigation":
|
||||
report = await self.repo.get_latest_report(str(task_id))
|
||||
@@ -1203,8 +1208,8 @@ class OperationalTaskService:
|
||||
await self.repo.lock(f"action:{action_id}")
|
||||
task = await self._task(task_id)
|
||||
self._require_actionable_task(task)
|
||||
self._require_real_action(task)
|
||||
action = await self._action(task_id, action_id)
|
||||
self._require_real_action(task, action.kind)
|
||||
require(action.kind == "create_work_order", "INVALID_ACTION_KIND",
|
||||
"Only work-order drafts can be edited", status=422)
|
||||
require(action.version == command.expected_version, "VERSION_CONFLICT",
|
||||
@@ -1259,8 +1264,8 @@ class OperationalTaskService:
|
||||
await self.repo.lock(f"action:{action_id}")
|
||||
task = await self._task(task_id)
|
||||
self._require_actionable_task(task)
|
||||
self._require_real_action(task)
|
||||
action = await self._action(task_id, action_id)
|
||||
self._require_real_action(task, action.kind)
|
||||
require(action.version == command.expected_version, "VERSION_CONFLICT",
|
||||
"Action version changed", status=409)
|
||||
require(action.approval_status == "pending", "INVALID_TRANSITION",
|
||||
@@ -1306,8 +1311,8 @@ class OperationalTaskService:
|
||||
await self.repo.lock(f"action:{action_id}")
|
||||
task = await self._task(task_id)
|
||||
self._require_actionable_task(task)
|
||||
self._require_real_action(task)
|
||||
action = await self._action(task_id, action_id)
|
||||
self._require_real_action(task, action.kind)
|
||||
require(action.version == command.expected_version, "VERSION_CONFLICT",
|
||||
"Action version changed", status=409)
|
||||
require(action.approval_status == "approved", "INVALID_TRANSITION",
|
||||
@@ -2151,9 +2156,10 @@ class OperationalTaskService:
|
||||
return task
|
||||
|
||||
@staticmethod
|
||||
def _require_real_action(task: dict) -> None:
|
||||
require(not (task.get("source") or {}).get("replay_context"), "REPLAY_ACTION_DISABLED",
|
||||
"当前任务的工单和设备动作未开放;请继续完成已授权的定位、地图展示、隔离影响分析与报告", status=409)
|
||||
def _require_real_action(task: dict, kind: str) -> None:
|
||||
require(kind == "create_work_order" or not (task.get("source") or {}).get("replay_context"),
|
||||
"REPLAY_ACTION_DISABLED",
|
||||
"回放任务不支持设备命令;可生成工单草稿,由有权限的用户确认发布", status=409)
|
||||
|
||||
@staticmethod
|
||||
def _require_actionable_task(task: dict) -> None:
|
||||
|
||||
Reference in New Issue
Block a user