feat(scada): add project monitoring replay
Generic Container CI/CD / test-build-publish (push) Successful in 3m39s
Server CI/CD v2 / build-test-publish-and-deploy (push) Successful in 3m39s

This commit is contained in:
2026-11-04 14:27:21 +08:00
parent f25423dec4
commit 26fa5d1909
53 changed files with 5153 additions and 20 deletions
@@ -19,13 +19,18 @@ CREATE TABLE IF NOT EXISTS projects (
gs_workspace VARCHAR(100) UNIQUE NOT NULL,
map_extent JSONB,
map_config JSONB NOT NULL DEFAULT '{}'::jsonb,
settings JSONB NOT NULL DEFAULT '{}'::jsonb,
status VARCHAR(20) DEFAULT 'active' NOT NULL,
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL,
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL,
CONSTRAINT projects_status_check CHECK (status IN ('active', 'inactive', 'archived'))
CONSTRAINT projects_status_check CHECK (status IN ('active', 'inactive', 'archived')),
CONSTRAINT projects_settings_object_check CHECK (jsonb_typeof(settings) = 'object')
);
ALTER TABLE projects ADD COLUMN IF NOT EXISTS map_config JSONB NOT NULL DEFAULT '{}'::jsonb;
ALTER TABLE projects ADD COLUMN IF NOT EXISTS settings JSONB NOT NULL DEFAULT '{}'::jsonb;
COMMENT ON COLUMN projects.settings IS
'项目级通用配置(JSON 对象,不存放密钥);scada_monitoring_interval_seconds 为监测任务间隔秒数。';
COMMENT ON COLUMN projects.map_config IS
'项目级地图配置(JSON);place_search 保存地名搜索启用状态、服务商、行政区编码及区域名称;保留既有图例与坐标参考配置。';
+46
View File
@@ -0,0 +1,46 @@
BEGIN;
DO $$ BEGIN
IF current_database() NOT IN ('tjwater_v2', 'tjwater2_v2') THEN
RAISE EXCEPTION '031_scada_replays requires an explicitly verified SCADA demo project';
END IF;
END $$;
CREATE TABLE IF NOT EXISTS peripheral_sample.scada_replays (
project_id uuid NOT NULL,
replay_id uuid NOT NULL,
request_id uuid NOT NULL,
status text NOT NULL CHECK (status IN ('ready','paused','completed','stopped','failed')),
document jsonb NOT NULL,
created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now(),
PRIMARY KEY (project_id,replay_id),
UNIQUE (project_id,request_id)
);
CREATE INDEX IF NOT EXISTS scada_replays_status_idx
ON peripheral_sample.scada_replays(project_id,status,created_at DESC);
CREATE TABLE IF NOT EXISTS peripheral_sample.scada_replay_batches (
project_id uuid NOT NULL,
replay_id uuid NOT NULL,
sequence bigint NOT NULL,
stage text NOT NULL CHECK(stage IN ('selected','clean_prepared','cleaned','detected')),
document jsonb NOT NULL,
created_at timestamptz NOT NULL DEFAULT now(),
PRIMARY KEY(project_id,replay_id,sequence),
FOREIGN KEY(project_id,replay_id) REFERENCES peripheral_sample.scada_replays(project_id,replay_id)
);
COMMENT ON TABLE peripheral_sample.scada_replays IS 'SCADA 演示回放的权威控制状态与处理检查点,不保存正式原始观测';
COMMENT ON COLUMN peripheral_sample.scada_replays.project_id IS '经认证与路由校验的所属项目';
COMMENT ON COLUMN peripheral_sample.scada_replays.replay_id IS '独立回放身份,不等同 Agent 执行编号';
COMMENT ON COLUMN peripheral_sample.scada_replays.request_id IS '创建请求的稳定幂等编号';
COMMENT ON COLUMN peripheral_sample.scada_replays.status IS '供领取和约束使用的 document.status 查询投影';
COMMENT ON COLUMN peripheral_sample.scada_replays.document IS '冻结配置、设备快照、时钟、检查点、规则状态和命令回执';
COMMENT ON COLUMN peripheral_sample.scada_replays.created_at IS '实际创建时间';
COMMENT ON COLUMN peripheral_sample.scada_replays.updated_at IS '实际控制或进度更新时间';
COMMENT ON TABLE peripheral_sample.scada_replay_batches IS 'SCADA raw 引用、清洗输出及检测阶段的恢复凭证,禁止重写 raw';
COMMENT ON COLUMN peripheral_sample.scada_replay_batches.project_id IS '所属项目';
COMMENT ON COLUMN peripheral_sample.scada_replay_batches.replay_id IS '所属回放';
COMMENT ON COLUMN peripheral_sample.scada_replay_batches.sequence IS '回放内单调批次序号';
COMMENT ON COLUMN peripheral_sample.scada_replay_batches.stage IS '已提交处理阶段';
COMMENT ON COLUMN peripheral_sample.scada_replay_batches.document IS '观测键、raw 摘要、确定的清洗结果与检测事件';
COMMENT ON COLUMN peripheral_sample.scada_replay_batches.created_at IS '实际登记批次时间';
COMMIT;
@@ -0,0 +1,54 @@
BEGIN;
DO $$
BEGIN
IF to_regclass('public.project_databases') IS NULL
OR to_regclass('public.user_project_membership') IS NULL THEN
RAISE EXCEPTION '033_metadata_project_settings must run in the project metadata database';
END IF;
END $$;
ALTER TABLE public.projects
ADD COLUMN IF NOT EXISTS settings jsonb NOT NULL DEFAULT '{}'::jsonb;
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM pg_constraint
WHERE conrelid = 'public.projects'::regclass
AND conname = 'projects_settings_object_check'
) THEN
ALTER TABLE public.projects
ADD CONSTRAINT projects_settings_object_check
CHECK (jsonb_typeof(settings) = 'object');
END IF;
IF EXISTS (
SELECT 1 FROM information_schema.columns
WHERE table_schema = 'public' AND table_name = 'projects'
AND column_name = 'scada_monitoring_interval_seconds'
) THEN
IF EXISTS (
SELECT 1 FROM public.projects
WHERE scada_monitoring_interval_seconds IS NOT NULL
AND settings ? 'scada_monitoring_interval_seconds'
AND settings->'scada_monitoring_interval_seconds'
IS DISTINCT FROM to_jsonb(scada_monitoring_interval_seconds)
) THEN
RAISE EXCEPTION 'Conflicting SCADA monitoring intervals; resolve before migration';
END IF;
UPDATE public.projects
SET settings = settings || jsonb_build_object(
'scada_monitoring_interval_seconds', scada_monitoring_interval_seconds
)
WHERE scada_monitoring_interval_seconds IS NOT NULL;
ALTER TABLE public.projects DROP COLUMN scada_monitoring_interval_seconds;
END IF;
END $$;
COMMENT ON COLUMN public.projects.settings IS
'项目级通用配置(JSON 对象,不存放密钥);scada_monitoring_interval_seconds 为监测任务间隔秒数。';
COMMIT;