# 调查进展与实时展示 调查执行中可持续发布正文和完整 Mermaid 图,尚未形成结论时也能发布。 Server 将阅读进展、业务报告和浏览器展示请求分别保存。运行结束仍要求同轮最终报告, 进展中的节点状态不参与调度、审批或结束判断。 ## 进展快照 `POST /api/v1/operational-tasks/{task_id}/agent-artifacts` 新增可选 `progress`: ```json { "request_id": "", "run_id": "", "source_session_id": "", "progress": { "summary": "正在核对投诉时段的监测记录,尚未形成结论。", "diagram": { "title": "当前调查进展", "mermaid": "flowchart LR\nread[核查监测记录] --> judge[判断证据范围]", "node_refs": [{ "node_id": "read", "label": "核查监测记录", "status": "running", "evidence_refs": [], "action_refs": [], "map_targets": [{"source_id": "pipes", "feature_id": ""}] }] } } } ``` 示例中的编号由实际任务和资产替换。正文最长 20,000 字符,`diagram` 可空。 图示沿用 [调查报告契约](investigation-report-contract.md) 的 Mermaid 安全限制。 节点状态可空,或为 `pending/running/completed/waiting_human/failed/skipped/cancelled`。 `map_targets` 只接受 `junctions/pipes/valves/pumps/tanks/reservoirs/scada` 七类来源和要素编号。 图中节点引用在本次发布内唯一,证据和动作引用必须属于该任务。跨修订可以修改节点编号、 分支和文字,Server 不验证依赖执行顺序或节点完工。 每次传入 `progress` 都完整替换该 run 的快照,传入 `diagram: null` 清除上一版图示。 省略 `progress` 保留原快照。同一个请求编号重试只发布一次。 `workbench.operational_task_progress` 每个任务和 run 保存最新一份快照,独立于七天事件清理。 任务详情和 Agent 上下文返回 `latest_progress`,包含 `revision/run_id/execution_generation/` `published_at/source_session_id/progress`。只读取当前执行代次,新轮次尚未发布时返回空。 快照更新发出 `operational.progress.changed`,前端据此读取最新内容。 ## 浏览器实时联动 `POST /api/v1/operational-tasks/{task_id}/presentation-events` 接收 `request_id/run_id/source_session_id/command`,要求任务执行权限。 Server 校验当前 run、绑定会话、有效租约和幂等身份,记录 `operational.presentation.requested`。 响应为 `recorded/event_seq/run_id/execution_generation`,表示展示请求已保存, 不表示浏览器已显示或现场动作已执行。 | command.action | 参数 | | --- | --- | | highlight_features | layer、feature_ids、effect:steady 或 pulse | | clear_highlights | 无 | | open_chart | chart_id、chart、非空 evidence_refs | | close_chart | chart_id | | set_overlay | overlay_id、title、mode:geometry 或 heatmap、evidence_id | | clear_overlay | overlay_id | 图表限定折线和柱形,包含 `title/chart_type/x_data/series`。最多 100 个横轴值和 8 组有限数值序列, 序列长度与横轴对齐,引用证据必须属于当前任务。地图高亮来源沿用上述七类。 覆盖层输入不接受模型提供的 `data`。Server 从证据的不可变数据快照提取现有 GeoJSON, 或使用已记录的 `longitude/latitude` 点位,将 `FeatureCollection` 加入事件中的 `command.data`。 热力图只接受记录中的点,权重为有限非负数,未记录权重时取 1。 没有坐标的地址、空数据、非法几何、模型自绘区域均不能生成覆盖层。该接口不做地址解析、 范围推算或污染传播计算。展示事件总大小上限为 512 KiB,超限返回 422。 `GET /api/v1/operational-tasks/{task_id}/presentation-cursor` 在同一事务中读取 `task_id/execution_generation/active_run/event_cursor`,其中活跃 run 仅返回 `run_id/generation/status`。 浏览器打开 Dev 联动、切换任务或断线重连时,以此建立新基线,只消费之后的展示事件。 跟随开关只影响当前浏览器的临时展示,正文和图示更新始终可读,调查仍由后台继续执行。 关闭 Dev 面板不改变跟随开关。关闭跟随开关、切换任务或断线时清理临时效果, 执行结束停止执行态动效,已显示的图表与覆盖层保留供阅读。不回放历史请求,不重新执行调查。 ## 人工反馈续调 外围同步器根据动作回执找到关联子工单。样板创建固定为 `revision=1`,首次创建回执不作为 新的现场反馈入队。后续版本可唤醒已结束调查,旧报告和证据保留,生成新的执行代次。 现场备注从工单 `activity.kind=feedback` 的记录进入反馈证据,仍标明样板合成来源。 重复请求或同一源版本重放不增加执行代次,取消调查拒绝新反馈。 同步器先送达各父调查反馈,再推进预约工单投影的源版本。定向源事件和动作回执补查保留, 处理源更新早于回执保存的情况。全量对账只为源版本变化补查已结束调查,本次不回填历史反馈。 原投诉收到新版本后同步推进其已有来源修订号。数据不足仍正常结束本轮, 只有新的可信业务输入进入后续轮次,不依赖图示节点制造人工等待。 ## 部署与验证 新增 [023 迁移](../resources/sql/023_operational_task_progress.sql) 仅适用于 `tjwater_v2`, 增加进展表和两个事件 topic 的 CHECK 取值。2026-10-10 已在目标库完成全库备份、 迁移事务回滚演练及正式应用,其他项目和旧任务未改动。 实际 worker 曾成功投递指定调查,模型发布进展和报告,但因 service 账号的任务权限过滤 导致 `approved_data_scopes` 为空,误作数据不足结束。这不是业务数据确实不足。 用户确认开放规则后,任务 API 已移除该额外过滤,沿用账号已有的项目读取权限。 在线上下文返回七类来源,同一身份实际读取 SCADA 设备和外围合成工单成功;没有新增 角色权限或放宽跨项目、审批和设备控制边界。旧委托路径保留兼容,已有权限无需委托凭据。 用户级 `tjwater-operational-worker.service` 已重新启用。旧报告保留,未自动重新调查, 不能把旧报告视为修复后的业务验收。任务创建时间下界和并发配置见 [执行 worker](operational-task-workers.md),未回填旧任务。 进展读接口在表尚未部署时返回空,写入口须在迁移与 Server 更新后启用。 按现有数据库发布流程应用迁移,再更新 Server、Agent 和前端,运行中的 worker 需要加载更新。 聚焦检查: ```bash conda run -n server python -m pytest tests/unit/test_operational_tasks.py tests/unit/test_operational_presentation.py tests/unit/test_peripheral_task_sync.py tests/unit/test_peripheral_push_sync.py tests/api/test_operational_task_dynamic_flow.py tests/api/test_openapi_contract.py -q git diff --check ``` 这些检查覆盖隔离 HTTP 与状态机,不代替共享数据库迁移、真实模型运行或现场复测验收。