-- ============================================================================= -- Migration: 2026-09-11-message-export-policy.sql -- Description: 为 chat_messages 新增正文类型、权威导出策略、版本与 SHA256 哈希 -- ============================================================================= -- 1. 结构变更:新增消息导出权威列 ALTER TABLE chat_messages ADD COLUMN IF NOT EXISTS content_kind TEXT NOT NULL DEFAULT '', ADD COLUMN IF NOT EXISTS export_policy TEXT NOT NULL DEFAULT 'none', ADD COLUMN IF NOT EXISTS content_revision INT NOT NULL DEFAULT 1, ADD COLUMN IF NOT EXISTS content_sha256 TEXT NOT NULL DEFAULT ''; -- 2. 导出策略枚举约束检查 (full | copy_only | none) DO $$ BEGIN IF NOT EXISTS ( SELECT 1 FROM pg_constraint WHERE conname = 'chk_chat_export_policy' ) THEN ALTER TABLE chat_messages ADD CONSTRAINT chk_chat_export_policy CHECK (export_policy IN ('full', 'copy_only', 'none')); END IF; END $$; -- 3. 索引优化:快速基于消息与版本校验 CREATE INDEX IF NOT EXISTS idx_chat_messages_export_policy ON chat_messages(id, export_policy); CREATE EXTENSION IF NOT EXISTS pgcrypto; -- 4. 存量消息安全保守回填:旧消息默认为 none,仅纯文本 assistant 消息标记为 copy_only UPDATE chat_messages SET content_kind = CASE WHEN role = 'user' THEN 'conversational_text' WHEN role = 'system' THEN 'system_notice' WHEN role = 'assistant' AND (metadata ? 'interactive_card') THEN 'action_card' WHEN role = 'assistant' AND length(trim(content)) > 0 THEN 'document_text' ELSE 'conversational_text' END, export_policy = CASE WHEN role = 'assistant' AND NOT (metadata ? 'interactive_card') AND length(trim(content)) >= 10 THEN 'full' WHEN role = 'assistant' AND length(trim(content)) > 0 THEN 'copy_only' ELSE 'none' END, content_revision = 1, content_sha256 = CASE WHEN length(content) > 0 THEN encode(sha256(convert_to(content, 'UTF8')), 'hex') ELSE '' END WHERE export_policy = 'none' AND role = 'assistant' AND length(trim(content)) > 0;