From 897b86db26643db0671870264d7ee945e891cd94 Mon Sep 17 00:00:00 2001 From: hailin Date: Fri, 6 Feb 2026 22:33:41 -0800 Subject: [PATCH] fix(database): sync init.sql schema with all migrations for 100% consistency MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit init.sql 作为全新部署的基础 schema,必须与所有 migration 的累积效果一致。 之前存在大量不一致: 1. 所有表缺少 tenant_id 列(migration 20260126 添加): - users, conversations, messages, orders, payments, ledger_entries - daily_statistics, monthly_financial_reports, audit_logs, evolution_logs - documents, document_embeddings, knowledge_articles, knowledge_chunks - user_memories, experiences, verification_codes, service_pricing - coupons, user_coupons, admin_users, admins - 每个表同步添加了 idx_xxx_tenant 索引 2. admins 表缺少 is_super_admin 列(migration 20260126 添加) 3. 缺少 4 个完整表定义(来自 migration 和 TypeORM entity): - token_usages: API 调用 Token 消耗记录 - files: 用户上传文件管理(含软删除) - mcp_server_configs: MCP Server 运行时配置(migration 20260206) - evaluation_rules: 评估门控规则配置(migration 20260206) 现在 init.sql(全新部署)= init.sql + 所有 migrations(增量升级)= 完全一致 Co-Authored-By: Claude Opus 4.6 --- .../docker/services/postgres/init.sql | 256 ++++++++++++++++++ 1 file changed, 256 insertions(+) diff --git a/infrastructure/docker/services/postgres/init.sql b/infrastructure/docker/services/postgres/init.sql index cf7f7d8..881068d 100644 --- a/infrastructure/docker/services/postgres/init.sql +++ b/infrastructure/docker/services/postgres/init.sql @@ -13,6 +13,8 @@ CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; -- UUID生成函数 -- =========================================== CREATE TABLE users ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + -- 租户ID(多租户支持) + tenant_id UUID, -- 用户类型: ANONYMOUS(匿名访客), REGISTERED(已注册用户) type VARCHAR(20) NOT NULL DEFAULT 'ANONYMOUS' CHECK (type IN ('ANONYMOUS', 'REGISTERED')), @@ -48,6 +50,9 @@ COMMENT ON COLUMN users.fingerprint IS '浏览器指纹,用于识别和关联 COMMENT ON COLUMN users.source_channel IS '用户来源渠道,用于统计分析'; COMMENT ON COLUMN users.tags IS '用户标签,JSON数组格式,用于用户分群'; +CREATE INDEX idx_users_tenant ON users(tenant_id); +CREATE INDEX idx_users_tenant_phone ON users(tenant_id, phone); +CREATE INDEX idx_users_tenant_fingerprint ON users(tenant_id, fingerprint); CREATE INDEX idx_users_fingerprint ON users(fingerprint); CREATE INDEX idx_users_phone ON users(phone); CREATE INDEX idx_users_type ON users(type); @@ -60,6 +65,8 @@ CREATE INDEX idx_users_created_at ON users(created_at); -- =========================================== CREATE TABLE conversations ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + -- 租户ID(多租户支持) + tenant_id UUID, -- 所属用户ID user_id UUID REFERENCES users(id) ON DELETE CASCADE, -- 对话状态: ACTIVE(进行中), ENDED(已结束), ARCHIVED(已归档), DELETED(软删除) @@ -99,6 +106,7 @@ COMMENT ON COLUMN conversations.category IS '主要咨询的移民类别,用 COMMENT ON COLUMN conversations.has_converted IS '是否产生付费转化,用于计算转化率'; COMMENT ON COLUMN conversations.rating IS '用户对对话的评分,1-5分'; +CREATE INDEX idx_conversations_tenant ON conversations(tenant_id); CREATE INDEX idx_conversations_user_id ON conversations(user_id); CREATE INDEX idx_conversations_status ON conversations(status); CREATE INDEX idx_conversations_category ON conversations(category); @@ -111,6 +119,8 @@ CREATE INDEX idx_conversations_has_converted ON conversations(has_converted) WHE -- =========================================== CREATE TABLE messages ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + -- 租户ID(多租户支持) + tenant_id UUID, -- 所属对话ID conversation_id UUID REFERENCES conversations(id) ON DELETE CASCADE, -- 消息角色: user(用户), assistant(AI助手), system(系统) @@ -134,6 +144,7 @@ COMMENT ON COLUMN messages.role IS '消息角色: user=用户发送, assistant=A COMMENT ON COLUMN messages.type IS '消息类型,用于区分普通文本、工具调用、支付请求等'; COMMENT ON COLUMN messages.metadata IS '元数据,存储工具调用参数、结果等扩展信息'; +CREATE INDEX idx_messages_tenant ON messages(tenant_id); CREATE INDEX idx_messages_conversation_id ON messages(conversation_id); CREATE INDEX idx_messages_role ON messages(role); CREATE INDEX idx_messages_created_at ON messages(created_at); @@ -144,6 +155,8 @@ CREATE INDEX idx_messages_created_at ON messages(created_at); -- =========================================== CREATE TABLE orders ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + -- 租户ID(多租户支持) + tenant_id UUID, -- 订单号(用于展示,格式:ORD + 年月日 + 序号) order_no VARCHAR(50) UNIQUE, -- 所属用户ID @@ -199,6 +212,7 @@ COMMENT ON COLUMN orders.order_no IS '订单号,用于对外展示,格式: COMMENT ON COLUMN orders.service_type IS '服务类型: ASSESSMENT=移民评估, CONSULTATION=付费咨询, DOCUMENT_REVIEW=文档审核'; COMMENT ON COLUMN orders.metadata IS '扩展数据,如评估结果详情等'; +CREATE INDEX idx_orders_tenant ON orders(tenant_id); CREATE INDEX idx_orders_order_no ON orders(order_no); CREATE INDEX idx_orders_user_id ON orders(user_id); CREATE INDEX idx_orders_status ON orders(status); @@ -212,6 +226,8 @@ CREATE INDEX idx_orders_paid_at ON orders(paid_at) WHERE paid_at IS NOT NULL; -- =========================================== CREATE TABLE payments ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + -- 租户ID(多租户支持) + tenant_id UUID, -- 关联的订单ID order_id UUID REFERENCES orders(id) ON DELETE CASCADE, -- 支付方式 @@ -247,6 +263,7 @@ COMMENT ON TABLE payments IS '支付表 - 存储支付交易记录'; COMMENT ON COLUMN payments.transaction_id IS '第三方支付平台的交易号,用于对账'; COMMENT ON COLUMN payments.callback_payload IS '支付回调的原始数据,用于问题排查和对账'; +CREATE INDEX idx_payments_tenant ON payments(tenant_id); CREATE INDEX idx_payments_order_id ON payments(order_id); CREATE INDEX idx_payments_status ON payments(status); CREATE INDEX idx_payments_method ON payments(method); @@ -259,6 +276,8 @@ CREATE INDEX idx_payments_created_at ON payments(created_at); -- =========================================== CREATE TABLE ledger_entries ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + -- 租户ID(多租户支持) + tenant_id UUID, -- 流水号(唯一标识) entry_no VARCHAR(50) UNIQUE NOT NULL, -- 流水类型 @@ -309,6 +328,7 @@ COMMENT ON COLUMN ledger_entries.entry_type IS '流水类型: INCOME=收入, REF COMMENT ON COLUMN ledger_entries.balance_after IS '该笔流水后的账户余额快照,用于对账'; COMMENT ON COLUMN ledger_entries.accounting_period IS '会计期间,格式YYYY-MM,用于月度报表'; +CREATE INDEX idx_ledger_entries_tenant ON ledger_entries(tenant_id); CREATE INDEX idx_ledger_entries_entry_no ON ledger_entries(entry_no); CREATE INDEX idx_ledger_entries_entry_type ON ledger_entries(entry_type); CREATE INDEX idx_ledger_entries_order_id ON ledger_entries(order_id); @@ -324,6 +344,8 @@ CREATE INDEX idx_ledger_entries_business_type ON ledger_entries(business_type); -- =========================================== CREATE TABLE daily_statistics ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + -- 租户ID(多租户支持) + tenant_id UUID, -- 统计日期 stat_date DATE NOT NULL, -- 统计维度: OVERALL(总体), CHANNEL(渠道), CATEGORY(移民类别) @@ -393,6 +415,7 @@ COMMENT ON COLUMN daily_statistics.dimension IS '统计维度: OVERALL=总体统 COMMENT ON COLUMN daily_statistics.conversion_rate IS '转化率 = 支付订单数 / 新对话数'; COMMENT ON COLUMN daily_statistics.estimated_api_cost IS '预估Claude API成本,基于Token消耗计算'; +CREATE INDEX idx_daily_statistics_tenant ON daily_statistics(tenant_id); CREATE INDEX idx_daily_statistics_stat_date ON daily_statistics(stat_date DESC); CREATE INDEX idx_daily_statistics_dimension ON daily_statistics(dimension, dimension_value); @@ -402,6 +425,8 @@ CREATE INDEX idx_daily_statistics_dimension ON daily_statistics(dimension, dimen -- =========================================== CREATE TABLE monthly_financial_reports ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + -- 租户ID(多租户支持) + tenant_id UUID, -- 报表月份(格式:YYYY-MM) report_month VARCHAR(7) NOT NULL UNIQUE, @@ -472,6 +497,7 @@ COMMENT ON COLUMN monthly_financial_reports.report_month IS '报表月份,格 COMMENT ON COLUMN monthly_financial_reports.revenue_by_category IS '按移民类别的收入明细,JSON格式'; COMMENT ON COLUMN monthly_financial_reports.status IS 'DRAFT=草稿可修改, CONFIRMED=已确认, LOCKED=已锁定不可修改'; +CREATE INDEX idx_monthly_financial_reports_tenant ON monthly_financial_reports(tenant_id); CREATE INDEX idx_monthly_financial_reports_month ON monthly_financial_reports(report_month DESC); CREATE INDEX idx_monthly_financial_reports_status ON monthly_financial_reports(status); @@ -481,6 +507,8 @@ CREATE INDEX idx_monthly_financial_reports_status ON monthly_financial_reports(s -- =========================================== CREATE TABLE audit_logs ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + -- 租户ID(多租户支持) + tenant_id UUID, -- 操作者ID(用户或管理员) actor_id UUID, -- 操作者类型: USER, ADMIN, SYSTEM @@ -520,6 +548,7 @@ COMMENT ON COLUMN audit_logs.action IS '操作类型,如: CREATE, UPDATE, DELE COMMENT ON COLUMN audit_logs.old_values IS '操作前的数据快照,用于审计和回滚'; COMMENT ON COLUMN audit_logs.new_values IS '操作后的数据快照'; +CREATE INDEX idx_audit_logs_tenant ON audit_logs(tenant_id); CREATE INDEX idx_audit_logs_actor_id ON audit_logs(actor_id); CREATE INDEX idx_audit_logs_actor_type ON audit_logs(actor_type); CREATE INDEX idx_audit_logs_action ON audit_logs(action); @@ -566,6 +595,8 @@ CREATE INDEX idx_outbox_aggregate ON outbox(aggregate_type, aggregate_id); -- =========================================== CREATE TABLE documents ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + -- 租户ID(多租户支持) + tenant_id UUID, -- 文档标题 title VARCHAR(255) NOT NULL, -- 文档内容(原始文本) @@ -600,6 +631,7 @@ COMMENT ON TABLE documents IS '知识文档表 - 存储移民知识库文档, COMMENT ON COLUMN documents.category IS '移民类别,GENERAL表示通用知识'; COMMENT ON COLUMN documents.last_verified_at IS '最后验证时间,确保信息的时效性'; +CREATE INDEX idx_documents_tenant ON documents(tenant_id); CREATE INDEX idx_documents_category ON documents(category); CREATE INDEX idx_documents_is_active ON documents(is_active); CREATE INDEX idx_documents_tags ON documents USING GIN(tags); @@ -611,6 +643,8 @@ CREATE INDEX idx_documents_created_at ON documents(created_at); -- =========================================== CREATE TABLE document_embeddings ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + -- 租户ID(多租户支持) + tenant_id UUID, -- 所属文档ID document_id UUID REFERENCES documents(id) ON DELETE CASCADE, -- 分块序号 @@ -628,6 +662,7 @@ CREATE TABLE document_embeddings ( COMMENT ON TABLE document_embeddings IS '文档向量嵌入表 - 存储文档分块的向量,用于语义搜索'; COMMENT ON COLUMN document_embeddings.embedding IS '1536维向量,使用text-embedding-3-small模型生成'; +CREATE INDEX idx_document_embeddings_tenant ON document_embeddings(tenant_id); CREATE INDEX idx_document_embeddings_document_id ON document_embeddings(document_id); -- IVFFlat索引,用于快速向量相似度搜索 CREATE INDEX idx_document_embeddings_embedding ON document_embeddings @@ -669,6 +704,8 @@ CREATE INDEX idx_system_configs_group ON system_configs(config_group); -- =========================================== CREATE TABLE admin_users ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + -- 租户ID(多租户支持) + tenant_id UUID, -- 用户名(唯一) username VARCHAR(100) UNIQUE NOT NULL, -- 邮箱 @@ -703,6 +740,7 @@ COMMENT ON TABLE admin_users IS '管理员用户表 - 存储后台管理员信 COMMENT ON COLUMN admin_users.role IS 'SUPER_ADMIN=超级管理员, ADMIN=管理员, OPERATOR=操作员, VIEWER=只读用户'; COMMENT ON COLUMN admin_users.failed_login_attempts IS '连续登录失败次数,超过阈值锁定账户'; +CREATE INDEX idx_admin_users_tenant ON admin_users(tenant_id); CREATE INDEX idx_admin_users_username ON admin_users(username); CREATE INDEX idx_admin_users_email ON admin_users(email); CREATE INDEX idx_admin_users_role ON admin_users(role); @@ -714,6 +752,8 @@ CREATE INDEX idx_admin_users_is_active ON admin_users(is_active); -- =========================================== CREATE TABLE experiences ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + -- 租户ID(多租户支持) + tenant_id UUID, -- 经验类型 type VARCHAR(50) NOT NULL CHECK (type IN ('FAQ_PATTERN', 'USER_CONCERN', 'EFFECTIVE_RESPONSE', 'CONVERSION_PATTERN', 'CONFUSION_POINT')), @@ -749,6 +789,7 @@ COMMENT ON TABLE experiences IS '经验库表 - 存储从对话中提取的经 COMMENT ON COLUMN experiences.type IS '经验类型: FAQ_PATTERN=常见问题模式, USER_CONCERN=用户关注点, EFFECTIVE_RESPONSE=有效回答, CONVERSION_PATTERN=转化模式, CONFUSION_POINT=困惑点'; COMMENT ON COLUMN experiences.confidence IS '置信度,值越高表示该经验越可靠'; +CREATE INDEX idx_experiences_tenant ON experiences(tenant_id); CREATE INDEX idx_experiences_type ON experiences(type); CREATE INDEX idx_experiences_category ON experiences(category); CREATE INDEX idx_experiences_confidence ON experiences(confidence DESC); @@ -760,6 +801,8 @@ CREATE INDEX idx_experiences_is_applied ON experiences(is_applied); -- =========================================== CREATE TABLE evolution_logs ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + -- 租户ID(多租户支持) + tenant_id UUID, -- 触发人(管理员ID) triggered_by UUID REFERENCES admin_users(id), -- 变更类型 @@ -792,6 +835,7 @@ COMMENT ON TABLE evolution_logs IS '进化日志表 - 记录系统配置和行 COMMENT ON COLUMN evolution_logs.type IS '变更类型: PROMPT_UPDATE=提示词更新, KNOWLEDGE_UPDATE=知识库更新, RULE_UPDATE=规则更新, BEHAVIOR_UPDATE=行为更新'; COMMENT ON COLUMN evolution_logs.status IS '变更状态流转: PROPOSED -> APPROVED -> APPLIED, 可回滚为ROLLED_BACK'; +CREATE INDEX idx_evolution_logs_tenant ON evolution_logs(tenant_id); CREATE INDEX idx_evolution_logs_status ON evolution_logs(status); CREATE INDEX idx_evolution_logs_type ON evolution_logs(type); CREATE INDEX idx_evolution_logs_triggered_by ON evolution_logs(triggered_by); @@ -803,6 +847,8 @@ CREATE INDEX idx_evolution_logs_created_at ON evolution_logs(created_at DESC); -- =========================================== CREATE TABLE verification_codes ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + -- 租户ID(多租户支持) + tenant_id UUID, -- 手机号 phone VARCHAR(20) NOT NULL, -- 验证码 @@ -821,6 +867,7 @@ CREATE TABLE verification_codes ( COMMENT ON TABLE verification_codes IS '验证码表 - 存储手机验证码,支持防刷和过期机制'; +CREATE INDEX idx_verification_codes_tenant ON verification_codes(tenant_id); CREATE INDEX idx_verification_codes_phone ON verification_codes(phone); CREATE INDEX idx_verification_codes_expires_at ON verification_codes(expires_at); CREATE INDEX idx_verification_codes_ip ON verification_codes(ip_address); @@ -831,6 +878,8 @@ CREATE INDEX idx_verification_codes_ip ON verification_codes(ip_address); -- =========================================== CREATE TABLE service_pricing ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + -- 租户ID(多租户支持) + tenant_id UUID, -- 服务类型 service_type VARCHAR(50) NOT NULL CHECK (service_type IN ('ASSESSMENT', 'CONSULTATION', 'DOCUMENT_REVIEW')), @@ -864,6 +913,7 @@ COMMENT ON TABLE service_pricing IS '服务定价表 - 存储各类服务的定 COMMENT ON COLUMN service_pricing.original_price IS '原价,用于显示折扣效果'; COMMENT ON COLUMN service_pricing.effective_from IS '价格生效开始时间,支持定时调价'; +CREATE INDEX idx_service_pricing_tenant ON service_pricing(tenant_id); CREATE INDEX idx_service_pricing_service_type ON service_pricing(service_type); CREATE INDEX idx_service_pricing_is_active ON service_pricing(is_active); @@ -873,6 +923,8 @@ CREATE INDEX idx_service_pricing_is_active ON service_pricing(is_active); -- =========================================== CREATE TABLE coupons ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + -- 租户ID(多租户支持) + tenant_id UUID, -- 优惠券码 code VARCHAR(50) UNIQUE NOT NULL, -- 优惠券名称 @@ -911,6 +963,7 @@ CREATE TABLE coupons ( COMMENT ON TABLE coupons IS '优惠券表 - 存储优惠券信息,支持固定金额和百分比折扣'; +CREATE INDEX idx_coupons_tenant ON coupons(tenant_id); CREATE INDEX idx_coupons_code ON coupons(code); CREATE INDEX idx_coupons_is_active ON coupons(is_active); CREATE INDEX idx_coupons_valid_until ON coupons(valid_until); @@ -921,6 +974,8 @@ CREATE INDEX idx_coupons_valid_until ON coupons(valid_until); -- =========================================== CREATE TABLE user_coupons ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + -- 租户ID(多租户支持) + tenant_id UUID, -- 用户ID user_id UUID REFERENCES users(id) ON DELETE CASCADE, -- 优惠券ID @@ -938,6 +993,7 @@ CREATE TABLE user_coupons ( COMMENT ON TABLE user_coupons IS '用户优惠券表 - 记录用户领取和使用优惠券的情况'; +CREATE INDEX idx_user_coupons_tenant ON user_coupons(tenant_id); CREATE INDEX idx_user_coupons_user_id ON user_coupons(user_id); CREATE INDEX idx_user_coupons_coupon_id ON user_coupons(coupon_id); CREATE INDEX idx_user_coupons_status ON user_coupons(status); @@ -1198,6 +1254,8 @@ COMMENT ON VIEW v_channel_statistics IS '渠道统计视图 - 按来源渠道汇 -- =========================================== CREATE TABLE knowledge_articles ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + -- 租户ID(多租户支持) + tenant_id UUID, -- 文章标题 title VARCHAR(500) NOT NULL, -- 文章内容(纯文本或Markdown) @@ -1241,6 +1299,7 @@ COMMENT ON COLUMN knowledge_articles.source IS '内容来源:MANUAL手动添 COMMENT ON COLUMN knowledge_articles.embedding IS '文章向量,1536维,用于pgvector语义搜索'; COMMENT ON COLUMN knowledge_articles.quality_score IS '质量评分,根据引用和反馈自动计算'; +CREATE INDEX idx_knowledge_articles_tenant ON knowledge_articles(tenant_id); CREATE INDEX idx_knowledge_articles_category ON knowledge_articles(category); CREATE INDEX idx_knowledge_articles_published ON knowledge_articles(is_published); CREATE INDEX idx_knowledge_articles_quality ON knowledge_articles(quality_score DESC); @@ -1252,6 +1311,8 @@ CREATE INDEX idx_knowledge_articles_embedding ON knowledge_articles USING ivffla -- =========================================== CREATE TABLE knowledge_chunks ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + -- 租户ID(多租户支持) + tenant_id UUID, -- 所属文章ID article_id UUID NOT NULL REFERENCES knowledge_articles(id) ON DELETE CASCADE, -- 块内容 @@ -1275,6 +1336,7 @@ COMMENT ON TABLE knowledge_chunks IS '知识块表 - 文章分块,用于精确 COMMENT ON COLUMN knowledge_chunks.chunk_type IS '块类型,用于理解内容结构'; COMMENT ON COLUMN knowledge_chunks.metadata IS '元数据,包含章节标题、前后块链接等'; +CREATE INDEX idx_knowledge_chunks_tenant ON knowledge_chunks(tenant_id); CREATE INDEX idx_knowledge_chunks_article ON knowledge_chunks(article_id); CREATE INDEX idx_knowledge_chunks_embedding ON knowledge_chunks USING ivfflat (embedding vector_cosine_ops) WITH (lists = 100); @@ -1284,6 +1346,8 @@ CREATE INDEX idx_knowledge_chunks_embedding ON knowledge_chunks USING ivfflat (e -- =========================================== CREATE TABLE user_memories ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + -- 租户ID(多租户支持) + tenant_id UUID, -- 用户ID user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, -- 记忆类型 @@ -1328,6 +1392,7 @@ COMMENT ON COLUMN user_memories.memory_type IS '记忆类型,用于分类管 COMMENT ON COLUMN user_memories.importance IS '重要性评分,影响检索优先级'; COMMENT ON COLUMN user_memories.is_expired IS '是否过期,用户情况变化时标记'; +CREATE INDEX idx_user_memories_tenant ON user_memories(tenant_id); CREATE INDEX idx_user_memories_user ON user_memories(user_id); CREATE INDEX idx_user_memories_type ON user_memories(memory_type); CREATE INDEX idx_user_memories_importance ON user_memories(user_id, importance DESC); @@ -1404,6 +1469,10 @@ CREATE INDEX idx_system_experiences_embedding ON system_experiences USING ivffla -- =========================================== CREATE TABLE admins ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + -- 租户ID(多租户支持) + tenant_id UUID, + -- 是否为超级管理员(跨租户管理) + is_super_admin BOOLEAN DEFAULT FALSE, -- 用户名(登录用) username VARCHAR(50) NOT NULL UNIQUE, -- 密码哈希 @@ -1437,6 +1506,8 @@ COMMENT ON TABLE admins IS '管理员表 - 管理后台用户,支持多角色 COMMENT ON COLUMN admins.role IS '角色:SUPER_ADMIN超管,ADMIN管理员,OPERATOR运营,VIEWER只读'; COMMENT ON COLUMN admins.permissions IS '细粒度权限列表,JSON数组格式'; +CREATE INDEX idx_admins_tenant ON admins(tenant_id); +CREATE INDEX idx_admins_super_admin ON admins(is_super_admin); CREATE INDEX idx_admins_username ON admins(username); CREATE INDEX idx_admins_role ON admins(role); CREATE INDEX idx_admins_active ON admins(is_active); @@ -1445,6 +1516,191 @@ CREATE INDEX idx_admins_active ON admins(is_active); INSERT INTO admins (username, password_hash, name, role, permissions) VALUES ('admin', '$2b$10$rQNDjKwYXOw8FNrFcD3e0.T8KCqVJLqDQT9gQR2KPnDqPvqK8VpKi', '系统管理员', 'SUPER_ADMIN', '["*"]'); +-- =========================================== +-- Token使用量记录表 (token_usages) +-- 记录每次API调用的Token消耗 +-- =========================================== +CREATE TABLE token_usages ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + -- 租户ID(多租户支持) + tenant_id UUID NOT NULL, + -- 用户ID + user_id UUID, + -- 对话ID + conversation_id UUID NOT NULL, + -- 消息ID + message_id UUID, + -- 模型名称 + model VARCHAR(50) NOT NULL, + -- 输入Token数 + input_tokens INT DEFAULT 0, + -- 输出Token数 + output_tokens INT DEFAULT 0, + -- 缓存创建Token数 + cache_creation_tokens INT DEFAULT 0, + -- 缓存读取Token数 + cache_read_tokens INT DEFAULT 0, + -- 总Token数 + total_tokens INT DEFAULT 0, + -- 预估成本(美元) + estimated_cost DECIMAL(10, 6) DEFAULT 0, + -- 意图类型 + intent_type VARCHAR(30), + -- 工具调用次数 + tool_calls INT DEFAULT 0, + -- 回复长度 + response_length INT DEFAULT 0, + -- 延迟(毫秒) + latency_ms INT DEFAULT 0, + -- 创建时间 + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() +); + +COMMENT ON TABLE token_usages IS 'Token使用量记录表 - 记录每次API调用的Token消耗和成本'; + +CREATE INDEX idx_token_usages_tenant ON token_usages(tenant_id); +CREATE INDEX idx_token_usages_user ON token_usages(user_id); +CREATE INDEX idx_token_usages_conversation ON token_usages(conversation_id); +CREATE INDEX idx_token_usages_created ON token_usages(created_at); +CREATE INDEX idx_token_usages_model ON token_usages(model); + +-- =========================================== +-- 文件表 (files) +-- 存储用户上传的文件信息 +-- =========================================== +CREATE TABLE files ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + -- 租户ID(多租户支持) + tenant_id UUID NOT NULL, + -- 用户ID + user_id UUID NOT NULL, + -- 对话ID + conversation_id UUID, + -- 原始文件名 + original_name VARCHAR(500) NOT NULL, + -- 存储路径 + storage_path VARCHAR(1000) NOT NULL, + -- MIME类型 + mime_type VARCHAR(100) NOT NULL, + -- 文件类型 + type VARCHAR(50) NOT NULL, + -- 文件大小(字节) + size BIGINT NOT NULL, + -- 状态 + status VARCHAR(50) DEFAULT 'uploading', + -- 缩略图路径 + thumbnail_path VARCHAR(1000), + -- 元数据 + metadata JSONB, + -- 提取的文本内容 + extracted_text TEXT, + -- 错误信息 + error_message VARCHAR(1000), + -- 创建时间 + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + -- 更新时间 + updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + -- 软删除时间 + deleted_at TIMESTAMP WITH TIME ZONE +); + +COMMENT ON TABLE files IS '文件表 - 存储用户上传的文件信息'; + +CREATE INDEX idx_files_tenant ON files(tenant_id); +CREATE INDEX idx_files_tenant_user ON files(tenant_id, user_id); +CREATE INDEX idx_files_user_created ON files(user_id, created_at); +CREATE INDEX idx_files_conversation_created ON files(conversation_id, created_at); + +-- =========================================== +-- MCP服务器配置表 (mcp_server_configs) +-- 运行时MCP Server管理配置 +-- =========================================== +CREATE TABLE mcp_server_configs ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + -- 租户ID(NULL=全局) + tenant_id UUID, + -- 服务器唯一标识(用于工具名前缀) + server_id VARCHAR(100) NOT NULL, + -- 显示名称 + name VARCHAR(255) NOT NULL, + -- 描述 + description TEXT, + -- 传输方式: stdio 或 sse + transport VARCHAR(20) NOT NULL CHECK (transport IN ('stdio', 'sse')), + -- 命令(stdio模式) + command VARCHAR(500), + -- 命令参数(stdio模式) + args JSONB, + -- 环境变量(stdio模式) + env JSONB, + -- URL(sse模式) + url VARCHAR(500), + -- 工具是否可并发 + tools_concurrency_safe BOOLEAN DEFAULT TRUE, + -- 连接超时(毫秒) + connect_timeout_ms INTEGER DEFAULT 10000, + -- 工具调用超时(毫秒) + tool_timeout_ms INTEGER DEFAULT 30000, + -- 是否启用 + enabled BOOLEAN DEFAULT TRUE, + -- 创建时间 + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + -- 更新时间 + updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + -- 创建者ID + created_by UUID, + -- 更新者ID + updated_by UUID, + -- 唯一约束:同一租户下server_id唯一 + CONSTRAINT uq_mcp_server_tenant_server_id UNIQUE (tenant_id, server_id) +); + +COMMENT ON TABLE mcp_server_configs IS 'MCP服务器配置表 - 运行时MCP Server管理'; + +CREATE INDEX idx_mcp_server_configs_tenant ON mcp_server_configs(tenant_id); +CREATE INDEX idx_mcp_server_configs_enabled ON mcp_server_configs(tenant_id, enabled); + +-- =========================================== +-- 评估规则表 (evaluation_rules) +-- 管理员可配置的质量评估门控规则 +-- =========================================== +CREATE TABLE evaluation_rules ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + -- 租户ID + tenant_id UUID, + -- 适用的咨询阶段('*'表示所有阶段) + stage VARCHAR(50) NOT NULL, + -- 规则类型 + rule_type VARCHAR(50) NOT NULL, + -- 规则名称 + name VARCHAR(255) NOT NULL, + -- 规则描述 + description TEXT, + -- 规则配置参数(JSON) + config JSONB NOT NULL DEFAULT '{}', + -- 是否启用 + enabled BOOLEAN DEFAULT TRUE, + -- 优先级(越小越先执行) + priority INTEGER DEFAULT 0, + -- 失败时的动作: RETRY, SUPPLEMENT, WARN_AND_PASS, ESCALATE + failure_action VARCHAR(20) NOT NULL DEFAULT 'WARN_AND_PASS', + -- 创建者ID + created_by UUID, + -- 更新者ID + updated_by UUID, + -- 创建时间 + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + -- 更新时间 + updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() +); + +COMMENT ON TABLE evaluation_rules IS '评估规则表 - 管理员可配置的质量评估门控规则'; +COMMENT ON COLUMN evaluation_rules.stage IS '适用阶段: greeting, info_collection 等,* 表示所有阶段'; +COMMENT ON COLUMN evaluation_rules.failure_action IS '失败动作: RETRY=重试, SUPPLEMENT=补充, WARN_AND_PASS=警告放行, ESCALATE=升级'; + +CREATE INDEX idx_evaluation_rules_tenant ON evaluation_rules(tenant_id); +CREATE INDEX idx_evaluation_rules_tenant_stage ON evaluation_rules(tenant_id, stage, enabled); + -- =========================================== -- 结束 -- ===========================================