ai模块修改

This commit is contained in:
2025-12-15 15:26:05 +08:00
parent 3767150fd6
commit 8a03ede7dc
56 changed files with 3403 additions and 1119 deletions

View File

@@ -0,0 +1,51 @@
# Dify 服务代理配置
# Dify Web 前端
location /dify/ {
proxy_pass http://dify-web:3000/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# 支持 WebSocket
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
# 超时配置
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
}
# Dify API
location /dify/api/ {
proxy_pass http://dify-api:5001/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# 支持 WebSocket (SSE)
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
# 超时配置AI 响应可能较慢)
proxy_connect_timeout 300s;
proxy_send_timeout 300s;
proxy_read_timeout 300s;
# 禁用缓冲(支持流式响应)
proxy_buffering off;
}
# Dify App (公开应用)
location /dify/app/ {
proxy_pass http://dify-web:3000/app/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}

View File

@@ -0,0 +1,166 @@
CREATE SCHEMA IF NOT EXISTS ai;
-- AI智能体配置
DROP TABLE IF EXISTS ai.tb_agent CASCADE;
CREATE TABLE ai.tb_agent(
optsn VARCHAR(50) NOT NULL, -- 流水号
agent_id VARCHAR(50) NOT NULL, -- 智能体ID
name VARCHAR(50) NOT NULL, -- 智能体名称
description VARCHAR(500) DEFAULT NULL, -- 智能体描述
link VARCHAR(500) DEFAULT NULL, -- 智能体url
api_key VARCHAR(500) NOT NULL, -- dify智能体APIKEY
introduce VARCHAR(500) NOT NULL, -- 引导词
prompt_cards JSONB DEFAULT '[]'::jsonb, -- 提示卡片数组 [{file_id:'', prompt:''}]
category VARCHAR(50) NOT NULL, -- 分类
creator VARCHAR(50) DEFAULT NULL, -- 创建者
updater VARCHAR(50) DEFAULT NULL, -- 更新者
create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间
update_time TIMESTAMPTZ DEFAULT NULL, -- 更新时间
delete_time TIMESTAMPTZ DEFAULT NULL, -- 删除时间
deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除
PRIMARY KEY (agent_id),
UNIQUE (optsn),
UNIQUE (api_key)
);
-- AI智能体对话
DROP TABLE IF EXISTS ai.tb_chat CASCADE;
CREATE TABLE ai.tb_chat(
optsn VARCHAR(50) NOT NULL, -- 流水号
chat_id VARCHAR(50) NOT NULL, -- 对话ID
agent_id VARCHAR(50) NOT NULL, -- 智能体ID
user_id VARCHAR(50) NOT NULL, -- 用户ID
title VARCHAR(500) NOT NULL, -- 对话标题
create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间
update_time TIMESTAMPTZ DEFAULT NULL, -- 更新时间
delete_time TIMESTAMPTZ DEFAULT NULL, -- 删除时间
deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除
PRIMARY KEY (chat_id),
UNIQUE (optsn)
);
-- AI智能体对话消息
DROP TABLE IF EXISTS ai.tb_chat_message CASCADE;
CREATE TABLE ai.tb_chat_message(
optsn VARCHAR(50) NOT NULL, -- 流水号
message_id VARCHAR(50) NOT NULL, -- 消息ID
chat_id VARCHAR(50) NOT NULL, -- 对话ID
role VARCHAR(50) NOT NULL, -- 角色user-用户/assistant-智能体/recipient-客服
content TEXT NOT NULL, -- 消息内容
files VARCHAR(50)[] DEFAULT NULL, -- 文件id数组
create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间
update_time TIMESTAMPTZ DEFAULT NULL, -- 更新时间
delete_time TIMESTAMPTZ DEFAULT NULL, -- 删除时间
deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除
PRIMARY KEY (message_id),
UNIQUE (optsn)
);
-- 知识库配置 bidding和workcase2个服务使用
DROP TABLE IF EXISTS ai.tb_knowledge CASCADE;
CREATE TABLE ai.tb_knowledge(
-- 知识库dify相关配置
optsn VARCHAR(50) NOT NULL, -- 流水号
knowledge_id VARCHAR(50) NOT NULL, -- 知识库ID
title VARCHAR(255) NOT NULL, -- 知识库标题
avatar VARCHAR(255) DEFAULT NULL, -- 知识库头像
description VARCHAR(500) DEFAULT NULL, -- 知识库描述
dify_dataset_id VARCHAR(100) DEFAULT NULL, -- Dify知识库IDDataset ID
dify_indexing_technique VARCHAR(50) DEFAULT 'high_quality', -- Dify索引方式high_quality/economy
embedding_model VARCHAR(100) DEFAULT NULL, -- 向量模型名称
embedding_model_provider VARCHAR(100) DEFAULT NULL, -- 向量模型提供商
rerank_model VARCHAR(100) DEFAULT NULL, -- Rerank模型名称
rerank_model_provider VARCHAR(100) DEFAULT NULL, -- Rerank模型提供商
reranking_enable BOOLEAN DEFAULT false, -- 是否启用Rerank
retrieval_top_k INTEGER DEFAULT 2, -- 检索Top K返回前K个结果
retrieval_score_threshold DECIMAL(3,2) DEFAULT 0.00, -- 检索分数阈值0.00-1.00
document_count INTEGER DEFAULT 0, -- 文档数量
total_chunks INTEGER DEFAULT 0, -- 总分段数
-- 下面是服务使用
service VARCHAR(50) DEFAULT NULL, -- 所属服务 workcase、bidding
project_id VARCHAR(50) DEFAULT NULL, -- bidding所属项目ID
category VARCHAR(50) DEFAULT NULL, -- 所属分类 workcase 内部知识库、外部知识库
creator VARCHAR(50) NOT NULL, -- 创建者用户ID
dept_path VARCHAR(50) DEFAULT NULL, -- 创建者部门路径
updater VARCHAR(50) DEFAULT NULL, -- 更新者
create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间
update_time TIMESTAMPTZ DEFAULT NULL, -- 更新时间
delete_time TIMESTAMPTZ DEFAULT NULL, -- 删除时间
deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除
PRIMARY KEY (optsn),
UNIQUE (knowledge_id),
UNIQUE (dify_dataset_id)
);
-- 知识库配置表字段注释
COMMENT ON TABLE ai.tb_knowledge IS '知识库配置表';
COMMENT ON COLUMN ai.tb_knowledge.optsn IS '流水号';
COMMENT ON COLUMN ai.tb_knowledge.knowledge_id IS '知识库ID';
COMMENT ON COLUMN ai.tb_knowledge.title IS '知识库标题';
COMMENT ON COLUMN ai.tb_knowledge.avatar IS '知识库头像';
COMMENT ON COLUMN ai.tb_knowledge.description IS '知识库描述';
COMMENT ON COLUMN ai.tb_knowledge.dify_dataset_id IS 'Dify知识库IDDataset ID';
COMMENT ON COLUMN ai.tb_knowledge.dify_indexing_technique IS 'Dify索引方式high_quality/economy';
COMMENT ON COLUMN ai.tb_knowledge.embedding_model IS '向量模型名称';
COMMENT ON COLUMN ai.tb_knowledge.embedding_model_provider IS '向量模型提供商';
COMMENT ON COLUMN ai.tb_knowledge.rerank_model IS 'Rerank模型名称';
COMMENT ON COLUMN ai.tb_knowledge.rerank_model_provider IS 'Rerank模型提供商';
COMMENT ON COLUMN ai.tb_knowledge.reranking_enable IS '是否启用Rerank';
COMMENT ON COLUMN ai.tb_knowledge.retrieval_top_k IS '检索Top K返回前K个结果';
COMMENT ON COLUMN ai.tb_knowledge.retrieval_score_threshold IS '检索分数阈值0.00-1.00';
COMMENT ON COLUMN ai.tb_knowledge.document_count IS '文档数量';
COMMENT ON COLUMN ai.tb_knowledge.total_chunks IS '总分段数';
COMMENT ON COLUMN ai.tb_knowledge.service IS '所属服务 workcase、bidding';
COMMENT ON COLUMN ai.tb_knowledge.project_id IS 'bidding所属项目ID';
COMMENT ON COLUMN ai.tb_knowledge.category IS '所属分类 workcase 内部知识库、外部知识库';
COMMENT ON COLUMN ai.tb_knowledge.creator IS '创建者用户ID';
COMMENT ON COLUMN ai.tb_knowledge.dept_path IS '创建者部门路径';
COMMENT ON COLUMN ai.tb_knowledge.updater IS '更新者';
COMMENT ON COLUMN ai.tb_knowledge.create_time IS '创建时间';
COMMENT ON COLUMN ai.tb_knowledge.update_time IS '更新时间';
COMMENT ON COLUMN ai.tb_knowledge.delete_time IS '删除时间';
COMMENT ON COLUMN ai.tb_knowledge.deleted IS '是否删除';
-- bidding知识库根据project等变化
-- workcase知识库固定8个
-- workcase外部知识库4个知识库
-- 1. 设备操作指南
-- 2. 常见故障解决方案
-- 3. 三包外服务政策
-- 4. 配件咨询话术
-- workcase内部知识库4个知识库
-- 1. 技术维修手册
-- 2. 产品参数明细
-- 3. 内部服务流程规范
-- 4. 客户服务话术模板
-- 知识库文件 文件上传dify知识库对dify内的文件修改不生成新版本 只有重新上传才生成新版本
DROP TABLE IF EXISTS ai.tb_knowledge_file CASCADE;
CREATE TABLE ai.tb_knowledge_file(
optsn VARCHAR(50) NOT NULL, -- 流水号
knowledge_id VARCHAR(50) NOT NULL, -- 知识库ID
file_root_id VARCHAR(50) NOT NULL, -- 文件根ID
file_id VARCHAR(50) NOT NULL, -- 文件ID
dify_file_id VARCHAR(50) NOT NULL, -- dify文件ID
version VARCHAR(50) NOT NULL, -- 文件版本
create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间
update_time TIMESTAMPTZ DEFAULT NULL, -- 更新时间
delete_time TIMESTAMPTZ DEFAULT NULL, -- 删除时间
deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除
PRIMARY KEY (optsn),
UNIQUE (knowledge_id, file_id)
);
-- 知识库文件表字段注释
COMMENT ON TABLE ai.tb_knowledge_file IS '知识库文件表';
COMMENT ON COLUMN ai.tb_knowledge_file.optsn IS '流水号';
COMMENT ON COLUMN ai.tb_knowledge_file.knowledge_id IS '知识库ID';
COMMENT ON COLUMN ai.tb_knowledge_file.file_root_id IS '文件根ID';
COMMENT ON COLUMN ai.tb_knowledge_file.file_id IS '文件ID';
COMMENT ON COLUMN ai.tb_knowledge_file.dify_file_id IS 'dify文件ID';
COMMENT ON COLUMN ai.tb_knowledge_file.version IS '文件版本';
COMMENT ON COLUMN ai.tb_knowledge_file.create_time IS '创建时间';
COMMENT ON COLUMN ai.tb_knowledge_file.update_time IS '更新时间';
COMMENT ON COLUMN ai.tb_knowledge_file.delete_time IS '删除时间';
COMMENT ON COLUMN ai.tb_knowledge_file.deleted IS '是否删除';

View File

@@ -1,306 +0,0 @@
-- -- =============================
-- -- 智能体管理和平台基础设施模块
-- -- 支持智能体广场、API集成管理、智能体运维监控
-- -- =============================
-- CREATE SCHEMA IF NOT EXISTS agent;
-- -- 智能体定义表
-- DROP TABLE IF EXISTS agent.tb_agent CASCADE;
-- CREATE TABLE agent.tb_agent (
-- optsn VARCHAR(50) NOT NULL, -- 流水号
-- agent_id VARCHAR(50) NOT NULL, -- 智能体ID
-- agent_code VARCHAR(100) NOT NULL, -- 智能体编码(唯一标识)
-- agent_name VARCHAR(255) NOT NULL, -- 智能体名称
-- agent_type VARCHAR(50) NOT NULL, -- 智能体类型bidding-招投标/customer_service-客服/knowledge_assistant-知识助手/custom-自定义
-- display_name VARCHAR(255) NOT NULL, -- 展示名称
-- description TEXT, -- 智能体描述
-- icon VARCHAR(500), -- 图标URL
-- banner VARCHAR(500), -- Banner图URL
-- version VARCHAR(20) DEFAULT '1.0.0', -- 版本号
-- model_provider VARCHAR(50), -- 模型提供商openai/anthropic/baidu/aliyun/custom
-- model_name VARCHAR(100), -- 模型名称
-- model_config JSONB, -- 模型配置温度、最大tokens等
-- prompt_template TEXT, -- 提示词模板
-- system_prompt TEXT, -- 系统提示词
-- capabilities TEXT[], -- 能力列表
-- access_level VARCHAR(20) DEFAULT 'private', -- 访问级别public-公开/private-私有/internal-内部
-- is_published BOOLEAN DEFAULT false, -- 是否发布到智能体广场
-- usage_count INTEGER DEFAULT 0, -- 使用次数
-- rating DECIMAL(3,2) DEFAULT 0, -- 评分0-5
-- rating_count INTEGER DEFAULT 0, -- 评分人数
-- tags TEXT[], -- 标签数组
-- category VARCHAR(100), -- 分类
-- dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径
-- owner_user_id VARCHAR(50), -- 所有者用户ID
-- status VARCHAR(20) DEFAULT 'active', -- 状态active-激活/inactive-停用/under_maintenance-维护中
-- creator VARCHAR(50) DEFAULT NULL, -- 创建者
-- updater VARCHAR(50) DEFAULT NULL, -- 更新者
-- create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间
-- update_time TIMESTAMPTZ DEFAULT NULL, -- 更新时间
-- delete_time TIMESTAMPTZ DEFAULT NULL, -- 删除时间
-- deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除
-- PRIMARY KEY (agent_id),
-- UNIQUE (optsn),
-- UNIQUE (agent_code)
-- );
-- CREATE INDEX idx_agent_type ON agent.tb_agent(agent_type) WHERE deleted = false;
-- CREATE INDEX idx_agent_published ON agent.tb_agent(is_published) WHERE deleted = false AND is_published = true;
-- CREATE INDEX idx_agent_category ON agent.tb_agent(category) WHERE deleted = false;
-- COMMENT ON TABLE agent.tb_agent IS '智能体定义表';
-- COMMENT ON COLUMN agent.tb_agent.agent_type IS '智能体类型bidding/customer_service/knowledge_assistant/custom';
-- -- 智能体会话表
-- DROP TABLE IF EXISTS agent.tb_agent_session CASCADE;
-- CREATE TABLE agent.tb_agent_session (
-- optsn VARCHAR(50) NOT NULL, -- 流水号
-- session_id VARCHAR(50) NOT NULL, -- 会话ID
-- agent_id VARCHAR(50) NOT NULL, -- 智能体ID
-- user_id VARCHAR(50) NOT NULL, -- 用户ID
-- session_type VARCHAR(30) DEFAULT 'chat', -- 会话类型chat-对话/task-任务/workflow-工作流
-- session_name VARCHAR(255), -- 会话名称
-- context JSONB, -- 会话上下文
-- session_status VARCHAR(20) DEFAULT 'active', -- 会话状态active-活跃/paused-暂停/ended-结束
-- start_time TIMESTAMPTZ DEFAULT now(), -- 开始时间
-- end_time TIMESTAMPTZ, -- 结束时间
-- message_count INTEGER DEFAULT 0, -- 消息数量
-- token_usage INTEGER DEFAULT 0, -- Token使用量
-- cost DECIMAL(10,4) DEFAULT 0, -- 成本
-- dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径
-- creator VARCHAR(50) DEFAULT NULL, -- 创建者
-- create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间
-- update_time TIMESTAMPTZ DEFAULT NULL, -- 更新时间
-- deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除
-- PRIMARY KEY (session_id),
-- UNIQUE (optsn),
-- FOREIGN KEY (agent_id) REFERENCES agent.tb_agent(agent_id)
-- );
-- CREATE INDEX idx_session_agent ON agent.tb_agent_session(agent_id) WHERE deleted = false;
-- CREATE INDEX idx_session_user ON agent.tb_agent_session(user_id) WHERE deleted = false;
-- COMMENT ON TABLE agent.tb_agent_session IS '智能体会话表';
-- -- 智能体消息表
-- DROP TABLE IF EXISTS agent.tb_agent_message CASCADE;
-- CREATE TABLE agent.tb_agent_message (
-- optsn VARCHAR(50) NOT NULL, -- 流水号
-- message_id VARCHAR(50) NOT NULL, -- 消息ID
-- session_id VARCHAR(50) NOT NULL, -- 会话ID
-- agent_id VARCHAR(50) NOT NULL, -- 智能体ID
-- role VARCHAR(20) NOT NULL, -- 角色user-用户/assistant-助手/system-系统/function-函数
-- content TEXT, -- 消息内容
-- content_type VARCHAR(30) DEFAULT 'text', -- 内容类型text-文本/image-图片/file-文件/structured-结构化数据
-- function_call JSONB, -- 函数调用JSON格式
-- function_response JSONB, -- 函数响应
-- token_count INTEGER, -- Token数量
-- model_name VARCHAR(100), -- 使用的模型
-- kb_references JSONB, -- 知识库引用JSON数组
-- metadata JSONB, -- 消息元数据
-- dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径
-- create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间
-- deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除
-- PRIMARY KEY (message_id),
-- UNIQUE (optsn),
-- FOREIGN KEY (session_id) REFERENCES agent.tb_agent_session(session_id),
-- FOREIGN KEY (agent_id) REFERENCES agent.tb_agent(agent_id)
-- );
-- CREATE INDEX idx_msg_session ON agent.tb_agent_message(session_id, create_time) WHERE deleted = false;
-- CREATE INDEX idx_msg_agent ON agent.tb_agent_message(agent_id) WHERE deleted = false;
-- COMMENT ON TABLE agent.tb_agent_message IS '智能体消息表';
-- -- 智能体工具表
-- DROP TABLE IF EXISTS agent.tb_agent_tool CASCADE;
-- CREATE TABLE agent.tb_agent_tool (
-- optsn VARCHAR(50) NOT NULL, -- 流水号
-- tool_id VARCHAR(50) NOT NULL, -- 工具ID
-- tool_code VARCHAR(100) NOT NULL, -- 工具编码
-- tool_name VARCHAR(255) NOT NULL, -- 工具名称
-- tool_type VARCHAR(50) NOT NULL, -- 工具类型api-API调用/function-函数/plugin-插件/integration-集成
-- description TEXT, -- 工具描述
-- function_schema JSONB, -- 函数SchemaOpenAI function calling格式
-- api_endpoint VARCHAR(500), -- API端点
-- api_method VARCHAR(10), -- API方法GET/POST/PUT/DELETE
-- api_headers JSONB, -- API请求头
-- auth_type VARCHAR(30), -- 认证类型none/api_key/oauth2/bearer
-- auth_config JSONB, -- 认证配置(加密存储)
-- request_template TEXT, -- 请求模板
-- response_template TEXT, -- 响应模板
-- timeout_seconds INTEGER DEFAULT 30, -- 超时时间(秒)
-- retry_count INTEGER DEFAULT 3, -- 重试次数
-- is_enabled BOOLEAN DEFAULT true, -- 是否启用
-- usage_count INTEGER DEFAULT 0, -- 使用次数
-- dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径
-- creator VARCHAR(50) DEFAULT NULL, -- 创建者
-- updater VARCHAR(50) DEFAULT NULL, -- 更新者
-- create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间
-- update_time TIMESTAMPTZ DEFAULT NULL, -- 更新时间
-- delete_time TIMESTAMPTZ DEFAULT NULL, -- 删除时间
-- deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除
-- PRIMARY KEY (tool_id),
-- UNIQUE (optsn),
-- UNIQUE (tool_code)
-- );
-- CREATE INDEX idx_tool_type ON agent.tb_agent_tool(tool_type) WHERE deleted = false;
-- COMMENT ON TABLE agent.tb_agent_tool IS '智能体工具表';
-- -- API集成注册表
-- DROP TABLE IF EXISTS agent.tb_api_integration CASCADE;
-- CREATE TABLE agent.tb_api_integration (
-- optsn VARCHAR(50) NOT NULL, -- 流水号
-- integration_id VARCHAR(50) NOT NULL, -- 集成ID
-- integration_name VARCHAR(255) NOT NULL, -- 集成名称
-- integration_type VARCHAR(50) NOT NULL, -- 集成类型rest_api/soap/graphql/webhook/mq
-- provider VARCHAR(100), -- 提供商
-- base_url VARCHAR(500), -- 基础URL
-- version VARCHAR(20), -- API版本
-- auth_type VARCHAR(30) DEFAULT 'none', -- 认证类型
-- auth_config JSONB, -- 认证配置(加密存储)
-- endpoints JSONB, -- 端点列表JSON数组
-- rate_limit INTEGER, -- 速率限制(请求/秒)
-- timeout_seconds INTEGER DEFAULT 30, -- 超时时间
-- retry_config JSONB, -- 重试配置
-- health_check_url VARCHAR(500), -- 健康检查URL
-- health_status VARCHAR(20) DEFAULT 'unknown', -- 健康状态healthy-健康/unhealthy-不健康/unknown-未知
-- last_health_check TIMESTAMPTZ, -- 最后健康检查时间
-- documentation_url VARCHAR(500), -- 文档URL
-- is_enabled BOOLEAN DEFAULT true, -- 是否启用
-- dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径
-- creator VARCHAR(50) DEFAULT NULL, -- 创建者
-- updater VARCHAR(50) DEFAULT NULL, -- 更新者
-- create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间
-- update_time TIMESTAMPTZ DEFAULT NULL, -- 更新时间
-- delete_time TIMESTAMPTZ DEFAULT NULL, -- 删除时间
-- deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除
-- PRIMARY KEY (integration_id),
-- UNIQUE (optsn)
-- );
-- CREATE INDEX idx_integration_type ON agent.tb_api_integration(integration_type) WHERE deleted = false;
-- CREATE INDEX idx_integration_health ON agent.tb_api_integration(health_status) WHERE deleted = false;
-- COMMENT ON TABLE agent.tb_api_integration IS 'API集成注册表';
-- -- API调用日志表
-- DROP TABLE IF EXISTS agent.tb_api_call_log CASCADE;
-- CREATE TABLE agent.tb_api_call_log (
-- optsn VARCHAR(50) NOT NULL, -- 流水号
-- log_id VARCHAR(50) NOT NULL, -- 日志ID
-- integration_id VARCHAR(50), -- 集成ID
-- tool_id VARCHAR(50), -- 工具ID
-- agent_id VARCHAR(50), -- 智能体ID
-- session_id VARCHAR(50), -- 会话ID
-- user_id VARCHAR(50), -- 用户ID
-- endpoint VARCHAR(500) NOT NULL, -- 请求端点
-- method VARCHAR(10) NOT NULL, -- 请求方法
-- request_headers JSONB, -- 请求头
-- request_body TEXT, -- 请求体
-- response_status INTEGER, -- 响应状态码
-- response_headers JSONB, -- 响应头
-- response_body TEXT, -- 响应体
-- duration_ms INTEGER, -- 请求耗时(毫秒)
-- is_success BOOLEAN, -- 是否成功
-- error_message TEXT, -- 错误信息
-- retry_count INTEGER DEFAULT 0, -- 重试次数
-- ip_address VARCHAR(45), -- IP地址
-- dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径
-- create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间
-- PRIMARY KEY (log_id),
-- UNIQUE (optsn)
-- );
-- CREATE INDEX idx_api_log_integration ON agent.tb_api_call_log(integration_id, create_time DESC);
-- CREATE INDEX idx_api_log_agent ON agent.tb_api_call_log(agent_id, create_time DESC);
-- CREATE INDEX idx_api_log_status ON agent.tb_api_call_log(is_success, create_time DESC);
-- COMMENT ON TABLE agent.tb_api_call_log IS 'API调用日志表';
-- -- 智能体监控指标表
-- DROP TABLE IF EXISTS agent.tb_agent_metrics CASCADE;
-- CREATE TABLE agent.tb_agent_metrics (
-- optsn VARCHAR(50) NOT NULL, -- 流水号
-- metric_id VARCHAR(50) NOT NULL, -- 指标ID
-- agent_id VARCHAR(50) NOT NULL, -- 智能体ID
-- metric_date DATE NOT NULL, -- 指标日期
-- metric_hour INTEGER, -- 指标小时0-23
-- total_sessions INTEGER DEFAULT 0, -- 总会话数
-- active_sessions INTEGER DEFAULT 0, -- 活跃会话数
-- total_messages INTEGER DEFAULT 0, -- 总消息数
-- total_tokens BIGINT DEFAULT 0, -- 总Token数
-- total_cost DECIMAL(10,4) DEFAULT 0, -- 总成本
-- avg_response_time INTEGER, -- 平均响应时间(毫秒)
-- success_rate DECIMAL(5,4), -- 成功率
-- error_count INTEGER DEFAULT 0, -- 错误次数
-- api_call_count INTEGER DEFAULT 0, -- API调用次数
-- avg_rating DECIMAL(3,2), -- 平均评分
-- rating_count INTEGER DEFAULT 0, -- 评分数量
-- dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径
-- create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间
-- update_time TIMESTAMPTZ DEFAULT NULL, -- 更新时间
-- PRIMARY KEY (metric_id),
-- UNIQUE (optsn),
-- UNIQUE (agent_id, metric_date, metric_hour),
-- FOREIGN KEY (agent_id) REFERENCES agent.tb_agent(agent_id)
-- );
-- CREATE INDEX idx_metrics_agent_date ON agent.tb_agent_metrics(agent_id, metric_date DESC);
-- COMMENT ON TABLE agent.tb_agent_metrics IS '智能体监控指标表';
-- -- 智能体异常日志表
-- DROP TABLE IF EXISTS agent.tb_agent_error_log CASCADE;
-- CREATE TABLE agent.tb_agent_error_log (
-- optsn VARCHAR(50) NOT NULL, -- 流水号
-- log_id VARCHAR(50) NOT NULL, -- 日志ID
-- agent_id VARCHAR(50) NOT NULL, -- 智能体ID
-- session_id VARCHAR(50), -- 会话ID
-- error_type VARCHAR(50) NOT NULL, -- 错误类型model_error-模型错误/api_error-API错误/timeout-超时/rate_limit-限流/other-其他
-- error_code VARCHAR(50), -- 错误代码
-- error_message TEXT NOT NULL, -- 错误信息
-- stack_trace TEXT, -- 堆栈跟踪
-- request_context JSONB, -- 请求上下文
-- severity VARCHAR(20) DEFAULT 'error', -- 严重级别critical-致命/error-错误/warning-警告
-- is_resolved BOOLEAN DEFAULT false, -- 是否已解决
-- resolution_notes TEXT, -- 解决方案备注
-- dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径
-- create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间
-- resolve_time TIMESTAMPTZ, -- 解决时间
-- PRIMARY KEY (log_id),
-- UNIQUE (optsn),
-- FOREIGN KEY (agent_id) REFERENCES agent.tb_agent(agent_id)
-- );
-- CREATE INDEX idx_error_agent ON agent.tb_agent_error_log(agent_id, create_time DESC);
-- CREATE INDEX idx_error_severity ON agent.tb_agent_error_log(severity) WHERE is_resolved = false;
-- COMMENT ON TABLE agent.tb_agent_error_log IS '智能体异常日志表';
-- -- 智能体评价表
-- DROP TABLE IF EXISTS agent.tb_agent_rating CASCADE;
-- CREATE TABLE agent.tb_agent_rating (
-- optsn VARCHAR(50) NOT NULL, -- 流水号
-- rating_id VARCHAR(50) NOT NULL, -- 评价ID
-- agent_id VARCHAR(50) NOT NULL, -- 智能体ID
-- session_id VARCHAR(50), -- 会话ID
-- user_id VARCHAR(50) NOT NULL, -- 用户ID
-- rating INTEGER NOT NULL, -- 评分1-5星
-- dimensions JSONB, -- 分维度评分(准确性、速度、友好度等)
-- feedback TEXT, -- 评价反馈
-- tags TEXT[], -- 标签
-- is_anonymous BOOLEAN DEFAULT false, -- 是否匿名
-- dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径
-- create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间
-- deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除
-- PRIMARY KEY (rating_id),
-- UNIQUE (optsn),
-- FOREIGN KEY (agent_id) REFERENCES agent.tb_agent(agent_id)
-- );
-- CREATE INDEX idx_rating_agent ON agent.tb_agent_rating(agent_id) WHERE deleted = false;
-- COMMENT ON TABLE agent.tb_agent_rating IS '智能体评价表';

View File

@@ -1,156 +0,0 @@
-- =============================
-- 泰豪电源AI数智化平台 - 完整数据库初始化脚本
-- 包含所有业务模块的表结构
-- =============================
-- 安装必要的扩展
CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; -- UUID生成
CREATE EXTENSION IF NOT EXISTS "pg_trgm"; -- 全文搜索
CREATE EXTENSION IF NOT EXISTS "btree_gin"; -- GIN索引支持
-- CREATE EXTENSION IF NOT EXISTS "vector"; -- pgvector向量检索需要单独安装
-- =============================
-- 1. 系统基础模块(权限、用户、部门、角色)
-- =============================
\i createTablePermission.sql
\i createTableUser.sql
-- =============================
-- 2. 文件管理模块
-- =============================
\i createTableFile.sql
-- =============================
-- 3. 消息通知模块
-- =============================
\i createTableMessage.sql
-- =============================
-- 4. 日志模块
-- =============================
\i createTableLog.sql
-- =============================
-- 5. 配置管理模块
-- =============================
\i createTableConfig.sql
-- =============================
-- 6. 知识库管理模块
-- =============================
\i createTableKnowledge.sql
-- =============================
-- 7. 招投标智能体业务模块
-- =============================
\i createTableBidding.sql
-- =============================
-- 8. 智能客服系统业务模块
-- =============================
\i createTableCustomerService.sql
-- =============================
-- 9. 智能体管理和平台基础设施模块
-- =============================
\i createTableAgent.sql
-- =============================
-- 创建视图
-- =============================
-- 用户完整信息视图
CREATE OR REPLACE VIEW sys.v_user_full_info AS
SELECT
u.user_id,
u.email,
u.phone,
u.wechat_id,
u.status,
ui.avatar,
ui.full_name,
ui.gender,
ui.level,
u.create_time,
u.update_time
FROM sys.tb_sys_user u
LEFT JOIN sys.tb_sys_user_info ui ON u.user_id = ui.user_id
WHERE u.deleted = false AND (ui.deleted = false OR ui.deleted IS NULL);
-- 用户角色权限视图
CREATE OR REPLACE VIEW sys.v_user_role_permission AS
SELECT DISTINCT
ur.user_id,
r.role_id,
r.name AS role_name,
p.permission_id,
p.code AS permission_code,
p.name AS permission_name,
m.module_id,
m.name AS module_name
FROM sys.tb_sys_user_role ur
JOIN sys.tb_sys_role r ON ur.role_id = r.role_id
JOIN sys.tb_sys_role_permission rp ON r.role_id = rp.role_id
JOIN sys.tb_sys_permission p ON rp.permission_id = p.permission_id
LEFT JOIN sys.tb_sys_module m ON p.module_id = m.module_id
WHERE ur.deleted = false
AND r.deleted = false
AND rp.deleted = false
AND p.deleted = false;
-- 智能体使用统计视图
CREATE OR REPLACE VIEW agent.v_agent_usage_stats AS
SELECT
a.agent_id,
a.agent_name,
a.agent_type,
COUNT(DISTINCT s.session_id) AS total_sessions,
COUNT(DISTINCT s.user_id) AS unique_users,
SUM(s.message_count) AS total_messages,
AVG(s.token_usage) AS avg_token_usage,
AVG(r.rating) AS avg_rating,
COUNT(r.rating_id) AS rating_count,
MAX(s.start_time) AS last_used_time
FROM agent.tb_agent a
LEFT JOIN agent.tb_agent_session s ON a.agent_id = s.agent_id AND s.deleted = false
LEFT JOIN agent.tb_agent_rating r ON a.agent_id = r.agent_id AND r.deleted = false
WHERE a.deleted = false
GROUP BY a.agent_id, a.agent_name, a.agent_type;
-- 客服工单统计视图
CREATE OR REPLACE VIEW customer_service.v_ticket_stats AS
SELECT
t.ticket_status,
t.priority,
t.ticket_type,
COUNT(*) AS ticket_count,
AVG(EXTRACT(EPOCH FROM (t.resolution_time - t.create_time))/3600) AS avg_resolution_hours,
AVG(t.customer_rating) AS avg_rating,
COUNT(CASE WHEN t.is_overdue THEN 1 END) AS overdue_count
FROM customer_service.tb_ticket t
WHERE t.deleted = false
GROUP BY t.ticket_status, t.priority, t.ticket_type;
-- 招投标项目统计视图
CREATE OR REPLACE VIEW bidding.v_project_stats AS
SELECT
p.project_status,
p.project_type,
COUNT(*) AS project_count,
SUM(p.budget_amount) AS total_budget,
SUM(CASE WHEN p.winning_status = 'won' THEN 1 ELSE 0 END) AS won_count,
SUM(CASE WHEN p.winning_status = 'won' THEN p.winning_amount ELSE 0 END) AS total_won_amount,
AVG(CASE WHEN p.winning_status = 'won' THEN p.winning_amount / NULLIF(p.budget_amount, 0) ELSE NULL END) AS avg_win_rate
FROM bidding.tb_bidding_project p
WHERE p.deleted = false
GROUP BY p.project_status, p.project_type;
COMMENT ON VIEW sys.v_user_full_info IS '用户完整信息视图';
COMMENT ON VIEW sys.v_user_role_permission IS '用户角色权限视图';
COMMENT ON VIEW agent.v_agent_usage_stats IS '智能体使用统计视图';
COMMENT ON VIEW customer_service.v_ticket_stats IS '客服工单统计视图';
COMMENT ON VIEW bidding.v_project_stats IS '招投标项目统计视图';
-- =============================
-- 数据库初始化完成
-- =============================

View File

@@ -15,6 +15,8 @@ CREATE TABLE file.tb_sys_file (
-- TbSysFileDTO 特有字段 -- TbSysFileDTO 特有字段
file_id VARCHAR(50) NOT NULL, -- 文件ID (主键) file_id VARCHAR(50) NOT NULL, -- 文件ID (主键)
file_root_id VARCHAR(50) DEFAULT NULL, -- 文件根ID
version VARCHAR(50) DEFAULT NULL, -- 文件版本
name VARCHAR(255) NOT NULL, -- 文件名 name VARCHAR(255) NOT NULL, -- 文件名
path VARCHAR(500) NOT NULL, -- 文件路径 path VARCHAR(500) NOT NULL, -- 文件路径
size BIGINT NOT NULL, -- 文件大小(字节) size BIGINT NOT NULL, -- 文件大小(字节)
@@ -50,6 +52,8 @@ COMMENT ON COLUMN file.tb_sys_file.deleted IS '是否已删除0-未删除1
-- TbSysFileDTO 特有字段注释 -- TbSysFileDTO 特有字段注释
COMMENT ON COLUMN file.tb_sys_file.file_id IS '文件ID (主键)'; COMMENT ON COLUMN file.tb_sys_file.file_id IS '文件ID (主键)';
COMMENT ON COLUMN file.tb_sys_file.file_root_id IS '文件根ID';
COMMENT ON COLUMN file.tb_sys_file.version IS '文件版本';
COMMENT ON COLUMN file.tb_sys_file.name IS '文件名'; COMMENT ON COLUMN file.tb_sys_file.name IS '文件名';
COMMENT ON COLUMN file.tb_sys_file.path IS '文件路径'; COMMENT ON COLUMN file.tb_sys_file.path IS '文件路径';
COMMENT ON COLUMN file.tb_sys_file.size IS '文件大小(字节)'; COMMENT ON COLUMN file.tb_sys_file.size IS '文件大小(字节)';
@@ -73,48 +77,3 @@ CREATE INDEX idx_file_bucket ON file.tb_sys_file(bucket_name) WHERE deleted = 0;
CREATE INDEX idx_file_status ON file.tb_sys_file(status) WHERE deleted = 0; CREATE INDEX idx_file_status ON file.tb_sys_file(status) WHERE deleted = 0;
CREATE INDEX idx_file_create_time ON file.tb_sys_file(create_time) WHERE deleted = 0; CREATE INDEX idx_file_create_time ON file.tb_sys_file(create_time) WHERE deleted = 0;
CREATE INDEX idx_file_md5 ON file.tb_sys_file(md5_hash) WHERE deleted = 0; CREATE INDEX idx_file_md5 ON file.tb_sys_file(md5_hash) WHERE deleted = 0;
-- =============================
-- 文件关联表
-- =============================
DROP TABLE IF EXISTS file.tb_file_relation CASCADE;
CREATE TABLE file.tb_file_relation (
optsn VARCHAR(50) NOT NULL,
relation_id VARCHAR(50) NOT NULL,
file_id VARCHAR(50) NOT NULL,
object_type VARCHAR(50) NOT NULL, -- 对象类型bidding_project/ticket/document等
object_id VARCHAR(50) NOT NULL, -- 对象ID
relation_type VARCHAR(30) DEFAULT 'attachment', -- 关联类型attachment-附件/avatar-头像/banner-横幅
order_num INTEGER DEFAULT 0, -- 排序号
service_type VARCHAR(50), -- 服务类型继承自object_type
creator VARCHAR(50) DEFAULT NULL, -- 创建者
updater VARCHAR(50) DEFAULT NULL, -- 更新者
create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间
update_time TIMESTAMPTZ DEFAULT NULL, -- 更新时间
delete_time TIMESTAMPTZ DEFAULT NULL, -- 删除时间
deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除
PRIMARY KEY (relation_id),
UNIQUE (optsn),
FOREIGN KEY (file_id) REFERENCES file.tb_sys_file(file_id)
);
COMMENT ON TABLE file.tb_file_relation IS '文件关联表';
COMMENT ON COLUMN file.tb_file_relation.optsn IS '流水号';
COMMENT ON COLUMN file.tb_file_relation.relation_id IS '关联ID';
COMMENT ON COLUMN file.tb_file_relation.file_id IS '文件ID';
COMMENT ON COLUMN file.tb_file_relation.object_type IS '对象类型bidding_project/ticket/document等';
COMMENT ON COLUMN file.tb_file_relation.object_id IS '对象ID';
COMMENT ON COLUMN file.tb_file_relation.relation_type IS '关联类型attachment-附件/avatar-头像/banner-横幅';
COMMENT ON COLUMN file.tb_file_relation.order_num IS '排序号';
COMMENT ON COLUMN file.tb_file_relation.service_type IS '服务类型继承自object_type';
COMMENT ON COLUMN file.tb_file_relation.creator IS '创建者';
COMMENT ON COLUMN file.tb_file_relation.updater IS '更新者';
COMMENT ON COLUMN file.tb_file_relation.create_time IS '创建时间';
COMMENT ON COLUMN file.tb_file_relation.update_time IS '更新时间';
COMMENT ON COLUMN file.tb_file_relation.delete_time IS '删除时间';
COMMENT ON COLUMN file.tb_file_relation.deleted IS '是否删除';
-- 文件关联表索引
CREATE INDEX idx_file_relation_object ON file.tb_file_relation(object_type, object_id) WHERE deleted = false;
CREATE INDEX idx_file_relation_file ON file.tb_file_relation(file_id) WHERE deleted = false;
CREATE INDEX idx_file_relation_service ON file.tb_file_relation(service_type) WHERE deleted = false;

View File

@@ -1,153 +0,0 @@
-- =============================
-- 知识库管理模块
-- 支持:招投标知识库、客服知识库、企业内部知识库
-- =============================
CREATE SCHEMA IF NOT EXISTS knowledge;
-- 知识库表(多租户知识库定义)
DROP TABLE IF EXISTS knowledge.tb_knowledge_base CASCADE;
CREATE TABLE knowledge.tb_knowledge_base (
optsn VARCHAR(50) NOT NULL, -- 流水号
agent_id VARCHAR(50) NOT NULL, -- 智能体id
knowledge_id VARCHAR(50) NOT NULL, -- 知识库ID
name VARCHAR(255) NOT NULL, -- 知识库名称
kb_type VARCHAR(50) NOT NULL, -- 知识库类型bidding-招投标/customer_service-客服/internal-内部协同
access_level VARCHAR(20) NOT NULL DEFAULT 'private', -- 访问级别public-公开/private-私有/internal-内部
description TEXT, -- 知识库描述
storage_path VARCHAR(500), -- 存储路径
version VARCHAR(20) DEFAULT '1.0', -- 当前版本号
config JSONB DEFAULT NULL, -- 知识库配置JSON格式索引配置、检索参数等
service_type VARCHAR(50), -- 服务类型bidding/customer_service/internal冗余kb_type便于查询
dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径
status VARCHAR(20) NOT NULL DEFAULT 'active', -- 状态active-激活/inactive-停用/archived-归档
creator VARCHAR(50) DEFAULT NULL, -- 创建者
updater VARCHAR(50) DEFAULT NULL, -- 更新者
create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间
update_time TIMESTAMPTZ DEFAULT NULL, -- 更新时间
delete_time TIMESTAMPTZ DEFAULT NULL, -- 删除时间
deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除
PRIMARY KEY (knowledge_id),
UNIQUE (optsn)
);
CREATE INDEX idx_kb_type ON knowledge.tb_knowledge_base(kb_type) WHERE deleted = false;
CREATE INDEX idx_kb_service ON knowledge.tb_knowledge_base(service_type) WHERE deleted = false;
CREATE INDEX idx_kb_dept_path ON knowledge.tb_knowledge_base(dept_path) WHERE deleted = false;
COMMENT ON TABLE knowledge.tb_knowledge_base IS '知识库表';
COMMENT ON COLUMN knowledge.tb_knowledge_base.kb_type IS '知识库类型bidding-招投标/customer_service-客服/internal-内部协同';
COMMENT ON COLUMN knowledge.tb_knowledge_base.service_type IS '服务类型冗余存储便于快速过滤与kb_type保持一致';
COMMENT ON COLUMN knowledge.tb_knowledge_base.access_level IS '访问级别public-公开/private-私有/internal-内部';
-- 知识文档表
DROP TABLE IF EXISTS knowledge.tb_knowledge_document CASCADE;
CREATE TABLE knowledge.tb_knowledge_document (
optsn VARCHAR(50) NOT NULL, -- 流水号
doc_id VARCHAR(50) NOT NULL, -- 文档ID
knowledge_id VARCHAR(50) NOT NULL, -- 所属知识库ID
title VARCHAR(500) NOT NULL, -- 文档标题
doc_type VARCHAR(50) NOT NULL, -- 文档类型text-文本/pdf/word/excel/image/video
category VARCHAR(100), -- 文档分类(自动或手动分类)
content TEXT, -- 文档内容(文本类型)
content_summary TEXT, -- 内容摘要AI生成
file_id VARCHAR(50), -- 关联文件表ID
file_path VARCHAR(500), -- 文件路径
file_size BIGINT, -- 文件大小(字节)
mime_type VARCHAR(100), -- MIME类型
version INTEGER DEFAULT 1, -- 文档版本号(仅做记录)
root_doc_id VARCHAR(50), -- 根文档ID版本组标识保留用于整体版本管理
tags TEXT[], -- 文档标签数组
keywords TEXT[], -- 关键词数组AI提取
embedding_status VARCHAR(20) DEFAULT 'pending', -- 向量化状态pending-待处理/processing-处理中/completed-完成/failed-失败
embedding_model VARCHAR(100), -- 使用的向量化模型
chunk_count INTEGER DEFAULT 0, -- 切片数量
metadata JSONB DEFAULT NULL, -- 文档元数据JSON格式
source_url VARCHAR(500), -- 来源URL
service_type VARCHAR(50), -- 服务类型(继承自知识库)
dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径
status VARCHAR(20) NOT NULL DEFAULT 'active', -- 状态active-激活/inactive-停用/archived-归档
creator VARCHAR(50) DEFAULT NULL, -- 创建者
updater VARCHAR(50) DEFAULT NULL, -- 更新者
create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间
update_time TIMESTAMPTZ DEFAULT NULL, -- 更新时间
delete_time TIMESTAMPTZ DEFAULT NULL, -- 删除时间
deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除
PRIMARY KEY (doc_id),
UNIQUE (optsn)
);
CREATE INDEX idx_doc_kb ON knowledge.tb_knowledge_document(knowledge_id) WHERE deleted = false;
CREATE INDEX idx_doc_service ON knowledge.tb_knowledge_document(service_type) WHERE deleted = false;
CREATE INDEX idx_doc_category ON knowledge.tb_knowledge_document(category) WHERE deleted = false;
CREATE INDEX idx_doc_embedding_status ON knowledge.tb_knowledge_document(embedding_status) WHERE deleted = false;
CREATE INDEX idx_doc_tags ON knowledge.tb_knowledge_document USING GIN(tags) WHERE deleted = false;
CREATE INDEX idx_doc_root ON knowledge.tb_knowledge_document(root_doc_id) WHERE deleted = false;
COMMENT ON TABLE knowledge.tb_knowledge_document IS '知识文档表文档级元数据版本控制在chunk级别';
COMMENT ON COLUMN knowledge.tb_knowledge_document.service_type IS '服务类型(从知识库继承,用于服务间隔离)';
COMMENT ON COLUMN knowledge.tb_knowledge_document.version IS '文档版本号仅做记录实际版本控制在chunk级别';
COMMENT ON COLUMN knowledge.tb_knowledge_document.root_doc_id IS '根文档ID用于文档整体版本管理可选';
COMMENT ON COLUMN knowledge.tb_knowledge_document.embedding_status IS '向量化状态pending/processing/completed/failed';
-- 知识文档片段表用于RAG检索
DROP TABLE IF EXISTS knowledge.tb_knowledge_chunk CASCADE;
CREATE TABLE knowledge.tb_knowledge_chunk (
optsn VARCHAR(50) NOT NULL, -- 流水号
chunk_id VARCHAR(50) NOT NULL, -- 片段ID
doc_id VARCHAR(50) NOT NULL, -- 所属文档ID
knowledge_id VARCHAR(50) NOT NULL, -- 所属知识库ID
chunk_index INTEGER NOT NULL, -- 片段索引(在文档中的顺序)
content TEXT NOT NULL, -- 片段内容
content_length INTEGER, -- 内容长度
embedding vector(1536), -- 向量嵌入假设使用OpenAI 1536维
chunk_type VARCHAR(20) DEFAULT 'text', -- 片段类型text-文本/table-表格/image-图片
version INTEGER DEFAULT 1, -- chunk版本号每次修改自动+1
root_chunk_id VARCHAR(50), -- 根chunk ID同一chunk的不同版本共享此ID
is_current BOOLEAN DEFAULT true, -- 是否当前使用的版本
position_info JSONB, -- 位置信息(页码、坐标等)
metadata JSONB, -- 片段元数据
dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径
creator VARCHAR(50) DEFAULT NULL, -- 创建者
create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间
update_time TIMESTAMPTZ DEFAULT NULL, -- 更新时间
deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除
PRIMARY KEY (chunk_id),
UNIQUE (optsn)
);
CREATE INDEX idx_chunk_doc ON knowledge.tb_knowledge_chunk(doc_id) WHERE deleted = false;
CREATE INDEX idx_chunk_kb ON knowledge.tb_knowledge_chunk(knowledge_id) WHERE deleted = false;
CREATE INDEX idx_chunk_root_current ON knowledge.tb_knowledge_chunk(root_chunk_id, is_current) WHERE deleted = false;
CREATE INDEX idx_chunk_current ON knowledge.tb_knowledge_chunk(is_current) WHERE deleted = false AND is_current = true;
-- 向量检索索引需要安装pgvector扩展建议只索引当前版本
-- CREATE INDEX idx_chunk_embedding ON knowledge.tb_knowledge_chunk USING ivfflat (embedding vector_cosine_ops) WITH (lists = 100) WHERE deleted = false AND is_current = true;
COMMENT ON TABLE knowledge.tb_knowledge_chunk IS '知识文档片段表RAG检索基本单位支持chunk级版本控制';
COMMENT ON COLUMN knowledge.tb_knowledge_chunk.version IS 'chunk版本号整数每次修改自动+1';
COMMENT ON COLUMN knowledge.tb_knowledge_chunk.root_chunk_id IS '根chunk ID同一chunk的所有版本共享此ID首次创建时等于chunk_id';
COMMENT ON COLUMN knowledge.tb_knowledge_chunk.is_current IS '是否当前使用的版本每个root_chunk_id只有一个is_current=trueRAG检索时只使用当前版本';
COMMENT ON COLUMN knowledge.tb_knowledge_chunk.embedding IS '向量嵌入需要pgvector扩展建议只为is_current=true的chunk生成';
-- 知识访问日志表
DROP TABLE IF EXISTS knowledge.tb_knowledge_access_log CASCADE;
CREATE TABLE knowledge.tb_knowledge_access_log (
optsn VARCHAR(50) NOT NULL, -- 流水号
log_id VARCHAR(50) NOT NULL, -- 日志ID
knowledge_id VARCHAR(50), -- 知识库ID
doc_id VARCHAR(50), -- 文档ID
user_id VARCHAR(50) NOT NULL, -- 用户ID
access_type VARCHAR(20) NOT NULL, -- 访问类型view-查看/download-下载/search-搜索/edit-编辑
query_text TEXT, -- 搜索查询文本
result_count INTEGER, -- 搜索结果数量
ip_address VARCHAR(45), -- IP地址
user_agent TEXT, -- 用户代理
dept_path VARCHAR(255) DEFAULT NULL, -- 部门全路径
create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间
PRIMARY KEY (log_id),
UNIQUE (optsn)
);
CREATE INDEX idx_access_log_user ON knowledge.tb_knowledge_access_log(user_id, create_time DESC);
CREATE INDEX idx_access_log_kb ON knowledge.tb_knowledge_access_log(knowledge_id, create_time DESC);
COMMENT ON TABLE knowledge.tb_knowledge_access_log IS '知识访问日志表';

View File

@@ -24,8 +24,8 @@
-- 5. 配置管理模块 -- 5. 配置管理模块
\i createTableConfig.sql \i createTableConfig.sql
-- 6. 知识库管理模块 -- 6. AI模块 智能体+知识库
\i createTableKnowledge.sql \i createTableAI.sql
-- 7. 招投标业务模块 -- 7. 招投标业务模块
\i createTableBidding.sql \i createTableBidding.sql
@@ -33,9 +33,6 @@
-- 8. 智能客服业务模块 -- 8. 智能客服业务模块
\i createTableWorkcase.sql \i createTableWorkcase.sql
-- 9. 智能体模块(暂不启用)
-- \i createTableAgent.sql
-- ============================= -- =============================
-- 第二阶段:初始化基础数据 -- 第二阶段:初始化基础数据
-- ============================= -- =============================

View File

@@ -62,6 +62,23 @@ INSERT INTO config.tb_sys_config (
('CFG-0416', 'cfg_sms_tpl_register', 'sms.templateCode.register','注册验证码模板', 'SMS_491985030', 'String', 'input', '注册验证码模板编码', NULL, NULL, 'notify', 'mod_message', 130, 1, '注册验证码短信模板', 'system', NULL, NULL, now(), NULL, NULL, false), ('CFG-0416', 'cfg_sms_tpl_register', 'sms.templateCode.register','注册验证码模板', 'SMS_491985030', 'String', 'input', '注册验证码模板编码', NULL, NULL, 'notify', 'mod_message', 130, 1, '注册验证码短信模板', 'system', NULL, NULL, now(), NULL, NULL, false),
('CFG-0417', 'cfg_sms_timeout', 'sms.timeout', '请求超时时间', '30000', 'INTEGER', 'input', '请求超时时间(毫秒)', NULL, NULL, 'notify', 'mod_message', 140, 1, 'API请求超时时间5000-60000', 'system', NULL, NULL, now(), NULL, NULL, false), ('CFG-0417', 'cfg_sms_timeout', 'sms.timeout', '请求超时时间', '30000', 'INTEGER', 'input', '请求超时时间(毫秒)', NULL, NULL, 'notify', 'mod_message', 140, 1, 'API请求超时时间5000-60000', 'system', NULL, NULL, now(), NULL, NULL, false),
-- Dify AI 配置
-- Dify 基础配置
('CFG-0450', 'cfg_dify_api_base', 'dify.apiBaseUrl', 'Dify API地址', 'http://localhost:8000/v1', 'String', 'input', 'Dify API基础地址', NULL, NULL, 'dify', 'mod_agent', 10, 1, 'Dify服务的API基础地址如 http://localhost/dify/api', 'system', NULL, NULL, now(), NULL, NULL, false),
('CFG-0452', 'cfg_dify_knowledge_key','dify.knowledgeApiKey', '知识库API密钥', 'dataset-nupqKP4LONpzdXmGthIrbjeJ', 'String', 'password', '知识库API密钥', NULL, NULL, 'dify', 'mod_agent', 30, 1, '用于访问Dify知识库的API密钥', 'system', NULL, NULL, now(), NULL, NULL, false),
('CFG-0453', 'cfg_dify_timeout', 'dify.timeout', '请求超时时间', '60', 'INTEGER', 'input', '请求超时时间(秒)', NULL, NULL, 'dify', 'mod_agent', 40, 1, 'API请求的超时时间10-600秒', 'system', NULL, NULL, now(), NULL, NULL, false),
('CFG-0454', 'cfg_dify_conn_timeout','dify.connectTimeout', '连接超时时间', '10', 'INTEGER', 'input', '连接超时时间(秒)', NULL, NULL, 'dify', 'mod_agent', 50, 1, 'API连接的超时时间5-60秒', 'system', NULL, NULL, now(), NULL, NULL, false),
('CFG-0455', 'cfg_dify_read_timeout','dify.readTimeout', '读取超时时间', '60', 'INTEGER', 'input', '读取超时时间(秒)', NULL, NULL, 'dify', 'mod_agent', 60, 1, 'API读取响应的超时时间10-600秒', 'system', NULL, NULL, now(), NULL, NULL, false),
('CFG-0456', 'cfg_dify_stream_timeout','dify.streamTimeout', '流式响应超时时间', '300', 'INTEGER', 'input', '流式响应超时时间(秒)', NULL, NULL, 'dify', 'mod_agent', 70, 1, '流式API响应的超时时间30-1800秒', 'system', NULL, NULL, now(), NULL, NULL, false),
-- Dify 上传配置
('CFG-0460', 'cfg_dify_upload_types','dify.upload.allowedTypes','允许的文件类型', 'pdf,txt,docx,doc,md,html,htm,xlsx,xls,csv', 'String', 'textarea', '上传文件允许的类型', NULL, NULL, 'dify', 'mod_agent', 80, 1, '支持上传的文件类型列表,逗号分隔', 'system', NULL, NULL, now(), NULL, NULL, false),
('CFG-0461', 'cfg_dify_upload_max', 'dify.upload.maxSize', '最大文件大小', '50', 'INTEGER', 'input', '最大文件大小MB', NULL, NULL, 'dify', 'mod_agent', 90, 1, '单个文件上传的最大大小限制1-500MB', 'system', NULL, NULL, now(), NULL, NULL, false),
-- Dify 知识库配置
('CFG-0470', 'cfg_dify_index_tech', 'dify.dataset.defaultIndexingTechnique','默认索引方式', 'high_quality', 'String', 'select', '默认索引方式', NULL, '["high_quality", "economy"]'::json, 'dify', 'mod_agent', 100, 1, '知识库文档的默认索引方式high_quality高质量或 economy经济', 'system', NULL, NULL, now(), NULL, NULL, false),
('CFG-0471', 'cfg_dify_embed_model', 'dify.dataset.defaultEmbeddingModel','默认Embedding模型', 'text-embedding-ada-002', 'String', 'input', '默认Embedding模型', NULL, NULL, 'dify', 'mod_agent', 110, 1, '知识库使用的默认Embedding模型名称', 'system', NULL, NULL, now(), NULL, NULL, false),
-- 日志与审计 -- 日志与审计
('CFG-0501', 'cfg_log_level', 'log.level', '日志级别', 'INFO', 'String', 'select', '系统日志级别', NULL, '["DEBUG", "INFO", "WARN", "ERROR"]'::json, 'log', 'mod_system', 10, 0, 'DEBUG/INFO/WARN/ERROR', 'system', NULL, NULL, now(), NULL, NULL, false), ('CFG-0501', 'cfg_log_level', 'log.level', '日志级别', 'INFO', 'String', 'select', '系统日志级别', NULL, '["DEBUG", "INFO", "WARN", "ERROR"]'::json, 'log', 'mod_system', 10, 0, 'DEBUG/INFO/WARN/ERROR', 'system', NULL, NULL, now(), NULL, NULL, false),
('CFG-0502', 'cfg_audit_retention', 'audit.retentionDays', '审计日志保留', '90', 'INTEGER', 'input', '审计日志保留天数', NULL, NULL, 'log', 'mod_system', 20, 0, '合规按需调整', 'system', NULL, NULL, now(), NULL, NULL, false), ('CFG-0502', 'cfg_audit_retention', 'audit.retentionDays', '审计日志保留', '90', 'INTEGER', 'input', '审计日志保留天数', NULL, NULL, 'log', 'mod_system', 20, 0, '合规按需调整', 'system', NULL, NULL, now(), NULL, NULL, false),

View File

@@ -1,5 +0,0 @@
package org.xyzh.agent.client;
public class DifyClient {
}

View File

@@ -10,12 +10,25 @@
</parent> </parent>
<groupId>org.xyzh</groupId> <groupId>org.xyzh</groupId>
<artifactId>agent</artifactId> <artifactId>ai</artifactId>
<version>1.0.0</version> <version>1.0.0</version>
<properties> <properties>
<maven.compiler.source>21</maven.compiler.source> <maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target> <maven.compiler.target>21</maven.compiler.target>
</properties> </properties>
<dependencies>
<dependency>
<groupId>org.xyzh.apis</groupId>
<artifactId>api-ai</artifactId>
<version>${urban-lifeline.version}</version>
</dependency>
<dependency>
<groupId>org.xyzh.apis</groupId>
<artifactId>api-system</artifactId>
<version>${urban-lifeline.version}</version>
</dependency>
</dependencies>
</project> </project>

View File

@@ -1,4 +1,4 @@
package org.xyzh.agent; package org.xyzh.ai;
import org.apache.dubbo.config.spring.context.annotation.EnableDubbo; import org.apache.dubbo.config.spring.context.annotation.EnableDubbo;
import org.slf4j.Logger; import org.slf4j.Logger;

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,44 @@
package org.xyzh.ai.client.callback;
/**
* @description 流式响应回调接口
* @filename StreamCallback.java
* @author AI Assistant
* @copyright xyzh
* @since 2025-11-04
*/
public interface StreamCallback {
/**
* 接收到消息片段
* @param message 消息内容
*/
void onMessage(String message);
/**
* 消息结束(包含元数据)
* @param metadata JSON格式的元数据
*/
void onMessageEnd(String metadata);
/**
* 接收到Dify原始事件用于转发完整事件数据
* @param eventType 事件类型如workflow_started、node_started等
* @param eventData 完整的事件JSON数据
*/
default void onEvent(String eventType, String eventData) {
// 默认实现:不处理
}
/**
* 流式响应完成
*/
void onComplete();
/**
* 发生错误
* @param error 错误对象
*/
void onError(Throwable error);
}

View File

@@ -0,0 +1,75 @@
package org.xyzh.ai.client.dto;
import com.alibaba.fastjson2.annotation.JSONField;
import lombok.Data;
import java.util.List;
import java.util.Map;
import org.xyzh.api.ai.dto.DifyFileInfo;
/**
* @description 对话请求
* @filename ChatRequest.java
* @author AI Assistant
* @copyright xyzh
* @since 2025-12-15
*/
@Data
public class ChatRequest {
/**
* 输入变量
*/
private Map<String, Object> inputs;
/**
* 用户问题
*/
private String query;
/**
* 响应模式streaming流式、blocking阻塞
*/
@JSONField(name = "response_mode")
private String responseMode = "streaming";
/**
* 对话ID继续对话时传入
*/
@JSONField(name = "conversation_id")
private String conversationId;
/**
* 用户标识
*/
private String user;
/**
* 上传的文件列表
*/
private List<DifyFileInfo> files;
/**
* 自动生成标题
*/
@JSONField(name = "auto_generate_name")
private Boolean autoGenerateName = true;
/**
* 指定的数据集ID列表知识库检索
*/
@JSONField(name = "dataset_ids")
private List<String> datasetIds;
/**
* 温度参数0.0-1.0
*/
private Double temperature;
/**
* 最大token数
*/
@JSONField(name = "max_tokens")
private Integer maxTokens;
}

View File

@@ -0,0 +1,121 @@
package org.xyzh.ai.client.dto;
import com.alibaba.fastjson2.annotation.JSONField;
import lombok.Data;
import java.util.List;
import java.util.Map;
/**
* @description 对话响应(阻塞模式)
* @filename ChatResponse.java
* @author AI Assistant
* @copyright xyzh
* @since 2025-11-04
*/
@Data
public class ChatResponse {
/**
* 消息ID
*/
@JSONField(name = "message_id")
private String messageId;
/**
* 对话ID
*/
@JSONField(name = "conversation_id")
private String conversationId;
/**
* 模式
*/
private String mode;
/**
* 回答内容
*/
private String answer;
/**
* 元数据
*/
private Map<String, Object> metadata;
/**
* 创建时间
*/
@JSONField(name = "created_at")
private Long createdAt;
/**
* Token使用情况
*/
private Usage usage;
/**
* 检索信息
*/
@JSONField(name = "retrieval_info")
private List<RetrievalInfo> retrievalInfo;
@Data
public static class Usage {
@JSONField(name = "prompt_tokens")
private Integer promptTokens;
@JSONField(name = "prompt_unit_price")
private String promptUnitPrice;
@JSONField(name = "prompt_price_unit")
private String promptPriceUnit;
@JSONField(name = "prompt_price")
private String promptPrice;
@JSONField(name = "completion_tokens")
private Integer completionTokens;
@JSONField(name = "completion_unit_price")
private String completionUnitPrice;
@JSONField(name = "completion_price_unit")
private String completionPriceUnit;
@JSONField(name = "completion_price")
private String completionPrice;
@JSONField(name = "total_tokens")
private Integer totalTokens;
@JSONField(name = "total_price")
private String totalPrice;
private String currency;
private Double latency;
}
@Data
public static class RetrievalInfo {
@JSONField(name = "dataset_id")
private String datasetId;
@JSONField(name = "dataset_name")
private String datasetName;
@JSONField(name = "document_id")
private String documentId;
@JSONField(name = "document_name")
private String documentName;
@JSONField(name = "segment_id")
private String segmentId;
private Double score;
private String content;
}
}

View File

@@ -0,0 +1,50 @@
package org.xyzh.ai.client.dto;
import lombok.Data;
import java.util.List;
import com.alibaba.fastjson2.annotation.JSONField;
/**
* @description 对话列表响应
* @filename ConversationListResponse.java
* @author AI Assistant
* @copyright xyzh
* @since 2025-11-04
*/
@Data
public class ConversationListResponse {
private Integer limit;
@JSONField(name = "has_more")
private Boolean hasMore;
private List<ConversationInfo> data;
@Data
public static class ConversationInfo {
private String id;
private String name;
private List<InputInfo> inputs;
private String status;
private String introduction;
@JSONField(name = "created_at")
private Long createdAt;
@JSONField(name = "updated_at")
private Long updatedAt;
}
@Data
public static class InputInfo {
private String key;
private String value;
}
}

View File

@@ -0,0 +1,55 @@
package org.xyzh.ai.client.dto;
import com.alibaba.fastjson2.annotation.JSONField;
import lombok.Data;
/**
* @description 创建知识库请求
* @filename DatasetCreateRequest.java
* @author AI Assistant
* @copyright xyzh
* @since 2025-11-04
*/
@Data
public class DatasetCreateRequest {
/**
* 知识库名称
*/
private String name;
/**
* 知识库描述
*/
private String description;
/**
* 索引方式high_quality高质量、economy经济
*/
@JSONField(name = "indexing_technique")
private String indexingTechnique = "high_quality";
/**
* Embedding模型
*/
@JSONField(name = "embedding_model")
private String embeddingModel;
/**
* Embedding模型提供商
*/
@JSONField(name = "embedding_model_provider")
private String embeddingModelProvider;
/**
* 检索模型配置(包含 Rerank、Top K、Score 阈值等)
*/
@JSONField(name = "retrieval_model")
private RetrievalModel retrievalModel;
/**
* 权限only_me仅自己、all_team_members团队所有成员
*/
private String permission = "only_me";
}

View File

@@ -0,0 +1,55 @@
package org.xyzh.ai.client.dto;
import com.alibaba.fastjson2.annotation.JSONField;
import lombok.Data;
/**
* @description 创建知识库响应
* @filename DatasetCreateResponse.java
* @author AI Assistant
* @copyright xyzh
* @since 2025-11-04
*/
@Data
public class DatasetCreateResponse {
/**
* 知识库ID
*/
private String id;
/**
* 知识库名称
*/
private String name;
/**
* 描述
*/
private String description;
/**
* 索引方式
*/
@JSONField(name = "indexing_technique")
private String indexingTechnique;
/**
* Embedding模型
*/
@JSONField(name = "embedding_model")
private String embeddingModel;
/**
* 创建时间
*/
@JSONField(name = "created_at")
private Long createdAt;
/**
* 创建人
*/
@JSONField(name = "created_by")
private String createdBy;
}

View File

@@ -0,0 +1,82 @@
package org.xyzh.ai.client.dto;
import com.alibaba.fastjson2.annotation.JSONField;
import lombok.Data;
/**
* @description 知识库详情响应
* @filename DatasetDetailResponse.java
* @author AI Assistant
* @copyright xyzh
* @since 2025-11-04
*/
@Data
public class DatasetDetailResponse {
private String id;
private String name;
private String description;
@JSONField(name = "indexing_technique")
private String indexingTechnique;
@JSONField(name = "embedding_model")
private String embeddingModel;
@JSONField(name = "embedding_model_provider")
private String embeddingModelProvider;
@JSONField(name = "embedding_available")
private Boolean embeddingAvailable;
@JSONField(name = "retrieval_model_dict")
private RetrievalModelDict retrievalModelDict;
@JSONField(name = "document_count")
private Integer documentCount;
@JSONField(name = "word_count")
private Integer wordCount;
@JSONField(name = "app_count")
private Integer appCount;
@JSONField(name = "created_by")
private String createdBy;
@JSONField(name = "created_at")
private Long createdAt;
@JSONField(name = "updated_at")
private Long updatedAt;
@Data
public static class RetrievalModelDict {
@JSONField(name = "search_method")
private String searchMethod;
@JSONField(name = "reranking_enable")
private Boolean rerankingEnable;
@JSONField(name = "reranking_model")
private RerankingModel rerankingModel;
@JSONField(name = "top_k")
private Integer topK;
@JSONField(name = "score_threshold_enabled")
private Boolean scoreThresholdEnabled;
}
@Data
public static class RerankingModel {
@JSONField(name = "reranking_provider_name")
private String rerankingProviderName;
@JSONField(name = "reranking_model_name")
private String rerankingModelName;
}
}

View File

@@ -0,0 +1,61 @@
package org.xyzh.ai.client.dto;
import com.alibaba.fastjson2.annotation.JSONField;
import lombok.Data;
import java.util.List;
/**
* @description 知识库列表响应
* @filename DatasetListResponse.java
* @author AI Assistant
* @copyright xyzh
* @since 2025-11-04
*/
@Data
public class DatasetListResponse {
/**
* 知识库列表
*/
private List<DatasetInfo> data;
/**
* 是否有更多
*/
@JSONField(name = "has_more")
private Boolean hasMore;
/**
* 分页限制
*/
private Integer limit;
/**
* 总数
*/
private Integer total;
/**
* 当前页
*/
private Integer page;
@Data
public static class DatasetInfo {
private String id;
private String name;
private String description;
private String permission;
@JSONField(name = "document_count")
private Integer documentCount;
@JSONField(name = "word_count")
private Integer wordCount;
@JSONField(name = "created_by")
private String createdBy;
@JSONField(name = "created_at")
private Long createdAt;
@JSONField(name = "updated_at")
private Long updatedAt;
}
}

View File

@@ -0,0 +1,50 @@
package org.xyzh.ai.client.dto;
import com.alibaba.fastjson2.annotation.JSONField;
import lombok.Data;
/**
* @description Dify知识库更新请求
* @filename DatasetUpdateRequest.java
* @author AI Assistant
* @copyright xyzh
* @since 2025-11-04
*/
@Data
public class DatasetUpdateRequest {
/**
* 知识库名称
*/
private String name;
/**
* 知识库描述
*/
private String description;
/**
* 索引方式high_quality/economy
*/
@JSONField(name = "indexing_technique")
private String indexingTechnique;
/**
* Embedding模型
*/
@JSONField(name = "embedding_model")
private String embeddingModel;
/**
* Embedding模型提供商
*/
@JSONField(name = "embedding_model_provider")
private String embeddingModelProvider;
/**
* 检索模型配置(包含 Rerank、Top K、Score 阈值等)
*/
@JSONField(name = "retrieval_model")
private RetrievalModel retrievalModel;
}

View File

@@ -0,0 +1,96 @@
package org.xyzh.ai.client.dto;
import com.alibaba.fastjson2.annotation.JSONField;
import lombok.Data;
import java.util.List;
/**
* @description Dify文档列表响应
* @filename DocumentListResponse.java
* @author AI Assistant
* @copyright xyzh
* @since 2025-11-07
*/
@Data
public class DocumentListResponse {
private List<Document> data;
@JSONField(name = "has_more")
private Boolean hasMore;
private Integer limit;
private Integer total;
private Integer page;
/**
* 文档信息
*/
@Data
public static class Document {
private String id;
private Integer position;
@JSONField(name = "data_source_type")
private String dataSourceType;
@JSONField(name = "data_source_info")
private DataSourceInfo dataSourceInfo;
@JSONField(name = "dataset_process_rule_id")
private String datasetProcessRuleId;
private String name;
@JSONField(name = "created_from")
private String createdFrom;
@JSONField(name = "created_by")
private String createdBy;
@JSONField(name = "created_at")
private Long createdAt;
private Integer tokens;
@JSONField(name = "indexing_status")
private String indexingStatus;
private String error;
private Boolean enabled;
@JSONField(name = "disabled_at")
private Long disabledAt;
@JSONField(name = "disabled_by")
private String disabledBy;
private Boolean archived;
@JSONField(name = "display_status")
private String displayStatus;
@JSONField(name = "word_count")
private Integer wordCount;
@JSONField(name = "hit_count")
private Integer hitCount;
@JSONField(name = "doc_form")
private String docForm;
}
/**
* 数据源信息
*/
@Data
public static class DataSourceInfo {
@JSONField(name = "upload_file_id")
private String uploadFileId;
}
}

View File

@@ -0,0 +1,95 @@
package org.xyzh.ai.client.dto;
import com.alibaba.fastjson2.annotation.JSONField;
import lombok.Data;
import java.util.List;
/**
* @description 文档处理状态响应
* @filename DocumentStatusResponse.java
* @author AI Assistant
* @copyright xyzh
* @since 2025-11-04
*/
@Data
public class DocumentStatusResponse {
/**
* 文档列表
*/
private List<DocumentStatus> data;
@Data
public static class DocumentStatus {
/**
* 文档ID
*/
private String id;
/**
* 索引状态waiting、parsing、cleaning、splitting、indexing、completed、error
*/
@JSONField(name = "indexing_status")
private String indexingStatus;
/**
* 处理开始时间
*/
@JSONField(name = "processing_started_at")
private Long processingStartedAt;
/**
* 解析完成时间
*/
@JSONField(name = "parsing_completed_at")
private Long parsingCompletedAt;
/**
* 清洗完成时间
*/
@JSONField(name = "cleaning_completed_at")
private Long cleaningCompletedAt;
/**
* 分割完成时间
*/
@JSONField(name = "splitting_completed_at")
private Long splittingCompletedAt;
/**
* 完成时间
*/
@JSONField(name = "completed_at")
private Long completedAt;
/**
* 暂停时间
*/
@JSONField(name = "paused_at")
private Long pausedAt;
/**
* 错误信息
*/
private String error;
/**
* 停止时间
*/
@JSONField(name = "stopped_at")
private Long stoppedAt;
/**
* 分段数量
*/
@JSONField(name = "completed_segments")
private Integer completedSegments;
/**
* 总分段数
*/
@JSONField(name = "total_segments")
private Integer totalSegments;
}
}

View File

@@ -0,0 +1,95 @@
package org.xyzh.ai.client.dto;
import com.alibaba.fastjson2.annotation.JSONField;
import lombok.Data;
/**
* @description 文档上传请求
* @filename DocumentUploadRequest.java
* @author AI Assistant
* @copyright xyzh
* @since 2025-11-04
*/
@Data
public class DocumentUploadRequest {
/**
* 文档名称
*/
private String name;
/**
* 索引方式
*/
@JSONField(name = "indexing_technique")
private String indexingTechnique;
/**
* 处理规则
*/
@JSONField(name = "process_rule")
private ProcessRule processRule;
@Data
public static class ProcessRule {
/**
* 分段模式automatic自动、custom自定义
*/
private String mode = "automatic";
/**
* 预处理规则
*/
private Rules rules;
@Data
public static class Rules {
/**
* 自动分段配置
*/
@JSONField(name = "pre_processing_rules")
private PreProcessingRules preProcessingRules;
/**
* 分段配置
*/
private Segmentation segmentation;
}
@Data
public static class PreProcessingRules {
/**
* 移除额外空格
*/
@JSONField(name = "remove_extra_spaces")
private Boolean removeExtraSpaces = true;
/**
* 移除URL和邮箱
*/
@JSONField(name = "remove_urls_emails")
private Boolean removeUrlsEmails = false;
}
@Data
public static class Segmentation {
/**
* 分隔符
*/
private String separator = "\\n";
/**
* 最大分段长度
*/
@JSONField(name = "max_tokens")
private Integer maxTokens = 1000;
/**
* 分段重叠长度
*/
@JSONField(name = "chunk_overlap")
private Integer chunkOverlap = 50;
}
}
}

View File

@@ -0,0 +1,145 @@
package org.xyzh.ai.client.dto;
import com.alibaba.fastjson2.annotation.JSONField;
import lombok.Data;
/**
* @description 文档上传响应(根据 Dify API 返回结构)
* @filename DocumentUploadResponse.java
* @author AI Assistant
* @copyright xyzh
* @since 2025-11-04
*/
@Data
public class DocumentUploadResponse {
/**
* 文档详细信息
*/
private Document document;
/**
* 批次ID用于查询处理状态
*/
private String batch;
/**
* 文档详细信息
*/
@Data
public static class Document {
/**
* 文档ID
*/
private String id;
/**
* 文档名称
*/
private String name;
/**
* 位置(序号)
*/
private Integer position;
/**
* 数据源类型
*/
@JSONField(name = "data_source_type")
private String dataSourceType;
/**
* 数据源信息
*/
@JSONField(name = "data_source_info")
private Object dataSourceInfo;
/**
* 数据集处理规则ID
*/
@JSONField(name = "dataset_process_rule_id")
private String datasetProcessRuleId;
/**
* 创建来源
*/
@JSONField(name = "created_from")
private String createdFrom;
/**
* 创建人
*/
@JSONField(name = "created_by")
private String createdBy;
/**
* 创建时间(时间戳)
*/
@JSONField(name = "created_at")
private Long createdAt;
/**
* Token数量
*/
private Integer tokens;
/**
* 索引状态
*/
@JSONField(name = "indexing_status")
private String indexingStatus;
/**
* 错误信息
*/
private String error;
/**
* 是否启用
*/
private Boolean enabled;
/**
* 禁用时间
*/
@JSONField(name = "disabled_at")
private Long disabledAt;
/**
* 禁用人
*/
@JSONField(name = "disabled_by")
private String disabledBy;
/**
* 是否归档
*/
private Boolean archived;
/**
* 显示状态
*/
@JSONField(name = "display_status")
private String displayStatus;
/**
* 字数
*/
@JSONField(name = "word_count")
private Integer wordCount;
/**
* 命中次数
*/
@JSONField(name = "hit_count")
private Integer hitCount;
/**
* 文档形式
*/
@JSONField(name = "doc_form")
private String docForm;
}
}

View File

@@ -0,0 +1,144 @@
package org.xyzh.ai.client.dto;
import com.alibaba.fastjson2.annotation.JSONField;
import lombok.Data;
import java.util.List;
import java.util.Map;
/**
* @description Dify嵌入模型响应
* @filename EmbeddingModelResponse.java
* @author AI Assistant
* @since 2025-11-06
*/
@Data
public class EmbeddingModelResponse {
/**
* 模型提供商列表
*/
@JSONField(name = "data")
private List<ModelProvider> data;
/**
* 模型提供商
*/
@Data
public static class ModelProvider {
/**
* 提供商标识
*/
@JSONField(name = "provider")
private String provider;
/**
* 提供商标签
*/
@JSONField(name = "label")
private Map<String, String> label;
/**
* 小图标
*/
@JSONField(name = "icon_small")
private Map<String, String> iconSmall;
/**
* 大图标
*/
@JSONField(name = "icon_large")
private Map<String, String> iconLarge;
/**
* 状态
*/
@JSONField(name = "status")
private String status;
/**
* 模型列表
*/
@JSONField(name = "models")
private List<Model> models;
}
/**
* 模型详情
*/
@Data
public static class Model {
/**
* 模型名称
*/
@JSONField(name = "model")
private String model;
/**
* 模型标签
*/
@JSONField(name = "label")
private Map<String, String> label;
/**
* 模型类型
*/
@JSONField(name = "model_type")
private String modelType;
/**
* 特性列表
*/
@JSONField(name = "features")
private List<Object> features;
/**
* 获取来源
*/
@JSONField(name = "fetch_from")
private String fetchFrom;
/**
* 模型属性
*/
@JSONField(name = "model_properties")
private ModelProperties modelProperties;
/**
* 是否已弃用
*/
@JSONField(name = "deprecated")
private Boolean deprecated;
/**
* 状态
*/
@JSONField(name = "status")
private String status;
/**
* 是否启用负载均衡
*/
@JSONField(name = "load_balancing_enabled")
private Boolean loadBalancingEnabled;
}
/**
* 模型属性
*/
@Data
public static class ModelProperties {
/**
* 上下文大小
*/
@JSONField(name = "context_size")
private Integer contextSize;
/**
* 最大分块数
*/
@JSONField(name = "max_chunks")
private Integer maxChunks;
}
}

View File

@@ -0,0 +1,94 @@
package org.xyzh.ai.client.dto;
import com.alibaba.fastjson2.annotation.JSONField;
import lombok.Data;
import java.util.List;
/**
* @description 消息历史响应
* @filename MessageHistoryResponse.java
* @author AI Assistant
* @copyright xyzh
* @since 2025-11-04
*/
@Data
public class MessageHistoryResponse {
private Integer limit;
@JSONField(name = "has_more")
private Boolean hasMore;
private List<MessageInfo> data;
@Data
public static class MessageInfo {
private String id;
@JSONField(name = "conversation_id")
private String conversationId;
private List<MessageContent> inputs;
private String query;
private String answer;
@JSONField(name = "message_files")
private List<MessageFile> messageFiles;
private Feedback feedback;
@JSONField(name = "retriever_resources")
private List<RetrieverResource> retrieverResources;
@JSONField(name = "created_at")
private Long createdAt;
@JSONField(name = "agent_thoughts")
private List<Object> agentThoughts;
}
@Data
public static class MessageContent {
private String key;
private String value;
}
@Data
public static class MessageFile {
private String id;
private String type;
private String url;
@JSONField(name = "belongs_to")
private String belongsTo;
}
@Data
public static class Feedback {
private String rating;
}
@Data
public static class RetrieverResource {
@JSONField(name = "dataset_id")
private String datasetId;
@JSONField(name = "dataset_name")
private String datasetName;
@JSONField(name = "document_id")
private String documentId;
@JSONField(name = "document_name")
private String documentName;
@JSONField(name = "segment_id")
private String segmentId;
private Double score;
private String content;
}
}

View File

@@ -0,0 +1,56 @@
package org.xyzh.ai.client.dto;
import com.alibaba.fastjson2.annotation.JSONField;
import lombok.Data;
import java.util.List;
import java.util.Map;
/**
* @description Dify Rerank模型响应
* @filename RerankModelResponse.java
* @author AI Assistant
* @copyright xyzh
* @since 2025-11-06
*/
@Data
public class RerankModelResponse {
private List<ModelProvider> data;
@Data
public static class ModelProvider {
private String provider;
private Map<String, String> label; // e.g., {"en_US": "Cohere", "zh_Hans": "Cohere"}
@JSONField(name = "icon_small")
private String iconSmall;
@JSONField(name = "icon_large")
private String iconLarge;
private String status; // e.g., "active"
private List<Model> models;
}
@Data
public static class Model {
private String model; // e.g., "rerank-multilingual-v3.0"
private Map<String, String> label;
@JSONField(name = "model_type")
private String modelType; // e.g., "rerank"
private List<String> features;
@JSONField(name = "fetch_from")
private String fetchFrom;
@JSONField(name = "model_properties")
private ModelProperties modelProperties;
private Boolean deprecated;
private String status; // e.g., "active"
private String provider; // 模型提供商(可能在 model 数据中)
}
@Data
public static class ModelProperties {
@JSONField(name = "context_size")
private Integer contextSize;
@JSONField(name = "max_chunks")
private Integer maxChunks;
}
}

View File

@@ -0,0 +1,76 @@
package org.xyzh.ai.client.dto;
import com.alibaba.fastjson2.annotation.JSONField;
import lombok.Data;
/**
* @description Dify检索模型配置Retrieval Model
* @filename RetrievalModel.java
* @author AI Assistant
* @copyright xyzh
* @since 2025-11-06
*/
@Data
public class RetrievalModel {
/**
* 搜索方法vector_search向量搜索、full_text_search全文搜索、hybrid_search混合搜索
*/
@JSONField(name = "search_method")
private String searchMethod;
/**
* Rerank是否启用
*/
@JSONField(name = "reranking_enable")
private Boolean rerankingEnable;
/**
* Rerank模式字符串值为 "reranking_model"
*/
@JSONField(name = "reranking_mode")
private String rerankingMode;
/**
* Rerank模型配置当 reranking_enable=true 时必须设置)
*/
@JSONField(name = "reranking_model")
private RerankingModel rerankingModel;
/**
* Top K返回前K个结果
*/
@JSONField(name = "top_k")
private Integer topK;
/**
* 分数阈值0.00-1.00
*/
@JSONField(name = "score_threshold")
private Double scoreThreshold;
/**
* 是否启用分数阈值
*/
@JSONField(name = "score_threshold_enabled")
private Boolean scoreThresholdEnabled;
/**
* Rerank模型配置嵌套对象
*/
@Data
public static class RerankingModel {
/**
* Rerank模型提供商
*/
@JSONField(name = "reranking_provider_name")
private String rerankingProviderName;
/**
* Rerank模型名称
*/
@JSONField(name = "reranking_model_name")
private String rerankingModelName;
}
}

View File

@@ -0,0 +1,33 @@
package org.xyzh.ai.client.dto;
import com.alibaba.fastjson2.annotation.JSONField;
import lombok.Data;
/**
* @description 知识库检索请求
* @filename RetrievalRequest.java
* @author AI Assistant
* @copyright xyzh
* @since 2025-11-04
*/
@Data
public class RetrievalRequest {
/**
* 查询文本
*/
private String query;
/**
* 返回的最相关结果数量
*/
@JSONField(name = "top_k")
private Integer topK = 3;
/**
* 相似度阈值0-1
*/
@JSONField(name = "score_threshold")
private Double scoreThreshold = 0.7;
}

View File

@@ -0,0 +1,88 @@
package org.xyzh.ai.client.dto;
import com.alibaba.fastjson2.annotation.JSONField;
import lombok.Data;
import java.util.List;
import java.util.Map;
/**
* @description 知识库检索响应
* @filename RetrievalResponse.java
* @author AI Assistant
* @copyright xyzh
* @since 2025-11-04
*/
@Data
public class RetrievalResponse {
/**
* 查询ID
*/
@JSONField(name = "query_id")
private String queryId;
/**
* 检索结果列表
*/
private List<RetrievalRecord> records;
@Data
public static class RetrievalRecord {
/**
* 分段内容
*/
private String content;
/**
* 相似度分数
*/
private Double score;
/**
* 标题
*/
private String title;
/**
* 元数据
*/
private Map<String, Object> metadata;
/**
* 文档ID
*/
@JSONField(name = "document_id")
private String documentId;
/**
* 文档名称
*/
@JSONField(name = "document_name")
private String documentName;
/**
* 分段ID
*/
@JSONField(name = "segment_id")
private String segmentId;
/**
* 分段位置
*/
@JSONField(name = "segment_position")
private Integer segmentPosition;
/**
* 索引节点ID
*/
@JSONField(name = "index_node_id")
private String indexNodeId;
/**
* 索引节点哈希
*/
@JSONField(name = "index_node_hash")
private String indexNodeHash;
}
}

View File

@@ -0,0 +1,170 @@
package org.xyzh.ai.config;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.xyzh.api.system.service.SysConfigService;
import jakarta.annotation.PostConstruct;
/**
* @description Dify配置类
* @filename DifyConfig.java
* @author AI Assistant
* @copyright xyzh
* @since 2025-11-04
*/
@Slf4j
@Data
@Configuration
public class DifyConfig {
@Autowired
private SysConfigService sysConfigService;
/**
* Dify API基础地址
*/
private String apiBaseUrl = "http://192.168.130.131/v1";
private String knowledgeApiKey="dataset-nupqKP4LONpzdXmGthIrbjeJ";
/**
* 请求超时时间(秒)
*/
private Integer timeout = 60;
/**
* 连接超时时间(秒)
*/
private Integer connectTimeout = 10;
/**
* 读取超时时间(秒)
*/
private Integer readTimeout = 60;
/**
* 流式响应超时时间(秒)
*/
private Integer streamTimeout = 300;
/**
* 上传文件配置
*/
private Upload upload = new Upload();
/**
* 知识库配置
*/
private Dataset dataset = new Dataset();
/**
* 初始化配置,从数据库加载
*/
@PostConstruct
public void init() {
try {
log.info("开始从数据库加载Dify配置...");
// 基础配置
loadStringConfig("dify.apiBaseUrl", val -> this.apiBaseUrl = val);
loadStringConfig("dify.knowledgeApiKey", val -> this.knowledgeApiKey = val);
loadIntegerConfig("dify.timeout", val -> this.timeout = val);
loadIntegerConfig("dify.connectTimeout", val -> this.connectTimeout = val);
loadIntegerConfig("dify.readTimeout", val -> this.readTimeout = val);
loadIntegerConfig("dify.streamTimeout", val -> this.streamTimeout = val);
// Upload配置
loadStringConfig("dify.upload.allowedTypes", val -> {
if (val != null && !val.trim().isEmpty()) {
this.upload.allowedTypes = val.split(",");
}
});
loadIntegerConfig("dify.upload.maxSize", val -> this.upload.maxSize = val);
// Dataset配置
loadStringConfig("dify.dataset.defaultIndexingTechnique", val -> this.dataset.defaultIndexingTechnique = val);
loadStringConfig("dify.dataset.defaultEmbeddingModel", val -> this.dataset.defaultEmbeddingModel = val);
log.info("Dify配置加载完成 - API地址: {}", apiBaseUrl);
} catch (Exception e) {
log.error("加载Dify配置失败将使用默认值", e);
}
}
/**
* 加载字符串配置
*/
private void loadStringConfig(String key, java.util.function.Consumer<String> setter) {
try {
String value = sysConfigService.getStringConfig(key);
if (value != null && !value.trim().isEmpty()) {
setter.accept(value);
log.debug("加载配置: {} = {}", key, value);
}
} catch (Exception e) {
log.warn("加载配置失败: {}, 错误: {}", key, e.getMessage());
}
}
/**
* 加载整数配置
*/
private void loadIntegerConfig(String key, java.util.function.Consumer<Integer> setter) {
try {
Integer value = sysConfigService.getIntConfig(key);
if (value != null) {
setter.accept(value);
log.debug("加载配置: {} = {}", key, value);
}
} catch (Exception e) {
log.warn("加载配置失败: {}, 错误: {}", key, e.getMessage());
}
}
@Data
public static class Upload {
/**
* 支持的文件类型
*/
private String[] allowedTypes = {"pdf", "txt", "docx", "doc", "md", "html", "htm"};
/**
* 最大文件大小MB
*/
private Integer maxSize = 50;
}
@Data
public static class Dataset {
/**
* 默认索引方式high_quality/economy
*/
private String defaultIndexingTechnique = "high_quality";
/**
* 默认Embedding模型
*/
private String defaultEmbeddingModel = "text-embedding-ada-002";
}
/**
* 验证配置是否有效
*/
public boolean isValid() {
return apiBaseUrl != null && !apiBaseUrl.trim().isEmpty();
}
/**
* 获取完整的API URL
*/
public String getFullApiUrl(String endpoint) {
String baseUrl = apiBaseUrl.endsWith("/") ? apiBaseUrl.substring(0, apiBaseUrl.length() - 1) : apiBaseUrl;
String path = endpoint.startsWith("/") ? endpoint : "/" + endpoint;
return baseUrl + path;
}
}

View File

@@ -0,0 +1,42 @@
package org.xyzh.ai.exception;
/**
* @description Dify API调用异常
* @filename DifyException.java
* @author AI Assistant
* @copyright xyzh
* @since 2025-11-04
*/
public class DifyException extends RuntimeException {
private static final long serialVersionUID = 1L;
private Integer code;
public DifyException(String message) {
super(message);
}
public DifyException(Integer code, String message) {
super(message);
this.code = code;
}
public DifyException(String message, Throwable cause) {
super(message, cause);
}
public DifyException(Integer code, String message, Throwable cause) {
super(message, cause);
this.code = code;
}
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
}

View File

@@ -1,51 +0,0 @@
package org.xyzh.api.agent.dto;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.xyzh.common.dto.BaseDTO;
import io.swagger.v3.oas.annotations.media.Schema;
import com.fasterxml.jackson.databind.JsonNode;
/**
* 知识库DTO
* 用于创建和更新知识库
*/
@Data
@EqualsAndHashCode(callSuper = true)
@Schema(description = "知识库DTO")
public class KnowledgeBaseDTO extends BaseDTO {
private static final long serialVersionUID = 1L;
@Schema(description = "知识库ID更新时需要")
private String knowledgeId;
@Schema(description = "智能体ID")
private String agentId;
@Schema(description = "知识库名称")
private String name;
@Schema(description = "知识库类型bidding-招投标/customer_service-客服/internal-内部协同")
private String kbType;
@Schema(description = "访问级别public-公开/private-私有/internal-内部")
private String accessLevel;
@Schema(description = "知识库描述")
private String description;
@Schema(description = "存储路径")
private String storagePath;
@Schema(description = "当前版本号")
private String version;
@Schema(description = "知识库配置")
private JsonNode config;
@Schema(description = "服务类型")
private String serviceType;
@Schema(description = "状态active-激活/inactive-停用/archived-归档")
private String status;
}

View File

@@ -1,48 +0,0 @@
package org.xyzh.api.agent.dto;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.xyzh.common.dto.BaseDTO;
import io.swagger.v3.oas.annotations.media.Schema;
import com.fasterxml.jackson.databind.JsonNode;
/**
* 知识文档片段DTO
* 用于创建和更新知识文档片段
*/
@Data
@EqualsAndHashCode(callSuper = true)
@Schema(description = "知识文档片段DTO")
public class KnowledgeChunkDTO extends BaseDTO {
private static final long serialVersionUID = 1L;
@Schema(description = "片段ID更新时需要")
private String chunkId;
@Schema(description = "所属文档ID")
private String docId;
@Schema(description = "所属知识库ID")
private String knowledgeId;
@Schema(description = "片段索引")
private Integer chunkIndex;
@Schema(description = "片段内容")
private String content;
@Schema(description = "片段类型text-文本/table-表格/image-图片")
private String chunkType;
@Schema(description = "根chunk ID")
private String rootChunkId;
@Schema(description = "是否当前版本", defaultValue = "true")
private Boolean isCurrent;
@Schema(description = "位置信息")
private JsonNode positionInfo;
@Schema(description = "片段元数据")
private JsonNode metadata;
}

View File

@@ -1,67 +0,0 @@
package org.xyzh.api.agent.dto;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.xyzh.common.dto.BaseDTO;
import io.swagger.v3.oas.annotations.media.Schema;
import com.fasterxml.jackson.databind.JsonNode;
import java.util.List;
/**
* 知识文档DTO
* 用于创建和更新知识文档
*/
@Data
@EqualsAndHashCode(callSuper = true)
@Schema(description = "知识文档DTO")
public class KnowledgeDocumentDTO extends BaseDTO {
private static final long serialVersionUID = 1L;
@Schema(description = "文档ID更新时需要")
private String docId;
@Schema(description = "所属知识库ID")
private String knowledgeId;
@Schema(description = "文档标题")
private String title;
@Schema(description = "文档类型text-文本/pdf/word/excel/image/video")
private String docType;
@Schema(description = "文档分类")
private String category;
@Schema(description = "文档内容(文本类型)")
private String content;
@Schema(description = "关联文件ID")
private String fileId;
@Schema(description = "文件路径")
private String filePath;
@Schema(description = "文件大小")
private Long fileSize;
@Schema(description = "MIME类型")
private String mimeType;
@Schema(description = "根文档ID")
private String rootDocId;
@Schema(description = "文档标签")
private List<String> tags;
@Schema(description = "来源URL")
private String sourceUrl;
@Schema(description = "服务类型")
private String serviceType;
@Schema(description = "文档元数据")
private JsonNode metadata;
@Schema(description = "状态active-激活/inactive-停用/archived-归档")
private String status;
}

View File

@@ -1,93 +0,0 @@
package org.xyzh.api.agent.vo;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.xyzh.common.vo.BaseVO;
import io.swagger.v3.oas.annotations.media.Schema;
import com.alibaba.fastjson2.annotation.JSONField;
import com.fasterxml.jackson.databind.JsonNode;
import java.util.Date;
/**
* 知识库VO
* 用于前端展示知识库信息
*/
@Data
@EqualsAndHashCode(callSuper = true)
@Schema(description = "知识库VO")
public class KnowledgeBaseVO extends BaseVO {
private static final long serialVersionUID = 1L;
@Schema(description = "知识库ID")
private String knowledgeId;
@Schema(description = "智能体ID")
private String agentId;
@Schema(description = "智能体名称")
private String agentName;
@Schema(description = "知识库名称")
private String name;
@Schema(description = "知识库类型")
private String kbType;
@Schema(description = "知识库类型名称")
private String kbTypeName;
@Schema(description = "访问级别")
private String accessLevel;
@Schema(description = "访问级别名称")
private String accessLevelName;
@Schema(description = "知识库描述")
private String description;
@Schema(description = "存储路径")
private String storagePath;
@Schema(description = "当前版本号")
private String version;
@Schema(description = "知识库配置")
private JsonNode config;
@Schema(description = "服务类型")
private String serviceType;
@Schema(description = "部门名称")
private String deptName;
@Schema(description = "状态")
private String status;
@Schema(description = "状态名称")
private String statusName;
@Schema(description = "状态颜色")
private String statusColor;
@Schema(description = "文档总数")
private Long documentCount;
@Schema(description = "已向量化文档数")
private Long embeddedDocCount;
@Schema(description = "chunk总数")
private Long chunkCount;
@Schema(description = "总存储大小(字节)")
private Long totalSize;
@Schema(description = "总存储大小格式化显示")
private String totalSizeFormatted;
@Schema(description = "最后同步时间", format = "date-time")
@JSONField(format = "yyyy-MM-dd HH:mm:ss")
private Date lastSyncTime;
@Schema(description = "创建者姓名")
private String creatorName;
}

View File

@@ -1,68 +0,0 @@
package org.xyzh.api.agent.vo;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.xyzh.common.vo.BaseVO;
import io.swagger.v3.oas.annotations.media.Schema;
import com.alibaba.fastjson2.annotation.JSONField;
import com.fasterxml.jackson.databind.JsonNode;
import java.util.Date;
/**
* 知识文档片段VO
* 用于前端展示知识文档片段信息
*/
@Data
@EqualsAndHashCode(callSuper = true)
@Schema(description = "知识文档片段VO")
public class KnowledgeChunkVO extends BaseVO {
private static final long serialVersionUID = 1L;
@Schema(description = "片段ID")
private String chunkId;
@Schema(description = "所属文档ID")
private String docId;
@Schema(description = "文档标题")
private String docTitle;
@Schema(description = "所属知识库ID")
private String knowledgeId;
@Schema(description = "知识库名称")
private String knowledgeName;
@Schema(description = "片段索引")
private Integer chunkIndex;
@Schema(description = "片段内容")
private String content;
@Schema(description = "内容长度")
private Integer contentLength;
@Schema(description = "片段类型")
private String chunkType;
@Schema(description = "片段类型名称")
private String chunkTypeName;
@Schema(description = "版本号")
private Integer version;
@Schema(description = "根chunk ID")
private String rootChunkId;
@Schema(description = "是否当前版本", defaultValue = "false")
private Boolean isCurrent;
@Schema(description = "位置信息")
private JsonNode positionInfo;
@Schema(description = "片段元数据")
private JsonNode metadata;
@Schema(description = "部门名称")
private String deptName;
}

View File

@@ -1,114 +0,0 @@
package org.xyzh.api.agent.vo;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.xyzh.common.vo.BaseVO;
import io.swagger.v3.oas.annotations.media.Schema;
import com.alibaba.fastjson2.annotation.JSONField;
import com.fasterxml.jackson.databind.JsonNode;
import java.util.Date;
import java.util.List;
/**
* 知识文档VO
* 用于前端展示知识文档信息
*/
@Data
@EqualsAndHashCode(callSuper = true)
@Schema(description = "知识文档VO")
public class KnowledgeDocumentVO extends BaseVO {
private static final long serialVersionUID = 1L;
@Schema(description = "文档ID")
private String docId;
@Schema(description = "所属知识库ID")
private String knowledgeId;
@Schema(description = "知识库名称")
private String knowledgeName;
@Schema(description = "文档标题")
private String title;
@Schema(description = "文档类型")
private String docType;
@Schema(description = "文档类型名称")
private String docTypeName;
@Schema(description = "文档分类")
private String category;
@Schema(description = "文档内容")
private String content;
@Schema(description = "内容摘要")
private String contentSummary;
@Schema(description = "关联文件ID")
private String fileId;
@Schema(description = "文件路径")
private String filePath;
@Schema(description = "文件下载URL")
private String fileUrl;
@Schema(description = "文件大小")
private Long fileSize;
@Schema(description = "文件大小格式化显示")
private String fileSizeFormatted;
@Schema(description = "MIME类型")
private String mimeType;
@Schema(description = "版本号")
private Integer version;
@Schema(description = "根文档ID")
private String rootDocId;
@Schema(description = "文档标签")
private List<String> tags;
@Schema(description = "关键词")
private List<String> keywords;
@Schema(description = "向量化状态")
private String embeddingStatus;
@Schema(description = "向量化状态名称")
private String embeddingStatusName;
@Schema(description = "使用的向量化模型")
private String embeddingModel;
@Schema(description = "切片数量")
private Integer chunkCount;
@Schema(description = "文档元数据")
private JsonNode metadata;
@Schema(description = "来源URL")
private String sourceUrl;
@Schema(description = "服务类型")
private String serviceType;
@Schema(description = "部门名称")
private String deptName;
@Schema(description = "状态")
private String status;
@Schema(description = "状态名称")
private String statusName;
@Schema(description = "创建者姓名")
private String creatorName;
@Schema(description = "更新者姓名")
private String updaterName;
}

View File

@@ -9,8 +9,8 @@
<version>1.0.0</version> <version>1.0.0</version>
</parent> </parent>
<groupId>org.xyzh</groupId> <groupId>org.xyzh.apis</groupId>
<artifactId>api-agent</artifactId> <artifactId>api-ai</artifactId>
<version>1.0.0</version> <version>1.0.0</version>
<properties> <properties>

View File

@@ -1,4 +1,4 @@
package org.xyzh.api.agent; package org.xyzh.api.ai;
/** /**
* Agent服务接口 * Agent服务接口

View File

@@ -0,0 +1,99 @@
package org.xyzh.api.ai.dto;
import com.alibaba.fastjson2.annotation.JSONField;
import lombok.Data;
/**
* @description Dify文件信息用于对话文件上传和请求
* @filename DifyFileInfo.java
* @author AI Assistant
* @copyright xyzh
* @since 2025-12-15
*/
@Data
public class DifyFileInfo {
/**
* 文件IDDify返回
*/
private String id;
/**
* 文件名
*/
private String name;
/**
* 文件大小(字节)
*/
private Integer size;
/**
* 文件扩展名
*/
private String extension;
/**
* 文件MIME类型
*/
@JSONField(name = "mime_type")
private String mimeType;
/**
* 上传人ID
*/
@JSONField(name = "created_by")
private String createdBy;
/**
* 上传时间(时间戳)
*/
@JSONField(name = "created_at")
private Long createdAt;
/**
* 预览URL
*/
@JSONField(name = "preview_url")
private String previewUrl;
/**
* 源文件URL
*/
@JSONField(name = "source_url")
private String sourceUrl;
/**
* 文件类型image、document、audio、video、file
*/
private String type;
/**
* 传输方式remote_url、local_file
*/
@JSONField(name = "transfer_method")
private String transferMethod;
/**
* 文件URL或ID
*/
private String url;
/**
* 本地文件上传ID
*/
@JSONField(name = "upload_file_id")
private String uploadFileId;
/**
* 系统文件ID
*/
@JSONField(name = "sys_file_id")
private String sysFileId;
/**
* 文件路径(从系统文件表获取)
*/
@JSONField(name = "file_path")
private String filePath;
}

View File

@@ -0,0 +1,14 @@
package org.xyzh.api.ai.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
@Schema(description = "智能体提示卡")
public class PromptCard {
@Schema(description = "文件ID")
private String fileId;
@Schema(description = "提示词")
private String prompt;
}

View File

@@ -0,0 +1,41 @@
package org.xyzh.api.ai.dto;
import java.util.List;
import org.xyzh.common.dto.BaseDTO;
import lombok.Data;
import lombok.EqualsAndHashCode;
import io.swagger.v3.oas.annotations.media.Schema;
@Data
@EqualsAndHashCode(callSuper = true)
@Schema(description = "系统文件DTO")
public class TbAgent extends BaseDTO{
private static final long serialVersionUID = 1L;
@Schema(description = "智能体ID")
private String agentId;
@Schema(description = "智能体名称")
private String name;
@Schema(description = "智能体描述")
private String description;
@Schema(description = "智能体url")
private String link;
@Schema(description = "智能体APIKEY")
private String apiKey;
@Schema(description = "引导词")
private String introduce;
@Schema(description = "提示卡片数组")
private List<PromptCard> promptCards;
@Schema(description = "分类")
private String category;
}

View File

@@ -0,0 +1,25 @@
package org.xyzh.api.ai.dto;
import org.xyzh.common.dto.BaseDTO;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
@Schema(description = "AI智能体对话")
public class TbChat extends BaseDTO{
private static final long serialVersionUID = 1L;
@Schema(description = "对话ID")
private String chatId;
@Schema(description = "智能体ID")
private String agentId;
@Schema(description = "用户ID")
private String userId;
@Schema(description = "对话标题")
private String title;
}

View File

@@ -0,0 +1,30 @@
package org.xyzh.api.ai.dto;
import java.util.List;
import org.xyzh.common.dto.BaseDTO;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
@Schema(description = "AI智能体对话消息")
public class TbChatMessage extends BaseDTO{
private static final long serialVersionUID = 1L;
@Schema(description = "消息ID")
private String messageId;
@Schema(description = "对话ID")
private String chatId;
@Schema(description = "角色")
private String role;
@Schema(description = "内容")
private String content;
@Schema(description = "文件ID数组")
private List<String> files;
}

View File

@@ -0,0 +1,68 @@
package org.xyzh.api.ai.dto;
import org.xyzh.common.dto.BaseDTO;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
@Schema(description = "知识库配置")
public class TbKnowledge extends BaseDTO{
private static final long serialVersionUID = 1L;
@Schema(description = "知识库ID")
private String knowledgeId;
@Schema(description = "知识库标题")
private String title;
@Schema(description = "知识库头像")
private String avatar;
@Schema(description = "知识库描述")
private String description;
@Schema(description = "Dify知识库IDDataset ID")
private String difyDatasetId;
@Schema(description = "Dify索引方式high_quality/economy")
private String difyIndexingTechnique;
@Schema(description = "向量模型名称")
private String embeddingModel;
@Schema(description = "向量模型提供商")
private String embeddingModelProvider;
@Schema(description = "Rerank模型名称")
private String rerankModel;
@Schema(description = "Rerank模型提供商")
private String rerankModelProvider;
@Schema(description = "是否启用Rerank0否 1是")
private Integer rerankingEnable;
@Schema(description = "检索Top K返回前K个结果")
private Integer retrievalTopK;
@Schema(description = "检索分数阈值0.00-1.00")
private Double retrievalScoreThreshold;
@Schema(description = "文档数量")
private Integer documentCount;
@Schema(description = "总分段数")
private Integer totalChunks;
@Schema(description = "所属服务 workcase、bidding")
private String service;
@Schema(description = "bidding所属项目ID")
private String projectId;
@Schema(description = "所属分类 workcase 内部知识库、外部知识库")
private String category;
}

View File

@@ -0,0 +1,28 @@
package org.xyzh.api.ai.dto;
import org.xyzh.common.dto.BaseDTO;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
@Schema(description = "知识库文件")
public class TbKnowledgeFile extends BaseDTO{
private final static long serialVersionUID = 1L;
@Schema(description = "知识库ID")
private String knowledgeId;
@Schema(description = "文件ID")
private String fileId;
@Schema(description = "文件根ID")
private String fileRootId;
@Schema(description = "Dify文件ID")
private String difyFileId;
@Schema(description = "文件版本")
private String version;
}

View File

@@ -0,0 +1,12 @@
package org.xyzh.api.ai.vo;
import org.xyzh.common.vo.BaseVO;
import lombok.Data;
import io.swagger.v3.oas.annotations.media.Schema;
@Data
@Schema(description = "智能体VO")
public class AgentVO extends BaseVO{
}

View File

@@ -22,6 +22,12 @@ public class TbSysFileDTO extends BaseDTO {
@Schema(description = "文件ID (主键)") @Schema(description = "文件ID (主键)")
private String fileId; private String fileId;
@Schema(description = "文件根ID")
private String fileRootId;
@Schema(description = "文件版本")
private String version;
@Schema(description = "文件名") @Schema(description = "文件名")
private String name; private String name;

View File

@@ -21,7 +21,7 @@
<module>api-log</module> <module>api-log</module>
<module>api-system</module> <module>api-system</module>
<module>api-crontab</module> <module>api-crontab</module>
<module>api-agent</module> <module>api-ai</module>
<module>api-bidding</module> <module>api-bidding</module>
<module>api-platform</module> <module>api-platform</module>
<module>api-workcase</module> <module>api-workcase</module>

View File

@@ -0,0 +1,59 @@
package org.xyzh.common.utils.json;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedTypes;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* @description PostgreSQL VARCHAR数组类型处理器
* @filename StringArrayTypeHandler.java
* @author yslg
* @copyright yslg
* @since 2025-12-15
*/
@MappedTypes({List.class})
public class StringArrayTypeHandler extends BaseTypeHandler<List<String>> {
@Override
public void setNonNullParameter(PreparedStatement ps, int i, List<String> parameter, JdbcType jdbcType) throws SQLException {
if (parameter == null || parameter.isEmpty()) {
ps.setArray(i, null);
} else {
ps.setArray(i, ps.getConnection().createArrayOf("varchar", parameter.toArray()));
}
}
@Override
public List<String> getNullableResult(ResultSet rs, String columnName) throws SQLException {
java.sql.Array array = rs.getArray(columnName);
return parseArray(array);
}
@Override
public List<String> getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
java.sql.Array array = rs.getArray(columnIndex);
return parseArray(array);
}
@Override
public List<String> getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
java.sql.Array array = cs.getArray(columnIndex);
return parseArray(array);
}
private List<String> parseArray(java.sql.Array array) throws SQLException {
if (array == null) {
return null;
}
Object[] objects = (Object[]) array.getArray();
return new ArrayList<>(Arrays.asList((String[]) objects));
}
}

View File

@@ -17,7 +17,7 @@
<module>file</module> <module>file</module>
<module>message</module> <module>message</module>
<module>crontab</module> <module>crontab</module>
<module>agent</module> <module>ai</module>
<module>bidding</module> <module>bidding</module>
<module>platform</module> <module>platform</module>
<module>workcase</module> <module>workcase</module>

View File

@@ -0,0 +1,2 @@
1. 不再自己构建知识库使用dify的知识库
2. 服务的知识库表,只对文件进行重新上传才产生版本,对知识库内文档分段等修改不生成新的版本