-- AI-Ops 初始化 schema -- 表前缀 ai_ops_,避免与桥项目表名冲突 -- 1. 告警规则 CREATE TABLE IF NOT EXISTS ai_ops_rules ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), name VARCHAR(128) NOT NULL, metric_source VARCHAR(64) NOT NULL, metric_name VARCHAR(128) NOT NULL, threshold_type VARCHAR(16) NOT NULL CHECK (threshold_type IN ('>', '<', '=', 'regex')), threshold_value TEXT NOT NULL, duration_min INT NOT NULL DEFAULT 1 CHECK (duration_min >= 1), level VARCHAR(8) NOT NULL CHECK (level IN ('P0', 'P1', 'P2', 'P3')), channel_ids UUID[] NOT NULL DEFAULT '{}', healing_action VARCHAR(32) DEFAULT NULL, healing_config JSONB DEFAULT NULL, is_sandboxed BOOLEAN NOT NULL DEFAULT FALSE, enabled BOOLEAN NOT NULL DEFAULT TRUE, created_by VARCHAR(64) NOT NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), version INT NOT NULL DEFAULT 1, CONSTRAINT uq_rules_name UNIQUE (name) ); CREATE INDEX IF NOT EXISTS idx_rules_enabled ON ai_ops_rules(enabled); -- 2. 告警事件 CREATE TABLE IF NOT EXISTS ai_ops_alerts ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), rule_id UUID NOT NULL REFERENCES ai_ops_rules(id) ON DELETE CASCADE, level VARCHAR(8) NOT NULL, resource_type VARCHAR(64) NOT NULL, resource_id VARCHAR(128) NOT NULL, current_value TEXT NOT NULL, threshold_value TEXT NOT NULL, status VARCHAR(16) NOT NULL DEFAULT 'triggered' CHECK (status IN ('triggered', 'notified', 'healing', 'resolved', 'escalated', 'acknowledged')), is_aggregated BOOLEAN NOT NULL DEFAULT FALSE, aggregated_count INT DEFAULT 0, parent_alert_id UUID NULL REFERENCES ai_ops_alerts(id) ON DELETE SET NULL, started_at TIMESTAMPTZ NOT NULL, resolved_at TIMESTAMPTZ NULL, acknowledged_by VARCHAR(64) NULL, acknowledged_at TIMESTAMPTZ NULL ); CREATE INDEX IF NOT EXISTS idx_alerts_status ON ai_ops_alerts(status); CREATE INDEX IF NOT EXISTS idx_alerts_started_at ON ai_ops_alerts(started_at DESC); CREATE INDEX IF NOT EXISTS idx_alerts_resource ON ai_ops_alerts(resource_type, resource_id); -- 3. 自愈执行记录 CREATE TABLE IF NOT EXISTS ai_ops_healings ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), alert_id UUID NOT NULL REFERENCES ai_ops_alerts(id) ON DELETE CASCADE, action_type VARCHAR(32) NOT NULL CHECK (action_type IN ('switch_route', 'throttle', 'restart_instance', 'invoke_script', 'isolate_node')), config JSONB NOT NULL, status VARCHAR(16) NOT NULL DEFAULT 'pending' CHECK (status IN ('pending', 'succeeded', 'failed', 'rolled_back')), dry_run BOOLEAN NOT NULL DEFAULT FALSE, result_detail JSONB NULL, error_code VARCHAR(16) NULL, started_at TIMESTAMPTZ NOT NULL, completed_at TIMESTAMPTZ NULL ); CREATE INDEX IF NOT EXISTS idx_healings_alert ON ai_ops_healings(alert_id); -- 4. 通知渠道 CREATE TABLE IF NOT EXISTS ai_ops_channels ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), name VARCHAR(128) NOT NULL, channel_type VARCHAR(32) NOT NULL CHECK (channel_type IN ('webhook', 'email', 'feishu', 'wechat', 'sms')), config JSONB NOT NULL, priority INT NOT NULL DEFAULT 1, enabled BOOLEAN NOT NULL DEFAULT TRUE, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); -- 5. 审计日志(append-only,禁止更新和删除) CREATE TABLE IF NOT EXISTS ai_ops_audits ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), tenant_id VARCHAR(64) NOT NULL, object_type VARCHAR(64) NOT NULL, object_id VARCHAR(128) NOT NULL, action VARCHAR(32) NOT NULL CHECK (action IN ('create', 'update', 'delete', 'rollback')), before_state JSONB NULL, after_state JSONB NULL, request_id VARCHAR(64) NOT NULL, result_code VARCHAR(16) NOT NULL, source_ip VARCHAR(45) NOT NULL, actor_id VARCHAR(64) NOT NULL, risk_level VARCHAR(8) NOT NULL DEFAULT 'normal' CHECK (risk_level IN ('normal', 'high', 'critical')), parent_audit_id UUID NULL REFERENCES ai_ops_audits(id) ON DELETE SET NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE INDEX IF NOT EXISTS idx_audits_tenant_created ON ai_ops_audits(tenant_id, created_at DESC); CREATE INDEX IF NOT EXISTS idx_audits_object ON ai_ops_audits(object_type, object_id); CREATE INDEX IF NOT EXISTS idx_audits_actor ON ai_ops_audits(actor_id, created_at DESC); CREATE INDEX IF NOT EXISTS idx_audits_request ON ai_ops_audits(request_id); -- 审计日志防篡改触发器(仅允许插入,禁止更新和删除) CREATE OR REPLACE FUNCTION ai_ops_audit_readonly() RETURNS TRIGGER AS $$ BEGIN RAISE EXCEPTION 'ai_ops_audits is append-only. Updates and deletes are not allowed.'; END; $$ LANGUAGE plpgsql; DROP TRIGGER IF EXISTS trg_ai_ops_audits_no_update ON ai_ops_audits; CREATE TRIGGER trg_ai_ops_audits_no_update BEFORE UPDATE ON ai_ops_audits FOR EACH ROW EXECUTE FUNCTION ai_ops_audit_readonly(); DROP TRIGGER IF EXISTS trg_ai_ops_audits_no_delete ON ai_ops_audits; CREATE TRIGGER trg_ai_ops_audits_no_delete BEFORE DELETE ON ai_ops_audits FOR EACH ROW EXECUTE FUNCTION ai_ops_audit_readonly(); -- 6. 时序指标缓存(降级方案,主存储推荐 Prometheus / VictoriaMetrics) CREATE TABLE IF NOT EXISTS ai_ops_metrics ( id BIGSERIAL PRIMARY KEY, metric_name VARCHAR(128) NOT NULL, labels JSONB NOT NULL DEFAULT '{}', value DOUBLE PRECISION NOT NULL, recorded_at TIMESTAMPTZ NOT NULL ); CREATE INDEX IF NOT EXISTS idx_metrics_name_time ON ai_ops_metrics(metric_name, recorded_at DESC); -- 分区表(按天分区,自动清理 > 7 天的分区) CREATE TABLE IF NOT EXISTS ai_ops_metrics_p ( id BIGSERIAL NOT NULL, metric_name VARCHAR(128) NOT NULL, labels JSONB NOT NULL DEFAULT '{}', value DOUBLE PRECISION NOT NULL, recorded_at TIMESTAMPTZ NOT NULL, PRIMARY KEY (id, recorded_at) ) PARTITION BY RANGE (recorded_at); -- 创建当前日分区 CREATE TABLE IF NOT EXISTS ai_ops_metrics_p_default PARTITION OF ai_ops_metrics_p DEFAULT; -- 启用 pg_partman 扩展后可自动管理分区(建议生产环境使用) -- SELECT partman.create_parent('public.ai_ops_metrics_p', 'recorded_at', 'native', 'daily'); -- 7. 配置版本管理(支持审计回滚) CREATE TABLE IF NOT EXISTS ai_ops_configs ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), object_type VARCHAR(64) NOT NULL, object_id VARCHAR(128) NOT NULL, config_data JSONB NOT NULL, version INT NOT NULL DEFAULT 1, created_by VARCHAR(64) NOT NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), CONSTRAINT uq_configs_object_version UNIQUE (object_type, object_id, version) ); CREATE INDEX IF NOT EXISTS idx_configs_object ON ai_ops_configs(object_type, object_id, created_at DESC); -- 8. 状态快照(自愈回滚用) CREATE TABLE IF NOT EXISTS ai_ops_snapshots ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), healing_id UUID NOT NULL REFERENCES ai_ops_healings(id) ON DELETE CASCADE, snapshot_type VARCHAR(32) NOT NULL CHECK (snapshot_type IN ('route', 'rate_limit', 'instance', 'script', 'node')), before_state JSONB NOT NULL, after_state JSONB NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE INDEX IF NOT EXISTS idx_snapshots_healing ON ai_ops_snapshots(healing_id);