feat(db): add workbench schema comments
This commit is contained in:
@@ -52,11 +52,14 @@
|
||||
test "$(psql "$PROJECT_BIZ_DSN" -XAtc 'select current_database()')" = "tjwater_v2"
|
||||
```
|
||||
|
||||
完成备份和回滚演练后,再应用:
|
||||
完成备份和回滚演练后,再按依赖顺序应用。`017` 是工作台范围的注释迁移,要求告警、工单、任务和 Run 表均已存在:
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
迁移不会创建或修改外围系统的业务表,也不授予 `PUBLIC` 权限。生产运行前需按部署身份显式授予新表权限。SQL 文件包含自己的事务边界,不能把外层 `ROLLBACK` 当作演练;具体要求见数据库变更门禁。
|
||||
`017_workbench_schema_comments.sql` 只增加数据库可见的表、列和 schema 注释,不改变数据、约束或索引。迁移不会创建或修改外围系统的业务表,也不授予 `PUBLIC` 权限。生产运行前需按部署身份显式授予新表权限。SQL 文件包含自己的事务边界,不能把外层 `ROLLBACK` 当作演练;具体要求见数据库变更门禁。
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
|
||||
截至 2026-09-24,`014_operational_tasks.sql` 和 `015_workbench_events_and_task_runs.sql` 已应用到 `tjwater_v2`、`szh_v2`、`md_v2`、`fengyang_v2` 和 `zjb`。这是已有基线,不代表后续迁移可以继续批量应用。
|
||||
|
||||
`017_workbench_schema_comments.sql` 只为 `workbench` schema 及其 17 张表和关键列增加数据库可见注释。它先在 `tjwater_v2` 应用;其他项目数据库保持原状,直到 `tjwater_v2` 验证通过并获得明确扩展确认。
|
||||
|
||||
后续数据库变更默认只写入 `tjwater_v2`。其他项目保持现状,必须等 `tjwater_v2` 验证通过并由维护者明确确认后,再逐库扩展。禁止把“所有活跃项目”作为写操作的默认范围。
|
||||
|
||||
## 变更顺序
|
||||
|
||||
@@ -10,6 +10,8 @@ DDL 位于 `resources/sql/008_workbench_alert_events.sql`。任务通过 `POST /
|
||||
|
||||
`workbench.work_orders` 的 DDL 位于 `resources/sql/011_workbench_work_orders.sql`。每行保存一个只读工单投影,文档必须满足 `WorkbenchRuntimeWorkOrder`:
|
||||
|
||||
`resources/sql/017_workbench_schema_comments.sql` 在 `008`、`011`、`014` 和 `015` 之后执行,为工作台 schema、17 张表和关键列增加数据库可见的用途说明。
|
||||
|
||||
- 一张主工单包含一个或多个 `subtasks`。
|
||||
- 每个子任务保存自己的位置、班组、时间窗和复令要求。
|
||||
- 每个子任务包含一个或多个有序 `stages`,`sequence` 从 1 连续递增,阶段编号在子任务内唯一,最多只有一个 `current` 阶段。
|
||||
|
||||
@@ -0,0 +1,171 @@
|
||||
-- Database-visible descriptions for the workbench schema.
|
||||
|
||||
BEGIN;
|
||||
|
||||
COMMENT ON SCHEMA workbench IS
|
||||
'项目级工作台业务投影、工况任务、执行记录、通知及事件日志。';
|
||||
|
||||
COMMENT ON TABLE workbench.alert_events IS
|
||||
'告警来源提交的追加式状态事件,用于幂等接收 raised、acknowledged 和 resolved 事实。';
|
||||
COMMENT ON COLUMN workbench.alert_events.producer_id IS
|
||||
'告警事件生产者标识;与 event_id 共同确定来源侧幂等性。';
|
||||
COMMENT ON COLUMN workbench.alert_events.event_id IS
|
||||
'生产者范围内的事件唯一标识。';
|
||||
COMMENT ON COLUMN workbench.alert_events.event_type IS
|
||||
'告警状态变化类型:raised、acknowledged 或 resolved。';
|
||||
COMMENT ON COLUMN workbench.alert_events.document IS
|
||||
'经过接口模型校验的完整告警事件文档。';
|
||||
|
||||
COMMENT ON TABLE workbench.alerts IS
|
||||
'每项告警的最新状态投影,供工作台列表和状态查询读取。';
|
||||
COMMENT ON COLUMN workbench.alerts.revision IS
|
||||
'告警投影的单调修订号,用于拒绝过期更新。';
|
||||
COMMENT ON COLUMN workbench.alerts.status IS
|
||||
'告警当前状态:active、acknowledged 或 resolved。';
|
||||
COMMENT ON COLUMN workbench.alerts.document IS
|
||||
'经过接口模型校验的完整告警投影文档。';
|
||||
|
||||
COMMENT ON TABLE workbench.domain_event_log IS
|
||||
'项目级持久事件日志,为 SSE 断线补读和工作进程唤醒提供单调游标;不保存业务正文。';
|
||||
COMMENT ON COLUMN workbench.domain_event_log.event_seq IS
|
||||
'跨项目单调递增的事件游标,也是 SSE 事件 ID。';
|
||||
COMMENT ON COLUMN workbench.domain_event_log.topic IS
|
||||
'固定白名单中的工作台事件主题。';
|
||||
COMMENT ON COLUMN workbench.domain_event_log.aggregate_revision IS
|
||||
'事件产生时聚合对象的业务修订号。';
|
||||
COMMENT ON COLUMN workbench.domain_event_log.metadata IS
|
||||
'用于定向失效的最小元数据;客户端必须回查权威业务接口。';
|
||||
|
||||
COMMENT ON TABLE workbench.operational_tasks IS
|
||||
'工况任务的权威业务投影,保存任务生命周期和当前计划、报告引用。';
|
||||
COMMENT ON COLUMN workbench.operational_tasks.status IS
|
||||
'兼容旧接口的任务与执行混合状态;新业务以 lifecycle_status 和活动 Run 为准。';
|
||||
COMMENT ON COLUMN workbench.operational_tasks.lifecycle_status IS
|
||||
'任务业务生命周期:open、waiting_human、completed、failed 或 cancelled。';
|
||||
COMMENT ON COLUMN workbench.operational_tasks.revision IS
|
||||
'任务业务投影的单调修订号;Agent 心跳不得增加该值。';
|
||||
COMMENT ON COLUMN workbench.operational_tasks.execution_generation IS
|
||||
'兼容字段,记录当前执行代次;执行权威记录位于 operational_task_runs。';
|
||||
COMMENT ON COLUMN workbench.operational_tasks.execution_request_id IS
|
||||
'兼容字段,记录当前执行请求号;执行权威记录位于 operational_task_runs。';
|
||||
COMMENT ON COLUMN workbench.operational_tasks.agent_session_id IS
|
||||
'兼容字段,记录当前 Agent 会话;活动会话以 operational_task_runs 为准。';
|
||||
COMMENT ON COLUMN workbench.operational_tasks.agent_lease_expires_at IS
|
||||
'兼容字段,记录 Agent 租约到期时间;活动租约以 operational_task_runs 为准。';
|
||||
COMMENT ON COLUMN workbench.operational_tasks.document IS
|
||||
'经过接口模型校验的完整任务文档;标量列承担约束和索引。';
|
||||
|
||||
COMMENT ON TABLE workbench.operational_task_runs IS
|
||||
'工况任务的每代 Agent 执行记录,保存队列状态、租约、心跳、重试和终态。';
|
||||
COMMENT ON COLUMN workbench.operational_task_runs.generation IS
|
||||
'同一任务内单调递增的执行代次,用于阻止旧执行写回。';
|
||||
COMMENT ON COLUMN workbench.operational_task_runs.execution_request_id IS
|
||||
'投递给 Agent 的幂等执行请求号。';
|
||||
COMMENT ON COLUMN workbench.operational_task_runs.status IS
|
||||
'执行状态:queued、claimed、running、succeeded、failed、timed_out 或 cancelled。';
|
||||
COMMENT ON COLUMN workbench.operational_task_runs.revision IS
|
||||
'Run 的单调修订号;心跳只更新该修订号。';
|
||||
COMMENT ON COLUMN workbench.operational_task_runs.lease_expires_at IS
|
||||
'已领取或运行中执行的租约到期时间。';
|
||||
COMMENT ON COLUMN workbench.operational_task_runs.next_dispatch_at IS
|
||||
'失败退避后允许再次投递的最早时间。';
|
||||
|
||||
COMMENT ON TABLE workbench.operational_task_requests IS
|
||||
'工况命令幂等记录,按项目、命令范围和请求号保存请求摘要及既有响应。';
|
||||
COMMENT ON COLUMN workbench.operational_task_requests.scope IS
|
||||
'幂等请求所属的命令范围。';
|
||||
COMMENT ON COLUMN workbench.operational_task_requests.request_digest IS
|
||||
'规范化请求内容摘要,用于识别请求号复用但内容不同的冲突。';
|
||||
COMMENT ON COLUMN workbench.operational_task_requests.response IS
|
||||
'首次成功处理后保存的响应,用于幂等重放。';
|
||||
|
||||
COMMENT ON TABLE workbench.operational_task_evidence IS
|
||||
'工况任务采集或引用的证据记录,以 evidence_id 在任务内唯一标识。';
|
||||
COMMENT ON COLUMN workbench.operational_task_evidence.evidence_id IS
|
||||
'任务范围内稳定的证据标识。';
|
||||
COMMENT ON COLUMN workbench.operational_task_evidence.document IS
|
||||
'经过接口模型校验的完整证据文档。';
|
||||
|
||||
COMMENT ON TABLE workbench.operational_task_plans IS
|
||||
'工况任务按修订号保存的计划版本,用于审批和执行代次核验。';
|
||||
COMMENT ON COLUMN workbench.operational_task_plans.revision IS
|
||||
'任务范围内单调递增的计划修订号。';
|
||||
COMMENT ON COLUMN workbench.operational_task_plans.document IS
|
||||
'经过接口模型校验的完整计划文档。';
|
||||
|
||||
COMMENT ON TABLE workbench.operational_task_reports IS
|
||||
'工况任务按修订号保存的阶段或最终报告版本。';
|
||||
COMMENT ON COLUMN workbench.operational_task_reports.revision IS
|
||||
'任务范围内单调递增的报告修订号。';
|
||||
COMMENT ON COLUMN workbench.operational_task_reports.document IS
|
||||
'经过接口模型校验的完整报告文档。';
|
||||
|
||||
COMMENT ON TABLE workbench.operational_task_actions IS
|
||||
'任务建议动作的当前投影,保存审批状态、执行状态和最新版本。';
|
||||
COMMENT ON COLUMN workbench.operational_task_actions.version IS
|
||||
'动作当前版本号,用于乐观并发和审批前置校验。';
|
||||
COMMENT ON COLUMN workbench.operational_task_actions.approval_status IS
|
||||
'人工审批状态:pending、approved 或 rejected。';
|
||||
COMMENT ON COLUMN workbench.operational_task_actions.execution_status IS
|
||||
'外部动作执行状态;accepted 不等同于 succeeded。';
|
||||
COMMENT ON COLUMN workbench.operational_task_actions.document IS
|
||||
'经过接口模型校验的完整动作当前态文档。';
|
||||
|
||||
COMMENT ON TABLE workbench.operational_task_action_revisions IS
|
||||
'任务动作的不可变版本历史,用于审计审批和执行状态变化。';
|
||||
COMMENT ON COLUMN workbench.operational_task_action_revisions.version IS
|
||||
'动作范围内单调递增的历史版本号。';
|
||||
COMMENT ON COLUMN workbench.operational_task_action_revisions.document IS
|
||||
'该版本对应的完整动作快照。';
|
||||
|
||||
COMMENT ON TABLE workbench.operational_task_external_feedback IS
|
||||
'外围工单或其他外部系统反馈的幂等任务投影,用于触发后续分析。';
|
||||
COMMENT ON COLUMN workbench.operational_task_external_feedback.source_event_id IS
|
||||
'外部来源事件的稳定标识,用于拒绝重复反馈。';
|
||||
COMMENT ON COLUMN workbench.operational_task_external_feedback.source_revision IS
|
||||
'外部来源对象的修订号。';
|
||||
COMMENT ON COLUMN workbench.operational_task_external_feedback.document IS
|
||||
'经过接口模型校验的完整外部反馈文档。';
|
||||
|
||||
COMMENT ON TABLE workbench.operational_task_notifications IS
|
||||
'工况任务产生的用户通知内容;它是业务收件箱,不承担事件总线职责。';
|
||||
COMMENT ON COLUMN workbench.operational_task_notifications.document IS
|
||||
'经过接口模型校验的完整通知文档。';
|
||||
|
||||
COMMENT ON TABLE workbench.operational_task_notification_reads IS
|
||||
'用户维度的通知已读状态,使同一通知可由多个用户独立确认。';
|
||||
COMMENT ON COLUMN workbench.operational_task_notification_reads.user_id IS
|
||||
'读取通知的产品用户标识。';
|
||||
COMMENT ON COLUMN workbench.operational_task_notification_reads.version IS
|
||||
'用户读取后返回的通知视图版本。';
|
||||
COMMENT ON COLUMN workbench.operational_task_notification_reads.read_at IS
|
||||
'该用户首次确认已读的时间。';
|
||||
|
||||
COMMENT ON TABLE workbench.operational_task_schedules IS
|
||||
'周期性工况任务的调度定义,保存下一触发时间、间隔、启用状态和版本。';
|
||||
COMMENT ON COLUMN workbench.operational_task_schedules.schedule_key IS
|
||||
'项目范围内稳定的调度业务键。';
|
||||
COMMENT ON COLUMN workbench.operational_task_schedules.next_run_at IS
|
||||
'下一次允许生成任务的计划时间。';
|
||||
COMMENT ON COLUMN workbench.operational_task_schedules.interval_seconds IS
|
||||
'两次计划触发之间的秒数,最小值为 60。';
|
||||
COMMENT ON COLUMN workbench.operational_task_schedules.version IS
|
||||
'调度定义的单调版本号,用于乐观并发。';
|
||||
COMMENT ON COLUMN workbench.operational_task_schedules.document IS
|
||||
'经过接口模型校验的完整调度定义文档。';
|
||||
|
||||
COMMENT ON TABLE workbench.operational_schedule_occurrences IS
|
||||
'周期调度的幂等触发记录,将一个 schedule 的到期时刻关联到唯一任务。';
|
||||
COMMENT ON COLUMN workbench.operational_schedule_occurrences.due_at IS
|
||||
'本次调度理论到期时间;与 schedule_id 共同防止重复触发。';
|
||||
COMMENT ON COLUMN workbench.operational_schedule_occurrences.task_id IS
|
||||
'本次调度触发所创建的工况任务。';
|
||||
|
||||
COMMENT ON TABLE workbench.work_orders IS
|
||||
'从外围工单系统同步或受控种子写入的只读预约工单投影。';
|
||||
COMMENT ON COLUMN workbench.work_orders.work_order_id IS
|
||||
'来源系统中的稳定工单标识。';
|
||||
COMMENT ON COLUMN workbench.work_orders.document IS
|
||||
'经过接口模型校验的完整工作台工单文档。';
|
||||
|
||||
COMMIT;
|
||||
@@ -30,7 +30,6 @@ pytestmark = pytest.mark.skipif(
|
||||
os.environ.get("OPERATIONAL_TASKS_STORAGE_TEST") != "1", reason="explicit PostgreSQL test opt-in required",
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def anyio_backend():
|
||||
return "asyncio"
|
||||
@@ -40,7 +39,13 @@ def anyio_backend():
|
||||
async def test_postgres_task_artifacts_approval_and_project_isolation_roll_back():
|
||||
migration = "\n".join(
|
||||
(Path(__file__).parents[2] / f"resources/sql/{name}").read_text()
|
||||
for name in ("014_operational_tasks.sql", "015_workbench_events_and_task_runs.sql")
|
||||
for name in (
|
||||
"008_workbench_alert_events.sql",
|
||||
"011_workbench_work_orders.sql",
|
||||
"014_operational_tasks.sql",
|
||||
"015_workbench_events_and_task_runs.sql",
|
||||
"017_workbench_schema_comments.sql",
|
||||
)
|
||||
)
|
||||
# The migration is normally atomic itself. This test owns an outer rollback
|
||||
# transaction, so it executes its statements without transaction delimiters.
|
||||
@@ -51,6 +56,16 @@ async def test_postgres_task_artifacts_approval_and_project_isolation_roll_back(
|
||||
try:
|
||||
async with conn.transaction(force_rollback=True):
|
||||
await conn.execute(migration)
|
||||
comment_rows = await (
|
||||
await conn.execute(
|
||||
"""SELECT c.relname, obj_description(c.oid, 'pg_class') AS description
|
||||
FROM pg_class c
|
||||
JOIN pg_namespace n ON n.oid=c.relnamespace
|
||||
WHERE n.nspname='workbench' AND c.relkind='r'""",
|
||||
)
|
||||
).fetchall()
|
||||
assert len(comment_rows) >= 17
|
||||
assert all(row["description"] for row in comment_rows)
|
||||
repository = OperationalTaskRepository(conn, context.project_id)
|
||||
service = OperationalTaskService(repository, context, permissions_for_context(context))
|
||||
now = datetime.now(timezone.utc)
|
||||
|
||||
Reference in New Issue
Block a user