Files
knowledge-governance/internal/platform/postgres/migrations/000002_source_governance.up.sql
T
zheromiku dcdc8d1ddd feat: 知识治理平台(含已验证发布/回滚契约)
全栈知识治理平台首次入库(P1-P10 + 发布链路模拟验证闭环):

- 治理核心:源目录/变更集/人工审核/发布批次/发布执行(fail-closed 闸口)
- 测试通道:WeKnora 测试 KB 上传/轮询/验收;poll 完成时写入
  parser_generation/index_generation,修复 manual/seed 路径发布闸口永久拦截
- 发布适配器 v071(本地 source=测试角色 / 云端 target=生产角色 模拟验证通过):
  * ES 导出/导入 mapping 自适应(.keyword 子字段、可选 tenant_id)
  * OSS locator 支持嵌套 scheme;chunks.seq_id 由目标序列分配
  * bundle 归档同身份重试原子替换;verifier 按 bundle chunk_type 计数
  * 回滚 job payload 支持 prior_execution_id(strict 解码)
- 三通道发布+回滚演练实测:publish 4/4 verified,检索命中,
  rollback 4/4 rolled_back,目标端 PG is_enabled=false、ES is_enabled=false
- 前端 A·Data-console 风格全站统一,120 用例通过
- 门禁:go vet / go test 全包通过,gofmt 干净
2026-09-30 10:54:06 +08:00

115 lines
5.2 KiB
SQL

CREATE TABLE IF NOT EXISTS source_connections (
id VARCHAR(36) PRIMARY KEY,
source_type VARCHAR(20) NOT NULL
CHECK (source_type IN ('pp', 'alist', 'dingtalk')),
name VARCHAR(255) NOT NULL,
base_url TEXT NOT NULL DEFAULT '',
secret_ref VARCHAR(255) NOT NULL DEFAULT '',
enabled BOOLEAN NOT NULL DEFAULT FALSE,
schedule JSONB NOT NULL DEFAULT '{}'::jsonb
CHECK (jsonb_typeof(schedule) = 'object'),
last_health JSONB NOT NULL DEFAULT '{}'::jsonb
CHECK (jsonb_typeof(last_health) = 'object'),
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
UNIQUE (name)
);
CREATE TABLE IF NOT EXISTS source_items (
id VARCHAR(36) PRIMARY KEY,
source_connection_id VARCHAR(36) NOT NULL
REFERENCES source_connections(id) ON DELETE RESTRICT,
stable_external_id TEXT NOT NULL,
canonical_uri_hash CHAR(64) NOT NULL
CHECK (canonical_uri_hash ~ '^[0-9a-f]{64}$'),
parent_external_id TEXT NOT NULL DEFAULT '',
normalized_path TEXT NOT NULL DEFAULT '',
title TEXT NOT NULL,
item_type VARCHAR(20) NOT NULL
CHECK (item_type IN ('document', 'file', 'link', 'video')),
size_bytes BIGINT CHECK (size_bytes IS NULL OR size_bytes >= 0),
mime TEXT NOT NULL DEFAULT '',
revision TEXT NOT NULL DEFAULT '',
updated_at_source TIMESTAMPTZ,
visibility VARCHAR(20) NOT NULL DEFAULT 'internal'
CHECK (visibility IN ('internal', 'external')),
visibility_origin VARCHAR(30) NOT NULL DEFAULT 'manual'
CHECK (visibility_origin IN ('pp_public_tag', 'configured_scope', 'manual')),
last_seen_scan_id VARCHAR(36) NOT NULL DEFAULT '',
source_change VARCHAR(20) NOT NULL DEFAULT 'unchanged'
CHECK (source_change IN ('unchanged', 'added', 'updated', 'deleted', 'unavailable')),
absence_streak INTEGER NOT NULL DEFAULT 0 CHECK (absence_streak >= 0),
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
UNIQUE (source_connection_id, stable_external_id)
);
CREATE TABLE IF NOT EXISTS content_objects (
id VARCHAR(36) PRIMARY KEY,
sha256 CHAR(64) NOT NULL
CHECK (sha256 ~ '^[0-9a-f]{64}$'),
size_bytes BIGINT NOT NULL CHECK (size_bytes >= 0),
mime TEXT NOT NULL DEFAULT '',
validated_format VARCHAR(50) NOT NULL DEFAULT '',
local_spool_ref TEXT NOT NULL DEFAULT '',
content_state VARCHAR(20) NOT NULL DEFAULT 'unknown'
CHECK (content_state IN ('unknown', 'downloading', 'verified', 'invalid', 'over_limit', 'failed')),
first_discovered_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
last_discovered_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
UNIQUE (sha256)
);
CREATE TABLE IF NOT EXISTS source_item_versions (
id VARCHAR(36) PRIMARY KEY,
source_item_id VARCHAR(36) NOT NULL
REFERENCES source_items(id) ON DELETE RESTRICT,
revision TEXT NOT NULL,
content_object_id VARCHAR(36)
REFERENCES content_objects(id) ON DELETE RESTRICT,
content_state VARCHAR(20) NOT NULL DEFAULT 'unknown'
CHECK (content_state IN ('unknown', 'downloading', 'verified', 'invalid', 'over_limit', 'failed')),
download_error TEXT NOT NULL DEFAULT '',
raw_record_summary JSONB NOT NULL DEFAULT '{}'::jsonb
CHECK (jsonb_typeof(raw_record_summary) = 'object'),
source_updated_at TIMESTAMPTZ,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
UNIQUE (source_item_id, revision)
);
CREATE TABLE IF NOT EXISTS test_publications (
id VARCHAR(36) PRIMARY KEY,
source_item_version_id VARCHAR(36) NOT NULL
REFERENCES source_item_versions(id) ON DELETE RESTRICT,
test_kb_id VARCHAR(255) NOT NULL,
knowledge_id VARCHAR(255) NOT NULL DEFAULT '',
ingest_state VARCHAR(20) NOT NULL DEFAULT 'pending'
CHECK (ingest_state IN ('pending', 'uploading', 'uploaded', 'failed')),
processing_state VARCHAR(20) NOT NULL DEFAULT 'pending'
CHECK (processing_state IN ('pending', 'processing', 'finalizing', 'completed', 'failed', 'stalled')),
retrieval_state VARCHAR(20) NOT NULL DEFAULT 'unpublished'
CHECK (retrieval_state IN ('unpublished', 'enabled', 'disabling', 'disabled', 'enabling', 'failed')),
parser_generation VARCHAR(255) NOT NULL DEFAULT '',
index_generation VARCHAR(255) NOT NULL DEFAULT '',
last_error TEXT NOT NULL DEFAULT '',
retry_count INTEGER NOT NULL DEFAULT 0 CHECK (retry_count >= 0),
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
UNIQUE (source_item_version_id, test_kb_id)
);
CREATE INDEX IF NOT EXISTS idx_source_items_change
ON source_items (source_connection_id, source_change, updated_at DESC);
CREATE INDEX IF NOT EXISTS idx_source_items_visibility
ON source_items (visibility, updated_at DESC);
CREATE INDEX IF NOT EXISTS idx_source_item_versions_content
ON source_item_versions (content_object_id)
WHERE content_object_id IS NOT NULL;
CREATE INDEX IF NOT EXISTS idx_test_publications_state
ON test_publications (processing_state, retrieval_state, updated_at DESC);