feat(database): 接入多模块 Liquibase 迁移 / Add multi-module Liquibase migrations

为后端服务增加 MySQL 与 PostgreSQL 的表结构、初始化数据和独立 Liquibase 入口。\nAdd database schemas, seed data, and isolated Liquibase changelog entrypoints for backend services.
This commit is contained in:
疯狂的狮子Li
2026-08-30 00:17:51 +08:00
parent 53f417bd0c
commit 3db0478da3
43 changed files with 5919 additions and 6 deletions
+12
View File
@@ -12,6 +12,18 @@
<artifactId>ruoyi-snailai-server</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-liquibase</artifactId>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
<!-- Snail AI 服务端 -->
<dependency>
<groupId>com.aizuda</groupId>
@@ -0,0 +1,13 @@
databaseChangeLog:
- include:
file: mysql/ai_table.sql
relativeToChangelogFile: true
- include:
file: mysql/ai_data.sql
relativeToChangelogFile: true
- include:
file: postgresql/ai_table.sql
relativeToChangelogFile: true
- include:
file: postgresql/ai_data.sql
relativeToChangelogFile: true
@@ -0,0 +1,23 @@
--liquibase formatted sql
--changeset seewis:ai-mysql-data-baseline dbms:mysql splitStatements:true
-- ============================================================
-- 九、初始化数据
-- ============================================================
-- 默认管理员:admin / admin123
INSERT INTO sai_user VALUES (1, 2, NULL, 'admin', 'admin', '', 'pbkdf2$120000$c25haWwtYWktYWRtaW4tMQ==$kakglT/wYKOgv/77Ah1stie58d/JbY2nGgq5DwgUBw4=', NULL, NOW(), NOW());
-- 插入常见的AI提供商
INSERT INTO sai_model_provider (provider_name, provider_key, description, is_enabled)
VALUES ('OpenAI', 'openai', 'OpenAI官方模型 (GPT-4, GPT-3.5等)', 1),
('Claude', 'claude', 'Anthropic Claude模型', 1),
('Ollama', 'ollama', '本地开源模型 (Llama, Mistral等)', 1),
('Google Gemini', 'gemini', 'Google Gemini模型', 1),
('阿里云百炼', 'qwen', '阿里云百炼 OpenAI 兼容模型 (Qwen等)', 1),
('DeepSeek', 'deepseek', 'DeepSeek OpenAI 兼容模型', 1),
('智谱AI', 'zhipu', '智谱AI OpenAI 兼容模型 (GLM等)', 1);
INSERT INTO sai_app VALUES (1, '1', 'ruoyi-ai', '', 'SAI_566a6bfbc26e4998b4841cc927d50c5d', 'LEAST_LOAD', 1, NOW(), NOW());
@@ -0,0 +1,572 @@
--liquibase formatted sql
--changeset seewis:ai-mysql-table-baseline dbms:mysql splitStatements:true
-- ============================================================
-- Snail AI MySQL 全量建表脚本(仅 CREATE,无 ALTER)
-- 使用:mysql -u user -p database < ry-ai.sql
-- ============================================================
-- ============================================================
-- 一、用户与权限
-- ============================================================
-- 1.1 用户表
-- ### 用户表
CREATE TABLE sai_user
(
id BIGINT AUTO_INCREMENT PRIMARY KEY,
role INT,
totals INT,
username VARCHAR(255),
nickname VARCHAR(128) DEFAULT NULL COMMENT '用户昵称',
email VARCHAR(64),
password VARCHAR(255) NOT NULL,
resource_id BIGINT DEFAULT NULL COMMENT '头像资源ID,关联 sai_resource.id',
create_dt DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
update_dt DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE KEY uk_username (username)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4;
-- 1.2 OpenAPI 外部用户映射表
-- ### OpenAPI 外部用户映射表
CREATE TABLE sai_openapi_user
(
id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
app_id VARCHAR(128) NOT NULL COMMENT '关联 sai_app.app_id',
open_id VARCHAR(64) NOT NULL COMMENT '平台分配的唯一标识(UUID)',
platform_user_id BIGINT NOT NULL COMMENT '关联 sai_user.id,注册时自动创建',
external_id VARCHAR(256) DEFAULT NULL COMMENT '外部系统的用户标识(可选,幂等用)',
create_dt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
update_dt TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE KEY uk_app_open (app_id, open_id),
UNIQUE KEY uk_app_external (app_id, external_id),
INDEX idx_open_id (open_id),
INDEX idx_platform_user (platform_user_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
COMMENT='OpenAPI 外部用户映射表';
-- ============================================================
-- 二、AI 模型管理
-- ============================================================
-- 2.1 AI 模型提供商表
-- ### AI 模型提供商表
CREATE TABLE IF NOT EXISTS sai_model_provider
(
id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
provider_name VARCHAR(255) NOT NULL COMMENT '提供商名称',
provider_key VARCHAR(50) NOT NULL COMMENT '提供商标识符',
description TEXT COMMENT '提供商描述',
icon_url VARCHAR(500) COMMENT 'LOGO图标URL',
is_enabled TINYINT(1) DEFAULT 1 COMMENT '是否启用',
created_dt TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
updated_dt TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
UNIQUE KEY uk_provider_name (provider_name),
UNIQUE KEY uk_provider_key (provider_key)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_unicode_ci COMMENT = 'AI模型提供商表';
CREATE INDEX idx_provider_key ON sai_model_provider (provider_key);
CREATE INDEX idx_is_enabled ON sai_model_provider (is_enabled);
-- 2.2 AI模型配置表
-- ### AI 模型配置表
CREATE TABLE IF NOT EXISTS sai_model_config
(
id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
provider_id BIGINT NOT NULL COMMENT '提供商ID',
model_name VARCHAR(255) NOT NULL COMMENT '模型名称',
model_key VARCHAR(100) NOT NULL COMMENT '模型标识符',
model_type VARCHAR(50) NOT NULL COMMENT '模型类型(CHAT/EMBEDDING/RERANKER/IMAGE/SPEECH)',
adapter_key VARCHAR(100) COMMENT '底层协议适配器标识(openai-compatible/http等)',
description VARCHAR(1000) COMMENT '模型描述',
api_key VARCHAR(1000) COMMENT 'API密钥(加密存储)',
api_endpoint VARCHAR(500) COMMENT 'API端点URL',
config_json TEXT COMMENT '模型参数配置(JSON格式)',
owner_id BIGINT COMMENT '所有者ID(NULL=全局,具体值=用户ID)',
scope VARCHAR(20) NOT NULL DEFAULT 'GLOBAL' COMMENT '作用域(GLOBAL/PERSONAL)',
is_default TINYINT(1) DEFAULT 0 COMMENT '是否为默认模型',
is_enabled TINYINT(1) DEFAULT 1 COMMENT '是否启用',
created_dt TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
updated_dt TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
KEY fk_provider_id (provider_id)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_unicode_ci COMMENT = 'AI模型配置表';
CREATE INDEX idx_provider_model_type ON sai_model_config (provider_id, model_type);
CREATE INDEX idx_model_type_enabled ON sai_model_config (model_type, is_enabled);
CREATE INDEX idx_owner_id ON sai_model_config (owner_id);
CREATE INDEX idx_is_default ON sai_model_config (is_default);
CREATE INDEX idx_scope ON sai_model_config (scope);
CREATE INDEX idx_model_key ON sai_model_config (model_key);
-- 2.3 模型使用统计表
-- ### 模型使用统计表
CREATE TABLE IF NOT EXISTS sai_model_usage_stat
(
id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
model_id BIGINT NOT NULL COMMENT '模型ID',
user_id BIGINT NOT NULL COMMENT '用户ID',
total_calls BIGINT DEFAULT 0 COMMENT '总调用次数',
success_calls BIGINT DEFAULT 0 COMMENT '成功调用次数',
failed_calls BIGINT DEFAULT 0 COMMENT '失败调用次数',
total_tokens_used BIGINT DEFAULT 0 COMMENT '总Token使用量',
total_cost DECIMAL(18, 8) DEFAULT 0 COMMENT '总费用',
avg_response_time BIGINT DEFAULT 0 COMMENT '平均响应时间(毫秒)',
last_used_dt TIMESTAMP NULL DEFAULT NULL COMMENT '最后使用时间',
created_dt TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
updated_dt TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
UNIQUE KEY unique_model_user (model_id, user_id),
KEY fk_stat_model_id (model_id)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_unicode_ci COMMENT = '模型使用统计表';
CREATE INDEX idx_model_id ON sai_model_usage_stat (model_id);
CREATE INDEX idx_user_id ON sai_model_usage_stat (user_id);
CREATE INDEX idx_last_used_dt ON sai_model_usage_stat (last_used_dt);
-- ============================================================
-- 三、智能体(Agent)
-- ============================================================
-- 3.1 智能体主表
-- ### 智能体主表
CREATE TABLE IF NOT EXISTS sai_agent
(
id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255) NOT NULL COMMENT '智能体名称',
description TEXT COMMENT '智能体描述',
avatar VARCHAR(512) COMMENT '头像URL',
instruction TEXT COMMENT '系统指令(System Prompt)',
greeting TEXT COMMENT '欢迎语',
preset_questions TEXT COMMENT '预设问题列表(JSON数组字符串)',
chat_model_id BIGINT COMMENT '关联的对话模型ID',
memory_enabled TINYINT(1) DEFAULT 0 COMMENT '是否启用记忆库',
mcp_enabled TINYINT(1) DEFAULT 0 COMMENT '是否启用MCP',
skill_enabled TINYINT(1) DEFAULT 0 COMMENT '是否启用Skill',
web_search_enabled TINYINT(1) DEFAULT 0 COMMENT '是否启用联网搜索',
rag_enabled TINYINT(1) DEFAULT 0 COMMENT '是否启用RAG',
rag_ids VARCHAR(64) NULL COMMENT '绑定的RAG ID列表,逗号分隔,最多5个',
rag_call_mode TINYINT DEFAULT 1 COMMENT 'RAG调用方式: 1=智能调用 2=强制调用',
short_term_memory_size INT DEFAULT 20 COMMENT '短期记忆滑动窗口保留条数',
creator_id BIGINT COMMENT '创建者用户ID',
is_featured TINYINT(1) DEFAULT 0 COMMENT '是否精选',
view_count INT DEFAULT 0 COMMENT '浏览次数',
status TINYINT DEFAULT 1 COMMENT '状态: 1-活跃 2-非活跃 3-已废弃 4-已禁用',
config TEXT COMMENT '扩展配置(预留)',
app_id VARCHAR(128) NULL COMMENT '关联应用ID(NULL=本地执行)',
create_dt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
update_dt TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_unicode_ci COMMENT = '智能体表';
CREATE INDEX idx_agent_creator ON sai_agent (creator_id);
CREATE INDEX idx_agent_featured ON sai_agent (is_featured);
-- 3.2 智能体对话表
-- ### 智能体对话表
CREATE TABLE IF NOT EXISTS sai_agent_conversation
(
id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
agent_id BIGINT NOT NULL COMMENT '智能体ID',
user_id BIGINT NOT NULL COMMENT '用户ID',
conversation_id VARCHAR(64) NOT NULL COMMENT '对话ID(UUID)',
title VARCHAR(255) COMMENT '对话标题',
create_dt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
update_dt TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE KEY uk_conv_id (conversation_id)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_unicode_ci COMMENT = '智能体对话表';
CREATE INDEX idx_agent_conv_agent ON sai_agent_conversation (agent_id);
CREATE INDEX idx_agent_conv_user ON sai_agent_conversation (user_id);
-- 3.3 智能体对话消息记录表
-- ### 智能体对话消息记录表
CREATE TABLE IF NOT EXISTS sai_agent_conversation_record
(
id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
agent_id BIGINT NOT NULL COMMENT '智能体ID',
conversation_id VARCHAR(64) NOT NULL COMMENT '对话ID',
user_id BIGINT NOT NULL COMMENT '用户ID',
role VARCHAR(16) DEFAULT 'user' COMMENT 'user/assistant',
content TEXT COMMENT '消息内容',
thinking TEXT COMMENT '思考过程(仅assistant)',
metadata LONGTEXT DEFAULT NULL COMMENT '消息扩展元数据JSON',
status INT DEFAULT 1 COMMENT '1=成功,2=失败,3=进行中',
input_tokens INT DEFAULT 0 COMMENT '输入Token数(prompt)',
output_tokens INT DEFAULT 0 COMMENT '输出Token数(completion)',
cache_tokens INT DEFAULT 0 COMMENT '缓存命中Token数',
create_dt TIMESTAMP DEFAULT CURRENT_TIMESTAMP
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_unicode_ci COMMENT = '智能体对话消息记录';
CREATE INDEX idx_agent_rec_conv ON sai_agent_conversation_record (conversation_id);
-- 3.4 智能体使用统计表
-- ### 智能体使用统计表
CREATE TABLE IF NOT EXISTS sai_agent_usage_stat
(
id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
agent_id BIGINT NOT NULL COMMENT '智能体ID',
user_id BIGINT NOT NULL COMMENT '用户ID',
message_count INT DEFAULT 0 COMMENT '消息条数',
conversation_count INT DEFAULT 0 COMMENT '对话轮次',
stat_date DATE NOT NULL COMMENT '统计日期',
create_dt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
update_dt TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE KEY uk_agent_user_date (agent_id, user_id, stat_date)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_unicode_ci COMMENT = '智能体使用统计';
CREATE INDEX idx_usage_agent ON sai_agent_usage_stat (agent_id);
CREATE INDEX idx_usage_date ON sai_agent_usage_stat (stat_date);
-- 3.5 用户订阅的智能体(多对多)
-- ### 用户智能体订阅关联表
CREATE TABLE IF NOT EXISTS sai_user_agent
(
id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
user_id BIGINT NOT NULL COMMENT '用户ID',
agent_id BIGINT NOT NULL COMMENT '智能体ID',
create_dt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE KEY uk_user_agent (user_id, agent_id)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_unicode_ci COMMENT = '用户订阅的智能体';
CREATE INDEX idx_user_agent_user ON sai_user_agent (user_id);
-- ============================================================
-- 四、RAG 知识库
-- ============================================================
-- 4.1 知识库主表
-- ### RAG 知识库表
CREATE TABLE sai_rag
(
id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255) NOT NULL,
description TEXT,
icon VARCHAR(512),
embedding_model_id BIGINT(128) NOT NULL,
dimension_of_vector_model INT NOT NULL COMMENT '向量维度',
rerank_model_id BIGINT(128),
search_engine_instance_id BIGINT(128),
vector_store_instance_id BIGINT(128),
search_engine_enable TINYINT(1) DEFAULT 0,
delimiter VARCHAR(32) DEFAULT '\n\n',
rag_enhancement TEXT,
config TEXT DEFAULT NULL COMMENT 'RAG检索和问答的页面配置参数',
dedup_strategy TINYINT(1) NOT NULL DEFAULT 2 COMMENT '去重策略: 0=NONE 1=BY_NAME 2=BY_CONTENT 3=BY_NAME_OR_CONTENT',
dedup_action TINYINT(1) NOT NULL DEFAULT 0 COMMENT '冲突动作: 0=REJECT 1=SKIP 2=OVERWRITE',
upload_confirm TINYINT(1) NOT NULL DEFAULT 1 COMMENT '上传前二次确认: 0-关 1-开',
create_dt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
update_dt TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_unicode_ci;
-- 4.2 RAG 文档表
-- ### RAG 文档表
CREATE TABLE sai_rag_document
(
id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
rag_id BIGINT NOT NULL,
name VARCHAR(255),
file_type VARCHAR(32),
source_type VARCHAR(32),
source_path VARCHAR(1024),
storage_path VARCHAR(1024),
storage_type VARCHAR(32) DEFAULT 'LOCAL',
file_size BIGINT DEFAULT 0,
content TEXT,
status TINYINT(1) DEFAULT 0 COMMENT '状态: 0-待处理 1-解析中 2-处理中 3-处理完成 4-处理失败',
error_msg TEXT,
chunk_count INT DEFAULT 0,
page_count INT DEFAULT 0 COMMENT 'Docling parse page count',
element_count INT DEFAULT 0 COMMENT 'Docling parse element count',
table_count INT DEFAULT 0 COMMENT 'Docling parse table count',
image_count INT DEFAULT 0 COMMENT 'Docling parse image count',
parse_time INT DEFAULT 0 COMMENT 'Parse duration in milliseconds',
md_content LONGTEXT DEFAULT NULL COMMENT 'Docling markdown content',
doc_metadata LONGTEXT DEFAULT NULL COMMENT 'Docling document metadata JSON',
content_hash VARCHAR(64) DEFAULT NULL COMMENT '文件内容SHA-256哈希,用于去重',
resource_id BIGINT DEFAULT NULL COMMENT '关联资源库 sai_resource.id',
create_dt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
update_dt TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_unicode_ci;
CREATE INDEX idx_rag_doc_rag ON sai_rag_document (rag_id);
CREATE INDEX idx_rag_content_hash ON sai_rag_document (rag_id, content_hash);
CREATE INDEX idx_rag_name ON sai_rag_document (rag_id, name);
CREATE INDEX idx_rag_doc_resource ON sai_rag_document (resource_id);
-- ### RAG 文档图片表
CREATE TABLE sai_rag_document_image
(
id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
rag_id BIGINT NOT NULL,
document_id BIGINT NOT NULL,
chunk_id BIGINT DEFAULT NULL,
resource_id BIGINT DEFAULT NULL,
image_index INT,
image_url VARCHAR(1024),
caption TEXT,
figure_no VARCHAR(64),
figure_title VARCHAR(512),
section_title VARCHAR(512),
source_page INT,
document_name VARCHAR(255),
ocr_text TEXT,
create_dt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
update_dt TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_unicode_ci;
CREATE INDEX idx_rag_doc_image_rag ON sai_rag_document_image (rag_id);
CREATE INDEX idx_rag_doc_image_document ON sai_rag_document_image (document_id);
CREATE INDEX idx_rag_doc_image_chunk ON sai_rag_document_image (chunk_id);
CREATE INDEX idx_rag_doc_image_resource ON sai_rag_document_image (resource_id);
-- 4.3 RAG 分块表
-- ### RAG 文档分块表
CREATE TABLE sai_rag_chunk
(
id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
rag_id BIGINT NOT NULL,
document_id BIGINT NOT NULL,
paragraph_index INT,
chunk_index INT,
content TEXT,
token_count INT,
vector_id VARCHAR(128),
content_hash VARCHAR(64) DEFAULT NULL COMMENT 'chunk内容SHA-256,用于向量去重',
source_type VARCHAR(20) NOT NULL DEFAULT 'TEXT' COMMENT 'chunk来源类型:TEXT文本、IMAGE图片',
create_dt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
update_dt TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_unicode_ci;
CREATE INDEX idx_rag_chunk_rag ON sai_rag_chunk (rag_id);
CREATE INDEX idx_rag_chunk_document ON sai_rag_chunk (document_id);
CREATE INDEX idx_chunk_rag_hash ON sai_rag_chunk (rag_id, content_hash);
CREATE INDEX idx_chunk_rag_source_type ON sai_rag_chunk (rag_id, source_type);
-- ============================================================
-- 五、MCP 服务管理
-- ============================================================
-- 5.1 MCP 服务配置表
-- ### MCP 服务配置表
CREATE TABLE IF NOT EXISTS sai_mcp_server
(
id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255) NOT NULL COMMENT 'MCP服务名称',
description TEXT COMMENT 'MCP服务描述',
transport_type TINYINT(1) DEFAULT 1 COMMENT '传输类型: 1-SSE 2-Streamable HTTP 3-Stdio',
base_uri VARCHAR(1024) COMMENT '服务基础地址(SSE/Streamable HTTP时使用)',
endpoint VARCHAR(1024) COMMENT '端点路径(SSE/Streamable HTTP时可选)',
command VARCHAR(1024) COMMENT 'Stdio命令(Stdio时必填)',
args TEXT COMMENT 'Stdio命令参数(JSON数组)',
env_vars TEXT COMMENT 'Stdio环境变量(JSON对象)',
timeout BIGINT DEFAULT 60000 COMMENT '超时时间(毫秒)',
headers TEXT COMMENT '请求头(JSON对象)',
last_connect_dt TIMESTAMP NULL COMMENT '最后连接时间',
creator_id BIGINT COMMENT '创建者用户ID',
create_dt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
update_dt TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_unicode_ci COMMENT = 'MCP服务配置表';
CREATE INDEX idx_mcp_server_creator ON sai_mcp_server (creator_id);
-- 5.2 智能体与MCP服务关联表(多对多)
-- ### 智能体与 MCP 服务关联表
CREATE TABLE IF NOT EXISTS sai_agent_mcp_server
(
id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
agent_id BIGINT NOT NULL COMMENT '智能体ID',
mcp_server_id BIGINT NOT NULL COMMENT 'MCP服务ID',
create_dt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE KEY uk_agent_mcp (agent_id, mcp_server_id)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_unicode_ci COMMENT = '智能体MCP服务关联表';
CREATE INDEX idx_agent_mcp_agent ON sai_agent_mcp_server (agent_id);
CREATE INDEX idx_agent_mcp_server ON sai_agent_mcp_server (mcp_server_id);
-- ============================================================
-- 六、Skill 技能包管理
-- ============================================================
-- 6.1 Skill 技能包表
-- ### Skill 技能包表
CREATE TABLE IF NOT EXISTS sai_skill
(
id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255) NOT NULL COMMENT 'Skill名称(从SKILL.md解析)',
description TEXT COMMENT 'Skill描述(从SKILL.md解析)',
file_name VARCHAR(255) COMMENT '上传的zip文件名',
file_path VARCHAR(1024) COMMENT '解压后存储路径',
file_size BIGINT DEFAULT 0 COMMENT '文件大小(字节)',
skill_content LONGTEXT COMMENT 'SKILL.md正文内容(去除frontmatter)',
storage_path VARCHAR(500) DEFAULT NULL COMMENT '对象存储相对路径前缀(如 skills/123/)',
version BIGINT DEFAULT 0 COMMENT '版本号,文件变更时自增,用于缓存一致性校验',
has_files TINYINT(1) DEFAULT 0 COMMENT '是否包含支撑文件(0=仅SKILL.md,1=有scripts/references等)',
creator_id BIGINT COMMENT '创建者用户ID',
create_dt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
update_dt TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_unicode_ci COMMENT = 'Skill技能包表';
CREATE INDEX idx_skill_creator ON sai_skill (creator_id);
-- 6.2 Skill 支撑文件内容表
-- ### Skill 支撑文件表
CREATE TABLE IF NOT EXISTS sai_skill_file
(
id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
skill_id BIGINT NOT NULL COMMENT 'Skill ID',
file_path VARCHAR(255) NOT NULL COMMENT '文件相对路径',
content LONGTEXT NOT NULL COMMENT '文件内容',
file_size INT NOT NULL COMMENT '文件大小(字节)',
encoding VARCHAR(50) DEFAULT 'utf-8' COMMENT '编码方式',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
UNIQUE KEY uk_skill_path (skill_id, file_path) COMMENT '同一Skill不能有重复的文件路径',
KEY idx_skill_id (skill_id) COMMENT '技能ID索引'
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_unicode_ci COMMENT = 'Skill支撑文件内容表';
-- 6.3 智能体与Skill关联表(多对多)
-- ### 智能体与 Skill 关联表
CREATE TABLE IF NOT EXISTS sai_agent_skill
(
id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
agent_id BIGINT NOT NULL COMMENT '智能体ID',
skill_id BIGINT NOT NULL COMMENT 'Skill ID',
create_dt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE KEY uk_agent_skill (agent_id, skill_id)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_unicode_ci COMMENT = '智能体Skill关联表';
CREATE INDEX idx_agent_skill_agent ON sai_agent_skill (agent_id);
CREATE INDEX idx_agent_skill_skill ON sai_agent_skill (skill_id);
-- ============================================================
-- 七、客户端应用与节点
-- ============================================================
-- 7.1 客户端应用
-- ### 客户端应用表
CREATE TABLE IF NOT EXISTS sai_app
(
id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
app_id VARCHAR(128) NOT NULL COMMENT '应用唯一标识',
app_name VARCHAR(255) NOT NULL COMMENT '应用名称',
description VARCHAR(512) COMMENT '应用描述',
token VARCHAR(128) NOT NULL COMMENT '通信认证令牌',
route_strategy VARCHAR(32) DEFAULT 'LEAST_LOAD' COMMENT '路由策略',
status TINYINT(1) DEFAULT 1 COMMENT '1=启用, 0=停用',
create_dt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
update_dt TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE KEY uk_app_id (app_id)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_unicode_ci COMMENT = '客户端应用';
-- 7.2 AI客户端实例节点
-- ### AI 客户端节点表
CREATE TABLE IF NOT EXISTS sai_client_node
(
id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
app_id VARCHAR(128) NOT NULL COMMENT '所属应用ID',
host_id VARCHAR(128) NOT NULL COMMENT '客户端实例唯一标识',
host_ip VARCHAR(64) NOT NULL COMMENT '客户端IP',
grpc_port INT NOT NULL COMMENT '客户端gRPC端口',
max_concurrent INT DEFAULT 10 COMMENT '最大并发对话数',
active_chats INT DEFAULT 0 COMMENT '当前活跃对话数',
supported_providers TEXT COMMENT '支持的模型提供商(JSON数组)',
labels TEXT COMMENT '路由标签',
expire_dt DATETIME NOT NULL COMMENT '过期时间(心跳更新)',
create_dt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
update_dt TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE KEY uk_client_node (app_id, host_id),
INDEX idx_app_expire (app_id, expire_dt)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_unicode_ci COMMENT = 'AI客户端实例节点';
-- ============================================================
-- 八、存储与资源
-- ============================================================
-- 8.1 存储实例
-- ### 存储实例表
CREATE TABLE IF NOT EXISTS sai_store_instance
(
id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(128) NOT NULL COMMENT '实例名称',
category TINYINT(1) NOT NULL COMMENT '分类: 1-向量库 2-搜索引擎',
type TINYINT(1) NOT NULL COMMENT '类型: 1-PG_VECTOR 2-MILVUS 3-ELASTICSEARCH 4-PG_FULLTEXT',
config TEXT DEFAULT NULL COMMENT '连接参数 JSON',
status TINYINT(1) DEFAULT 1 COMMENT '状态: 0-停用 1-启用',
is_default TINYINT(1) DEFAULT 0 COMMENT '是否为该 category 下默认实例',
create_dt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
update_dt TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_unicode_ci COMMENT = '存储实例';
CREATE INDEX idx_store_instance_category ON sai_store_instance (category);
CREATE INDEX idx_store_instance_type ON sai_store_instance (type);
-- 8.2 通用资源存储
-- ### 通用资源存储表
CREATE TABLE IF NOT EXISTS sai_resource
(
id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
storage_key VARCHAR(512) NOT NULL COMMENT '存储键(相对路径或对象Key)',
original_name VARCHAR(255) NOT NULL COMMENT '原始文件名',
file_size BIGINT DEFAULT 0 COMMENT '文件大小(bytes)',
mime_type VARCHAR(128) COMMENT 'MIME类型',
storage_type VARCHAR(32) NOT NULL DEFAULT 'LOCAL' COMMENT '存储类型: LOCAL/MINIO',
access_url VARCHAR(1024) COMMENT '访问URL',
biz_type VARCHAR(64) NOT NULL DEFAULT 'GENERAL' COMMENT '业务类型: AVATAR/ATTACHMENT/DOCUMENT/GENERAL',
biz_id BIGINT COMMENT '关联业务ID',
creator_id BIGINT COMMENT '上传者ID',
create_dt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
update_dt TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE KEY uk_storage_key (storage_key),
INDEX idx_biz (biz_type, biz_id),
INDEX idx_creator (creator_id)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_unicode_ci COMMENT = '通用资源存储';
@@ -0,0 +1,28 @@
--liquibase formatted sql
--changeset seewis:ai-postgresql-data-baseline dbms:postgresql splitStatements:false
-- ============================================================
-- 九、初始化数据
-- ============================================================
-- 默认管理员:admin / admin123
INSERT INTO sai_user VALUES (1, 2, NULL, 'admin', 'admin', '', 'pbkdf2$120000$c25haWwtYWktYWRtaW4tMQ==$kakglT/wYKOgv/77Ah1stie58d/JbY2nGgq5DwgUBw4=', NULL, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
ON CONFLICT (id) DO NOTHING;
SELECT setval(pg_get_serial_sequence('sai_user', 'id'), COALESCE((SELECT MAX(id) FROM sai_user), 1), TRUE);
-- 插入常见的AI提供商
INSERT INTO sai_model_provider (provider_name, provider_key, description, is_enabled)
VALUES ('OpenAI', 'openai', 'OpenAI官方模型 (GPT-4, GPT-3.5等)', TRUE),
('Claude', 'claude', 'Anthropic Claude模型', TRUE),
('Ollama', 'ollama', '本地开源模型 (Llama, Mistral等)', TRUE),
('Google Gemini', 'gemini', 'Google Gemini模型', TRUE),
('阿里云百炼', 'qwen', '阿里云百炼 OpenAI 兼容模型 (Qwen等)', TRUE),
('DeepSeek', 'deepseek', 'DeepSeek OpenAI 兼容模型', TRUE),
('智谱AI', 'zhipu', '智谱AI OpenAI 兼容模型 (GLM等)', TRUE)
ON CONFLICT (provider_key) DO NOTHING;
INSERT INTO sai_app VALUES (1, '1', 'ruoyi-ai', '', 'SAI_566a6bfbc26e4998b4841cc927d50c5d', 'LEAST_LOAD', 1, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
ON CONFLICT (id) DO NOTHING;
SELECT setval(pg_get_serial_sequence('sai_app', 'id'), COALESCE((SELECT MAX(id) FROM sai_app), 1), TRUE);
@@ -0,0 +1,847 @@
--liquibase formatted sql
--changeset seewis:ai-postgresql-table-baseline dbms:postgresql splitStatements:false
-- ============================================================
-- Snail AI PostgreSQL 全量建表脚本(仅 CREATE,无 ALTER)
-- 使用:psql -U user -d snail_ai -f postgres_ry_ai.sql
-- 结构来源:snail_ai_schema.sql
-- ============================================================
-- ============================================================
-- 公共触发器函数:模拟 MySQL ON UPDATE CURRENT_TIMESTAMP
-- ============================================================
CREATE OR REPLACE FUNCTION update_timestamp()
RETURNS TRIGGER AS $$
BEGIN
NEW.update_dt = CURRENT_TIMESTAMP;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE OR REPLACE FUNCTION update_timestamp_updated()
RETURNS TRIGGER AS $$
BEGIN
NEW.updated_dt = CURRENT_TIMESTAMP;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE OR REPLACE FUNCTION update_timestamp_updated_at()
RETURNS TRIGGER AS $$
BEGIN
NEW.updated_at = CURRENT_TIMESTAMP;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
-- ============================================================
-- 一、用户与权限
-- ============================================================
-- 1.1 用户表
-- ### 用户表
CREATE TABLE sai_user
(
id BIGSERIAL PRIMARY KEY,
role INT,
totals INT,
username VARCHAR(255),
nickname VARCHAR(128) DEFAULT NULL,
email VARCHAR(64),
password VARCHAR(255) NOT NULL,
resource_id BIGINT DEFAULT NULL,
create_dt TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
update_dt TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT uk_username UNIQUE (username)
);
COMMENT ON COLUMN sai_user.nickname IS '用户昵称';
COMMENT ON COLUMN sai_user.resource_id IS '头像资源ID,关联 sai_resource.id';
CREATE TRIGGER trigger_sai_user_update
BEFORE UPDATE ON sai_user
FOR EACH ROW
EXECUTE FUNCTION update_timestamp();
-- 1.2 OpenAPI 外部用户映射表
-- ### OpenAPI 外部用户映射表
CREATE TABLE sai_openapi_user
(
id BIGSERIAL PRIMARY KEY,
app_id VARCHAR(128) NOT NULL,
open_id VARCHAR(64) NOT NULL,
platform_user_id BIGINT NOT NULL,
external_id VARCHAR(256) DEFAULT NULL,
create_dt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
update_dt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT uk_app_open UNIQUE (app_id, open_id),
CONSTRAINT uk_app_external UNIQUE (app_id, external_id)
);
COMMENT ON TABLE sai_openapi_user IS 'OpenAPI 外部用户映射表';
COMMENT ON COLUMN sai_openapi_user.app_id IS '关联 sai_app.app_id';
COMMENT ON COLUMN sai_openapi_user.open_id IS '平台分配的唯一标识(UUID)';
COMMENT ON COLUMN sai_openapi_user.platform_user_id IS '关联 sai_user.id,注册时自动创建';
COMMENT ON COLUMN sai_openapi_user.external_id IS '外部系统的用户标识(可选,幂等用)';
CREATE TRIGGER trigger_sai_openapi_user_update
BEFORE UPDATE ON sai_openapi_user
FOR EACH ROW
EXECUTE FUNCTION update_timestamp();
CREATE INDEX idx_open_id ON sai_openapi_user (open_id);
CREATE INDEX idx_platform_user ON sai_openapi_user (platform_user_id);
-- ============================================================
-- 二、AI 模型管理
-- ============================================================
-- 2.1 AI 模型提供商表
-- ### AI 模型提供商表
CREATE TABLE IF NOT EXISTS sai_model_provider
(
id BIGSERIAL PRIMARY KEY,
provider_name VARCHAR(255) NOT NULL,
provider_key VARCHAR(50) NOT NULL,
description TEXT,
icon_url VARCHAR(500),
is_enabled BOOLEAN DEFAULT TRUE,
created_dt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_dt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT uk_provider_name UNIQUE (provider_name),
CONSTRAINT uk_provider_key UNIQUE (provider_key)
);
COMMENT ON TABLE sai_model_provider IS 'AI模型提供商表';
COMMENT ON COLUMN sai_model_provider.provider_name IS '提供商名称';
COMMENT ON COLUMN sai_model_provider.provider_key IS '提供商标识符';
COMMENT ON COLUMN sai_model_provider.description IS '提供商描述';
COMMENT ON COLUMN sai_model_provider.icon_url IS 'LOGO图标URL';
COMMENT ON COLUMN sai_model_provider.is_enabled IS '是否启用';
COMMENT ON COLUMN sai_model_provider.created_dt IS '创建时间';
COMMENT ON COLUMN sai_model_provider.updated_dt IS '更新时间';
CREATE TRIGGER trigger_sai_model_provider_update
BEFORE UPDATE ON sai_model_provider
FOR EACH ROW
EXECUTE FUNCTION update_timestamp_updated();
CREATE INDEX idx_provider_key ON sai_model_provider (provider_key);
CREATE INDEX idx_is_enabled ON sai_model_provider (is_enabled);
-- 2.2 AI模型配置表
-- ### AI 模型配置表
CREATE TABLE IF NOT EXISTS sai_model_config
(
id BIGSERIAL PRIMARY KEY,
provider_id BIGINT NOT NULL,
model_name VARCHAR(255) NOT NULL,
model_key VARCHAR(100) NOT NULL,
model_type VARCHAR(50) NOT NULL,
adapter_key VARCHAR(100),
description VARCHAR(1000),
api_key VARCHAR(1000),
api_endpoint VARCHAR(500),
config_json TEXT,
owner_id BIGINT,
scope VARCHAR(20) NOT NULL DEFAULT 'GLOBAL',
is_default BOOLEAN DEFAULT FALSE,
is_enabled BOOLEAN DEFAULT TRUE,
created_dt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_dt TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
COMMENT ON TABLE sai_model_config IS 'AI模型配置表';
COMMENT ON COLUMN sai_model_config.provider_id IS '提供商ID';
COMMENT ON COLUMN sai_model_config.model_name IS '模型名称';
COMMENT ON COLUMN sai_model_config.model_key IS '模型标识符';
COMMENT ON COLUMN sai_model_config.model_type IS '模型类型(CHAT/EMBEDDING/RERANKER/IMAGE/SPEECH)';
COMMENT ON COLUMN sai_model_config.adapter_key IS '底层协议适配器标识(openai-compatible/http等)';
COMMENT ON COLUMN sai_model_config.description IS '模型描述';
COMMENT ON COLUMN sai_model_config.api_key IS 'API密钥(加密存储)';
COMMENT ON COLUMN sai_model_config.api_endpoint IS 'API端点URL';
COMMENT ON COLUMN sai_model_config.config_json IS '模型参数配置(JSON格式)';
COMMENT ON COLUMN sai_model_config.owner_id IS '所有者ID(NULL=全局,具体值=用户ID)';
COMMENT ON COLUMN sai_model_config.scope IS '作用域(GLOBAL/PERSONAL)';
COMMENT ON COLUMN sai_model_config.is_default IS '是否为默认模型';
COMMENT ON COLUMN sai_model_config.is_enabled IS '是否启用';
COMMENT ON COLUMN sai_model_config.created_dt IS '创建时间';
COMMENT ON COLUMN sai_model_config.updated_dt IS '更新时间';
CREATE TRIGGER trigger_sai_model_config_update
BEFORE UPDATE ON sai_model_config
FOR EACH ROW
EXECUTE FUNCTION update_timestamp_updated();
CREATE INDEX fk_provider_id ON sai_model_config (provider_id);
CREATE INDEX idx_provider_model_type ON sai_model_config (provider_id, model_type);
CREATE INDEX idx_model_type_enabled ON sai_model_config (model_type, is_enabled);
CREATE INDEX idx_owner_id ON sai_model_config (owner_id);
CREATE INDEX idx_is_default ON sai_model_config (is_default);
CREATE INDEX idx_scope ON sai_model_config (scope);
CREATE INDEX idx_model_key ON sai_model_config (model_key);
-- 2.3 模型使用统计表
-- ### 模型使用统计表
CREATE TABLE IF NOT EXISTS sai_model_usage_stat
(
id BIGSERIAL PRIMARY KEY,
model_id BIGINT NOT NULL,
user_id BIGINT NOT NULL,
total_calls BIGINT DEFAULT 0,
success_calls BIGINT DEFAULT 0,
failed_calls BIGINT DEFAULT 0,
total_tokens_used BIGINT DEFAULT 0,
total_cost DECIMAL(18, 8) DEFAULT 0,
avg_response_time BIGINT DEFAULT 0,
last_used_dt TIMESTAMP NULL DEFAULT NULL,
created_dt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_dt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT unique_model_user UNIQUE (model_id, user_id)
);
COMMENT ON TABLE sai_model_usage_stat IS '模型使用统计表';
COMMENT ON COLUMN sai_model_usage_stat.model_id IS '模型ID';
COMMENT ON COLUMN sai_model_usage_stat.user_id IS '用户ID';
COMMENT ON COLUMN sai_model_usage_stat.total_calls IS '总调用次数';
COMMENT ON COLUMN sai_model_usage_stat.success_calls IS '成功调用次数';
COMMENT ON COLUMN sai_model_usage_stat.failed_calls IS '失败调用次数';
COMMENT ON COLUMN sai_model_usage_stat.total_tokens_used IS '总Token使用量';
COMMENT ON COLUMN sai_model_usage_stat.total_cost IS '总费用';
COMMENT ON COLUMN sai_model_usage_stat.avg_response_time IS '平均响应时间(毫秒)';
COMMENT ON COLUMN sai_model_usage_stat.last_used_dt IS '最后使用时间';
COMMENT ON COLUMN sai_model_usage_stat.created_dt IS '创建时间';
COMMENT ON COLUMN sai_model_usage_stat.updated_dt IS '更新时间';
CREATE TRIGGER trigger_sai_model_usage_stat_update
BEFORE UPDATE ON sai_model_usage_stat
FOR EACH ROW
EXECUTE FUNCTION update_timestamp_updated();
CREATE INDEX fk_stat_model_id ON sai_model_usage_stat (model_id);
CREATE INDEX idx_model_id ON sai_model_usage_stat (model_id);
CREATE INDEX idx_user_id ON sai_model_usage_stat (user_id);
CREATE INDEX idx_last_used_dt ON sai_model_usage_stat (last_used_dt);
-- ============================================================
-- 三、智能体(Agent)
-- ============================================================
-- 3.1 智能体主表
-- ### 智能体主表
CREATE TABLE IF NOT EXISTS sai_agent
(
id BIGSERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL,
description TEXT,
avatar VARCHAR(512),
instruction TEXT,
greeting TEXT,
preset_questions TEXT,
chat_model_id BIGINT,
memory_enabled BOOLEAN DEFAULT FALSE,
mcp_enabled BOOLEAN DEFAULT FALSE,
skill_enabled BOOLEAN DEFAULT FALSE,
web_search_enabled BOOLEAN DEFAULT FALSE,
rag_enabled BOOLEAN DEFAULT FALSE,
rag_ids VARCHAR(64) NULL,
rag_call_mode SMALLINT DEFAULT 1,
short_term_memory_size INT DEFAULT 20,
creator_id BIGINT,
is_featured BOOLEAN DEFAULT FALSE,
view_count INT DEFAULT 0,
status SMALLINT DEFAULT 1,
config TEXT,
app_id VARCHAR(128) NULL,
create_dt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
update_dt TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
COMMENT ON TABLE sai_agent IS '智能体表';
COMMENT ON COLUMN sai_agent.name IS '智能体名称';
COMMENT ON COLUMN sai_agent.description IS '智能体描述';
COMMENT ON COLUMN sai_agent.avatar IS '头像URL';
COMMENT ON COLUMN sai_agent.instruction IS '系统指令(System Prompt)';
COMMENT ON COLUMN sai_agent.greeting IS '欢迎语';
COMMENT ON COLUMN sai_agent.preset_questions IS '预设问题列表(JSON数组字符串)';
COMMENT ON COLUMN sai_agent.chat_model_id IS '关联的对话模型ID';
COMMENT ON COLUMN sai_agent.memory_enabled IS '是否启用记忆库';
COMMENT ON COLUMN sai_agent.mcp_enabled IS '是否启用MCP';
COMMENT ON COLUMN sai_agent.skill_enabled IS '是否启用Skill';
COMMENT ON COLUMN sai_agent.web_search_enabled IS '是否启用联网搜索';
COMMENT ON COLUMN sai_agent.rag_enabled IS '是否启用RAG';
COMMENT ON COLUMN sai_agent.rag_ids IS '绑定的RAG ID列表,逗号分隔,最多5个';
COMMENT ON COLUMN sai_agent.rag_call_mode IS 'RAG调用方式: 1=智能调用 2=强制调用';
COMMENT ON COLUMN sai_agent.short_term_memory_size IS '短期记忆滑动窗口保留条数';
COMMENT ON COLUMN sai_agent.creator_id IS '创建者用户ID';
COMMENT ON COLUMN sai_agent.is_featured IS '是否精选';
COMMENT ON COLUMN sai_agent.view_count IS '浏览次数';
COMMENT ON COLUMN sai_agent.status IS '状态: 1-活跃 2-非活跃 3-已废弃 4-已禁用';
COMMENT ON COLUMN sai_agent.config IS '扩展配置(预留)';
COMMENT ON COLUMN sai_agent.app_id IS '关联应用ID(NULL=本地执行)';
CREATE TRIGGER trigger_sai_agent_update
BEFORE UPDATE ON sai_agent
FOR EACH ROW
EXECUTE FUNCTION update_timestamp();
CREATE INDEX idx_agent_creator ON sai_agent (creator_id);
CREATE INDEX idx_agent_featured ON sai_agent (is_featured);
-- 3.2 智能体对话表
-- ### 智能体对话表
CREATE TABLE IF NOT EXISTS sai_agent_conversation
(
id BIGSERIAL PRIMARY KEY,
agent_id BIGINT NOT NULL,
user_id BIGINT NOT NULL,
conversation_id VARCHAR(64) NOT NULL,
title VARCHAR(255),
create_dt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
update_dt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT uk_conv_id UNIQUE (conversation_id)
);
COMMENT ON TABLE sai_agent_conversation IS '智能体对话表';
COMMENT ON COLUMN sai_agent_conversation.agent_id IS '智能体ID';
COMMENT ON COLUMN sai_agent_conversation.user_id IS '用户ID';
COMMENT ON COLUMN sai_agent_conversation.conversation_id IS '对话ID(UUID)';
COMMENT ON COLUMN sai_agent_conversation.title IS '对话标题';
CREATE TRIGGER trigger_sai_agent_conversation_update
BEFORE UPDATE ON sai_agent_conversation
FOR EACH ROW
EXECUTE FUNCTION update_timestamp();
CREATE INDEX idx_agent_conv_agent ON sai_agent_conversation (agent_id);
CREATE INDEX idx_agent_conv_user ON sai_agent_conversation (user_id);
-- 3.3 智能体对话消息记录表
-- ### 智能体对话消息记录表
CREATE TABLE IF NOT EXISTS sai_agent_conversation_record
(
id BIGSERIAL PRIMARY KEY,
agent_id BIGINT NOT NULL,
conversation_id VARCHAR(64) NOT NULL,
user_id BIGINT NOT NULL,
role VARCHAR(16) DEFAULT 'user',
content TEXT,
thinking TEXT,
metadata TEXT DEFAULT NULL,
status INT DEFAULT 1,
input_tokens INT DEFAULT 0,
output_tokens INT DEFAULT 0,
cache_tokens INT DEFAULT 0,
create_dt TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
COMMENT ON TABLE sai_agent_conversation_record IS '智能体对话消息记录';
COMMENT ON COLUMN sai_agent_conversation_record.agent_id IS '智能体ID';
COMMENT ON COLUMN sai_agent_conversation_record.conversation_id IS '对话ID';
COMMENT ON COLUMN sai_agent_conversation_record.user_id IS '用户ID';
COMMENT ON COLUMN sai_agent_conversation_record.role IS 'user/assistant';
COMMENT ON COLUMN sai_agent_conversation_record.content IS '消息内容';
COMMENT ON COLUMN sai_agent_conversation_record.thinking IS '思考过程(仅assistant)';
COMMENT ON COLUMN sai_agent_conversation_record.metadata IS '消息扩展元数据JSON';
COMMENT ON COLUMN sai_agent_conversation_record.status IS '1=成功,2=失败,3=进行中';
COMMENT ON COLUMN sai_agent_conversation_record.input_tokens IS '输入Token数(prompt)';
COMMENT ON COLUMN sai_agent_conversation_record.output_tokens IS '输出Token数(completion)';
COMMENT ON COLUMN sai_agent_conversation_record.cache_tokens IS '缓存命中Token数';
CREATE INDEX idx_agent_rec_conv ON sai_agent_conversation_record (conversation_id);
-- 3.4 智能体使用统计表
-- ### 智能体使用统计表
CREATE TABLE IF NOT EXISTS sai_agent_usage_stat
(
id BIGSERIAL PRIMARY KEY,
agent_id BIGINT NOT NULL,
user_id BIGINT NOT NULL,
message_count INT DEFAULT 0,
conversation_count INT DEFAULT 0,
stat_date DATE NOT NULL,
create_dt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
update_dt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT uk_agent_user_date UNIQUE (agent_id, user_id, stat_date)
);
COMMENT ON TABLE sai_agent_usage_stat IS '智能体使用统计';
COMMENT ON COLUMN sai_agent_usage_stat.agent_id IS '智能体ID';
COMMENT ON COLUMN sai_agent_usage_stat.user_id IS '用户ID';
COMMENT ON COLUMN sai_agent_usage_stat.message_count IS '消息条数';
COMMENT ON COLUMN sai_agent_usage_stat.conversation_count IS '对话轮次';
COMMENT ON COLUMN sai_agent_usage_stat.stat_date IS '统计日期';
CREATE TRIGGER trigger_sai_agent_usage_stat_update
BEFORE UPDATE ON sai_agent_usage_stat
FOR EACH ROW
EXECUTE FUNCTION update_timestamp();
CREATE INDEX idx_usage_agent ON sai_agent_usage_stat (agent_id);
CREATE INDEX idx_usage_date ON sai_agent_usage_stat (stat_date);
-- 3.5 用户订阅的智能体(多对多)
-- ### 用户智能体订阅关联表
CREATE TABLE IF NOT EXISTS sai_user_agent
(
id BIGSERIAL PRIMARY KEY,
user_id BIGINT NOT NULL,
agent_id BIGINT NOT NULL,
create_dt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT uk_user_agent UNIQUE (user_id, agent_id)
);
COMMENT ON TABLE sai_user_agent IS '用户订阅的智能体';
COMMENT ON COLUMN sai_user_agent.user_id IS '用户ID';
COMMENT ON COLUMN sai_user_agent.agent_id IS '智能体ID';
CREATE INDEX idx_user_agent_user ON sai_user_agent (user_id);
-- ============================================================
-- 四、RAG 知识库
-- ============================================================
-- 4.1 知识库主表
-- ### RAG 知识库表
CREATE TABLE sai_rag
(
id BIGSERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL,
description TEXT,
icon VARCHAR(512),
embedding_model_id BIGINT NOT NULL,
dimension_of_vector_model INT NOT NULL,
rerank_model_id BIGINT,
search_engine_instance_id BIGINT,
vector_store_instance_id BIGINT,
search_engine_enable BOOLEAN DEFAULT FALSE,
delimiter VARCHAR(32) DEFAULT E'\n\n',
rag_enhancement TEXT,
config TEXT DEFAULT NULL,
dedup_strategy SMALLINT NOT NULL DEFAULT 2,
dedup_action SMALLINT NOT NULL DEFAULT 0,
upload_confirm BOOLEAN NOT NULL DEFAULT TRUE,
create_dt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
update_dt TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
COMMENT ON COLUMN sai_rag.dimension_of_vector_model IS '向量维度';
COMMENT ON COLUMN sai_rag.config IS 'RAG检索和问答的页面配置参数';
COMMENT ON COLUMN sai_rag.dedup_strategy IS '去重策略: 0=NONE 1=BY_NAME 2=BY_CONTENT 3=BY_NAME_OR_CONTENT';
COMMENT ON COLUMN sai_rag.dedup_action IS '冲突动作: 0=REJECT 1=SKIP 2=OVERWRITE';
COMMENT ON COLUMN sai_rag.upload_confirm IS '上传前二次确认: 0-关 1-开';
CREATE TRIGGER trigger_sai_rag_update
BEFORE UPDATE ON sai_rag
FOR EACH ROW
EXECUTE FUNCTION update_timestamp();
-- 4.2 RAG 文档表
-- ### RAG 文档表
CREATE TABLE sai_rag_document
(
id BIGSERIAL PRIMARY KEY,
rag_id BIGINT NOT NULL,
name VARCHAR(255),
file_type VARCHAR(32),
source_type VARCHAR(32),
source_path VARCHAR(1024),
storage_path VARCHAR(1024),
storage_type VARCHAR(32) DEFAULT 'LOCAL',
file_size BIGINT DEFAULT 0,
content TEXT,
status SMALLINT DEFAULT 0,
error_msg TEXT,
chunk_count INT DEFAULT 0,
page_count INT DEFAULT 0,
element_count INT DEFAULT 0,
table_count INT DEFAULT 0,
image_count INT DEFAULT 0,
parse_time INT DEFAULT 0,
md_content TEXT DEFAULT NULL,
doc_metadata TEXT DEFAULT NULL,
content_hash VARCHAR(64) DEFAULT NULL,
resource_id BIGINT DEFAULT NULL,
create_dt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
update_dt TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
COMMENT ON COLUMN sai_rag_document.status IS '状态: 0-待处理 1-解析中 2-处理中 3-处理完成 4-处理失败';
COMMENT ON COLUMN sai_rag_document.content_hash IS '文件内容SHA-256哈希,用于去重';
COMMENT ON COLUMN sai_rag_document.resource_id IS '关联资源库 sai_resource.id';
CREATE TRIGGER trigger_sai_rag_document_update
BEFORE UPDATE ON sai_rag_document
FOR EACH ROW
EXECUTE FUNCTION update_timestamp();
CREATE INDEX idx_rag_doc_rag ON sai_rag_document (rag_id);
CREATE INDEX idx_rag_content_hash ON sai_rag_document (rag_id, content_hash);
CREATE INDEX idx_rag_name ON sai_rag_document (rag_id, name);
CREATE INDEX idx_rag_doc_resource ON sai_rag_document (resource_id);
-- ### RAG 文档图片表
CREATE TABLE sai_rag_document_image
(
id BIGSERIAL PRIMARY KEY,
rag_id BIGINT NOT NULL,
document_id BIGINT NOT NULL,
chunk_id BIGINT DEFAULT NULL,
resource_id BIGINT DEFAULT NULL,
image_index INT,
image_url VARCHAR(1024),
caption TEXT,
figure_no VARCHAR(64),
figure_title VARCHAR(512),
section_title VARCHAR(512),
source_page INT,
document_name VARCHAR(255),
ocr_text TEXT,
create_dt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
update_dt TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
COMMENT ON TABLE sai_rag_document_image IS 'RAG document parsed image table';
COMMENT ON COLUMN sai_rag_document_image.resource_id IS 'Linked resource id in sai_resource';
CREATE TRIGGER trigger_sai_rag_document_image_update
BEFORE UPDATE ON sai_rag_document_image
FOR EACH ROW
EXECUTE FUNCTION update_timestamp();
CREATE INDEX idx_rag_doc_image_rag ON sai_rag_document_image (rag_id);
CREATE INDEX idx_rag_doc_image_document ON sai_rag_document_image (document_id);
CREATE INDEX idx_rag_doc_image_chunk ON sai_rag_document_image (chunk_id);
CREATE INDEX idx_rag_doc_image_resource ON sai_rag_document_image (resource_id);
-- 4.3 RAG 分块表
-- ### RAG 文档分块表
CREATE TABLE sai_rag_chunk
(
id BIGSERIAL PRIMARY KEY,
rag_id BIGINT NOT NULL,
document_id BIGINT NOT NULL,
paragraph_index INT,
chunk_index INT,
content TEXT,
token_count INT,
vector_id VARCHAR(128),
content_hash VARCHAR(64) DEFAULT NULL,
source_type VARCHAR(20) NOT NULL DEFAULT 'TEXT',
create_dt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
update_dt TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
COMMENT ON COLUMN sai_rag_chunk.content_hash IS 'chunk内容SHA-256,用于向量去重';
COMMENT ON COLUMN sai_rag_chunk.source_type IS 'chunk来源类型:TEXT文本、IMAGE图片';
CREATE TRIGGER trigger_sai_rag_chunk_update
BEFORE UPDATE ON sai_rag_chunk
FOR EACH ROW
EXECUTE FUNCTION update_timestamp();
CREATE INDEX idx_rag_chunk_rag ON sai_rag_chunk (rag_id);
CREATE INDEX idx_rag_chunk_document ON sai_rag_chunk (document_id);
CREATE INDEX idx_chunk_rag_hash ON sai_rag_chunk (rag_id, content_hash);
CREATE INDEX idx_chunk_rag_source_type ON sai_rag_chunk (rag_id, source_type);
-- ============================================================
-- 五、MCP 服务管理
-- ============================================================
-- 5.1 MCP 服务配置表
-- ### MCP 服务配置表
CREATE TABLE IF NOT EXISTS sai_mcp_server
(
id BIGSERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL,
description TEXT,
transport_type SMALLINT DEFAULT 1,
base_uri VARCHAR(1024),
endpoint VARCHAR(1024),
command VARCHAR(1024),
args TEXT,
env_vars TEXT,
timeout BIGINT DEFAULT 60000,
headers TEXT,
last_connect_dt TIMESTAMP NULL,
creator_id BIGINT,
create_dt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
update_dt TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
COMMENT ON TABLE sai_mcp_server IS 'MCP服务配置表';
COMMENT ON COLUMN sai_mcp_server.name IS 'MCP服务名称';
COMMENT ON COLUMN sai_mcp_server.description IS 'MCP服务描述';
COMMENT ON COLUMN sai_mcp_server.transport_type IS '传输类型: 1-SSE 2-Streamable HTTP 3-Stdio';
COMMENT ON COLUMN sai_mcp_server.base_uri IS '服务基础地址(SSE/Streamable HTTP时使用)';
COMMENT ON COLUMN sai_mcp_server.endpoint IS '端点路径(SSE/Streamable HTTP时可选)';
COMMENT ON COLUMN sai_mcp_server.command IS 'Stdio命令(Stdio时必填)';
COMMENT ON COLUMN sai_mcp_server.args IS 'Stdio命令参数(JSON数组)';
COMMENT ON COLUMN sai_mcp_server.env_vars IS 'Stdio环境变量(JSON对象)';
COMMENT ON COLUMN sai_mcp_server.timeout IS '超时时间(毫秒)';
COMMENT ON COLUMN sai_mcp_server.headers IS '请求头(JSON对象)';
COMMENT ON COLUMN sai_mcp_server.last_connect_dt IS '最后连接时间';
COMMENT ON COLUMN sai_mcp_server.creator_id IS '创建者用户ID';
CREATE TRIGGER trigger_sai_mcp_server_update
BEFORE UPDATE ON sai_mcp_server
FOR EACH ROW
EXECUTE FUNCTION update_timestamp();
CREATE INDEX idx_mcp_server_creator ON sai_mcp_server (creator_id);
-- 5.2 智能体与MCP服务关联表(多对多)
-- ### 智能体与 MCP 服务关联表
CREATE TABLE IF NOT EXISTS sai_agent_mcp_server
(
id BIGSERIAL PRIMARY KEY,
agent_id BIGINT NOT NULL,
mcp_server_id BIGINT NOT NULL,
create_dt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT uk_agent_mcp UNIQUE (agent_id, mcp_server_id)
);
COMMENT ON TABLE sai_agent_mcp_server IS '智能体MCP服务关联表';
COMMENT ON COLUMN sai_agent_mcp_server.agent_id IS '智能体ID';
COMMENT ON COLUMN sai_agent_mcp_server.mcp_server_id IS 'MCP服务ID';
CREATE INDEX idx_agent_mcp_agent ON sai_agent_mcp_server (agent_id);
CREATE INDEX idx_agent_mcp_server ON sai_agent_mcp_server (mcp_server_id);
-- ============================================================
-- 六、Skill 技能包管理
-- ============================================================
-- 6.1 Skill 技能包表
-- ### Skill 技能包表
CREATE TABLE IF NOT EXISTS sai_skill
(
id BIGSERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL,
description TEXT,
file_name VARCHAR(255),
file_path VARCHAR(1024),
file_size BIGINT DEFAULT 0,
skill_content TEXT,
storage_path VARCHAR(500) DEFAULT NULL,
version BIGINT DEFAULT 0,
has_files BOOLEAN DEFAULT FALSE,
creator_id BIGINT,
create_dt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
update_dt TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
COMMENT ON TABLE sai_skill IS 'Skill技能包表';
COMMENT ON COLUMN sai_skill.name IS 'Skill名称(从SKILL.md解析)';
COMMENT ON COLUMN sai_skill.description IS 'Skill描述(从SKILL.md解析)';
COMMENT ON COLUMN sai_skill.file_name IS '上传的zip文件名';
COMMENT ON COLUMN sai_skill.file_path IS '解压后存储路径';
COMMENT ON COLUMN sai_skill.file_size IS '文件大小(字节)';
COMMENT ON COLUMN sai_skill.skill_content IS 'SKILL.md正文内容(去除frontmatter)';
COMMENT ON COLUMN sai_skill.storage_path IS '对象存储相对路径前缀(如 skills/123/)';
COMMENT ON COLUMN sai_skill.version IS '版本号,文件变更时自增,用于缓存一致性校验';
COMMENT ON COLUMN sai_skill.has_files IS '是否包含支撑文件(0=仅SKILL.md,1=有scripts/references等)';
COMMENT ON COLUMN sai_skill.creator_id IS '创建者用户ID';
CREATE TRIGGER trigger_sai_skill_update
BEFORE UPDATE ON sai_skill
FOR EACH ROW
EXECUTE FUNCTION update_timestamp();
CREATE INDEX idx_skill_creator ON sai_skill (creator_id);
-- 6.2 Skill 支撑文件内容表
-- ### Skill 支撑文件表
CREATE TABLE IF NOT EXISTS sai_skill_file
(
id BIGSERIAL PRIMARY KEY,
skill_id BIGINT NOT NULL,
file_path VARCHAR(255) NOT NULL,
content TEXT NOT NULL,
file_size INT NOT NULL,
encoding VARCHAR(50) DEFAULT 'utf-8',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT uk_skill_path UNIQUE (skill_id, file_path)
);
COMMENT ON TABLE sai_skill_file IS 'Skill支撑文件内容表';
COMMENT ON COLUMN sai_skill_file.skill_id IS 'Skill ID';
COMMENT ON COLUMN sai_skill_file.file_path IS '文件相对路径';
COMMENT ON COLUMN sai_skill_file.content IS '文件内容';
COMMENT ON COLUMN sai_skill_file.file_size IS '文件大小(字节)';
COMMENT ON COLUMN sai_skill_file.encoding IS '编码方式';
COMMENT ON COLUMN sai_skill_file.created_at IS '创建时间';
COMMENT ON COLUMN sai_skill_file.updated_at IS '更新时间';
CREATE TRIGGER trigger_sai_skill_file_update
BEFORE UPDATE ON sai_skill_file
FOR EACH ROW
EXECUTE FUNCTION update_timestamp_updated_at();
CREATE INDEX idx_skill_id ON sai_skill_file (skill_id);
-- 6.3 智能体与Skill关联表(多对多)
-- ### 智能体与 Skill 关联表
CREATE TABLE IF NOT EXISTS sai_agent_skill
(
id BIGSERIAL PRIMARY KEY,
agent_id BIGINT NOT NULL,
skill_id BIGINT NOT NULL,
create_dt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT uk_agent_skill UNIQUE (agent_id, skill_id)
);
COMMENT ON TABLE sai_agent_skill IS '智能体Skill关联表';
COMMENT ON COLUMN sai_agent_skill.agent_id IS '智能体ID';
COMMENT ON COLUMN sai_agent_skill.skill_id IS 'Skill ID';
CREATE INDEX idx_agent_skill_agent ON sai_agent_skill (agent_id);
CREATE INDEX idx_agent_skill_skill ON sai_agent_skill (skill_id);
-- ============================================================
-- 七、客户端应用与节点
-- ============================================================
-- 7.1 客户端应用
-- ### 客户端应用表
CREATE TABLE IF NOT EXISTS sai_app
(
id BIGSERIAL PRIMARY KEY,
app_id VARCHAR(128) NOT NULL,
app_name VARCHAR(255) NOT NULL,
description VARCHAR(512),
token VARCHAR(128) NOT NULL,
route_strategy VARCHAR(32) DEFAULT 'LEAST_LOAD',
status SMALLINT DEFAULT 1,
create_dt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
update_dt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT uk_app_id UNIQUE (app_id)
);
COMMENT ON TABLE sai_app IS '客户端应用';
COMMENT ON COLUMN sai_app.app_id IS '应用唯一标识';
COMMENT ON COLUMN sai_app.app_name IS '应用名称';
COMMENT ON COLUMN sai_app.description IS '应用描述';
COMMENT ON COLUMN sai_app.token IS '通信认证令牌';
COMMENT ON COLUMN sai_app.route_strategy IS '路由策略';
COMMENT ON COLUMN sai_app.status IS '1=启用, 0=停用';
CREATE TRIGGER trigger_sai_app_update
BEFORE UPDATE ON sai_app
FOR EACH ROW
EXECUTE FUNCTION update_timestamp();
-- 7.2 AI客户端实例节点
-- ### AI 客户端节点表
CREATE TABLE IF NOT EXISTS sai_client_node
(
id BIGSERIAL PRIMARY KEY,
app_id VARCHAR(128) NOT NULL,
host_id VARCHAR(128) NOT NULL,
host_ip VARCHAR(64) NOT NULL,
grpc_port INT NOT NULL,
max_concurrent INT DEFAULT 10,
active_chats INT DEFAULT 0,
supported_providers TEXT,
labels TEXT,
expire_dt TIMESTAMP NOT NULL,
create_dt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
update_dt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT uk_client_node UNIQUE (app_id, host_id)
);
COMMENT ON TABLE sai_client_node IS 'AI客户端实例节点';
COMMENT ON COLUMN sai_client_node.app_id IS '所属应用ID';
COMMENT ON COLUMN sai_client_node.host_id IS '客户端实例唯一标识';
COMMENT ON COLUMN sai_client_node.host_ip IS '客户端IP';
COMMENT ON COLUMN sai_client_node.grpc_port IS '客户端gRPC端口';
COMMENT ON COLUMN sai_client_node.max_concurrent IS '最大并发对话数';
COMMENT ON COLUMN sai_client_node.active_chats IS '当前活跃对话数';
COMMENT ON COLUMN sai_client_node.supported_providers IS '支持的模型提供商(JSON数组)';
COMMENT ON COLUMN sai_client_node.labels IS '路由标签';
COMMENT ON COLUMN sai_client_node.expire_dt IS '过期时间(心跳更新)';
CREATE TRIGGER trigger_sai_client_node_update
BEFORE UPDATE ON sai_client_node
FOR EACH ROW
EXECUTE FUNCTION update_timestamp();
CREATE INDEX idx_app_expire ON sai_client_node (app_id, expire_dt);
-- ============================================================
-- 八、存储与资源
-- ============================================================
-- 8.1 存储实例
-- ### 存储实例表
CREATE TABLE IF NOT EXISTS sai_store_instance
(
id BIGSERIAL PRIMARY KEY,
name VARCHAR(128) NOT NULL,
category SMALLINT NOT NULL,
type SMALLINT NOT NULL,
config TEXT DEFAULT NULL,
status SMALLINT DEFAULT 1,
is_default BOOLEAN DEFAULT FALSE,
create_dt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
update_dt TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
COMMENT ON TABLE sai_store_instance IS '存储实例';
COMMENT ON COLUMN sai_store_instance.name IS '实例名称';
COMMENT ON COLUMN sai_store_instance.category IS '分类: 1-向量库 2-搜索引擎';
COMMENT ON COLUMN sai_store_instance.type IS '类型: 1-PG_VECTOR 2-MILVUS 3-ELASTICSEARCH 4-PG_FULLTEXT';
COMMENT ON COLUMN sai_store_instance.config IS '连接参数 JSON';
COMMENT ON COLUMN sai_store_instance.status IS '状态: 0-停用 1-启用';
COMMENT ON COLUMN sai_store_instance.is_default IS '是否为该 category 下默认实例';
CREATE TRIGGER trigger_sai_store_instance_update
BEFORE UPDATE ON sai_store_instance
FOR EACH ROW
EXECUTE FUNCTION update_timestamp();
CREATE INDEX idx_store_instance_category ON sai_store_instance (category);
CREATE INDEX idx_store_instance_type ON sai_store_instance (type);
-- 8.2 通用资源存储
-- ### 通用资源存储表
CREATE TABLE IF NOT EXISTS sai_resource
(
id BIGSERIAL PRIMARY KEY,
storage_key VARCHAR(512) NOT NULL,
original_name VARCHAR(255) NOT NULL,
file_size BIGINT DEFAULT 0,
mime_type VARCHAR(128),
storage_type VARCHAR(32) NOT NULL DEFAULT 'LOCAL',
access_url VARCHAR(1024),
biz_type VARCHAR(64) NOT NULL DEFAULT 'GENERAL',
biz_id BIGINT,
creator_id BIGINT,
create_dt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
update_dt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT uk_storage_key UNIQUE (storage_key)
);
COMMENT ON TABLE sai_resource IS '通用资源存储';
COMMENT ON COLUMN sai_resource.storage_key IS '存储键(相对路径或对象Key)';
COMMENT ON COLUMN sai_resource.original_name IS '原始文件名';
COMMENT ON COLUMN sai_resource.file_size IS '文件大小(bytes)';
COMMENT ON COLUMN sai_resource.mime_type IS 'MIME类型';
COMMENT ON COLUMN sai_resource.storage_type IS '存储类型: LOCAL/MINIO';
COMMENT ON COLUMN sai_resource.access_url IS '访问URL';
COMMENT ON COLUMN sai_resource.biz_type IS '业务类型: AVATAR/ATTACHMENT/DOCUMENT/GENERAL';
COMMENT ON COLUMN sai_resource.biz_id IS '关联业务ID';
COMMENT ON COLUMN sai_resource.creator_id IS '上传者ID';
CREATE TRIGGER trigger_sai_resource_update
BEFORE UPDATE ON sai_resource
FOR EACH ROW
EXECUTE FUNCTION update_timestamp();
CREATE INDEX idx_biz ON sai_resource (biz_type, biz_id);
CREATE INDEX idx_creator ON sai_resource (creator_id);