-- ============================================================ -- 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 ); 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 '逻辑删除'; -- 默认角色种子数据 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 张表及对应索引。 -- ============================================================