feat(workbench): add dynamic task approval lifecycle
This commit is contained in:
@@ -0,0 +1,53 @@
|
||||
# 动态调查计划数据契约
|
||||
|
||||
调查任务从工单投诉内容或人工请求创建。Agent 在本次执行中生成 `execution-plan@2`,根据新证据追加修订;数据库保存不可变计划快照的 JSONB 文档,并不保存固定流程模板。任务、Run、证据、访问申请、审核动作和报告各有独立生命周期。计划中的 `gate_ref` 只引用已经持久化的访问申请或动作,不能把审批结果写成模型自行判断的文本。
|
||||
|
||||
## `execution-plan@2`
|
||||
|
||||
| 字段 | 含义 |
|
||||
| --- | --- |
|
||||
| `schema` | 固定为 `execution-plan@2`;旧快照继续按 `execution-plan@1` 读取 |
|
||||
| `title`、`reason` | 本次计划名称和制定或修订原因 |
|
||||
| `nodes[]` | 具有稳定 `id`、`title`、`owner`、`status`、`detail`、`kind`、`evidence_refs[]`、`action_refs[]` 的步骤 |
|
||||
| `nodes[].kind` | `activity` 单项执行;`decision` 条件判断;`parallel_split` 并行分叉;`parallel_join` 并行汇合;`gate` 人工审核或授权等待点 |
|
||||
| `nodes[].selected_edge_id` | 仅判断节点使用。判断完成时指向实际选择的出边;未选择时为 `null` |
|
||||
| `nodes[].gate_ref` | 仅审核节点使用。UUID 指向任务内已有的访问申请或待确认动作 |
|
||||
| `nodes[].revisits_node_id` | 再次核验时,新节点引用先前节点 ID;不创建回环连线 |
|
||||
| `edges[]` | 每条有稳定 `id`、`from`、`to` 和可空 `condition`;判断路线必须有条件文字 |
|
||||
|
||||
图必须无环且只有一个起点。只有 `decision` 和 `parallel_split` 可以有多条出边;`parallel_join` 至少有两条入边。选中路线的直接目标不能标为跳过;未选路线的直接目标不能标为执行中、待人工或已完成。执行中和已完成的步骤,前置步骤必须完成或跳过。已完成的业务判断与活动必须引用真实证据或动作;纯分叉、汇合和审核节点不要求伪造证据。服务端在发布时再次核验引用、当前 Run 和会话租约。
|
||||
|
||||
下面是最小示例;`gate_ref` 所指申请先通过独立接口创建,再发布包含审核节点的计划。审批后,Agent 从当前任务上下文读取结果并发布下一版计划。
|
||||
|
||||
```json
|
||||
{
|
||||
"schema": "execution-plan@2",
|
||||
"title": "水质投诉调查",
|
||||
"reason": "核对工单位置和当前风险",
|
||||
"nodes": [
|
||||
{"id": "risk", "title": "判断紧急风险", "owner": "agent", "status": "completed", "detail": "根据投诉记录判断", "kind": "decision", "selected_edge_id": "urgent", "evidence_refs": ["complaint-1"], "action_refs": []},
|
||||
{"id": "field", "title": "安排现场采样", "owner": "human", "status": "pending", "detail": "待安排现场人员", "kind": "activity", "evidence_refs": [], "action_refs": []},
|
||||
{"id": "routine", "title": "常规核查", "owner": "human", "status": "skipped", "detail": "本次选择紧急路线", "kind": "activity", "evidence_refs": [], "action_refs": []},
|
||||
{"id": "split", "title": "并行核对", "owner": "agent", "status": "pending", "detail": "授权后同时核对现场和数据", "kind": "parallel_split", "evidence_refs": [], "action_refs": []},
|
||||
{"id": "upstream", "title": "上游监测", "owner": "agent", "status": "pending", "detail": "待批准读取", "kind": "activity", "evidence_refs": [], "action_refs": []},
|
||||
{"id": "network", "title": "管网运行", "owner": "agent", "status": "pending", "detail": "待核对运行事件", "kind": "activity", "evidence_refs": [], "action_refs": []},
|
||||
{"id": "join", "title": "汇总调查证据", "owner": "agent", "status": "waiting", "detail": "等待现场与数据结果", "kind": "parallel_join", "evidence_refs": [], "action_refs": []},
|
||||
{"id": "access", "title": "审核数据范围", "owner": "human", "status": "waiting", "detail": "等待项目成员审核", "kind": "gate", "gate_ref": "84000000-0000-4000-8000-000000000001", "evidence_refs": [], "action_refs": []}
|
||||
],
|
||||
"edges": [
|
||||
{"id": "urgent", "from": "risk", "to": "access", "condition": "有紧急风险"},
|
||||
{"id": "normal", "from": "risk", "to": "routine", "condition": "无紧急风险"},
|
||||
{"id": "access-split", "from": "access", "to": "split", "condition": null},
|
||||
{"id": "field-route", "from": "split", "to": "field", "condition": "现场"},
|
||||
{"id": "upstream-route", "from": "split", "to": "upstream", "condition": "上游"},
|
||||
{"id": "network-route", "from": "split", "to": "network", "condition": "管网"},
|
||||
{"id": "field-join", "from": "field", "to": "join", "condition": null},
|
||||
{"id": "upstream-join", "from": "upstream", "to": "join", "condition": null},
|
||||
{"id": "network-join", "from": "network", "to": "join", "condition": null}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
实际调查可以把现场路径与 Agent 路径放在同一个并行分叉下,并在下一次修订中补充更多判断或并行组。已发布计划保留原样;拒绝授权、人工补充、复测或回访触发新 Run 时,Agent 读取当前任务事实后继续规划。旧 `execution-plan@1` 只有依赖关系,前端按原始前置条件展示,不推断它代表条件选择还是并行执行。
|
||||
|
||||
访问申请独立存于 `workbench.operational_task_access_requests`,包括请求范围、理由、申请节点、审核人、批准范围和版本;工单草稿及其审批、回执仍存于动作表。前端默认呈现选中路线,并在明确的分叉与汇合处组成并行组;未选路线折叠保留供核对。
|
||||
@@ -1,6 +1,15 @@
|
||||
# 异常处置通知路由
|
||||
|
||||
“异常处置”只展示需要当前用户判断、授权或处置的活动事项。通知表仍是用户收件箱,不替代 `domain_event_log`;列表按当前用户权限和已读状态过滤。
|
||||
“异常处置”只展示需要当前用户判断、授权或处置的活动事项。通知表仍是用户收件箱,不替代 `domain_event_log`;列表同时按通知生命周期、当前用户权限和已读状态过滤。
|
||||
|
||||
通知存在两个互不替代的状态维度:
|
||||
|
||||
- `lifecycle_status=active|resolved` 保存在通知文档中,表示对应业务事项是否仍需处理,是项目共享状态。
|
||||
- `status=unread|read` 由 `operational_task_notification_reads` 按用户计算,只表示某个用户是否看过。
|
||||
|
||||
新通知保存 `subject_type + subject_id`,明确关联任务、Run、访问申请或动作。访问申请审核、动作审批、外部协同恢复、任务重试和人工材料补充,会在更新权威业务状态的同一事务中把对应通知改为 `resolved`,并追加 `operational.notification.changed`。Header 只接收 `active` 通知;已解决通知不会因为某个用户尚未阅读而继续计数。
|
||||
|
||||
共享生命周期事件使用 `operational_notification` 聚合;用户点击已读使用 `operational_notification_read` 聚合,聚合 ID 为 `notification_id:user_id`。这样项目共享的解决状态与每个用户的已读修订不会占用同一聚合版本。外围来源撤销时,尚未提交的动作审批、访问审批和任务级待办一并解决;已经提交或结果未知的外部动作仍保留只读回执核对入口。
|
||||
|
||||
## 进入异常处置的类型
|
||||
|
||||
@@ -25,6 +34,10 @@
|
||||
|
||||
## 数据库应用范围
|
||||
|
||||
2026-09-24 本轮只允许在 `tjwater_v2` 执行 `020`。执行前必须确认 `current_database()`、完成备份,并在事务后核对:普通生命周期通知为零,审批、任务异常和外部协同异常仍保留。其他项目数据库待 `tjwater_v2` 验证通过后再单独评估,不批量应用。
|
||||
2026-09-24 本轮只允许在 `tjwater_v2` 执行 `020` 和 `022`。执行前必须确认 `current_database()`、完成备份,并在事务后核对:普通生命周期通知为零,仍待处理的审批保持 `active`,已经审批或恢复的事项为 `resolved`。`022` 只回填通知 JSON 文档及表注释,不新增表或物理列。其他项目数据库待 `tjwater_v2` 验证通过后再单独评估,不批量应用。
|
||||
|
||||
本次实际执行结果:删除 16 条普通生命周期通知,保留 2 条旧 `action_pending` 审批通知,活动业务告警仍为 0 条,并写入 `event_seq=39` 的批量失效事件。通知表和用户已读表的迁移前逻辑备份位于 `/home/zhifu/TJWaterBackups/notification-routing-20260924-gZvjsn/tjwater_v2-notifications-before-020.json`,SHA-256 为 `046d6c3a0888b981791f3f39ee1972f304aab18a7e6af035da742c069b97a369`。系统缺少 `pg_dump` 客户端,因此本轮没有生成新的整库 custom dump;上一轮整库备份未被覆盖。
|
||||
|
||||
`022_operational_notification_lifecycle.sql` 带有 `current_database()='tjwater_v2'` 硬门禁。实际执行后,2 条旧审批通知均已关联到动作:仍为 `pending/not_submitted` 的 1 条保持 `active`,已经 `approved/succeeded` 的 1 条变为 `resolved`;活动未读查询只返回前者。迁移写入批量失效事件 `event_seq=79`,并更新通知文档列注释。迁移前逻辑备份位于 `/home/zhifu/TJWaterBackups/notification-lifecycle-20260924-tLsVeI/tjwater_v2-notifications-before-022.json`,SHA-256 为 `09df5efd2c748536752eaf0df94ad455d88458bb99866ed2163d8a0db56979ea`。其他 BizDB 未执行;扩展仍需维护者明确确认。
|
||||
|
||||
历史通知本身没有业务对象 ID。回填只在任务内恰有一个候选动作或访问申请时建立关联;存在多个候选时保持 `active` 并关联任务,不按时间邻近关系猜测,也不会自动隐藏。后续由人工核对或专门迁移处理这类歧义数据。新写入始终携带精确 `subject_id`,不受此限制。
|
||||
|
||||
@@ -81,7 +81,9 @@ conda run -n server python scripts/sync_peripheral_work_orders.py
|
||||
|
||||
使用相同工作身份,并配置 `OPERATIONAL_ACTION_PERIPHERAL_URL`。同步器读取分页工单,将外围版本投影到 `external_work_order` 任务。合成来源在摘要和证据中明确标注;工单完成只表示该外部任务完成,不代表其他调查自动完成。
|
||||
|
||||
通用合成检查工单不属于预约任务。早期种子脚本创建的通信、压力和零流量检查工单已经退役,种子脚本不再恢复它们;同步器按不可编辑的专用 seed actor 跳过旧种子工单。投诉、爆管事件以及调查动作实际创建的现场工单继续同步。`018_remove_synthetic_check_task_projections.sql` 只清理专用 seed actor 创建的旧检查投影,不按用户可编辑的标题或状态删除任务,也不影响 `workbench.work_orders` 中保留的三条演示工单。
|
||||
工况任务面板的刷新按钮只重新读取当前项目的工作台快照,不直接触发外围系统全量对账。外围工单仍由独立 worker 使用服务身份周期同步,避免多标签页或普通用户刷新放大全量扫描,也避免浏览器项目与 worker 固定项目不一致。
|
||||
|
||||
通用合成检查工单不属于预约任务。早期种子脚本创建的通信、压力和零流量检查工单已经退役,种子脚本不再恢复它们;同步器按不可编辑的专用 seed actor 跳过旧种子工单。投诉、爆管事件以及调查动作实际创建的现场工单继续同步。`018_remove_synthetic_check_task_projections.sql` 只清理专用 seed actor 创建的旧检查投影,不按用户可编辑的标题或状态删除任务。三张独立演示工单已迁入外围样板,由同一同步器维护。
|
||||
|
||||
水质投诉和爆管上报类型的外围工单同时创建关联调查,即使当时没有浏览器打开也会入队。两类事件从外围样板测试面板模拟上报;工况页负责展示调查。调查中创建的外围工单通过回执编号关联,后续反馈送回原调查,由 Agent 重新分析。
|
||||
|
||||
@@ -91,7 +93,7 @@ conda run -n server python scripts/sync_peripheral_work_orders.py
|
||||
|
||||
## 调查数据读取
|
||||
|
||||
`OPERATIONAL_DATA_SERVER_URL` 默认本机 8000;外围样板数据读取需配置 `OPERATIONAL_DATA_PERIPHERAL_URL`。查询只允许预定义的数据源、最多 20 个设备和 24 小时时间范围,响应有大小限制。每次查询保存内容摘要对应的数据快照;缺失、拒绝、超限或未配置都返回明确错误,不生成正常数据结论。截断结果必须保留不完整标记。
|
||||
`OPERATIONAL_DATA_SERVER_URL` 默认本机 8000;外围样板数据读取需配置 `OPERATIONAL_DATA_PERIPHERAL_URL`。人工批准任务级读取范围后,Server 使用仅保存在自身运行环境中的 `OPERATIONAL_DATA_DELEGATION_BEARER` 查询来源;Agent 服务不接收此凭证。该凭证必须拥有目标项目和来源的读取权限,缺失时已批准的查询返回 503。查询只允许预定义的数据源、最多 20 个设备和 24 小时时间范围,响应有大小限制。每次查询保存内容摘要对应的数据快照;缺失、拒绝、超限或未配置都返回明确错误,不生成正常数据结论。截断结果必须保留不完整标记。
|
||||
|
||||
## 人工确认后的动作
|
||||
|
||||
|
||||
@@ -10,6 +10,12 @@
|
||||
预约任务采用单层结构:一张外围工单对应一条任务,工单快照直接提供状态、位置、负责人、预约时间、回执要求和按发生时间记录的 `activity`。活动仅由工单创建、修改或显式现场反馈产生;不能按当前状态补造派发、执行、复令三个阶段。旧快照中的 `subtasks` 仅在读取时兼容并丢弃,不回写外围工单或历史版本;后续外围修订同步为新结构。
|
||||
- `event_investigation`:投诉、爆管上报或人工发起的调查。Agent 查询受控数据,发布执行图、证据引用和报告;现场反馈会创建新的执行 generation,让 Agent 重新分析。
|
||||
|
||||
调查计划由 Agent 按本次工单或人工请求动态生成。新计划使用 `execution-plan@2`,节点声明执行步骤、条件判断、并行分叉/汇合和审核点;判断节点记录所选路线,审核点引用持久化申请或动作。重新分析追加计划修订和新节点,不使用环形连线。旧 `execution-plan@1` 快照保留原样读取。字段、约束与示例见 [`dynamic-investigation-plan.md`](dynamic-investigation-plan.md)。
|
||||
|
||||
数据读取申请通过 `POST /operational-tasks/{task_id}/access-requests` 创建,`POST .../access-requests/{access_request_id}/decision` 由具备项目审核权限及对应数据源读取权限的用户决定。申请、批准范围和审核身份保存在任务库中,批准或拒绝后重新排队分析。调查接口不会将服务身份继承的项目数据读取权限直接授予 Agent;批准的范围也只在该任务的有效执行期可查询。需配置 `OPERATIONAL_DATA_DELEGATION_BEARER` 为具备对应来源读取权限及该项目成员身份的专用产品凭证。未配置时返回 503,不以普通 Agent 会话的工具许可代替任务授权。
|
||||
|
||||
工单草稿可通过 `PATCH .../actions/{action_id}/draft` 修改并增加审计版本;`POST .../actions/{action_id}/confirm-dispatch` 使用一次人工操作批准当前版本并幂等提交,结果不明时继续使用回执核对。其他动作类型维持既有审批接口,不在调查页伪装为已支持的工单编辑动作。
|
||||
|
||||
创建请求使用 `request_id` 幂等。任务执行使用独立的 `run_id + execution_request_id`:Agent 必须领取当前 run,并通过 run 级心跳维持 60 秒租约。正常结束必须显式 complete;缺少同代最终计划或报告会以 `ARTIFACT_INCOMPLETE` 失败。`POST /operational-tasks/leases/recover` 会处理过期执行,连续三代超时后停止自动重试。
|
||||
|
||||
## 主要接口
|
||||
@@ -25,11 +31,15 @@
|
||||
| `POST /operational-tasks/{task_id}/runs/{run_id}/complete` | 校验同代最终产物并显式结束 run |
|
||||
| `POST /operational-tasks/{task_id}/runs/{run_id}/fail` | 使用稳定失败码显式终止 run |
|
||||
| `POST /operational-tasks/{task_id}/data-query` | 查询固定白名单数据源,同时保存有界原始快照并返回证据 ID |
|
||||
| `POST /operational-tasks/{task_id}/access-requests` | Agent 创建任务级数据访问申请 |
|
||||
| `POST .../access-requests/{access_request_id}/decision` | 人工批准部分或全部申请范围,或拒绝申请 |
|
||||
| `POST /operational-tasks/{task_id}/agent-artifacts` | 追加证据、计划快照和报告修订 |
|
||||
| `POST /operational-tasks/{task_id}/human-evidence` | 保存用户补充信息并重新排队分析 |
|
||||
| `POST /operational-tasks/{task_id}/external-feedback` | 幂等同步稳定外部事件流的单调版本 |
|
||||
| `POST /operational-tasks/{task_id}/actions` | 提议需要人工确认的动作 |
|
||||
| `POST .../actions/{action_id}/decision` | 有对应审批权限的用户确认或拒绝 |
|
||||
| `PATCH .../actions/{action_id}/draft` | 人工修改工单草稿并产生新审核版本 |
|
||||
| `POST .../actions/{action_id}/confirm-dispatch` | 一次确认当前草稿并幂等下发工单 |
|
||||
| `POST .../actions/{action_id}/execute` | 重新校验执行权限后通过类型化适配器提交 |
|
||||
| `POST .../actions/{action_id}/reconcile` | 仅使用固定执行请求号查询不确定或已提交动作的回执 |
|
||||
| `GET /operational-task-notifications` | 返回当前用户可见、按用户区分已读状态的通知 |
|
||||
@@ -54,15 +64,12 @@
|
||||
test "$(psql "$PROJECT_BIZ_DSN" -XAtc 'select current_database()')" = "tjwater_v2"
|
||||
```
|
||||
|
||||
完成备份和回滚演练后,再按依赖顺序应用。`017` 是工作台范围的注释迁移,要求告警、工单、任务和 Run 表均已存在:
|
||||
`008`、`011`、`014`、`015` 和 `017` 属于历史基线;不要在旧工单表退役后重新运行 `011`,否则会重建它。`019` 和 `022` 都带有 `tjwater_v2` 物理数据库名硬门禁,其他 BizDB 尚未应用。完成备份和回滚演练后,仅在已核对的 `tjwater_v2` 执行迁移:
|
||||
|
||||
```bash
|
||||
psql "$PROJECT_BIZ_DSN" -v ON_ERROR_STOP=1 -f resources/sql/008_workbench_alert_events.sql
|
||||
psql "$PROJECT_BIZ_DSN" -v ON_ERROR_STOP=1 -f resources/sql/011_workbench_work_orders.sql
|
||||
psql "$PROJECT_BIZ_DSN" -v ON_ERROR_STOP=1 -f resources/sql/014_operational_tasks.sql
|
||||
psql "$PROJECT_BIZ_DSN" -v ON_ERROR_STOP=1 -f resources/sql/015_workbench_events_and_task_runs.sql
|
||||
psql "$PROJECT_BIZ_DSN" -v ON_ERROR_STOP=1 -f resources/sql/017_workbench_schema_comments.sql
|
||||
psql "$PROJECT_BIZ_DSN" -v ON_ERROR_STOP=1 -f resources/sql/018_remove_synthetic_check_task_projections.sql
|
||||
psql "$PROJECT_BIZ_DSN" -v ON_ERROR_STOP=1 -f resources/sql/019_operational_task_access_requests.sql
|
||||
psql "$PROJECT_BIZ_DSN" -v ON_ERROR_STOP=1 -f resources/sql/021_retire_workbench_work_orders.sql
|
||||
psql "$PROJECT_BIZ_DSN" -v ON_ERROR_STOP=1 -f resources/sql/022_operational_notification_lifecycle.sql
|
||||
```
|
||||
|
||||
`017_workbench_schema_comments.sql` 只增加数据库可见的表、列和 schema 注释,不改变数据、约束或索引。`018_remove_synthetic_check_task_projections.sql` 读取 `peripheral_sample.work_orders` 的不可编辑创建者标识,删除错误进入预约任务的旧种子检查投影,不修改外围表,也不删除演示工单、事件工单或调查动作创建的工单。迁移不授予 `PUBLIC` 权限。生产运行前需按部署身份显式授予新表权限。SQL 文件包含自己的事务边界,不能把外层 `ROLLBACK` 当作演练;具体要求见数据库变更门禁。
|
||||
`017_workbench_schema_comments.sql` 只增加历史基线的数据库可见注释。`018_remove_synthetic_check_task_projections.sql` 只清理错误进入预约任务的旧种子检查投影。`020_prune_non_actionable_notifications.sql` 删除历史任务创建、报告更新和正常完成通知,保留审批与异常通知。`021` 只有在三条演示工单均已迁入外围样板并形成有效工况任务时才删除旧表,不修改外围源及历史修订。`022_operational_notification_lifecycle.sql` 仅限 `tjwater_v2`,为保留通知回填 `active|resolved` 生命周期和业务对象引用,不新增表。通知路由和状态流见 [`operational-notification-routing.md`](operational-notification-routing.md)。SQL 文件包含自己的事务边界,不能把外层 `ROLLBACK` 当作演练;具体要求见数据库变更门禁。
|
||||
|
||||
@@ -8,12 +8,18 @@
|
||||
|
||||
`017_workbench_schema_comments.sql` 只为 `workbench` schema 及其 17 张表和关键列增加数据库可见注释。它先在 `tjwater_v2` 应用;其他项目数据库保持原状,直到 `tjwater_v2` 验证通过并获得明确扩展确认。
|
||||
|
||||
`018_remove_synthetic_check_task_projections.sql` 清理曾被错误同步为预约任务的通用合成检查投影。它按完整 `peripheral_sample:<work_order_id>` 标识关联外围源,只匹配 `general` 类型、没有调查动作回执关联且由专用 seed actor 创建的旧检查任务;不使用用户可编辑的标题或工单状态作为删除依据。`workbench.work_orders` 中三条演示工单、投诉与爆管事件以及调查产生的现场采样工单均不在清理范围。同步器使用同一专用 seed actor 阻止旧种子检查再次生成。
|
||||
`019_operational_task_access_requests.sql` 已只在 `tjwater_v2` 应用,新增带表级和列级注释的任务访问申请表;初始记录数为 0。旧 `workbench.work_orders` 已退役,因此应用后 `workbench` schema 仍为 17 张表,而不是继续增加重复投影。其他项目数据库尚未应用 `019`。
|
||||
|
||||
预约工单结构调整无需 DDL。`seed_workbench_work_orders.py --project-id <已核对的项目 UUID>` 只重建该项目的三条演示预约投影,保留工单 ID 和标题,改为单层字段与显式活动记录。执行前必须核对脚本删除谓词实际命中的 ID;它不修改 `peripheral_sample.work_orders`、历史修订或调查任务。其他项目不得随 `tjwater_v2` 自动重建。
|
||||
未应用 `019` 的项目仍可读取既有工况任务,但创建访问申请会明确返回 `503 DEPENDENCY_UNAVAILABLE`,不会以缺表异常形成 500。单个任务同一时间只允许一个待审核访问申请;需要先审批或拒绝当前申请,避免 Agent 用不同请求号重复放大通知和事件。
|
||||
|
||||
`018_remove_synthetic_check_task_projections.sql` 清理曾被错误同步为预约任务的通用合成检查投影。它按完整 `peripheral_sample:<work_order_id>` 标识关联外围源,只匹配 `general` 类型、没有调查动作回执关联且由专用 seed actor 创建的旧检查任务;不使用用户可编辑的标题或工单状态作为删除依据。投诉与爆管事件以及调查产生的现场采样工单均不在清理范围。同步器使用同一专用 seed actor 阻止旧种子检查再次生成。
|
||||
|
||||
旧的三条演示工单已迁入 `peripheral_sample.work_orders`,并同步为有效的 `external_work_order` 任务。`021_retire_workbench_work_orders.sql` 仅在物理数据库为 `tjwater_v2`、旧表恰有这三条且每条都有有效外围工单和任务投影时删除旧表。其他四个项目数据库原本没有旧表,不执行本迁移。旧种子和迁入脚本已退役,不能再运行历史 `011` 重建双份数据。
|
||||
|
||||
后续数据库变更默认只写入 `tjwater_v2`。其他项目保持现状,必须等 `tjwater_v2` 验证通过并由维护者明确确认后,再逐库扩展。禁止把“所有活跃项目”作为写操作的默认范围。
|
||||
|
||||
通知生命周期迁移 `022_operational_notification_lifecycle.sql` 只修改现有通知 JSON 文档与列注释,不新增表或物理列。脚本带物理数据库名硬门禁;先在 `tjwater_v2` 核对待审批通知仍为 `active`、已审批通知变为 `resolved`,并验证 Header 只统计活动未读项。未取得维护者确认前,不向其他 BizDB 应用。
|
||||
|
||||
## 变更顺序
|
||||
|
||||
1. 核对目标 DSN,确认物理数据库名为 `tjwater_v2`,并确认没有其他项目共享该 BizDB。
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# 工作台工单与告警存储契约
|
||||
|
||||
工作台只聚合预约工单与任务主动推送的告警。浏览器不推导告警,也不写正式工单。
|
||||
工作台聚合外围来源工况任务与任务主动推送的告警。浏览器不推导告警,也不直接写工况任务。
|
||||
|
||||
## 任务告警
|
||||
|
||||
@@ -8,16 +8,8 @@ DDL 位于 `resources/sql/008_workbench_alert_events.sql`。任务通过 `POST /
|
||||
|
||||
## 预约工单
|
||||
|
||||
`workbench.work_orders` 的 DDL 位于 `resources/sql/011_workbench_work_orders.sql`。每行保存一个只读工单投影,文档必须满足 `WorkbenchRuntimeWorkOrder`:
|
||||
外围样板的 `peripheral_sample.work_orders` 是演示工单的管理来源;`activity` 只记录实际发生的创建、修改和现场反馈,不由总状态补造阶段。同步器将有效工单写入 `workbench.operational_tasks` 的 `external_work_order` 投影。原有三张独立演示工单也已迁入外围样板,并由同一面板管理。
|
||||
|
||||
`resources/sql/017_workbench_schema_comments.sql` 在 `008`、`011`、`014` 和 `015` 之后执行,为工作台 schema、17 张表和关键列增加数据库可见的用途说明。
|
||||
`resources/sql/011_workbench_work_orders.sql` 和 `017_workbench_schema_comments.sql` 属于历史基线;`021_retire_workbench_work_orders.sql` 在验证三个迁入目标及其任务投影后,仅在 `tjwater_v2` 删除旧表。不要重放 `011` 重建它。`GET /api/v1/workbench/runtime` 暂保留空的 `work_orders` 字段作为接口兼容。其他项目数据库没有旧表,不需要应用 `021`。
|
||||
|
||||
- 一张预约工单对应一条单层投影,直接保存位置、负责人、预约时间和回执要求。
|
||||
- `activity` 只记录已经发生的创建、修改和现场反馈,按递增 `sequence` 排列;展示层不得根据工单总状态补造派发、执行或复令阶段。
|
||||
- 旧文档中的 `subtasks` 在兼容读取时被丢弃,不再进入新接口契约;历史修订保留原始审计内容。
|
||||
|
||||
预约任务演示数据通过 `scripts/seed_workbench_work_orders.py` 直接写入。工作台继续显示三张原有演示工单,保留既有 ID、标题和状态,并改用回执要求与真实活动记录表达过程。脚本可重复执行,并会先清理旧的外围合成工单投影。`scripts/seed_peripheral_sample.py` 不再创建通信、压力和零流量检查工单。
|
||||
|
||||
截至 2026-09-24,本次数据清理和三张演示工单结构重建仅应用于物理数据库 `tjwater_v2`,项目为 `324ff7ee-c2d4-5184-a84b-48fb14a2933e`。核验结果为三张演示工单均使用单层结构,旧种子检查投影为 0;其他项目数据库未执行本次变更。扩大范围必须遵循 [`workbench-database-rollout.md`](workbench-database-rollout.md) 的逐库门禁。
|
||||
|
||||
工作台运行账号对工单表只需 `SELECT`,同步账号使用独立受控写权限;对告警事件表需要 `SELECT, INSERT`,对告警状态表需要 `SELECT, INSERT, UPDATE`。
|
||||
告警事件表需要 `SELECT, INSERT`,告警状态表需要 `SELECT, INSERT, UPDATE`。迁移及扩展门禁见 [`workbench-database-rollout.md`](workbench-database-rollout.md)。
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
# 工作台运行态
|
||||
|
||||
`GET /api/v1/workbench/runtime` 按当前项目读取 BizDB 中的分析成果、预约工单和任务告警。调查任务由独立的工况任务接口读取 `workbench.operational_tasks` 和 `workbench.operational_task_runs`,`sync-snapshot` 再把两部分合并;定时任务仍保留前端 mock,不进入后端工作队列。
|
||||
`GET /api/v1/workbench/runtime` 按当前项目读取 BizDB 中的分析成果和任务告警。外围样板工单经同步器进入 `workbench.operational_tasks`,由工况任务接口和 `sync-snapshot` 提供给前端;定时任务表暂时保留,但不进入当前产品主路径。
|
||||
|
||||
- `analysis.runs/results` 只用于分析成果、历史成果和兼容场景字段。
|
||||
- `workbench.work_orders` 是预约工单的后端持久化投影。主工单包含多个子任务,每个子任务包含自己的有序阶段。表存在时,空列表同样是权威结果,不回退到 mock 工单。
|
||||
- 旧的 `workbench.work_orders` 已由 `021_retire_workbench_work_orders.sql` 退役。`work_orders: []` 和 `work_order_availability: not_configured` 仅保留接口兼容,不表示没有外围工单。
|
||||
- `workbench.alert_events` 保存任务显式推送的事件,`workbench.alerts` 保存当前状态。分析结果、测点值和缺测状态不会自动变成告警。
|
||||
- 每个可选分区独立读取并返回 `ready`、`not_configured` 或 `degraded`,一个分区失败不会污染其他分区。
|
||||
|
||||
预约任务演示数据由 `scripts/seed_workbench_work_orders.py` 直接写入 `workbench.work_orders`,与外围样板的 synthetic 测试数据相互独立。种子集 `workbench-demo-orders@1` 恢复前端原有的三张 mock 工单,分别覆盖派发、执行和复令状态;脚本会在同一事务中删除旧的“外围合成数据核查”投影并幂等写入新记录。当前固定演示日期为 2026-09-17。外围种子脚本仍可重复执行,并会恢复曾被软删除的固定演示工单和测试数据。
|
||||
原有三张独立演示工单已迁入 `peripheral_sample.work_orders`,仍由外围样板面板管理,并由同步器投影为 `external_work_order` 工况任务。旧表退役前的完整数据库备份由部署记录保存,不能重新运行历史 `011_workbench_work_orders.sql` 或旧种子脚本重建双份数据。
|
||||
|
||||
Reference in New Issue
Block a user