-- humanization 规则与样例的运行时权威表(专题-09 数据权威迁移)。 -- 设计拍板:数据库是运行时规则/样例权威;Git YAML 只作为迁移种子、离线夹具和结构合同。 -- 生产读取失败关闭:数据库不可用时不得静默回退 Git 文件资产(--offline 显式离线除外)。 -- 全部对象 IF NOT EXISTS / OR REPLACE,重复 apply 幂等。 -- ══ example_ai_flavor_rule:规则记录(一条规则一行;可变表,改动经 content_sha256 审计)══ CREATE TABLE IF NOT EXISTS example_ai_flavor_rule ( id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, rule_id VARCHAR(32) NOT NULL, name VARCHAR(128) NOT NULL, layer VARCHAR(20) NOT NULL, carrier_scope VARCHAR(32) NOT NULL, default_disposition VARCHAR(20) NOT NULL, status VARCHAR(20) NOT NULL, version INTEGER NOT NULL, trigger_json JSONB NOT NULL, carve_out JSONB NOT NULL DEFAULT '[]'::jsonb, function_check JSONB NOT NULL DEFAULT '[]'::jsonb, sample_refs JSONB NOT NULL, case_card_ids JSONB NOT NULL DEFAULT '[]'::jsonb, fix_hint TEXT NOT NULL DEFAULT '', evidence TEXT NOT NULL DEFAULT '', payload JSONB NOT NULL, content_sha256 CHAR(64) NOT NULL, creator VARCHAR(64) NOT NULL DEFAULT '1', create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, updater VARCHAR(64) NOT NULL DEFAULT '1', update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, deleted BOOLEAN NOT NULL DEFAULT FALSE, tenant_id BIGINT NOT NULL DEFAULT 1, CONSTRAINT uk_example_ai_flavor_rule UNIQUE (tenant_id, rule_id), CONSTRAINT chk_example_ai_flavor_rule_id CHECK (rule_id ~ '^[a-z]{1,4}[0-9]{3}$'), CONSTRAINT chk_example_ai_flavor_rule_layer CHECK (layer IN ('mechanical','lexical','structural','density','semantic')), CONSTRAINT chk_example_ai_flavor_rule_carrier CHECK (carrier_scope IN ('narration','dialogue','monologue','in_text_carrier','all')), CONSTRAINT chk_example_ai_flavor_rule_disposition CHECK (default_disposition IN ('blocking','candidate','advisory')), CONSTRAINT chk_example_ai_flavor_rule_status CHECK (status IN ('candidate','active','deprecated')), CONSTRAINT chk_example_ai_flavor_rule_version CHECK (version >= 1), CONSTRAINT chk_example_ai_flavor_rule_json CHECK ( jsonb_typeof(trigger_json) = 'object' AND jsonb_typeof(carve_out) = 'array' AND jsonb_typeof(function_check) = 'array' AND jsonb_typeof(sample_refs) = 'object' AND jsonb_typeof(case_card_ids) = 'array' AND jsonb_typeof(payload) = 'object' ), CONSTRAINT chk_example_ai_flavor_rule_sha CHECK (content_sha256 ~ '^[0-9a-f]{64}$') ); CREATE INDEX IF NOT EXISTS idx_example_ai_flavor_rule_status ON example_ai_flavor_rule(tenant_id, status) WHERE deleted = FALSE; CREATE OR REPLACE TRIGGER trg_example_ai_flavor_rule_updated_at BEFORE UPDATE ON example_ai_flavor_rule FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); -- ══ example_ai_flavor_sample:样例记录(一条样例一行;可变表)══ CREATE TABLE IF NOT EXISTS example_ai_flavor_sample ( id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, sample_id VARCHAR(64) NOT NULL, sample_type VARCHAR(20) NOT NULL, carrier VARCHAR(32) NOT NULL, source VARCHAR(32) NOT NULL, source_license VARCHAR(32) NOT NULL DEFAULT 'synthetic', rules JSONB NOT NULL DEFAULT '[]'::jsonb, text TEXT NOT NULL, note TEXT NOT NULL DEFAULT '', case_card_id VARCHAR(64), source_ref VARCHAR(512) NOT NULL DEFAULT '', payload JSONB NOT NULL, content_sha256 CHAR(64) NOT NULL, creator VARCHAR(64) NOT NULL DEFAULT '1', create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, updater VARCHAR(64) NOT NULL DEFAULT '1', update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, deleted BOOLEAN NOT NULL DEFAULT FALSE, tenant_id BIGINT NOT NULL DEFAULT 1, CONSTRAINT uk_example_ai_flavor_sample UNIQUE (tenant_id, sample_id), CONSTRAINT chk_example_ai_flavor_sample_id CHECK (sample_id ~ '^(sf|snf|b)-[a-z0-9]{3,7}-[0-9]{2,4}$|^reg-[a-z0-9]{3,7}(-[0-9]{2,4})?$'), CONSTRAINT chk_example_ai_flavor_sample_type CHECK (sample_type IN ('sf','snf','boundary','regression')), CONSTRAINT chk_example_ai_flavor_sample_carrier CHECK (carrier IN ('narration','dialogue','monologue','in_text_carrier','mixed')), CONSTRAINT chk_example_ai_flavor_sample_source CHECK (source IN ('hand_written','synthetic','public_domain','licensed')), CONSTRAINT chk_example_ai_flavor_sample_license CHECK (source_license IN ('owned','licensed','public_domain','synthetic')), CONSTRAINT chk_example_ai_flavor_sample_json CHECK ( jsonb_typeof(rules) = 'array' AND jsonb_typeof(payload) = 'object' ), CONSTRAINT chk_example_ai_flavor_sample_sha CHECK (content_sha256 ~ '^[0-9a-f]{64}$'), CONSTRAINT chk_example_ai_flavor_sample_card CHECK ( case_card_id IS NULL OR case_card_id ~ '^case-[a-z0-9]{20}$' ) ); CREATE INDEX IF NOT EXISTS idx_example_ai_flavor_sample_type ON example_ai_flavor_sample(tenant_id, sample_type) WHERE deleted = FALSE; CREATE OR REPLACE TRIGGER trg_example_ai_flavor_sample_updated_at BEFORE UPDATE ON example_ai_flavor_sample FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); -- ══ example_ai_flavor_rule_event:规则生命周期事件(append-only:同步/激活/停用留痕)══ CREATE TABLE IF NOT EXISTS example_ai_flavor_rule_event ( id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, rule_id VARCHAR(32) NOT NULL, event VARCHAR(20) NOT NULL, rule_version INTEGER NOT NULL, status VARCHAR(20) NOT NULL, approver VARCHAR(64) NOT NULL DEFAULT '', note TEXT NOT NULL DEFAULT '', report_sha256 CHAR(64), content_sha256 CHAR(64) NOT NULL, creator VARCHAR(64) NOT NULL DEFAULT '1', create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, tenant_id BIGINT NOT NULL DEFAULT 1, CONSTRAINT chk_example_ai_flavor_rule_event_rule CHECK (rule_id ~ '^[a-z]{1,4}[0-9]{3}$'), CONSTRAINT chk_example_ai_flavor_rule_event_event CHECK (event IN ('synced','activated','deprecated')), CONSTRAINT chk_example_ai_flavor_rule_event_status CHECK (status IN ('candidate','active','deprecated')), CONSTRAINT chk_example_ai_flavor_rule_event_sha CHECK ( content_sha256 ~ '^[0-9a-f]{64}$' AND (report_sha256 IS NULL OR report_sha256 ~ '^[0-9a-f]{64}$') ) ); CREATE INDEX IF NOT EXISTS idx_example_ai_flavor_rule_event_rule ON example_ai_flavor_rule_event(tenant_id, rule_id, id DESC); CREATE OR REPLACE FUNCTION reject_example_ai_flavor_rule_event_mutation() RETURNS TRIGGER LANGUAGE plpgsql AS $$ BEGIN RAISE EXCEPTION '% 是 append-only 表,禁止 UPDATE/DELETE', TG_TABLE_NAME; END; $$; CREATE OR REPLACE TRIGGER trg_example_ai_flavor_rule_event_append_only BEFORE UPDATE OR DELETE ON example_ai_flavor_rule_event FOR EACH ROW EXECUTE FUNCTION reject_example_ai_flavor_rule_event_mutation(); CREATE OR REPLACE TRIGGER trg_example_ai_flavor_rule_event_no_truncate BEFORE TRUNCATE ON example_ai_flavor_rule_event FOR EACH STATEMENT EXECUTE FUNCTION example_reject_truncate(); GRANT SELECT ON example_ai_flavor_rule TO muse_read; GRANT SELECT ON example_ai_flavor_sample TO muse_read; GRANT SELECT ON example_ai_flavor_rule_event TO muse_read;