本地 RAG 知识库
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

507 lines
29 KiB

-- ============================================================
-- AI 智能客服系统 - 数据库初始化脚本(完整版)
-- 适用环境: PostgreSQL 12+ 且已安装 pgvector 扩展
-- 表数量: 14 张(基础 8 张 + P0 阶段新增 6 张)
--
-- ⚠️ 开发规范:任何涉及建表、增删改列、初始数据的变更,
-- 必须同时更新本脚本和 DatabaseInitConfig.java(自动初始化),
-- 保持两者一致,否则新部署环境会丢失变更。
--
-- 使用方法:
-- 方式一(psql 命令行):
-- psql -U postgres -f init-database.sql
-- 方式二(DataGrip / DBeaver / IntelliJ 等图形工具):
-- 先在 postgres 库中执行第 1 步(CREATE DATABASE),
-- 然后切换到 support_bot 库,注释掉第 1 步,执行剩余部分。
-- ============================================================
-- 1. 创建数据库(需在 postgres 默认库下执行)
-- 如果已在目标库中,注释掉下面这行
CREATE DATABASE support_bot ENCODING 'UTF8' LC_COLLATE 'en_US.UTF-8' LC_CTYPE 'en_US.UTF-8' TEMPLATE template0;
-- ============================================================
-- ★ 第 1 步结束 ★
-- Navicat / DataGrip / DBeaver 用户:请在此处停止,
-- 手动切换到 support_bot 数据库,再执行第 2 步及后续 SQL。
-- ============================================================
-- 2. 安装必要扩展
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
CREATE EXTENSION IF NOT EXISTS vector;
-- ============================================================
-- 表 1: chat_message — 聊天消息表
-- ============================================================
CREATE TABLE IF NOT EXISTS chat_message (
id BIGINT PRIMARY KEY,
conversation_id VARCHAR(64) NOT NULL,
message_type VARCHAR(20) NOT NULL,
content TEXT NOT NULL,
metadata JSONB NOT NULL DEFAULT '{}',
create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
is_delete BOOLEAN NOT NULL DEFAULT FALSE,
CONSTRAINT chk_message_type CHECK (message_type IN ('USER', 'ASSISTANT', 'SYSTEM'))
);
CREATE INDEX IF NOT EXISTS idx_chat_message_conversation_id ON chat_message (conversation_id);
CREATE INDEX IF NOT EXISTS idx_chat_message_create_time ON chat_message (create_time DESC);
CREATE INDEX IF NOT EXISTS idx_chat_message_type ON chat_message (message_type);
CREATE INDEX IF NOT EXISTS idx_chat_message_not_deleted ON chat_message (conversation_id) WHERE is_delete = FALSE;
COMMENT ON TABLE chat_message IS '聊天消息表';
COMMENT ON COLUMN chat_message.id IS '主键(雪花算法生成)';
COMMENT ON COLUMN chat_message.conversation_id IS '会话ID';
COMMENT ON COLUMN chat_message.message_type IS '消息类型: USER / ASSISTANT / SYSTEM';
COMMENT ON COLUMN chat_message.content IS '消息内容';
COMMENT ON COLUMN chat_message.metadata IS '元数据(JSON)';
COMMENT ON COLUMN chat_message.create_time IS '创建时间';
COMMENT ON COLUMN chat_message.update_time IS '更新时间';
COMMENT ON COLUMN chat_message.is_delete IS '逻辑删除: FALSE=正常 TRUE=已删除';
-- ============================================================
-- 表 2: knowledge_category — 知识库分类表
-- ============================================================
CREATE TABLE IF NOT EXISTS knowledge_category (
id BIGSERIAL PRIMARY KEY,
name VARCHAR(100) NOT NULL,
description TEXT,
parent_id BIGINT NOT NULL DEFAULT 0,
sort_order INTEGER NOT NULL DEFAULT 0,
document_count INTEGER NOT NULL DEFAULT 0,
create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
is_delete BOOLEAN NOT NULL DEFAULT FALSE
);
CREATE INDEX IF NOT EXISTS idx_knowledge_category_parent ON knowledge_category (parent_id);
COMMENT ON TABLE knowledge_category IS '知识库分类表';
COMMENT ON COLUMN knowledge_category.id IS '主键';
COMMENT ON COLUMN knowledge_category.name IS '分类名称';
COMMENT ON COLUMN knowledge_category.description IS '分类描述';
COMMENT ON COLUMN knowledge_category.parent_id IS '父分类ID(0 表示顶级分类)';
COMMENT ON COLUMN knowledge_category.sort_order IS '排序权重';
COMMENT ON COLUMN knowledge_category.document_count IS '关联文档数(冗余字段)';
COMMENT ON COLUMN knowledge_category.create_time IS '创建时间';
COMMENT ON COLUMN knowledge_category.is_delete IS '逻辑删除';
-- ============================================================
-- 表 3: knowledge_document — 知识文档表
-- ============================================================
CREATE TABLE IF NOT EXISTS knowledge_document (
id BIGSERIAL PRIMARY KEY,
title VARCHAR(500) NOT NULL,
source_name VARCHAR(500),
file_type VARCHAR(20) NOT NULL,
file_size BIGINT NOT NULL DEFAULT 0,
content TEXT,
category_id BIGINT NOT NULL DEFAULT 0,
tags JSONB NOT NULL DEFAULT '{}',
chunk_count INTEGER NOT NULL DEFAULT 0,
status VARCHAR(20) NOT NULL DEFAULT 'PROCESSING',
error_message TEXT,
content_hash VARCHAR(64),
create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
is_delete BOOLEAN NOT NULL DEFAULT FALSE
);
CREATE INDEX IF NOT EXISTS idx_knowledge_document_category ON knowledge_document (category_id);
CREATE INDEX IF NOT EXISTS idx_knowledge_document_status ON knowledge_document (status);
CREATE INDEX IF NOT EXISTS idx_knowledge_document_create_time ON knowledge_document (create_time DESC);
CREATE INDEX IF NOT EXISTS idx_knowledge_document_content_hash ON knowledge_document (content_hash);
COMMENT ON TABLE knowledge_document IS '知识文档表';
COMMENT ON COLUMN knowledge_document.id IS '主键';
COMMENT ON COLUMN knowledge_document.title IS '文档标题';
COMMENT ON COLUMN knowledge_document.source_name IS '原始文件名';
COMMENT ON COLUMN knowledge_document.file_type IS '文件类型';
COMMENT ON COLUMN knowledge_document.file_size IS '文件大小(字节)';
COMMENT ON COLUMN knowledge_document.content IS '原文内容(截断预览)';
COMMENT ON COLUMN knowledge_document.category_id IS '所属分类ID';
COMMENT ON COLUMN knowledge_document.tags IS '标签(JSON)';
COMMENT ON COLUMN knowledge_document.chunk_count IS '分块数量';
COMMENT ON COLUMN knowledge_document.status IS '状态: PROCESSING / READY / FAILED';
COMMENT ON COLUMN knowledge_document.error_message IS '处理失败时的错误信息';
COMMENT ON COLUMN knowledge_document.content_hash IS '内容SHA-256哈希(用于去重)';
COMMENT ON COLUMN knowledge_document.create_time IS '创建时间';
COMMENT ON COLUMN knowledge_document.update_time IS '更新时间';
COMMENT ON COLUMN knowledge_document.is_delete IS '逻辑删除';
-- ============================================================
-- 表 4: customer_service_role — 客服角色表
-- ============================================================
CREATE TABLE IF NOT EXISTS customer_service_role (
id BIGSERIAL PRIMARY KEY,
role_key VARCHAR(64) NOT NULL UNIQUE,
name VARCHAR(100) NOT NULL,
description TEXT,
prompt TEXT,
sort_order INTEGER NOT NULL DEFAULT 0,
enabled BOOLEAN NOT NULL DEFAULT TRUE,
create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
is_delete BOOLEAN NOT NULL DEFAULT FALSE,
allowed_mcp_tools JSONB NOT NULL DEFAULT '[]'
);
CREATE INDEX IF NOT EXISTS idx_customer_service_role_enabled ON customer_service_role (enabled, sort_order);
COMMENT ON TABLE customer_service_role IS '客服角色表';
COMMENT ON COLUMN customer_service_role.id IS '主键';
COMMENT ON COLUMN customer_service_role.role_key IS '角色标识符(唯一)';
COMMENT ON COLUMN customer_service_role.name IS '角色名称';
COMMENT ON COLUMN customer_service_role.description IS '角色描述';
COMMENT ON COLUMN customer_service_role.prompt IS '系统提示词';
COMMENT ON COLUMN customer_service_role.sort_order IS '排序权重';
COMMENT ON COLUMN customer_service_role.enabled IS '是否启用';
COMMENT ON COLUMN customer_service_role.create_time IS '创建时间';
COMMENT ON COLUMN customer_service_role.update_time IS '更新时间';
COMMENT ON COLUMN customer_service_role.is_delete IS '逻辑删除';
COMMENT ON COLUMN customer_service_role.allowed_mcp_tools IS '允许使用的 MCP 工具列表(JSONB 数组,空=不允许,["*"]=全部允许)';
-- 默认角色种子数据
INSERT INTO customer_service_role (role_key, name, description, sort_order, enabled)
VALUES ('general', '客服', '通用客服角色', 0, TRUE)
ON CONFLICT (role_key) DO NOTHING;
INSERT INTO customer_service_role (role_key, name, description, sort_order, enabled)
VALUES ('finance', '财务', '财务相关客服角色', 1, TRUE)
ON CONFLICT (role_key) DO NOTHING;
INSERT INTO customer_service_role (role_key, name, description, sort_order, enabled)
VALUES ('administration', '行政', '行政相关客服角色', 2, TRUE)
ON CONFLICT (role_key) DO NOTHING;
-- ============================================================
-- 表 5: customer_service_role_category — 角色知识库关联表
-- ============================================================
CREATE TABLE IF NOT EXISTS customer_service_role_category (
id BIGSERIAL PRIMARY KEY,
role_id BIGINT NOT NULL,
category_id BIGINT NOT NULL,
create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
is_delete BOOLEAN NOT NULL DEFAULT FALSE,
CONSTRAINT uq_role_category UNIQUE (role_id, category_id)
);
CREATE INDEX IF NOT EXISTS idx_customer_service_role_category_role ON customer_service_role_category (role_id);
CREATE INDEX IF NOT EXISTS idx_customer_service_role_category_category ON customer_service_role_category (category_id);
COMMENT ON TABLE customer_service_role_category IS '客服角色知识库关联表';
COMMENT ON COLUMN customer_service_role_category.id IS '主键';
COMMENT ON COLUMN customer_service_role_category.role_id IS '角色ID';
COMMENT ON COLUMN customer_service_role_category.category_id IS '分类ID';
COMMENT ON COLUMN customer_service_role_category.create_time IS '创建时间';
COMMENT ON COLUMN customer_service_role_category.is_delete IS '逻辑删除';
-- ============================================================
-- 表 6: customer_account — 客服账号表
-- ============================================================
CREATE TABLE IF NOT EXISTS customer_account (
id BIGSERIAL PRIMARY KEY,
account_key VARCHAR(64) NOT NULL UNIQUE,
name VARCHAR(100) NOT NULL,
description TEXT,
role_id BIGINT,
enabled BOOLEAN NOT NULL DEFAULT TRUE,
create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
is_delete BOOLEAN NOT NULL DEFAULT FALSE
);
CREATE INDEX IF NOT EXISTS idx_customer_account_role ON customer_account (role_id);
CREATE INDEX IF NOT EXISTS idx_customer_account_enabled ON customer_account (enabled, id);
COMMENT ON TABLE customer_account IS '客服账号表';
COMMENT ON COLUMN customer_account.id IS '主键';
COMMENT ON COLUMN customer_account.account_key IS '账号标识符(唯一)';
COMMENT ON COLUMN customer_account.name IS '账号名称';
COMMENT ON COLUMN customer_account.description IS '账号描述';
COMMENT ON COLUMN customer_account.role_id IS '关联角色ID';
COMMENT ON COLUMN customer_account.enabled IS '是否启用';
COMMENT ON COLUMN customer_account.create_time IS '创建时间';
COMMENT ON COLUMN customer_account.update_time IS '更新时间';
COMMENT ON COLUMN customer_account.is_delete IS '逻辑删除';
-- 默认账号种子数据(引用角色表的 ID)
INSERT INTO customer_account (account_key, name, description, role_id, enabled)
SELECT 'service', '客服账号', '通用客服账号', id, TRUE
FROM customer_service_role WHERE role_key = 'general'
ON CONFLICT (account_key) DO NOTHING;
INSERT INTO customer_account (account_key, name, description, role_id, enabled)
SELECT 'finance', '财务账号', '财务客服账号', id, TRUE
FROM customer_service_role WHERE role_key = 'finance'
ON CONFLICT (account_key) DO NOTHING;
INSERT INTO customer_account (account_key, name, description, role_id, enabled)
SELECT 'administration', '行政账号', '行政客服账号', id, TRUE
FROM customer_service_role WHERE role_key = 'administration'
ON CONFLICT (account_key) DO NOTHING;
-- ============================================================
-- 表 7: conversation_session — 会话归属表
-- ============================================================
CREATE TABLE IF NOT EXISTS conversation_session (
conversation_id VARCHAR(64) PRIMARY KEY,
account_id BIGINT,
external_account_id VARCHAR(128),
role_id BIGINT,
create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX IF NOT EXISTS idx_conversation_session_account ON conversation_session (account_id);
CREATE INDEX IF NOT EXISTS idx_conversation_session_external_account ON conversation_session (external_account_id);
CREATE INDEX IF NOT EXISTS idx_conversation_session_role ON conversation_session (role_id);
COMMENT ON TABLE conversation_session IS '会话归属表';
COMMENT ON COLUMN conversation_session.conversation_id IS '会话ID(主键)';
COMMENT ON COLUMN conversation_session.account_id IS '归属账号ID';
COMMENT ON COLUMN conversation_session.role_id IS '归属角色ID';
COMMENT ON COLUMN conversation_session.create_time IS '创建时间';
COMMENT ON COLUMN conversation_session.update_time IS '更新时间';
-- ============================================================
-- 表 8: vector_store — 向量存储表
-- 注意: Spring AI PgVectorStore 配置了 initializeSchema(true),
-- 应用启动时会自动建表。此处手动建表作为备份方案。
-- 向量维度默认 1024,如使用不同维度的 Embedding 模型
-- 需修改下面的 VECTOR(1024) 并重建。
-- ============================================================
CREATE TABLE IF NOT EXISTS vector_store (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
content TEXT NOT NULL,
metadata JSONB NOT NULL DEFAULT '{}',
embedding VECTOR(1024) NOT NULL,
create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
-- HNSW 向量索引(余弦距离)
CREATE INDEX IF NOT EXISTS idx_vector_store_embedding ON vector_store
USING hnsw (embedding vector_cosine_ops) WITH (m = 16, ef_construction = 64);
CREATE INDEX IF NOT EXISTS idx_vector_store_create_time ON vector_store (create_time DESC);
-- JSONB 元数据 GIN 索引
CREATE INDEX IF NOT EXISTS idx_vector_store_metadata ON vector_store USING gin (metadata);
COMMENT ON TABLE vector_store IS '向量存储表(RAG 知识库向量化数据)';
COMMENT ON COLUMN vector_store.id IS '主键(UUID)';
COMMENT ON COLUMN vector_store.content IS '文档内容';
COMMENT ON COLUMN vector_store.metadata IS '元数据(JSON)';
COMMENT ON COLUMN vector_store.embedding IS '向量嵌入(维度 1024)';
COMMENT ON COLUMN vector_store.create_time IS '创建时间';
COMMENT ON COLUMN vector_store.update_time IS '更新时间';
-- 全文检索列(混合检索 KEYWORD / HYBRID 模式使用)
ALTER TABLE vector_store ADD COLUMN IF NOT EXISTS content_tsvector tsvector;
CREATE INDEX IF NOT EXISTS idx_vector_store_tsvector ON vector_store USING gin(content_tsvector);
COMMENT ON COLUMN vector_store.content_tsvector IS '全文检索向量列(tsvector,由触发器自动维护)';
-- 全文检索触发器函数
CREATE OR REPLACE FUNCTION update_content_tsvector() RETURNS trigger AS $$
BEGIN
NEW.content_tsvector := to_tsvector('simple', coalesce(NEW.content, ''));
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
DROP TRIGGER IF EXISTS trg_update_content_tsvector ON vector_store;
CREATE TRIGGER trg_update_content_tsvector
BEFORE INSERT OR UPDATE ON vector_store
FOR EACH ROW EXECUTE FUNCTION update_content_tsvector();
-- ============================================================
-- 表 9: ai_model_config — AI 大模型配置表
-- ============================================================
CREATE TABLE IF NOT EXISTS ai_model_config (
id BIGINT PRIMARY KEY,
name VARCHAR(100) NOT NULL,
app_type VARCHAR(32) NOT NULL,
provider VARCHAR(64) NOT NULL,
api_key VARCHAR(256),
model_name VARCHAR(128) NOT NULL,
temperature DOUBLE PRECISION DEFAULT 0.7,
max_tokens INTEGER DEFAULT 2048,
base_url VARCHAR(256),
extra_config JSONB DEFAULT '{}' NOT NULL,
is_active BOOLEAN DEFAULT FALSE NOT NULL,
priority INTEGER DEFAULT 0 NOT NULL,
description VARCHAR(500),
create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
is_delete BOOLEAN NOT NULL DEFAULT FALSE
);
CREATE INDEX IF NOT EXISTS idx_ai_model_config_app_type ON ai_model_config (app_type);
CREATE INDEX IF NOT EXISTS idx_ai_model_config_active ON ai_model_config (is_active) WHERE is_delete = FALSE;
COMMENT ON TABLE ai_model_config IS 'AI 大模型配置表(管理多套模型配置,按应用类型绑定)';
COMMENT ON COLUMN ai_model_config.id IS '主键(雪花算法生成)';
COMMENT ON COLUMN ai_model_config.name IS '配置名称';
COMMENT ON COLUMN ai_model_config.app_type IS '应用类型: CHAT / EMBEDDING / RAG_REWRITE / RERANK';
COMMENT ON COLUMN ai_model_config.provider IS '模型提供商: dashscope / openai / deepseek / moonshot / zhipu / volcengine 等';
COMMENT ON COLUMN ai_model_config.api_key IS 'API Key(数据库加密存储,前端脱敏展示)';
COMMENT ON COLUMN ai_model_config.model_name IS '模型名称(如 qwen-turbo、gpt-4o)';
COMMENT ON COLUMN ai_model_config.temperature IS '温度参数(控制输出随机性,0.0~2.0)';
COMMENT ON COLUMN ai_model_config.max_tokens IS '最大 Token 数(单次生成上限)';
COMMENT ON COLUMN ai_model_config.base_url IS 'API 基础地址(可选,用于私有化部署或第三方厂商)';
COMMENT ON COLUMN ai_model_config.extra_config IS '扩展配置(JSONB,存储 topP、dimensions 等自定义参数)';
COMMENT ON COLUMN ai_model_config.is_active IS '是否激活(每种 App 类型只能有一个激活配置)';
COMMENT ON COLUMN ai_model_config.priority IS '优先级(数值越大越优先,多套配置时生效)';
COMMENT ON COLUMN ai_model_config.description IS '配置描述说明';
COMMENT ON COLUMN ai_model_config.create_time IS '创建时间';
COMMENT ON COLUMN ai_model_config.update_time IS '更新时间';
COMMENT ON COLUMN ai_model_config.is_delete IS '逻辑删除: FALSE=正常 TRUE=已删除';
-- ============================================================
-- 表 10: sensitive_word — 敏感词表(P0-004 内容安全过滤)
-- ============================================================
CREATE TABLE IF NOT EXISTS sensitive_word (
id BIGSERIAL PRIMARY KEY,
word VARCHAR(256) NOT NULL,
category VARCHAR(64) NOT NULL DEFAULT 'custom',
level INTEGER NOT NULL DEFAULT 1,
is_active BOOLEAN NOT NULL DEFAULT TRUE,
remark VARCHAR(512),
create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
is_delete BOOLEAN NOT NULL DEFAULT FALSE
);
-- 唯一索引:同一分类下敏感词不重复(仅对未删除记录生效)
CREATE UNIQUE INDEX IF NOT EXISTS uk_sensitive_word_word_category ON sensitive_word (word, category) WHERE is_delete = FALSE;
CREATE INDEX IF NOT EXISTS idx_sensitive_word_active ON sensitive_word (is_active) WHERE is_delete = FALSE;
COMMENT ON TABLE sensitive_word IS '敏感词表(DFA 引擎驱动的内容安全过滤)';
COMMENT ON COLUMN sensitive_word.id IS '主键(雪花算法生成)';
COMMENT ON COLUMN sensitive_word.word IS '敏感词内容';
COMMENT ON COLUMN sensitive_word.category IS '分类: politics / porn / abuse / custom,默认 custom';
COMMENT ON COLUMN sensitive_word.level IS '级别: 1=脱敏(MASK) / 2=拦截(BLOCK)';
COMMENT ON COLUMN sensitive_word.is_active IS '是否启用';
COMMENT ON COLUMN sensitive_word.remark IS '备注说明';
COMMENT ON COLUMN sensitive_word.create_time IS '创建时间';
COMMENT ON COLUMN sensitive_word.update_time IS '更新时间';
COMMENT ON COLUMN sensitive_word.is_delete IS '逻辑删除: FALSE=正常 TRUE=已删除';
-- ============================================================
-- 表 11: content_audit_log — 内容审计日志表(P0-004,只追加不删除)
-- ============================================================
CREATE TABLE IF NOT EXISTS content_audit_log (
id BIGSERIAL PRIMARY KEY,
session_id VARCHAR(64),
direction VARCHAR(16) NOT NULL,
original_text TEXT NOT NULL,
hit_words JSONB NOT NULL DEFAULT '[]',
action_taken VARCHAR(32) NOT NULL,
create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX IF NOT EXISTS idx_audit_log_created ON content_audit_log (create_time DESC);
CREATE INDEX IF NOT EXISTS idx_audit_log_session ON content_audit_log (session_id);
COMMENT ON TABLE content_audit_log IS '内容审计日志表(记录所有敏感词命中事件,只追加不删除)';
COMMENT ON COLUMN content_audit_log.id IS '主键(雪花算法生成)';
COMMENT ON COLUMN content_audit_log.session_id IS '会话ID(关联 conversation_session)';
COMMENT ON COLUMN content_audit_log.direction IS '检测方向: INPUT(用户输入) / OUTPUT(AI 输出)';
COMMENT ON COLUMN content_audit_log.original_text IS '原始违规内容(截断至前 50 字)';
COMMENT ON COLUMN content_audit_log.hit_words IS '命中词列表(JSON 数组,记录触发的敏感词)';
COMMENT ON COLUMN content_audit_log.action_taken IS '采取的动作: PASS(放行) / MASK(脱敏) / BLOCK(拦截)';
COMMENT ON COLUMN content_audit_log.create_time IS '创建时间(事件发生时间)';
-- ============================================================
-- 表 12: message_feedback — 消息反馈表(P0-002 用户反馈系统)
-- ============================================================
CREATE TABLE IF NOT EXISTS message_feedback (
id BIGSERIAL PRIMARY KEY,
message_id VARCHAR(128) NOT NULL,
conversation_id VARCHAR(64) NOT NULL,
feedback_type VARCHAR(16) NOT NULL,
reason_category VARCHAR(64),
reason_comment TEXT,
create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
is_delete BOOLEAN NOT NULL DEFAULT FALSE
);
-- 唯一索引:每条消息只能有一条反馈(重复提交覆盖,upsert 语义)
CREATE UNIQUE INDEX IF NOT EXISTS uk_message_feedback_message ON message_feedback (message_id) WHERE is_delete = FALSE;
CREATE INDEX IF NOT EXISTS idx_feedback_conversation ON message_feedback (conversation_id);
CREATE INDEX IF NOT EXISTS idx_feedback_type ON message_feedback (feedback_type) WHERE is_delete = FALSE;
CREATE INDEX IF NOT EXISTS idx_feedback_created ON message_feedback (create_time DESC);
COMMENT ON TABLE message_feedback IS '消息反馈表(用户对 AI 回复的点赞/点踩反馈)';
COMMENT ON COLUMN message_feedback.id IS '主键(雪花算法生成)';
COMMENT ON COLUMN message_feedback.message_id IS 'AI 消息ID(前端 msgId 或 UUID,唯一索引字段)';
COMMENT ON COLUMN message_feedback.conversation_id IS '会话ID(关联 chat_message)';
COMMENT ON COLUMN message_feedback.feedback_type IS '反馈类型: THUMBS_UP(有帮助) / THUMBS_DOWN(没帮助)';
COMMENT ON COLUMN message_feedback.reason_category IS '点踩原因分类: inaccurate / irrelevant / incomplete / other(仅 THUMBS_DOWN 时可选)';
COMMENT ON COLUMN message_feedback.reason_comment IS '自由文本补充说明(可选)';
COMMENT ON COLUMN message_feedback.create_time IS '创建时间';
COMMENT ON COLUMN message_feedback.update_time IS '更新时间(重复提交时覆盖)';
COMMENT ON COLUMN message_feedback.is_delete IS '逻辑删除: FALSE=正常 TRUE=已删除';
-- ============================================================
-- 表 13: knowledge_faq — FAQ 知识库表(P0-003 意图识别 + FAQ 精准匹配)
-- ============================================================
CREATE TABLE IF NOT EXISTS knowledge_faq (
id BIGSERIAL PRIMARY KEY,
question TEXT NOT NULL,
answer TEXT NOT NULL,
similar_questions TEXT NOT NULL DEFAULT '[]',
category VARCHAR(128),
status VARCHAR(20) NOT NULL DEFAULT 'ENABLED',
priority INTEGER NOT NULL DEFAULT 0,
hit_count BIGINT NOT NULL DEFAULT 0,
source VARCHAR(64) DEFAULT 'manual',
create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
is_delete BOOLEAN NOT NULL DEFAULT FALSE
);
CREATE INDEX IF NOT EXISTS idx_faq_status ON knowledge_faq (status) WHERE is_delete = FALSE;
CREATE INDEX IF NOT EXISTS idx_faq_category ON knowledge_faq (category);
CREATE INDEX IF NOT EXISTS idx_faq_priority ON knowledge_faq (priority DESC);
COMMENT ON TABLE knowledge_faq IS 'FAQ 知识库表(用于意图路由后的精准问答匹配)';
COMMENT ON COLUMN knowledge_faq.id IS '主键(雪花算法生成)';
COMMENT ON COLUMN knowledge_faq.question IS '标准问题';
COMMENT ON COLUMN knowledge_faq.answer IS '标准答案';
COMMENT ON COLUMN knowledge_faq.similar_questions IS '相似问题列表(JSON 字符串数组,用于扩展匹配范围)';
COMMENT ON COLUMN knowledge_faq.category IS 'FAQ 分类';
COMMENT ON COLUMN knowledge_faq.status IS '状态: ENABLED(启用) / DISABLED(禁用)';
COMMENT ON COLUMN knowledge_faq.priority IS '优先级(数值越大越优先匹配,默认 0)';
COMMENT ON COLUMN knowledge_faq.hit_count IS '命中次数统计(用于分析高频问题)';
COMMENT ON COLUMN knowledge_faq.source IS '来源: manual(手动录入) / import(批量导入)';
COMMENT ON COLUMN knowledge_faq.create_time IS '创建时间';
COMMENT ON COLUMN knowledge_faq.update_time IS '更新时间';
COMMENT ON COLUMN knowledge_faq.is_delete IS '逻辑删除: FALSE=正常 TRUE=已删除';
-- ============================================================
-- 表 14: faq_embedding — FAQ 向量索引表(P0-003,FAQ 语义匹配用)
-- ============================================================
CREATE TABLE IF NOT EXISTS faq_embedding (
id BIGSERIAL PRIMARY KEY,
faq_id BIGINT NOT NULL,
embedding vector(1024) NOT NULL,
model_name VARCHAR(64) NOT NULL,
create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX IF NOT EXISTS idx_faq_emb_faq_id ON faq_embedding (faq_id);
COMMENT ON TABLE faq_embedding IS 'FAQ 向量索引表(存储 FAQ 的语义向量,用于相似问题语义匹配)';
COMMENT ON COLUMN faq_embedding.id IS '主键';
COMMENT ON COLUMN faq_embedding.faq_id IS '关联 FAQ ID(对应 knowledge_faq.id)';
COMMENT ON COLUMN faq_embedding.embedding IS '向量嵌入(维度由 knowledge.vector.dimension 配置,默认 1024)';
COMMENT ON COLUMN faq_embedding.model_name IS '使用的 Embedding 模型名称(用于检测模型变更后重新计算)';
COMMENT ON COLUMN faq_embedding.create_time IS '创建时间';
-- ============================================================
-- 完成!
-- 共创建 14 张表及对应索引。
-- ============================================================