feat(export): 实现单次智能体回复导出、私密文档保存与隐私防泄露全套闭环 (Phase 0 - Phase 2)
- Phase 0: 实现服务端回复导出策略矩阵引擎 (replyexport.EvaluatePolicy)、数据库迁移 (2026-09-11-message-export-policy.sql)、SSE done 事件挂载与能力探测 API - Phase 1: 构建前端 ReplyDocumentIR 语法树解析与渲染管道,支持 Markdown / DOCX 文件下载及带格式富文本剪贴板复制 - Phase 2: 实现私密自由文档 (freeform_documents) 领域服务、PostgreSQL 乐观锁版本管理、ACL 隐私隔离、一键共享与右侧栏编辑联动 - 专项修复: 1. 修复后台 API 路由注册并加入双向拉取同步,确保私密保存实时进入我的私密列表 2. 实现轻量回复专属 DOCX 生成与静默下载逻辑 (downloadDocxReply) 3. 将 LaTeX 公式转为标准 MathML 矢量标记,支持粘贴到 Microsoft Word / WPS 自动识别为原生公式
This commit is contained in:
@@ -0,0 +1,54 @@
|
||||
-- 2026-09-11-freeform-documents.sql
|
||||
-- 支持单条回复“保存到我的私密”及自由文档(Freeform Document)独立生命周期管理
|
||||
|
||||
CREATE TABLE IF NOT EXISTS freeform_documents (
|
||||
doc_id VARCHAR(64) PRIMARY KEY,
|
||||
project_id INT NOT NULL,
|
||||
owner_user_id INT NOT NULL,
|
||||
source_message_id BIGINT REFERENCES chat_messages(id) ON DELETE SET NULL,
|
||||
source_content_sha256 VARCHAR(64) NOT NULL DEFAULT '',
|
||||
title VARCHAR(255) NOT NULL,
|
||||
governance_mode VARCHAR(32) NOT NULL DEFAULT 'freeform',
|
||||
space VARCHAR(32) NOT NULL DEFAULT 'my_private',
|
||||
current_version_id INT NOT NULL DEFAULT 1,
|
||||
current_sha256 VARCHAR(64) NOT NULL DEFAULT '',
|
||||
is_deleted BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
deleted_at TIMESTAMPTZ,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
CONSTRAINT chk_freeform_space CHECK (space IN ('my_private', 'project_shared', 'trash')),
|
||||
CONSTRAINT chk_freeform_gov_mode CHECK (governance_mode = 'freeform')
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS freeform_document_versions (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
doc_id VARCHAR(64) NOT NULL REFERENCES freeform_documents(doc_id) ON DELETE CASCADE,
|
||||
version_no INT NOT NULL,
|
||||
content_sha256 VARCHAR(64) NOT NULL,
|
||||
content_markdown TEXT NOT NULL,
|
||||
created_by INT NOT NULL,
|
||||
change_summary VARCHAR(255) NOT NULL DEFAULT '',
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
CONSTRAINT uq_freeform_doc_version UNIQUE (doc_id, version_no)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS document_access_audit (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
doc_id VARCHAR(64) NOT NULL,
|
||||
user_id INT NOT NULL,
|
||||
action VARCHAR(64) NOT NULL,
|
||||
success BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
ip_address VARCHAR(64) NOT NULL DEFAULT '',
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- 索引与检索加速
|
||||
CREATE INDEX IF NOT EXISTS idx_freeform_docs_owner_space ON freeform_documents (owner_user_id, space) WHERE is_deleted = FALSE;
|
||||
CREATE INDEX IF NOT EXISTS idx_freeform_docs_project_space ON freeform_documents (project_id, space) WHERE is_deleted = FALSE;
|
||||
CREATE INDEX IF NOT EXISTS idx_freeform_docs_source_msg ON freeform_documents (source_message_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_doc_access_audit_lookup ON document_access_audit (doc_id, user_id, created_at DESC);
|
||||
|
||||
-- 同一用户针对同一消息生成私密文档的幂等唯一索引
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS uq_freeform_user_source_message
|
||||
ON freeform_documents (owner_user_id, source_message_id)
|
||||
WHERE is_deleted = FALSE AND source_message_id IS NOT NULL;
|
||||
@@ -0,0 +1,51 @@
|
||||
-- =============================================================================
|
||||
-- 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;
|
||||
Reference in New Issue
Block a user