mirror of
https://gitcode.com/ageerle/ruoyi-ai.git
synced 2026-04-09 01:37:32 +00:00
Merge remote-tracking branch 'upstream/v3.0.0' into v3.0.0-deploy
This commit is contained in:
41
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
41
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
---
|
||||||
|
name: 漏洞报告
|
||||||
|
about: 报告项目中的Bug或安全问题
|
||||||
|
title: '[Bug] '
|
||||||
|
labels: 'bug'
|
||||||
|
assignees: ''
|
||||||
|
---
|
||||||
|
|
||||||
|
## 问题描述
|
||||||
|
|
||||||
|
简要描述遇到的问题:
|
||||||
|
|
||||||
|
## 复现步骤
|
||||||
|
|
||||||
|
1.
|
||||||
|
2.
|
||||||
|
3.
|
||||||
|
|
||||||
|
## 期望行为
|
||||||
|
|
||||||
|
描述你期望发生的情况:
|
||||||
|
|
||||||
|
## 实际行为
|
||||||
|
|
||||||
|
描述实际发生的情况:
|
||||||
|
|
||||||
|
## 环境信息
|
||||||
|
|
||||||
|
| 项目 | 信息 |
|
||||||
|
|:---|:---|
|
||||||
|
| 操作系统 | |
|
||||||
|
| JDK版本 | |
|
||||||
|
| 项目版本 | |
|
||||||
|
|
||||||
|
## 截图/日志
|
||||||
|
|
||||||
|
如有相关信息,请在此粘贴:
|
||||||
|
|
||||||
|
## 补充说明
|
||||||
|
|
||||||
|
其他补充信息:
|
||||||
1
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
1
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
blank_issues_enabled: true
|
||||||
15
.github/ISSUE_TEMPLATE/custom.md
vendored
Normal file
15
.github/ISSUE_TEMPLATE/custom.md
vendored
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
---
|
||||||
|
name: 自定义
|
||||||
|
about: 其他问题或讨论
|
||||||
|
title: ''
|
||||||
|
labels: ''
|
||||||
|
assignees: ''
|
||||||
|
---
|
||||||
|
|
||||||
|
## 描述
|
||||||
|
|
||||||
|
请详细描述你的问题或需求:
|
||||||
|
|
||||||
|
## 补充信息
|
||||||
|
|
||||||
|
如有其他补充,请在此填写:
|
||||||
57
.github/ISSUE_TEMPLATE/enterprise-collaboration.md
vendored
Normal file
57
.github/ISSUE_TEMPLATE/enterprise-collaboration.md
vendored
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
---
|
||||||
|
name: 企业AI应用合作登记
|
||||||
|
about: 登记企业信息与合作意向,获取免费技术支持
|
||||||
|
title: '[合作登记] 企业AI应用需求登记'
|
||||||
|
labels: '合作登记'
|
||||||
|
assignees: ''
|
||||||
|
---
|
||||||
|
|
||||||
|
## 登记说明
|
||||||
|
|
||||||
|
感谢您关注 RuoYi AI!我们团队今年的重点是帮助企业落地AI应用,如果贵公司符合要求,我们可以提供**免费的技术支持**。
|
||||||
|
|
||||||
|
请在下方评论中填写登记信息,格式如下:
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 登记格式预览
|
||||||
|
|
||||||
|
| 字段 | 内容 |
|
||||||
|
|:---|:---|
|
||||||
|
| 公司名称 | (必填) |
|
||||||
|
| 公司Logo地址 | (可选) |
|
||||||
|
| 所属行业 | (必填) |
|
||||||
|
| 公司所在地 | (必填) |
|
||||||
|
| 项目名称 | (必填) |
|
||||||
|
| 项目Logo地址 | (可选) |
|
||||||
|
| 项目简介 | (必填) |
|
||||||
|
| 当前AI应用状态 | □ 尚未开始 □ 规划中 □ 已有初步应用 □ 已有成熟应用 |
|
||||||
|
| 计划落地时间 | □ 1个月内 □ 1-3个月 □ 3-6个月 □ 6个月以上 |
|
||||||
|
| 当前痛点或挑战 | (可选) |
|
||||||
|
| 公司简介 | (可选) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 可复制格式
|
||||||
|
|
||||||
|
复制下方内容并在评论中填写:
|
||||||
|
|
||||||
|
```
|
||||||
|
| 字段 | 内容 |
|
||||||
|
|:---|:---|
|
||||||
|
| 公司名称 | |
|
||||||
|
| 公司Logo地址 | |
|
||||||
|
| 所属行业 | |
|
||||||
|
| 公司所在地 | |
|
||||||
|
| 项目名称 | |
|
||||||
|
| 项目Logo地址 | |
|
||||||
|
| 项目简介 | |
|
||||||
|
| 当前AI应用状态 | |
|
||||||
|
| 计划落地时间 | |
|
||||||
|
| 当前痛点或挑战 | |
|
||||||
|
| 公司简介 | |
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
> **温馨提示**:提交的信息仅用于合作沟通,不会对外公开。
|
||||||
31
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
31
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
---
|
||||||
|
name: 想法建议
|
||||||
|
about: 提出新功能建议或改进想法
|
||||||
|
title: '[Feature] '
|
||||||
|
labels: 'enhancement'
|
||||||
|
assignees: ''
|
||||||
|
---
|
||||||
|
|
||||||
|
## 建议类型
|
||||||
|
|
||||||
|
□ 新功能 □ 功能改进 □ 文档完善 □ 其他
|
||||||
|
|
||||||
|
## 建议描述
|
||||||
|
|
||||||
|
清晰描述你的建议内容:
|
||||||
|
|
||||||
|
## 使用场景
|
||||||
|
|
||||||
|
描述这个功能在什么场景下会用到:
|
||||||
|
|
||||||
|
## 期望效果
|
||||||
|
|
||||||
|
描述你期望的效果:
|
||||||
|
|
||||||
|
## 参考示例
|
||||||
|
|
||||||
|
如有类似的参考实现或产品,请提供链接:
|
||||||
|
|
||||||
|
## 补充说明
|
||||||
|
|
||||||
|
其他补充信息:
|
||||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -21,10 +21,13 @@ target/
|
|||||||
|
|
||||||
### IntelliJ IDEA ###
|
### IntelliJ IDEA ###
|
||||||
.idea
|
.idea
|
||||||
|
.claude
|
||||||
|
.github
|
||||||
*.iws
|
*.iws
|
||||||
*.iml
|
*.iml
|
||||||
*.ipr
|
*.ipr
|
||||||
|
|
||||||
|
|
||||||
### JRebel ###
|
### JRebel ###
|
||||||
rebel.xml
|
rebel.xml
|
||||||
|
|
||||||
@@ -48,3 +51,4 @@ data/
|
|||||||
!*/build/*.xml
|
!*/build/*.xml
|
||||||
|
|
||||||
.flattened-pom.xml
|
.flattened-pom.xml
|
||||||
|
/.claude/settings.local.json
|
||||||
|
|||||||
78
README.md
78
README.md
@@ -17,12 +17,9 @@
|
|||||||
|
|
||||||
<img src="docs/image/logo.png" alt="RuoYi AI Logo" width="120" height="120">
|
<img src="docs/image/logo.png" alt="RuoYi AI Logo" width="120" height="120">
|
||||||
|
|
||||||
## 功能建议&bug提交:【腾讯文档】
|
|
||||||
https://docs.qq.com/sheet/DR3hoR3FVVkpJcnVm
|
|
||||||
|
|
||||||
### 企业级AI助手平台
|
### 企业级AI助手平台
|
||||||
|
|
||||||
*开箱即用的全栈AI平台,集成Coze、DIFY等主流AI平台,提供先进的RAG技术、知识图谱、数字人和AI流程编排能力*
|
*开箱即用的全栈AI平台,支持多智能体协同、Supervisor模式编排、多种决策模型,提供先进的RAG技术和可视化流程编排能力*
|
||||||
|
|
||||||
**[English](README_EN.md)** | **[📖 使用文档](https://doc.pandarobot.chat)** |
|
**[English](README_EN.md)** | **[📖 使用文档](https://doc.pandarobot.chat)** |
|
||||||
**[🚀 在线体验](https://web.pandarobot.chat)** | **[🐛 问题反馈](https://github.com/ageerle/ruoyi-ai/issues)** | **[💡 功能建议](https://github.com/ageerle/ruoyi-ai/issues)**
|
**[🚀 在线体验](https://web.pandarobot.chat)** | **[🐛 问题反馈](https://github.com/ageerle/ruoyi-ai/issues)** | **[💡 功能建议](https://github.com/ageerle/ruoyi-ai/issues)**
|
||||||
@@ -34,34 +31,23 @@ https://docs.qq.com/sheet/DR3hoR3FVVkpJcnVm
|
|||||||
|
|
||||||
## ✨ 核心亮点
|
## ✨ 核心亮点
|
||||||
|
|
||||||
### 智能AI引擎
|
| 模块 | 现有能力 | 扩展方向 |
|
||||||
- **多模型接入**:支持 OpenAI、DeepSeek、通义千问、智谱AI 等主流厂商的模型
|
|:---:|---|---|
|
||||||
- **多模态理解**:支持文本、图片、文档等多种格式的智能处理
|
| **模型管理** | 多模型接入(OpenAI/DeepSeek/通义/智谱)、多模态理解、Coze/DIFY/FastGPT平台集成 | 自动模式、容错机制 |
|
||||||
- **AI平台集成**:集成了 **扣子(Coze)**、**DIFY**、**FastGPT** 等主流AI应用平台
|
| **知识库** | 本地RAG + 向量库(Milvus/Weaviate) + 知识图谱 + 文档解析 +重排序 | 音频视频解析、知识出处 |
|
||||||
- **MCP能力集成**:基于模型上下文协议,打造可扩展的AI工具生态系统
|
| **工具管理** | Mcp协议集成、Skills能力 + 可扩展工具生态 | 工具插件市场、toolAgent自动加载工具 |
|
||||||
- **AI编程助手**:内置智能代码分析和项目脚手架生成能力
|
| **流程编排** | 可视化工作流设计器、节点拖拽编排、SSE流式执行,目前已经支持模型调用,邮件发送,人工审核等节点 | 更多节点类型 |
|
||||||
|
| **多智能体** | 基于Langchain4j的Agent框架、Supervisor模式编排,支持多种决策模型 | 智能体可配置 |
|
||||||
### 本地化RAG方案
|
| **AI编程** | 智能代码分析、项目脚手架生成、Copilot助手 | 代码生成优化 |
|
||||||
- **私有知识库**:基于 Langchain4j 框架 + BGE-large-zh-v1.5 中文向量模型实现本地私有知识库
|
|
||||||
- **多种向量库**:支持 Milvus、Weaviate、Qdrant 等主流向量数据库
|
|
||||||
- **数据安全可控**:支持完全本地部署,保护企业数据隐私
|
|
||||||
- **灵活模型部署**:兼容 Ollama、vLLM 等本地推理框架
|
|
||||||
|
|
||||||
### AI创作工具
|
|
||||||
- **AI 绘画创作**: 集成 MidJourney、GPT-4o-image
|
|
||||||
- **智能PPT生成**:一键将文本内容转换为精美演示文稿
|
|
||||||
|
|
||||||
### 知识图谱与智能编排
|
|
||||||
- **知识图谱构建**:自动从文档和对话中提取实体关系,构建可视化知识网络
|
|
||||||
- **AI 流程编排**:可视化工作流设计器,支持复杂AI任务的编排和自动化执行
|
|
||||||
- **数字人交互**:集成数字人形象,提供更自然的人机交互体验
|
|
||||||
|
|
||||||
## 🚀 快速体验
|
## 🚀 快速体验
|
||||||
|
|
||||||
### 在线演示
|
### 在线演示
|
||||||
|
|
||||||
- **用户端体验**:[web.pandarobot.chat](https://web.pandarobot.chat) (账号:admin 密码:admin123)
|
| 平台 | 地址 | 账号 |
|
||||||
- **管理后台**:[admin.pandarobot.chat](https://admin.pandarobot.chat) (账号:admin 密码:admin123)
|
|:------:|---|---|
|
||||||
|
| 用户端 | [web.pandarobot.chat](https://web.pandarobot.chat) | admin / admin123 |
|
||||||
|
| 管理后台 | [admin.pandarobot.chat](https://admin.pandarobot.chat) | admin / admin123 |
|
||||||
|
|
||||||
### 项目源码
|
### 项目源码
|
||||||
|
|
||||||
@@ -71,18 +57,19 @@ https://docs.qq.com/sheet/DR3hoR3FVVkpJcnVm
|
|||||||
| 🎨 用户前端 | [ruoyi-web](https://github.com/ageerle/ruoyi-web) | [ruoyi-web](https://gitee.com/ageerle/ruoyi-web) | [ruoyi-web](https://gitcode.com/ageerle/ruoyi-web) |
|
| 🎨 用户前端 | [ruoyi-web](https://github.com/ageerle/ruoyi-web) | [ruoyi-web](https://gitee.com/ageerle/ruoyi-web) | [ruoyi-web](https://gitcode.com/ageerle/ruoyi-web) |
|
||||||
| 🛠️ 管理后台 | [ruoyi-admin](https://github.com/ageerle/ruoyi-admin) | [ruoyi-admin](https://gitee.com/ageerle/ruoyi-admin) | [ruoyi-admin](https://gitcode.com/ageerle/ruoyi-admin) |
|
| 🛠️ 管理后台 | [ruoyi-admin](https://github.com/ageerle/ruoyi-admin) | [ruoyi-admin](https://gitee.com/ageerle/ruoyi-admin) | [ruoyi-admin](https://gitcode.com/ageerle/ruoyi-admin) |
|
||||||
|
|
||||||
|
### 合作项目
|
||||||
|
| 项目名称 | GitHub 仓库 | Gitee 仓库
|
||||||
|
|----------------|-------------------------------------------------------|------------------------------------------------------|
|
||||||
|
| element-plus-x | [element-plus-x](https://github.com/element-plus-x/Element-Plus-X) | [element-plus-x](https://gitee.com/he-jiayue/element-plus-x) |
|
||||||
|
|
||||||
## 🛠️ 技术架构
|
## 🛠️ 技术架构
|
||||||
|
|
||||||
### 核心框架
|
### 核心框架
|
||||||
- **后端架构**:Spring Boot 3.5 + Langchain4j
|
- **后端架构**:Spring Boot 4.0 + Spring ai 2.0 + Langchain4j
|
||||||
- **数据存储**:MySQL 8.0 + Redis + 向量数据库(Milvus/Weaviate/Qdrant)
|
- **数据存储**:MySQL 8.0 + Redis + 向量数据库(Milvus/Weaviate)
|
||||||
- **前端技术**:Vue 3 + Vben Admin + Element UI
|
- **前端技术**:Vue 3 + Vben Admin + element-plus-x
|
||||||
- **安全认证**:Sa-Token + JWT 双重保障
|
- **安全认证**:Sa-Token + JWT 双重保障
|
||||||
|
|
||||||
### 系统组件
|
|
||||||
- **文档处理**:PDF、Word、Excel 解析,图像智能分析
|
|
||||||
- **实时通信**:WebSocket 实时通信,SSE 流式响应
|
|
||||||
- **系统监控**:完善的日志体系、性能监控、服务健康检查
|
|
||||||
|
|
||||||
## 📚 使用文档
|
## 📚 使用文档
|
||||||
|
|
||||||
@@ -150,6 +137,29 @@ https://docs.qq.com/sheet/DR3hoR3FVVkpJcnVm
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
||||||
|
## 📺 视频教程
|
||||||
|
|
||||||
|
<div align="center">
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<td align="center">
|
||||||
|
<img src="docs/image/dy.png" alt="微信二维码" width="200" height="200"><br>
|
||||||
|
<strong>打开抖音扫一扫</strong><br>
|
||||||
|
<em>获取免费视频教程</em>
|
||||||
|
</td>
|
||||||
|
<td align="center">
|
||||||
|
<img src="docs/image/bibi.png" alt="QQ群二维码" width="200" height="200"><br>
|
||||||
|
<strong>打开B站扫一扫</strong><br>
|
||||||
|
<em>获取免费视频教程</em>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
<div align="center">
|
<div align="center">
|
||||||
|
|
||||||
**[⭐ 点个Star支持一下](https://github.com/ageerle/ruoyi-ai)** • **[ Fork 开始贡献](https://github.com/ageerle/ruoyi-ai/fork)** • **[📚 English](README_EN.md)** • **[📖 查看完整文档](https://doc.pandarobot.chat)**
|
**[⭐ 点个Star支持一下](https://github.com/ageerle/ruoyi-ai)** • **[ Fork 开始贡献](https://github.com/ageerle/ruoyi-ai/fork)** • **[📚 English](README_EN.md)** • **[📖 查看完整文档](https://doc.pandarobot.chat)**
|
||||||
|
|||||||
84
README_EN.md
84
README_EN.md
@@ -1,3 +1,4 @@
|
|||||||
|
|
||||||
# RuoYi AI
|
# RuoYi AI
|
||||||
|
|
||||||
<div align="center">
|
<div align="center">
|
||||||
@@ -19,64 +20,57 @@
|
|||||||
|
|
||||||
### Enterprise-Grade AI Assistant Platform
|
### Enterprise-Grade AI Assistant Platform
|
||||||
|
|
||||||
*An out-of-the-box intelligent AI platform that integrates mainstream AI platforms such as Coze and DIFY, providing advanced RAG technology, knowledge graphs, digital humans, and AI workflow orchestration capabilities*
|
*An out-of-the-box full-stack AI platform supporting multi-agent collaboration, Supervisor mode orchestration, and multiple decision models, with advanced RAG technology and visual workflow orchestration capabilities*
|
||||||
|
|
||||||
**[中文](README.md)** | **[📖 Documentation](https://doc.pandarobot.chat)** |
|
**[中文](README.md)** | **[📖 Documentation](https://doc.pandarobot.chat)** |
|
||||||
**[🚀 Live Demo](https://web.pandarobot.chat)** | **[🐛 Report Issues](https://github.com/ageerle/ruoyi-ai/issues)** | **[💡 Feature Requests](https://github.com/ageerle/ruoyi-ai/issues)**
|
**[🚀 Live Demo](https://web.pandarobot.chat)** | **[🐛 Report Issues](https://github.com/ageerle/ruoyi-ai/issues)** | **[💡 Feature Requests](https://github.com/ageerle/ruoyi-ai/issues)**
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## ✨ Core Features
|
## ✨ Core Features
|
||||||
|
|
||||||
### Intelligent AI Engine
|
| Module | Current Capabilities | Extension Direction |
|
||||||
- **Multi-Model Integration**: Supports mainstream LLM providers including OpenAI, DeepSeek, Alibaba's Tongyi Qianwen, and Zhipu AI
|
|:---:|---|---|
|
||||||
- **Multi-Modal Understanding**: Intelligently processes multiple formats including text, images, and documents
|
| **Model Management** | Multi-model integration (OpenAI/DeepSeek/Tongyi/Zhipu), multi-modal understanding, Coze/DIFY/FastGPT platform integration | Auto mode, fault tolerance |
|
||||||
- **AI Platform Integration**: Integrates mainstream AI application platforms like **Coze**, **DIFY**, and **FastGPT**
|
| **Knowledge Base** | Local RAG + Vector DB (Milvus/Weaviate) + Knowledge Graph + Document parsing + Reranking | Audio/video parsing, knowledge source |
|
||||||
- **MCP Capability Integration**: Build an extensible AI toolkit ecosystem based on the Model Context Protocol
|
| **Tool Management** | MCP protocol integration, Skills capability + Extensible tool ecosystem | Tool plugin marketplace, toolAgent auto-loading |
|
||||||
- **AI Coding Assistant**: Built-in intelligent code analysis and project scaffolding generation capabilities
|
| **Workflow Orchestration** | Visual workflow designer, drag-and-drop node orchestration, SSE streaming execution, currently supports model (with RAG) calls, email sending, manual review nodes | More node types |
|
||||||
|
| **Multi-Agent** | Agent framework based on Langchain4j, Supervisor mode orchestration, supports multiple decision models | Configurable agents |
|
||||||
### Local RAG Solution
|
| **AI Coding** | Intelligent code analysis, project scaffolding generation, Copilot assistant | Code generation optimization |
|
||||||
- **Private Knowledge Base**: Implements local private knowledge base based on Langchain4j framework + BGE-large-zh-v1.5 Chinese vector model
|
|
||||||
- **Multiple Vector Databases**: Supports mainstream vector databases including Milvus, Weaviate, and Qdrant
|
|
||||||
- **Data Security & Privacy**: Supports fully local deployment to protect enterprise data privacy
|
|
||||||
- **Flexible Model Deployment**: Compatible with local inference frameworks like Ollama and vLLM
|
|
||||||
|
|
||||||
### AI Creative Tools
|
|
||||||
- **AI Image Generation**: Integrates MidJourney and GPT-4o-image
|
|
||||||
- **Intelligent PPT Generation**: Convert text content to beautiful presentations with one click
|
|
||||||
|
|
||||||
### Knowledge Graph & Intelligent Orchestration
|
|
||||||
- **Knowledge Graph Construction**: Automatically extract entity relationships from documents and conversations, build visualized knowledge networks
|
|
||||||
- **AI Workflow Orchestration**: Visual workflow designer supporting complex AI task orchestration and automated execution
|
|
||||||
- **Digital Human Interaction**: Integrated digital avatars providing more natural human-machine interaction experience
|
|
||||||
|
|
||||||
## 🚀 Quick Start
|
## 🚀 Quick Start
|
||||||
|
|
||||||
### Live Demo
|
### Live Demo
|
||||||
|
|
||||||
- **User Experience**: [web.pandarobot.chat](https://web.pandarobot.chat) (Username: admin, Password: admin123)
|
| Platform | URL | Account |
|
||||||
- **Admin Dashboard**: [admin.pandarobot.chat](https://admin.pandarobot.chat) (Username: admin, Password: admin123)
|
|:------:|---|---|
|
||||||
|
| User Frontend | [web.pandarobot.chat](https://web.pandarobot.chat) | admin / admin123 |
|
||||||
|
| Admin Panel | [admin.pandarobot.chat](https://admin.pandarobot.chat) | admin / admin123 |
|
||||||
|
|
||||||
### Project Repositories
|
### Project Repositories
|
||||||
|
|
||||||
| Module | GitHub Repository | Gitee Repository | GitCode Repository |
|
| Module | GitHub Repository | Gitee Repository | GitCode Repository |
|
||||||
|------------------|-------------------------------------------------------|------------------------------------------------------|--------------------------------------------------------|
|
|----------|-------------------------------------------------------|------------------------------------------------------|--------------------------------------------------------|
|
||||||
| 🔧 Backend | [ruoyi-ai](https://github.com/ageerle/ruoyi-ai) | [ruoyi-ai](https://gitee.com/ageerle/ruoyi-ai) | [ruoyi-ai](https://gitcode.com/ageerle/ruoyi-ai) |
|
| 🔧 Backend | [ruoyi-ai](https://github.com/ageerle/ruoyi-ai) | [ruoyi-ai](https://gitee.com/ageerle/ruoyi-ai) | [ruoyi-ai](https://gitcode.com/ageerle/ruoyi-ai) |
|
||||||
| 🎨 User Frontend | [ruoyi-web](https://github.com/ageerle/ruoyi-web) | [ruoyi-web](https://gitee.com/ageerle/ruoyi-web) | [ruoyi-web](https://gitcode.com/ageerle/ruoyi-web) |
|
| 🎨 User Frontend | [ruoyi-web](https://github.com/ageerle/ruoyi-web) | [ruoyi-web](https://gitee.com/ageerle/ruoyi-web) | [ruoyi-web](https://gitcode.com/ageerle/ruoyi-web) |
|
||||||
| 🛠️ Admin Panel | [ruoyi-admin](https://github.com/ageerle/ruoyi-admin) | [ruoyi-admin](https://gitee.com/ageerle/ruoyi-admin) | [ruoyi-admin](https://gitcode.com/ageerle/ruoyi-admin) |
|
| 🛠️ Admin Panel | [ruoyi-admin](https://github.com/ageerle/ruoyi-admin) | [ruoyi-admin](https://gitee.com/ageerle/ruoyi-admin) | [ruoyi-admin](https://gitcode.com/ageerle/ruoyi-admin) |
|
||||||
|
|
||||||
|
### Partner Projects
|
||||||
|
| Project Name | GitHub Repository | Gitee Repository
|
||||||
|
|----------------|-------------------------------------------------------|------------------------------------------------------|
|
||||||
|
| element-plus-x | [element-plus-x](https://github.com/element-plus-x/Element-Plus-X) | [element-plus-x](https://gitee.com/he-jiayue/element-plus-x) |
|
||||||
|
|
||||||
## 🛠️ Technical Architecture
|
## 🛠️ Technical Architecture
|
||||||
|
|
||||||
### Core Framework
|
### Core Framework
|
||||||
- **Backend**: Spring Boot 3.5 + Langchain4j
|
- **Backend**: Spring Boot 4.0 + Spring AI 2.0 + Langchain4j
|
||||||
- **Data Storage**: MySQL 8.0 + Redis + Vector Databases (Milvus/Weaviate/Qdrant)
|
- **Data Storage**: MySQL 8.0 + Redis + Vector Databases (Milvus/Weaviate)
|
||||||
- **Frontend**: Vue 3 + Vben Admin + Element UI
|
- **Frontend**: Vue 3 + Vben Admin + element-plus-x
|
||||||
- **Security**: Sa-Token + JWT dual-layer security
|
- **Security**: Sa-Token + JWT dual-layer security
|
||||||
|
|
||||||
### System Components
|
|
||||||
- **Document Processing**: PDF, Word, and Excel parsing with intelligent image analysis
|
|
||||||
- **Real-Time Communication**: WebSocket real-time communication with SSE streaming responses
|
|
||||||
- **System Monitoring**: Comprehensive logging system, performance monitoring, and service health checks
|
|
||||||
|
|
||||||
## 📚 Documentation
|
## 📚 Documentation
|
||||||
|
|
||||||
@@ -122,21 +116,7 @@ Thanks to the following excellent open-source projects for their support:
|
|||||||
|
|
||||||
<div align="center">
|
<div align="center">
|
||||||
|
|
||||||
<table>
|
**[📱 Join Telegram Group](https://t.me/+LqooQAc5HxRmYmE1)**
|
||||||
<tr>
|
|
||||||
<td align="center">
|
|
||||||
<img src="docs/image/wx.png" alt="WeChat QR Code" width="200" height="200"><br>
|
|
||||||
<strong>Scan to Add Author's WeChat</strong><br>
|
|
||||||
<em>Invitation to join the group</em>
|
|
||||||
</td>
|
|
||||||
<td align="center">
|
|
||||||
<img src="docs/image/qq.png" alt="QQ Group QR Code" width="200" height="200"><br>
|
|
||||||
<strong>QQ Technical Discussion Group</strong><br>
|
|
||||||
<em>Technical discussions</em>
|
|
||||||
</td>
|
|
||||||
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -170,4 +150,4 @@ Thanks to the following excellent open-source projects for their support:
|
|||||||
|
|
||||||
[license-shield]: https://img.shields.io/github/license/ageerle/ruoyi-ai.svg?style=flat-square
|
[license-shield]: https://img.shields.io/github/license/ageerle/ruoyi-ai.svg?style=flat-square
|
||||||
|
|
||||||
[license-url]: https://github.com/ageerle/ruoyi-ai/blob/main/LICENSE
|
[license-url]: https://github.com/ageerle/ruoyi-ai/blob/main/LICENSE
|
||||||
|
|||||||
BIN
docs/image/bibi.png
Normal file
BIN
docs/image/bibi.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 70 KiB |
BIN
docs/image/dy.png
Normal file
BIN
docs/image/dy.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 156 KiB |
File diff suppressed because it is too large
Load Diff
14
pom.xml
14
pom.xml
@@ -50,16 +50,17 @@
|
|||||||
<!-- 工作流配置 -->
|
<!-- 工作流配置 -->
|
||||||
<warm-flow.version>1.8.2</warm-flow.version>
|
<warm-flow.version>1.8.2</warm-flow.version>
|
||||||
<!-- 企业微信SDK -->
|
<!-- 企业微信SDK -->
|
||||||
<weixin-java-cp.version>4.4.0</weixin-java-cp.version>
|
<weixin-java-cp.version>4.6.0</weixin-java-cp.version>
|
||||||
<!-- Jackson XML -->
|
<!-- Jackson XML -->
|
||||||
<jackson-dataformat-xml.version>2.20.1</jackson-dataformat-xml.version>
|
<jackson-dataformat-xml.version>2.18.2</jackson-dataformat-xml.version>
|
||||||
<!-- AI 相关依赖 -->
|
<!-- AI 相关依赖 -->
|
||||||
<langchain4j.version>1.11.0</langchain4j.version>
|
<langchain4j.version>1.11.0</langchain4j.version>
|
||||||
<langchain4j.community.version>1.11.0-beta19</langchain4j.community.version>
|
<langchain4j.community.version>1.11.0-beta19</langchain4j.community.version>
|
||||||
<langchain4j.community.zhipu.ai.version>1.1.0-beta7</langchain4j.community.zhipu.ai.version>
|
|
||||||
<langgraph4j.version>1.5.3</langgraph4j.version>
|
<langgraph4j.version>1.5.3</langgraph4j.version>
|
||||||
<weaviate.version>1.19.6</weaviate.version>
|
<weaviate.version>1.19.6</weaviate.version>
|
||||||
<dify.version>1.0.7</dify.version>
|
<dify.version>1.0.7</dify.version>
|
||||||
|
<!-- Apache Commons Compress - 用于POI处理ZIP格式 -->
|
||||||
|
<commons-compress.version>1.27.1</commons-compress.version>
|
||||||
|
|
||||||
|
|
||||||
<avatar-generator.version>1.1.0</avatar-generator.version>
|
<avatar-generator.version>1.1.0</avatar-generator.version>
|
||||||
@@ -410,6 +411,13 @@
|
|||||||
<version>${jackson-dataformat-xml.version}</version>
|
<version>${jackson-dataformat-xml.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Apache Commons Compress - 用于POI处理ZIP格式,解决导出Excel时的NoSuchMethodError -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.commons</groupId>
|
||||||
|
<artifactId>commons-compress</artifactId>
|
||||||
|
<version>${commons-compress.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</dependencyManagement>
|
</dependencyManagement>
|
||||||
|
|
||||||
|
|||||||
@@ -110,6 +110,7 @@
|
|||||||
<artifactId>ruoyi-aiflow</artifactId>
|
<artifactId>ruoyi-aiflow</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>de.codecentric</groupId>
|
<groupId>de.codecentric</groupId>
|
||||||
<artifactId>spring-boot-admin-starter-client</artifactId>
|
<artifactId>spring-boot-admin-starter-client</artifactId>
|
||||||
|
|||||||
@@ -0,0 +1,48 @@
|
|||||||
|
package org.ruoyi.config;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.BeansException;
|
||||||
|
import org.springframework.beans.factory.config.BeanDefinition;
|
||||||
|
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||||
|
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||||
|
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* BeanDefinitionRegistry后置处理器
|
||||||
|
* 解决 MapStruct Plus 生成的 mapper 冲突问题
|
||||||
|
*
|
||||||
|
* @author ruoyi team
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
public class MapperConflictResolver implements BeanDefinitionRegistryPostProcessor {
|
||||||
|
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(MapperConflictResolver.class);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
|
||||||
|
String[] beanNames = registry.getBeanDefinitionNames();
|
||||||
|
|
||||||
|
// 查找冲突的 mapper bean
|
||||||
|
for (String beanName : beanNames) {
|
||||||
|
if (beanName.equals("chatMessageBoToChatMessageMapperImpl")) {
|
||||||
|
BeanDefinition beanDefinition = registry.getBeanDefinition(beanName);
|
||||||
|
String beanClassName = beanDefinition.getBeanClassName();
|
||||||
|
|
||||||
|
log.info("Found mapper bean: {} -> {}", beanName, beanClassName);
|
||||||
|
|
||||||
|
// 如果是 org.ruoyi.domain.bo.chat 包下的(冲突的),移除它
|
||||||
|
if (beanClassName != null && beanClassName.startsWith("org.ruoyi.domain.bo.chat")) {
|
||||||
|
log.warn("Removing conflicting bean definition: {} ({})", beanName, beanClassName);
|
||||||
|
registry.removeBeanDefinition(beanName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
|
||||||
|
// 不需要实现
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -125,7 +125,9 @@ security:
|
|||||||
- /*/api-docs/**
|
- /*/api-docs/**
|
||||||
- /warm-flow-ui/config
|
- /warm-flow-ui/config
|
||||||
- /workflow/run
|
- /workflow/run
|
||||||
|
- /user/qrcode
|
||||||
|
- /user/login/qrcode
|
||||||
|
- /weixin/check
|
||||||
# 多租户配置
|
# 多租户配置
|
||||||
tenant:
|
tenant:
|
||||||
# 是否开启
|
# 是否开启
|
||||||
@@ -216,6 +218,8 @@ springdoc:
|
|||||||
packages-to-scan: org.ruoyi.generator
|
packages-to-scan: org.ruoyi.generator
|
||||||
- group: 5.工作流模块
|
- group: 5.工作流模块
|
||||||
packages-to-scan: org.ruoyi.workflow
|
packages-to-scan: org.ruoyi.workflow
|
||||||
|
- group: 6.MCP模块
|
||||||
|
packages-to-scan: org.ruoyi.mcp
|
||||||
|
|
||||||
# 防止XSS攻击
|
# 防止XSS攻击
|
||||||
xss:
|
xss:
|
||||||
@@ -307,6 +311,7 @@ wechat:
|
|||||||
secret: ''
|
secret: ''
|
||||||
token: ''
|
token: ''
|
||||||
aesKey: ''
|
aesKey: ''
|
||||||
|
|
||||||
--- # Neo4j 知识图谱配置
|
--- # Neo4j 知识图谱配置
|
||||||
neo4j:
|
neo4j:
|
||||||
uri: bolt://117.72.192.162:7687
|
uri: bolt://117.72.192.162:7687
|
||||||
@@ -357,3 +362,14 @@ knowledge:
|
|||||||
cache-enabled: true
|
cache-enabled: true
|
||||||
# 缓存过期时间(分钟)
|
# 缓存过期时间(分钟)
|
||||||
cache-expire-minutes: 60
|
cache-expire-minutes: 60
|
||||||
|
|
||||||
|
--- # MCP 模块配置
|
||||||
|
app:
|
||||||
|
mcp:
|
||||||
|
client:
|
||||||
|
# 请求超时时间(秒)
|
||||||
|
request-timeout: 30
|
||||||
|
# 连接超时时间(秒)
|
||||||
|
connection-timeout: 10
|
||||||
|
# 最大重试次数
|
||||||
|
max-retries: 3
|
||||||
|
|||||||
@@ -15,4 +15,13 @@ public interface ConfigService {
|
|||||||
*/
|
*/
|
||||||
String getConfigValue(String configKey);
|
String getConfigValue(String configKey);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据配置类型和配置key获取值
|
||||||
|
*
|
||||||
|
* @param category 配置类型
|
||||||
|
* @param configKey 配置key
|
||||||
|
* @return 配置属性
|
||||||
|
*/
|
||||||
|
String getConfigValue(String category, String configKey);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,12 @@
|
|||||||
<groupId>cn.idev.excel</groupId>
|
<groupId>cn.idev.excel</groupId>
|
||||||
<artifactId>fastexcel</artifactId>
|
<artifactId>fastexcel</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Apache Commons Compress - 用于POI处理ZIP格式,解决Excel导出时的依赖冲突 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.commons</groupId>
|
||||||
|
<artifactId>commons-compress</artifactId>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
</project>
|
</project>
|
||||||
|
|||||||
@@ -6,9 +6,6 @@ import lombok.extern.slf4j.Slf4j;
|
|||||||
import org.ruoyi.common.core.utils.SpringUtils;
|
import org.ruoyi.common.core.utils.SpringUtils;
|
||||||
import org.ruoyi.common.sse.core.SseEmitterManager;
|
import org.ruoyi.common.sse.core.SseEmitterManager;
|
||||||
import org.ruoyi.common.sse.dto.SseMessageDto;
|
import org.ruoyi.common.sse.dto.SseMessageDto;
|
||||||
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SSE工具类
|
* SSE工具类
|
||||||
|
|||||||
@@ -121,16 +121,23 @@ public class GlobalExceptionHandler {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 拦截未知的运行时异常
|
* 拦截未知的运行时异常
|
||||||
|
* 注意:对于文件下载/导出等场景,IOException 可能是正常流程的一部分,
|
||||||
|
* 需要排除 export/download 等路径,避免干扰文件导出
|
||||||
*/
|
*/
|
||||||
@ResponseStatus(org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR)
|
|
||||||
@ExceptionHandler(IOException.class)
|
@ExceptionHandler(IOException.class)
|
||||||
public void handleIoException(IOException e, HttpServletRequest request) {
|
public R<Void> handleIoException(IOException e, HttpServletRequest request) {
|
||||||
String requestURI = request.getRequestURI();
|
String requestURI = request.getRequestURI();
|
||||||
if (requestURI.contains("sse")) {
|
if (requestURI.contains("sse")) {
|
||||||
// sse 经常性连接中断 例如关闭浏览器 直接屏蔽
|
// sse 经常性连接中断 例如关闭浏览器 直接屏蔽
|
||||||
return;
|
return null;
|
||||||
|
}
|
||||||
|
// 排除文件下载/导出相关的 IOException,让异常正常传播以便上层处理
|
||||||
|
if (requestURI.contains("/export") || requestURI.contains("/download")) {
|
||||||
|
// 重新抛出,让调用方处理
|
||||||
|
throw new RuntimeException("文件导出/下载IO异常: " + e.getMessage(), e);
|
||||||
}
|
}
|
||||||
log.error("请求地址'{}',连接中断", requestURI, e);
|
log.error("请求地址'{}',连接中断", requestURI, e);
|
||||||
|
return R.fail(e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -146,6 +153,13 @@ public class GlobalExceptionHandler {
|
|||||||
@ExceptionHandler(RuntimeException.class)
|
@ExceptionHandler(RuntimeException.class)
|
||||||
public R<Void> handleRuntimeException(RuntimeException e, HttpServletRequest request) {
|
public R<Void> handleRuntimeException(RuntimeException e, HttpServletRequest request) {
|
||||||
String requestURI = request.getRequestURI();
|
String requestURI = request.getRequestURI();
|
||||||
|
// 对于文件导出相关异常,不进行封装处理,让原始异常信息传播
|
||||||
|
Throwable cause = e.getCause();
|
||||||
|
if (requestURI.contains("/export") || requestURI.contains("/download")) {
|
||||||
|
log.error("请求地址'{}',文件导出/下载异常.", requestURI, e);
|
||||||
|
// 对于文件导出,直接返回异常信息,不进行额外封装
|
||||||
|
return R.fail(cause != null ? cause.getMessage() : e.getMessage());
|
||||||
|
}
|
||||||
log.error("请求地址'{}',发生未知异常.", requestURI, e);
|
log.error("请求地址'{}',发生未知异常.", requestURI, e);
|
||||||
return R.fail(e.getMessage());
|
return R.fail(e.getMessage());
|
||||||
}
|
}
|
||||||
|
|||||||
244
ruoyi-modules/ruoyi-chat/docs/frontend-guide.md
Normal file
244
ruoyi-modules/ruoyi-chat/docs/frontend-guide.md
Normal file
@@ -0,0 +1,244 @@
|
|||||||
|
# MCP工具管理前端开发指南
|
||||||
|
|
||||||
|
## 前置条件
|
||||||
|
|
||||||
|
- Node.js >= 16.0
|
||||||
|
- Vue 3
|
||||||
|
- Element Plus
|
||||||
|
- ECharts (用于图表展示)
|
||||||
|
- Axios (用于HTTP请求)
|
||||||
|
|
||||||
|
## 安装依赖
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install element-plus echarts axios
|
||||||
|
```
|
||||||
|
|
||||||
|
## 项目结构
|
||||||
|
|
||||||
|
```
|
||||||
|
ruoyi-ui/
|
||||||
|
├── src/
|
||||||
|
│ ├── api/
|
||||||
|
│ │ └── mcp/
|
||||||
|
│ │ └── tool.js # API接口
|
||||||
|
│ ├── views/
|
||||||
|
│ │ └── mcp/
|
||||||
|
│ │ ├── tool/
|
||||||
|
│ │ │ └── index.vue # 工具管理页面
|
||||||
|
│ │ ├── market/
|
||||||
|
│ │ │ └── index.vue # 市场管理页面
|
||||||
|
│ │ └── log/
|
||||||
|
│ │ └── index.vue # 调用日志页面
|
||||||
|
│ └── utils/
|
||||||
|
│ └── request.js # Axios封装
|
||||||
|
```
|
||||||
|
|
||||||
|
## 菜单配置
|
||||||
|
|
||||||
|
在系统菜单管理中添加以下菜单:
|
||||||
|
|
||||||
|
### 1. MCP工具管理
|
||||||
|
|
||||||
|
| 字段 | 值 |
|
||||||
|
|------|-----|
|
||||||
|
| 菜单名称 | MCP工具管理 |
|
||||||
|
| 菜单类型 | 目录 |
|
||||||
|
| 显示顺序 | 1 |
|
||||||
|
| 路由地址 | mcp |
|
||||||
|
| 组件路径 | |
|
||||||
|
|
||||||
|
#### 子菜单:工具列表
|
||||||
|
|
||||||
|
| 字段 | 值 |
|
||||||
|
|------|-----|
|
||||||
|
| 菜单名称 | 工具列表 |
|
||||||
|
| 菜单类型 | 菜单 |
|
||||||
|
| 显示顺序 | 1 |
|
||||||
|
| 路由地址 | tool |
|
||||||
|
| 组件路径 | mcp/tool/index |
|
||||||
|
| 权限标识 | mcp:tool:list |
|
||||||
|
|
||||||
|
#### 子菜单:市场管理
|
||||||
|
|
||||||
|
| 字段 | 值 |
|
||||||
|
|------|-----|
|
||||||
|
| 菜单名称 | 市场管理 |
|
||||||
|
| 菜单类型 | 菜单 |
|
||||||
|
| 显示顺序 | 2 |
|
||||||
|
| 路由地址 | market |
|
||||||
|
| 组件路径 | mcp/market/index |
|
||||||
|
| 权限标识 | mcp:market:list |
|
||||||
|
|
||||||
|
#### 子菜单:调用日志
|
||||||
|
|
||||||
|
| 字段 | 值 |
|
||||||
|
|------|-----|
|
||||||
|
| 菜单名称 | 调用日志 |
|
||||||
|
| 菜单类型 | 菜单 |
|
||||||
|
| 显示顺序 | 3 |
|
||||||
|
| 路由地址 | log |
|
||||||
|
| 组件路径 | mcp/log/index |
|
||||||
|
| 权限标识 | mcp:tool:query |
|
||||||
|
|
||||||
|
## 权限配置
|
||||||
|
|
||||||
|
| 权限标识 | 权限名称 | 说明 |
|
||||||
|
|----------|----------|------|
|
||||||
|
| mcp:tool:list | 工具列表 | 查看工具列表 |
|
||||||
|
| mcp:tool:query | 工具查询 | 查看工具详情 |
|
||||||
|
| mcp:tool:add | 工具新增 | 新增工具 |
|
||||||
|
| mcp:tool:edit | 工具修改 | 修改工具 |
|
||||||
|
| mcp:tool:remove | 工具删除 | 删除工具 |
|
||||||
|
| mcp:tool:export | 工具导出 | 导出工具数据 |
|
||||||
|
| mcp:market:list | 市场列表 | 查看市场列表 |
|
||||||
|
| mcp:market:query | 市场查询 | 查看市场详情 |
|
||||||
|
| mcp:market:add | 市场新增 | 新增市场 |
|
||||||
|
| mcp:market:edit | 市场修改 | 修改市场 |
|
||||||
|
| mcp:market:remove | 市场删除 | 删除市场 |
|
||||||
|
|
||||||
|
## 路由配置
|
||||||
|
|
||||||
|
在路由配置文件中添加:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
path: '/mcp',
|
||||||
|
component: Layout,
|
||||||
|
redirect: '/mcp/tool',
|
||||||
|
name: 'Mcp',
|
||||||
|
meta: { title: 'MCP工具管理', icon: 'tools' },
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: 'tool',
|
||||||
|
name: 'McpTool',
|
||||||
|
component: () => import('@/views/mcp/tool/index'),
|
||||||
|
meta: { title: '工具列表', icon: 'tool' }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'market',
|
||||||
|
name: 'McpMarket',
|
||||||
|
component: () => import('@/views/mcp/market/index'),
|
||||||
|
meta: { title: '市场管理', icon: 'shop' }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'log',
|
||||||
|
name: 'McpLog',
|
||||||
|
component: () => import('@/views/mcp/log/index'),
|
||||||
|
meta: { title: '调用日志', icon: 'document' }
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## API请求配置
|
||||||
|
|
||||||
|
确保Axios请求拦截器正确配置:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// src/utils/request.js
|
||||||
|
import axios from 'axios'
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
|
||||||
|
const service = axios.create({
|
||||||
|
baseURL: process.env.VUE_APP_BASE_API,
|
||||||
|
timeout: 30000
|
||||||
|
})
|
||||||
|
|
||||||
|
// 请求拦截器
|
||||||
|
service.interceptors.request.use(
|
||||||
|
config => {
|
||||||
|
// 添加token
|
||||||
|
const token = localStorage.getItem('Admin-Token')
|
||||||
|
if (token) {
|
||||||
|
config.headers['Authorization'] = 'Bearer ' + token
|
||||||
|
}
|
||||||
|
return config
|
||||||
|
},
|
||||||
|
error => {
|
||||||
|
return Promise.reject(error)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// 响应拦截器
|
||||||
|
service.interceptors.response.use(
|
||||||
|
response => {
|
||||||
|
const res = response.data
|
||||||
|
if (res.code !== 200) {
|
||||||
|
ElMessage.error(res.msg || '请求失败')
|
||||||
|
return Promise.reject(new Error(res.msg || '请求失败'))
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
},
|
||||||
|
error => {
|
||||||
|
ElMessage.error(error.message)
|
||||||
|
return Promise.reject(error)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
export default service
|
||||||
|
```
|
||||||
|
|
||||||
|
## 开发步骤
|
||||||
|
|
||||||
|
1. **复制代码文件**
|
||||||
|
- 将 `tool.js` 复制到 `src/api/mcp/` 目录
|
||||||
|
- 将 `*.vue` 文件复制到对应的视图目录
|
||||||
|
|
||||||
|
2. **安装依赖**
|
||||||
|
```bash
|
||||||
|
npm install element-plus echarts
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **配置路由**
|
||||||
|
- 在路由配置中添加MCP相关路由
|
||||||
|
|
||||||
|
4. **配置菜单**
|
||||||
|
- 在系统管理中添加菜单
|
||||||
|
|
||||||
|
5. **配置权限**
|
||||||
|
- 在系统管理中添加权限标识
|
||||||
|
|
||||||
|
6. **测试功能**
|
||||||
|
- 启动开发服务器
|
||||||
|
- 测试各项功能
|
||||||
|
|
||||||
|
## 注意事项
|
||||||
|
|
||||||
|
1. **工具类型说明**
|
||||||
|
- BUILTIN: 内置工具(系统自带,不可编辑)
|
||||||
|
- LOCAL: 本地STDIO工具(通过命令行启动)
|
||||||
|
- REMOTE: 远程HTTP工具(通过网络连接)
|
||||||
|
|
||||||
|
2. **配置JSON格式**
|
||||||
|
- LOCAL类型: `{"command": "npx", "args": ["-y", "@example/tool"], "env": {}}`
|
||||||
|
- REMOTE类型: `{"baseUrl": "http://localhost:8080/mcp"}`
|
||||||
|
|
||||||
|
3. **错误处理**
|
||||||
|
- 工具连接测试可能超时,请合理设置超时时间
|
||||||
|
- 删除工具前请确认没有正在运行的Agent使用该工具
|
||||||
|
|
||||||
|
4. **性能优化**
|
||||||
|
- 调用日志数据量大时,建议使用分页加载
|
||||||
|
- 图表数据建议缓存处理,避免频繁请求
|
||||||
|
|
||||||
|
## 常见问题
|
||||||
|
|
||||||
|
### 1. 跨域问题
|
||||||
|
在 `vue.config.js` 中配置代理:
|
||||||
|
```javascript
|
||||||
|
devServer: {
|
||||||
|
proxy: {
|
||||||
|
'/api': {
|
||||||
|
target: 'http://localhost:8080',
|
||||||
|
changeOrigin: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 图表不显示
|
||||||
|
确保ECharts容器有固定高度,并在数据加载后初始化图表。
|
||||||
|
|
||||||
|
### 3. 权限不生效
|
||||||
|
检查菜单权限配置和后端接口权限注解是否一致。
|
||||||
336
ruoyi-modules/ruoyi-chat/docs/mcp-api-spec.md
Normal file
336
ruoyi-modules/ruoyi-chat/docs/mcp-api-spec.md
Normal file
@@ -0,0 +1,336 @@
|
|||||||
|
# MCP工具管理模块 - API接口文档
|
||||||
|
|
||||||
|
## 概述
|
||||||
|
|
||||||
|
本文档描述了MCP工具管理模块的REST API接口,供前端开发人员参考。
|
||||||
|
|
||||||
|
## 基础信息
|
||||||
|
|
||||||
|
- **Base URL**: `/api/mcp`
|
||||||
|
- **认证方式**: Bearer Token (SaToken)
|
||||||
|
- **响应格式**: JSON
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. MCP工具管理
|
||||||
|
|
||||||
|
### 1.1 查询工具列表(分页)
|
||||||
|
|
||||||
|
**接口**: `GET /tool/list`
|
||||||
|
|
||||||
|
**权限**: `mcp:tool:list`
|
||||||
|
|
||||||
|
**请求参数**:
|
||||||
|
| 参数名 | 类型 | 必填 | 说明 |
|
||||||
|
|--------|------|------|------|
|
||||||
|
| name | String | 否 | 工具名称(模糊查询) |
|
||||||
|
| description | String | 否 | 工具描述(模糊查询) |
|
||||||
|
| type | String | 否 | 工具类型:LOCAL/REMOTE/BUILTIN |
|
||||||
|
| status | String | 否 | 状态:0-启用, 1-禁用 |
|
||||||
|
| pageNum | Integer | 是 | 页码,默认1 |
|
||||||
|
| pageSize | Integer | 是 | 每页数量,默认10 |
|
||||||
|
|
||||||
|
**响应示例**:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"rows": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"name": "ReadFileTool",
|
||||||
|
"description": "读取文件内容工具",
|
||||||
|
"type": "BUILTIN",
|
||||||
|
"status": "0",
|
||||||
|
"configJson": null,
|
||||||
|
"createTime": "2026-03-08 10:00:00",
|
||||||
|
"updateTime": "2026-03-08 10:00:00"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"total": 1
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 1.2 查询工具列表(不分页)
|
||||||
|
|
||||||
|
**接口**: `GET /tool/all`
|
||||||
|
|
||||||
|
**权限**: `mcp:tool:list`
|
||||||
|
|
||||||
|
**请求参数**:
|
||||||
|
| 参数名 | 类型 | 必填 | 说明 |
|
||||||
|
|--------|------|------|------|
|
||||||
|
| keyword | String | 否 | 关键词 |
|
||||||
|
| type | String | 否 | 工具类型 |
|
||||||
|
| status | String | 否 | 状态 |
|
||||||
|
|
||||||
|
**响应示例**:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"tools": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"name": "ReadFileTool",
|
||||||
|
"description": "读取文件内容工具",
|
||||||
|
"type": "BUILTIN",
|
||||||
|
"status": "0"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"total": 1
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 1.3 获取工具详情
|
||||||
|
|
||||||
|
**接口**: `GET /tool/{id}`
|
||||||
|
|
||||||
|
**权限**: `mcp:tool:query`
|
||||||
|
|
||||||
|
**路径参数**:
|
||||||
|
| 参数名 | 类型 | 必填 | 说明 |
|
||||||
|
|--------|------|------|------|
|
||||||
|
| id | Long | 是 | 工具ID |
|
||||||
|
|
||||||
|
### 1.4 新增工具
|
||||||
|
|
||||||
|
**接口**: `POST /tool`
|
||||||
|
|
||||||
|
**权限**: `mcp:tool:add`
|
||||||
|
|
||||||
|
**请求体**:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name": "MyMcpTool",
|
||||||
|
"description": "我的MCP工具",
|
||||||
|
"type": "REMOTE",
|
||||||
|
"status": "0",
|
||||||
|
"configJson": "{\"baseUrl\": \"http://localhost:8080/mcp\"}"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 1.5 修改工具
|
||||||
|
|
||||||
|
**接口**: `PUT /tool`
|
||||||
|
|
||||||
|
**权限**: `mcp:tool:edit`
|
||||||
|
|
||||||
|
**请求体**: 同新增工具
|
||||||
|
|
||||||
|
### 1.6 删除工具
|
||||||
|
|
||||||
|
**接口**: `DELETE /tool/{ids}`
|
||||||
|
|
||||||
|
**权限**: `mcp:tool:remove`
|
||||||
|
|
||||||
|
**路径参数**:
|
||||||
|
| 参数名 | 类型 | 必填 | 说明 |
|
||||||
|
|--------|------|------|------|
|
||||||
|
| ids | String | 是 | 工具ID,多个用逗号分隔 |
|
||||||
|
|
||||||
|
### 1.7 更新工具状态
|
||||||
|
|
||||||
|
**接口**: `PUT /tool/{id}/status`
|
||||||
|
|
||||||
|
**权限**: `mcp:tool:edit`
|
||||||
|
|
||||||
|
**路径参数**:
|
||||||
|
| 参数名 | 类型 | 必填 | 说明 |
|
||||||
|
|--------|------|------|------|
|
||||||
|
| id | Long | 是 | 工具ID |
|
||||||
|
|
||||||
|
**请求参数**:
|
||||||
|
| 参数名 | 类型 | 必填 | 说明 |
|
||||||
|
|--------|------|------|------|
|
||||||
|
| status | String | 是 | 状态:0-启用, 1-禁用 |
|
||||||
|
|
||||||
|
### 1.8 测试工具连接
|
||||||
|
|
||||||
|
**接口**: `POST /tool/{id}/test`
|
||||||
|
|
||||||
|
**权限**: `mcp:tool:query`
|
||||||
|
|
||||||
|
**响应示例**:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"message": "连接测试成功",
|
||||||
|
"toolCount": 5,
|
||||||
|
"tools": ["tool1", "tool2", "tool3", "tool4", "tool5"]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. MCP市场管理
|
||||||
|
|
||||||
|
### 2.1 查询市场列表
|
||||||
|
|
||||||
|
**接口**: `GET /market/list`
|
||||||
|
|
||||||
|
**权限**: `mcp:market:list`
|
||||||
|
|
||||||
|
**请求参数**:
|
||||||
|
| 参数名 | 类型 | 必填 | 说明 |
|
||||||
|
|--------|------|------|------|
|
||||||
|
| name | String | 否 | 市场名称 |
|
||||||
|
| description | String | 否 | 市场描述 |
|
||||||
|
| status | String | 否 | 状态 |
|
||||||
|
| pageNum | Integer | 是 | 页码 |
|
||||||
|
| pageSize | Integer | 是 | 每页数量 |
|
||||||
|
|
||||||
|
### 2.2 获取市场工具列表
|
||||||
|
|
||||||
|
**接口**: `GET /market/{marketId}/tools`
|
||||||
|
|
||||||
|
**权限**: `mcp:market:query`
|
||||||
|
|
||||||
|
**路径参数**:
|
||||||
|
| 参数名 | 类型 | 必填 | 说明 |
|
||||||
|
|--------|------|------|------|
|
||||||
|
| marketId | Long | 是 | 市场ID |
|
||||||
|
|
||||||
|
**请求参数**:
|
||||||
|
| 参数名 | 类型 | 必填 | 说明 |
|
||||||
|
|--------|------|------|------|
|
||||||
|
| page | Integer | 否 | 页码,默认1 |
|
||||||
|
| size | Integer | 否 | 每页数量,默认10 |
|
||||||
|
|
||||||
|
### 2.3 刷新市场工具
|
||||||
|
|
||||||
|
**接口**: `POST /market/{marketId}/refresh`
|
||||||
|
|
||||||
|
**权限**: `mcp:market:edit`
|
||||||
|
|
||||||
|
**响应示例**:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"message": "刷新成功",
|
||||||
|
"addedCount": 3,
|
||||||
|
"updatedCount": 5
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.4 加载工具到本地
|
||||||
|
|
||||||
|
**接口**: `POST /market/tool/{toolId}/load`
|
||||||
|
|
||||||
|
**权限**: `mcp:market:edit`
|
||||||
|
|
||||||
|
**路径参数**:
|
||||||
|
| 参数名 | 类型 | 必填 | 说明 |
|
||||||
|
|--------|------|------|------|
|
||||||
|
| toolId | Long | 是 | 市场工具ID |
|
||||||
|
|
||||||
|
### 2.5 批量加载工具
|
||||||
|
|
||||||
|
**接口**: `POST /market/tools/batchLoad`
|
||||||
|
|
||||||
|
**权限**: `mcp:market:edit`
|
||||||
|
|
||||||
|
**请求体**:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"toolIds": [1, 2, 3]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. 工具调用日志
|
||||||
|
|
||||||
|
### 3.1 查询调用日志
|
||||||
|
|
||||||
|
**接口**: `GET /tool/callLog`
|
||||||
|
|
||||||
|
**权限**: `mcp:tool:query`
|
||||||
|
|
||||||
|
**请求参数**:
|
||||||
|
| 参数名 | 类型 | 必填 | 说明 |
|
||||||
|
|--------|------|------|------|
|
||||||
|
| toolId | Long | 否 | 工具ID |
|
||||||
|
| sessionId | Long | 否 | 会话ID |
|
||||||
|
| startDate | Date | 否 | 开始日期 |
|
||||||
|
| endDate | Date | 否 | 结束日期 |
|
||||||
|
| pageNum | Integer | 是 | 页码 |
|
||||||
|
| pageSize | Integer | 是 | 每页数量 |
|
||||||
|
|
||||||
|
### 3.2 获取工具统计
|
||||||
|
|
||||||
|
**接口**: `GET /tool/{toolId}/metrics`
|
||||||
|
|
||||||
|
**权限**: `mcp:tool:query`
|
||||||
|
|
||||||
|
**响应示例**:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"toolId": 1,
|
||||||
|
"toolName": "ReadFileTool",
|
||||||
|
"today": {
|
||||||
|
"callCount": 100,
|
||||||
|
"successCount": 95,
|
||||||
|
"failureCount": 5,
|
||||||
|
"avgDurationMs": 150,
|
||||||
|
"successRate": 95.0
|
||||||
|
},
|
||||||
|
"week": {
|
||||||
|
"callCount": 500,
|
||||||
|
"successCount": 475,
|
||||||
|
"failureCount": 25,
|
||||||
|
"avgDurationMs": 160,
|
||||||
|
"successRate": 95.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. 状态码说明
|
||||||
|
|
||||||
|
| 状态码 | 说明 |
|
||||||
|
|--------|------|
|
||||||
|
| 200 | 请求成功 |
|
||||||
|
| 401 | 未认证 |
|
||||||
|
| 403 | 无权限 |
|
||||||
|
| 404 | 资源不存在 |
|
||||||
|
| 500 | 服务器错误 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. 前端页面需求
|
||||||
|
|
||||||
|
### 5.1 MCP工具管理页面 (`/mcp/tool`)
|
||||||
|
|
||||||
|
**功能**:
|
||||||
|
- 工具列表展示(分页)
|
||||||
|
- 工具搜索和筛选
|
||||||
|
- 新增/编辑/删除工具
|
||||||
|
- 工具状态切换
|
||||||
|
- 工具连接测试
|
||||||
|
|
||||||
|
**表格列**:
|
||||||
|
- 工具名称
|
||||||
|
- 工具描述
|
||||||
|
- 工具类型(标签显示)
|
||||||
|
- 状态(开关)
|
||||||
|
- 创建时间
|
||||||
|
- 操作(编辑、删除、测试)
|
||||||
|
|
||||||
|
### 5.2 MCP市场管理页面 (`/mcp/market`)
|
||||||
|
|
||||||
|
**功能**:
|
||||||
|
- 市场列表展示
|
||||||
|
- 市场工具浏览
|
||||||
|
- 刷新市场工具
|
||||||
|
- 加载工具到本地
|
||||||
|
|
||||||
|
### 5.3 工具调用日志页面 (`/mcp/log`)
|
||||||
|
|
||||||
|
**功能**:
|
||||||
|
- 调用日志列表
|
||||||
|
- 按工具/日期筛选
|
||||||
|
- 成功率统计
|
||||||
|
- 响应时间统计
|
||||||
|
|
||||||
|
**图表**:
|
||||||
|
- 每日调用次数趋势图
|
||||||
|
- 工具调用成功率饼图
|
||||||
|
- 平均响应时间柱状图
|
||||||
@@ -51,15 +51,9 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>dev.langchain4j</groupId>
|
<groupId>dev.langchain4j</groupId>
|
||||||
<artifactId>langchain4j-community-zhipu-ai</artifactId>
|
<artifactId>langchain4j-community-zhipu-ai</artifactId>
|
||||||
<version>${langchain4j.community.zhipu.ai.version}</version>
|
<version>${langchain4j.community.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!-- <dependency>-->
|
|
||||||
<!-- <groupId>dev.langchain4j</groupId>-->
|
|
||||||
<!-- <artifactId>langchain4j-community-zhipu-ai</artifactId>-->
|
|
||||||
<!-- <version>${langchain4j.community.version}</version>-->
|
|
||||||
<!-- </dependency>-->
|
|
||||||
|
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.zaxxer</groupId>
|
<groupId>com.zaxxer</groupId>
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import dev.langchain4j.service.UserMessage;
|
|||||||
import dev.langchain4j.service.V;
|
import dev.langchain4j.service.V;
|
||||||
|
|
||||||
|
|
||||||
public interface ChartGenerationAgent {
|
public interface ChartGenerationAgent extends Agent {
|
||||||
|
|
||||||
@SystemMessage("""
|
@SystemMessage("""
|
||||||
You are a chart generation specialist. Your only task is to generate Apache ECharts
|
You are a chart generation specialist. Your only task is to generate Apache ECharts
|
||||||
|
|||||||
@@ -5,42 +5,38 @@ import dev.langchain4j.service.SystemMessage;
|
|||||||
import dev.langchain4j.service.UserMessage;
|
import dev.langchain4j.service.UserMessage;
|
||||||
import dev.langchain4j.service.V;
|
import dev.langchain4j.service.V;
|
||||||
|
|
||||||
public interface McpAgent {
|
public interface McpAgent extends Agent {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 系统提示词:定义智能体身份、核心职责、强制遵守的规则
|
* 系统提示词:通用工具调用智能体
|
||||||
* 适配SSE流式特性,明确工具全来自远端MCP服务,仅做代理调用和结果整理
|
* 不限定具体工具类型,让 LangChain4j 自动传递工具描述给 LLM
|
||||||
*/
|
*/
|
||||||
@SystemMessage("""
|
@SystemMessage("""
|
||||||
你是专业的MCP服务工具代理智能体,核心能力是通过HTTP SSE流式传输协议,调用本地http://localhost:8085/sse地址上MCP服务端注册的所有工具。
|
你是一个AI助手,可以通过调用各种工具来帮助用户完成不同的任务。
|
||||||
你的核心工作职责:
|
|
||||||
1. 准确理解用户的自然语言请求,判断需要调用MCP服务端的哪一个/哪些工具;
|
|
||||||
2. 通过绑定的工具提供者,向MCP服务端发起工具调用请求,传递完整的工具执行参数;
|
|
||||||
3. 实时接收MCP服务端通过SSE流式返回的工具执行结果,保证结果片段的完整性;
|
|
||||||
4. 将流式结果按原始顺序整理为清晰、易懂的自然语言答案,返回给用户。
|
|
||||||
|
|
||||||
【强制遵守的核心规则 - 无例外】
|
【工具使用规则】
|
||||||
1. 所有工具调用必须通过远端MCP服务执行,严禁尝试本地执行任何业务逻辑;
|
1. 根据用户的请求,判断需要使用哪些工具
|
||||||
2. 处理SSE流式结果时,严格保留结果片段的返回顺序,不得打乱或遗漏;
|
2. 仔细阅读每个工具的描述,确保理解工具的功能和参数要求
|
||||||
3. 若MCP服务返回错误(如工具未找到、参数错误、执行失败),直接将错误信息友好反馈给用户,无需额外推理;
|
3. 使用正确的参数调用工具
|
||||||
4. 工具执行结果若为结构化数据(如JSON、表格),需格式化后返回,提升可读性。
|
4. 如果工具执行失败,向用户友好地说明错误原因,并尝试提供替代方案
|
||||||
|
5. 对于复杂任务,可以分步骤使用多个工具完成
|
||||||
|
6. 将工具执行结果以清晰易懂的方式呈现给用户
|
||||||
|
|
||||||
|
【响应格式】
|
||||||
|
- 直接回答用户的问题
|
||||||
|
- 如果使用了工具,说明使用了什么工具以及结果
|
||||||
|
- 如果遇到错误,提供友好的错误信息和解决建议
|
||||||
""")
|
""")
|
||||||
|
|
||||||
/**
|
|
||||||
* 用户消息模板:{{query}}为参数占位符,与方法入参的@V("query")绑定
|
|
||||||
*/
|
|
||||||
@UserMessage("""
|
@UserMessage("""
|
||||||
请通过调用MCP服务端的工具,处理用户的以下请求:
|
|
||||||
{{query}}
|
{{query}}
|
||||||
""")
|
""")
|
||||||
|
|
||||||
|
@Agent("通用工具调用智能体")
|
||||||
/**
|
/**
|
||||||
* 智能体标识:用于日志打印、监控追踪、多智能体协作时的身份识别
|
* 智能体对外调用入口
|
||||||
*/
|
* @param query 用户的自然语言请求
|
||||||
@Agent("MCP服务SSE流式代理智能体-连接本地8085端口")
|
* @return 处理结果
|
||||||
/**
|
|
||||||
* 智能体对外调用入口方法
|
|
||||||
* @param query 用户的自然语言请求(如:生成订单数据柱状图、查询今日天气)
|
|
||||||
* @V("query") 将方法入参值绑定到@UserMessage的{{query}}占位符中
|
|
||||||
* @return 整理后的MCP工具执行结果(流式结果会自动拼接为完整字符串)
|
|
||||||
*/
|
*/
|
||||||
String callMcpTool(@V("query") String query);
|
String callMcpTool(@V("query") String query);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package org.ruoyi.agent;
|
|||||||
|
|
||||||
import dev.langchain4j.agentic.Agent;
|
import dev.langchain4j.agentic.Agent;
|
||||||
import dev.langchain4j.service.SystemMessage;
|
import dev.langchain4j.service.SystemMessage;
|
||||||
import dev.langchain4j.service.TokenStream;
|
|
||||||
import dev.langchain4j.service.UserMessage;
|
import dev.langchain4j.service.UserMessage;
|
||||||
import dev.langchain4j.service.V;
|
import dev.langchain4j.service.V;
|
||||||
|
|
||||||
@@ -12,7 +11,7 @@ import dev.langchain4j.service.V;
|
|||||||
* and returning relevant data and analysis results.
|
* and returning relevant data and analysis results.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public interface SqlAgent {
|
public interface SqlAgent extends Agent {
|
||||||
|
|
||||||
@SystemMessage("""
|
@SystemMessage("""
|
||||||
This agent is designed for MySQL 5.7
|
This agent is designed for MySQL 5.7
|
||||||
|
|||||||
@@ -1,18 +0,0 @@
|
|||||||
package org.ruoyi.agent;
|
|
||||||
|
|
||||||
import dev.langchain4j.agentic.Agent;
|
|
||||||
import dev.langchain4j.service.TokenStream;
|
|
||||||
import dev.langchain4j.service.UserMessage;
|
|
||||||
import dev.langchain4j.service.V;
|
|
||||||
|
|
||||||
public interface StreamingCreativeWriter {
|
|
||||||
@UserMessage("""
|
|
||||||
You are a creative writer.
|
|
||||||
Generate a draft of a story no more than
|
|
||||||
3 sentences long around the given topic.
|
|
||||||
Return only the story and nothing else.
|
|
||||||
The topic is {{topic}}.
|
|
||||||
""")
|
|
||||||
@Agent("Generates a story based on the given topic")
|
|
||||||
TokenStream generateStory(@V("topic") String topic);
|
|
||||||
}
|
|
||||||
@@ -10,7 +10,7 @@ import dev.langchain4j.service.V;
|
|||||||
* A web search assistant that answers natural language questions by searching the internet
|
* A web search assistant that answers natural language questions by searching the internet
|
||||||
* and returning relevant information from web pages.
|
* and returning relevant information from web pages.
|
||||||
*/
|
*/
|
||||||
public interface WebSearchAgent {
|
public interface WebSearchAgent extends Agent {
|
||||||
|
|
||||||
@SystemMessage("""
|
@SystemMessage("""
|
||||||
You are a web search assistant. Answer questions by searching and retrieving web content.
|
You are a web search assistant. Answer questions by searching and retrieving web content.
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ import lombok.extern.slf4j.Slf4j;
|
|||||||
@Component
|
@Component
|
||||||
@DS("agent")
|
@DS("agent")
|
||||||
public class TableSchemaManager {
|
public class TableSchemaManager {
|
||||||
|
|
||||||
@Autowired(required = false)
|
@Autowired(required = false)
|
||||||
private DataSource agentDataSource;
|
private DataSource agentDataSource;
|
||||||
|
|
||||||
@@ -77,7 +77,7 @@ public class TableSchemaManager {
|
|||||||
loadAllowedTableSchemas();
|
loadAllowedTableSchemas();
|
||||||
initialized = true;
|
initialized = true;
|
||||||
log.info("Schema cache initialized with {} tables", schemaCache.size());
|
log.info("Schema cache initialized with {} tables", schemaCache.size());
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("Failed to initialize schema cache", e);
|
log.error("Failed to initialize schema cache", e);
|
||||||
}
|
}
|
||||||
@@ -87,7 +87,7 @@ public class TableSchemaManager {
|
|||||||
/**
|
/**
|
||||||
* 加载所有允许的表的结构信息
|
* 加载所有允许的表的结构信息
|
||||||
*/
|
*/
|
||||||
private void loadAllowedTableSchemas() throws SQLException {
|
private void loadAllowedTableSchemas() {
|
||||||
List<String> allowedTables = getAllowedTableNames();
|
List<String> allowedTables = getAllowedTableNames();
|
||||||
for (String tableName : allowedTables) {
|
for (String tableName : allowedTables) {
|
||||||
try {
|
try {
|
||||||
@@ -191,10 +191,7 @@ public class TableSchemaManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
List<String> allowedTables = getAllowedTableNames();
|
List<String> allowedTables = getAllowedTableNames();
|
||||||
return allowedTables.stream()
|
return allowedTables.stream().map(schemaCache::get).filter(Objects::nonNull).collect(Collectors.toList());
|
||||||
.map(schemaCache::get)
|
|
||||||
.filter(Objects::nonNull)
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -11,13 +11,14 @@ import java.util.Map;
|
|||||||
|
|
||||||
import javax.sql.DataSource;
|
import javax.sql.DataSource;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.ruoyi.common.core.utils.SpringUtils;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder;
|
import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder;
|
||||||
|
|
||||||
import dev.langchain4j.agent.tool.Tool;
|
import dev.langchain4j.agent.tool.Tool;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.ruoyi.mcp.service.core.BuiltinToolProvider;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 执行 SQL 查询的 Tool
|
* 执行 SQL 查询的 Tool
|
||||||
@@ -25,10 +26,12 @@ import lombok.extern.slf4j.Slf4j;
|
|||||||
*/
|
*/
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Component
|
@Component
|
||||||
public class ExecuteSqlQueryTool {
|
public class ExecuteSqlQueryTool implements BuiltinToolProvider {
|
||||||
|
|
||||||
@Autowired(required = false)
|
// 使用延迟初始化,避免在构造函数中调用 SpringUtils.getBean()
|
||||||
private DataSource dataSource;
|
private DataSource getDataSource() {
|
||||||
|
return SpringUtils.getBean(DataSource.class);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 执行 SELECT SQL 查询
|
* 执行 SELECT SQL 查询
|
||||||
@@ -52,6 +55,7 @@ public class ExecuteSqlQueryTool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
DataSource dataSource = getDataSource();
|
||||||
if (dataSource == null) {
|
if (dataSource == null) {
|
||||||
return "Error: Database datasource not configured";
|
return "Error: Database datasource not configured";
|
||||||
}
|
}
|
||||||
@@ -177,4 +181,19 @@ public class ExecuteSqlQueryTool {
|
|||||||
}
|
}
|
||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getToolName() {
|
||||||
|
return "execute_sql_query";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDisplayName() {
|
||||||
|
return "执行SQL查询";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDescription() {
|
||||||
|
return "Execute a SELECT SQL query and return the results. Example: SELECT * FROM sys_user";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,11 +4,12 @@ import java.util.List;
|
|||||||
|
|
||||||
import org.ruoyi.agent.domain.TableStructure;
|
import org.ruoyi.agent.domain.TableStructure;
|
||||||
import org.ruoyi.agent.manager.TableSchemaManager;
|
import org.ruoyi.agent.manager.TableSchemaManager;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.ruoyi.common.core.utils.SpringUtils;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import dev.langchain4j.agent.tool.Tool;
|
import dev.langchain4j.agent.tool.Tool;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.ruoyi.mcp.service.core.BuiltinToolProvider;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询数据库所有表的 Tool
|
* 查询数据库所有表的 Tool
|
||||||
@@ -16,11 +17,13 @@ import lombok.extern.slf4j.Slf4j;
|
|||||||
*/
|
*/
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Component
|
@Component
|
||||||
public class QueryAllTablesTool {
|
public class QueryAllTablesTool implements BuiltinToolProvider {
|
||||||
|
|
||||||
|
// 使用延迟初始化,避免在构造函数中调用 SpringUtils.getBean()
|
||||||
|
private TableSchemaManager getTableSchemaManager() {
|
||||||
|
return SpringUtils.getBean(TableSchemaManager.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private TableSchemaManager tableSchemaManager; // 注入管理器
|
|
||||||
/**
|
/**
|
||||||
* 查询数据库中所有表
|
* 查询数据库中所有表
|
||||||
* 返回数据库中存在的所有表的列表
|
* 返回数据库中存在的所有表的列表
|
||||||
@@ -30,36 +33,49 @@ public class QueryAllTablesTool {
|
|||||||
@Tool("Query all tables in the database and return table names and basic information")
|
@Tool("Query all tables in the database and return table names and basic information")
|
||||||
public String queryAllTables() {
|
public String queryAllTables() {
|
||||||
try {
|
try {
|
||||||
// 1. 从管理器获取所有允许的表结构信息(内部已包含初始化/缓存逻辑)
|
// 1. 从管理器获取所有允许的表结构信息(内部已包含初始化/缓存逻辑)
|
||||||
List<TableStructure> tableSchemas = tableSchemaManager.getAllowedTableSchemas();
|
List<TableStructure> tableSchemas = getTableSchemaManager().getAllowedTableSchemas();
|
||||||
|
|
||||||
if (tableSchemas == null || tableSchemas.isEmpty()) {
|
if (tableSchemas == null || tableSchemas.isEmpty()) {
|
||||||
return "No tables found in database or cache is empty.";
|
return "No tables found in database or cache is empty.";
|
||||||
}
|
|
||||||
|
|
||||||
// 2. 格式化结果
|
|
||||||
StringBuilder result = new StringBuilder();
|
|
||||||
result.append("Found ").append(tableSchemas.size()).append(" tables in cache:\n");
|
|
||||||
|
|
||||||
for (TableStructure schema : tableSchemas) {
|
|
||||||
String tableName = schema.getTableName();
|
|
||||||
String tableType = schema.getTableType() != null ? schema.getTableType() : "TABLE";
|
|
||||||
String tableComment = schema.getTableComment();
|
|
||||||
|
|
||||||
result.append(String.format("- %s (%s) - %s\n",
|
|
||||||
tableName,
|
|
||||||
tableType,
|
|
||||||
tableComment != null ? tableComment : "No comment"));
|
|
||||||
}
|
|
||||||
|
|
||||||
log.info("Successfully retrieved {} tables from schema cache", tableSchemas.size());
|
|
||||||
return result.toString();
|
|
||||||
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.error("Error retrieving tables from cache", e);
|
|
||||||
return "Error: " + e.getMessage();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 2. 格式化结果
|
||||||
|
StringBuilder result = new StringBuilder();
|
||||||
|
result.append("Found ").append(tableSchemas.size()).append(" tables in cache:\n");
|
||||||
|
|
||||||
|
for (TableStructure schema : tableSchemas) {
|
||||||
|
String tableName = schema.getTableName();
|
||||||
|
String tableType = schema.getTableType() != null ? schema.getTableType() : "TABLE";
|
||||||
|
String tableComment = schema.getTableComment();
|
||||||
|
|
||||||
|
result.append(String.format("- %s (%s) - %s\n",
|
||||||
|
tableName,
|
||||||
|
tableType,
|
||||||
|
tableComment != null ? tableComment : "No comment"));
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info("Successfully retrieved {} tables from schema cache", tableSchemas.size());
|
||||||
|
return result.toString();
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Error retrieving tables from cache", e);
|
||||||
|
return "Error: " + e.getMessage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getToolName() {
|
||||||
|
return "query_all_tables";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDisplayName() {
|
||||||
|
return "查询所有表";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDescription() {
|
||||||
|
return "Query all tables in the database and return table names and basic information";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,20 +6,23 @@ import java.sql.ResultSet;
|
|||||||
|
|
||||||
import javax.sql.DataSource;
|
import javax.sql.DataSource;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.ruoyi.common.core.utils.SpringUtils;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder;
|
import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder;
|
||||||
|
|
||||||
import dev.langchain4j.agent.tool.Tool;
|
import dev.langchain4j.agent.tool.Tool;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.ruoyi.mcp.service.core.BuiltinToolProvider;
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class QueryTableSchemaTool {
|
public class QueryTableSchemaTool implements BuiltinToolProvider {
|
||||||
|
|
||||||
@Autowired(required = false)
|
// 使用延迟初始化,避免在构造函数中调用 SpringUtils.getBean()
|
||||||
private DataSource dataSource;
|
private DataSource getDataSource() {
|
||||||
|
return SpringUtils.getBean(DataSource.class);
|
||||||
|
}
|
||||||
|
|
||||||
@Tool("Query the CREATE TABLE statement (DDL) for a specific table by table name")
|
@Tool("Query the CREATE TABLE statement (DDL) for a specific table by table name")
|
||||||
public String queryTableSchema(String tableName) {
|
public String queryTableSchema(String tableName) {
|
||||||
@@ -35,7 +38,7 @@ public class QueryTableSchemaTool {
|
|||||||
|
|
||||||
String sql = "SHOW CREATE TABLE `" + tableName + "`";
|
String sql = "SHOW CREATE TABLE `" + tableName + "`";
|
||||||
|
|
||||||
try (Connection connection = dataSource.getConnection();
|
try (Connection connection = getDataSource().getConnection();
|
||||||
PreparedStatement ps = connection.prepareStatement(sql);
|
PreparedStatement ps = connection.prepareStatement(sql);
|
||||||
ResultSet rs = ps.executeQuery()) {
|
ResultSet rs = ps.executeQuery()) {
|
||||||
|
|
||||||
@@ -54,4 +57,19 @@ public class QueryTableSchemaTool {
|
|||||||
DynamicDataSourceContextHolder.clear();
|
DynamicDataSourceContextHolder.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getToolName() {
|
||||||
|
return "query_table_schema";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDisplayName() {
|
||||||
|
return "查询表结构";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDescription() {
|
||||||
|
return "Query the CREATE TABLE statement (DDL) for a specific table by table name";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,93 @@
|
|||||||
|
package org.ruoyi.config.mcp;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.ruoyi.domain.entity.mcp.McpTool;
|
||||||
|
import org.ruoyi.enums.McpToolStatus;
|
||||||
|
import org.ruoyi.mapper.mcp.McpToolMapper;
|
||||||
|
import org.ruoyi.mcp.service.core.BuiltinToolDefinition;
|
||||||
|
import org.ruoyi.mcp.service.core.BuiltinToolRegistry;
|
||||||
|
import org.springframework.boot.ApplicationArguments;
|
||||||
|
import org.springframework.boot.ApplicationRunner;
|
||||||
|
import org.springframework.core.annotation.Order;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 系统工具初始化器
|
||||||
|
* 在应用启动时,将系统内置工具同步到数据库
|
||||||
|
* 这样可以统一管理所有工具,支持动态启用/禁用
|
||||||
|
*
|
||||||
|
* @author ruoyi team
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
@Order(999) // 确保在其他初始化器之后执行
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class SystemToolInitializer implements ApplicationRunner {
|
||||||
|
|
||||||
|
private final McpToolMapper mcpToolMapper;
|
||||||
|
private final BuiltinToolRegistry builtinToolRegistry;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional
|
||||||
|
public void run(ApplicationArguments args) {
|
||||||
|
log.info("开始同步系统内置工具到数据库...");
|
||||||
|
|
||||||
|
int addedCount = 0;
|
||||||
|
int existingCount = 0;
|
||||||
|
|
||||||
|
for (BuiltinToolDefinition tool : builtinToolRegistry.getAllBuiltinTools()) {
|
||||||
|
try {
|
||||||
|
boolean added = syncBuiltinTool(tool);
|
||||||
|
if (added) {
|
||||||
|
addedCount++;
|
||||||
|
} else {
|
||||||
|
existingCount++;
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("同步内置工具失败: {}", tool.name(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info("系统内置工具同步完成: 新增 {} 个, 已存在 {} 个", addedCount, existingCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 同步单个内置工具到数据库
|
||||||
|
*
|
||||||
|
* @param tool 工具定义
|
||||||
|
* @return 是否新增(true=新增, false=已存在)
|
||||||
|
*/
|
||||||
|
private boolean syncBuiltinTool(BuiltinToolDefinition tool) {
|
||||||
|
// 检查是否已存在
|
||||||
|
LambdaQueryWrapper<McpTool> wrapper = new LambdaQueryWrapper<>();
|
||||||
|
wrapper.eq(McpTool::getName, tool.name())
|
||||||
|
.eq(McpTool::getType, BuiltinToolRegistry.TYPE_BUILTIN);
|
||||||
|
|
||||||
|
McpTool existing = mcpToolMapper.selectOne(wrapper);
|
||||||
|
|
||||||
|
if (existing != null) {
|
||||||
|
// 已存在,更新描述信息(保留状态不变)
|
||||||
|
if (!tool.description().equals(existing.getDescription())) {
|
||||||
|
existing.setDescription(tool.description());
|
||||||
|
mcpToolMapper.updateById(existing);
|
||||||
|
log.debug("更新内置工具描述: {}", tool.name());
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增
|
||||||
|
McpTool newTool = new McpTool();
|
||||||
|
newTool.setName(tool.name());
|
||||||
|
newTool.setDescription(tool.description());
|
||||||
|
newTool.setType(BuiltinToolRegistry.TYPE_BUILTIN);
|
||||||
|
newTool.setStatus(McpToolStatus.ENABLED.getValue()); // 默认启用
|
||||||
|
newTool.setConfigJson(null); // 内置工具不需要配置
|
||||||
|
mcpToolMapper.insert(newTool);
|
||||||
|
|
||||||
|
log.info("新增内置工具: {} ({})", tool.name(), tool.displayName());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,172 @@
|
|||||||
|
package org.ruoyi.controller.mcp;
|
||||||
|
|
||||||
|
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.ruoyi.common.core.domain.R;
|
||||||
|
import org.ruoyi.common.excel.utils.ExcelUtil;
|
||||||
|
import org.ruoyi.common.idempotent.annotation.RepeatSubmit;
|
||||||
|
import org.ruoyi.common.log.annotation.Log;
|
||||||
|
import org.ruoyi.common.log.enums.BusinessType;
|
||||||
|
import org.ruoyi.common.mybatis.core.page.PageQuery;
|
||||||
|
import org.ruoyi.common.mybatis.core.page.TableDataInfo;
|
||||||
|
import org.ruoyi.common.web.core.BaseController;
|
||||||
|
import org.ruoyi.domain.bo.mcp.McpMarketBo;
|
||||||
|
import org.ruoyi.domain.dto.mcp.McpMarketListResult;
|
||||||
|
import org.ruoyi.domain.dto.mcp.McpMarketRefreshResult;
|
||||||
|
import org.ruoyi.domain.dto.mcp.McpMarketToolListResult;
|
||||||
|
import org.ruoyi.domain.vo.mcp.McpMarketVo;
|
||||||
|
import org.ruoyi.service.mcp.IMcpMarketService;
|
||||||
|
import org.springframework.validation.annotation.Validated;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MCP 市场管理 Controller
|
||||||
|
*
|
||||||
|
* @author ruoyi team
|
||||||
|
*/
|
||||||
|
@Validated
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/mcp/market")
|
||||||
|
public class McpMarketController extends BaseController {
|
||||||
|
|
||||||
|
private final IMcpMarketService mcpMarketService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询市场列表
|
||||||
|
*/
|
||||||
|
@SaCheckPermission("mcp:market:list")
|
||||||
|
@GetMapping("/list")
|
||||||
|
public TableDataInfo<McpMarketVo> list(McpMarketBo bo, PageQuery pageQuery) {
|
||||||
|
return mcpMarketService.selectPageList(bo, pageQuery);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询市场列表(不分页)
|
||||||
|
*/
|
||||||
|
@SaCheckPermission("mcp:market:list")
|
||||||
|
@GetMapping("/all")
|
||||||
|
public McpMarketListResult listAll(
|
||||||
|
@RequestParam(required = false) String keyword,
|
||||||
|
@RequestParam(required = false) String status) {
|
||||||
|
return mcpMarketService.listMarkets(keyword, status);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导出 MCP 市场列表
|
||||||
|
*/
|
||||||
|
@SaCheckPermission("mcp:market:export")
|
||||||
|
@Log(title = "MCP市场管理", businessType = BusinessType.EXPORT)
|
||||||
|
@PostMapping("/export")
|
||||||
|
public void export(McpMarketBo bo, HttpServletResponse response) {
|
||||||
|
List<McpMarketVo> list = mcpMarketService.queryList(bo);
|
||||||
|
ExcelUtil.exportExcel(list, "MCP市场", McpMarketVo.class, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据市场ID获取详细信息
|
||||||
|
*
|
||||||
|
* @param id 市场ID
|
||||||
|
*/
|
||||||
|
@SaCheckPermission("mcp:market:query")
|
||||||
|
@GetMapping("/{id}")
|
||||||
|
public R<McpMarketVo> getInfo(@PathVariable Long id) {
|
||||||
|
return R.ok(mcpMarketService.selectById(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新增市场
|
||||||
|
*/
|
||||||
|
@SaCheckPermission("mcp:market:add")
|
||||||
|
@Log(title = "MCP市场管理", businessType = BusinessType.INSERT)
|
||||||
|
@RepeatSubmit()
|
||||||
|
@PostMapping
|
||||||
|
public R<Void> add(@Validated @RequestBody McpMarketBo bo) {
|
||||||
|
mcpMarketService.insert(bo);
|
||||||
|
return R.ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改市场
|
||||||
|
*/
|
||||||
|
@SaCheckPermission("mcp:market:edit")
|
||||||
|
@Log(title = "MCP市场管理", businessType = BusinessType.UPDATE)
|
||||||
|
@RepeatSubmit()
|
||||||
|
@PutMapping
|
||||||
|
public R<Void> edit(@Validated @RequestBody McpMarketBo bo) {
|
||||||
|
mcpMarketService.update(bo);
|
||||||
|
return R.ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除市场
|
||||||
|
*
|
||||||
|
* @param ids 市场ID串
|
||||||
|
*/
|
||||||
|
@SaCheckPermission("mcp:market:remove")
|
||||||
|
@Log(title = "MCP市场管理", businessType = BusinessType.DELETE)
|
||||||
|
@DeleteMapping("/{ids}")
|
||||||
|
public R<Void> remove(@PathVariable Long[] ids) {
|
||||||
|
mcpMarketService.deleteByIds(List.of(ids));
|
||||||
|
return R.ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新市场状态
|
||||||
|
*/
|
||||||
|
@SaCheckPermission("mcp:market:edit")
|
||||||
|
@Log(title = "MCP市场管理", businessType = BusinessType.UPDATE)
|
||||||
|
@PutMapping("/{id}/status")
|
||||||
|
public R<Void> updateStatus(@PathVariable Long id, @RequestParam String status) {
|
||||||
|
mcpMarketService.updateStatus(id, status);
|
||||||
|
return R.ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取市场工具列表(分页)
|
||||||
|
*/
|
||||||
|
@SaCheckPermission("mcp:market:query")
|
||||||
|
@GetMapping("/{marketId}/tools")
|
||||||
|
public McpMarketToolListResult getMarketTools(
|
||||||
|
@PathVariable Long marketId,
|
||||||
|
@RequestParam(defaultValue = "1") int page,
|
||||||
|
@RequestParam(defaultValue = "10") int size) {
|
||||||
|
return mcpMarketService.getMarketTools(marketId, page, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 刷新市场工具列表
|
||||||
|
*/
|
||||||
|
@SaCheckPermission("mcp:market:edit")
|
||||||
|
@Log(title = "MCP市场管理", businessType = BusinessType.UPDATE)
|
||||||
|
@PostMapping("/{marketId}/refresh")
|
||||||
|
public R<McpMarketRefreshResult> refreshMarketTools(@PathVariable Long marketId) {
|
||||||
|
return R.ok(mcpMarketService.refreshMarketTools(marketId));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加载单个工具到本地
|
||||||
|
*/
|
||||||
|
@SaCheckPermission("mcp:market:add")
|
||||||
|
@Log(title = "MCP市场管理", businessType = BusinessType.INSERT)
|
||||||
|
@PostMapping("/tools/{toolId}/load")
|
||||||
|
public R<Void> loadToolToLocal(@PathVariable Long toolId) {
|
||||||
|
mcpMarketService.loadToolToLocal(toolId);
|
||||||
|
return R.ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量加载工具到本地
|
||||||
|
*/
|
||||||
|
@SaCheckPermission("mcp:market:add")
|
||||||
|
@Log(title = "MCP市场管理", businessType = BusinessType.INSERT)
|
||||||
|
@PostMapping("/tools/batch-load")
|
||||||
|
public R<Map<String, Object>> batchLoadTools(@RequestBody List<Long> toolIds) {
|
||||||
|
int successCount = mcpMarketService.batchLoadTools(toolIds);
|
||||||
|
return R.ok(Map.of("successCount", successCount));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,136 @@
|
|||||||
|
package org.ruoyi.controller.mcp;
|
||||||
|
|
||||||
|
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.ruoyi.common.core.domain.R;
|
||||||
|
import org.ruoyi.common.excel.utils.ExcelUtil;
|
||||||
|
import org.ruoyi.common.idempotent.annotation.RepeatSubmit;
|
||||||
|
import org.ruoyi.common.log.annotation.Log;
|
||||||
|
import org.ruoyi.common.log.enums.BusinessType;
|
||||||
|
import org.ruoyi.common.mybatis.core.page.PageQuery;
|
||||||
|
import org.ruoyi.common.mybatis.core.page.TableDataInfo;
|
||||||
|
import org.ruoyi.common.web.core.BaseController;
|
||||||
|
import org.ruoyi.domain.bo.mcp.McpToolBo;
|
||||||
|
import org.ruoyi.domain.dto.mcp.McpToolListResult;
|
||||||
|
import org.ruoyi.domain.dto.mcp.McpToolTestResult;
|
||||||
|
import org.ruoyi.domain.vo.mcp.McpToolVo;
|
||||||
|
import org.ruoyi.service.mcp.IMcpToolService;
|
||||||
|
import org.springframework.validation.annotation.Validated;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MCP 工具管理 Controller
|
||||||
|
*
|
||||||
|
* @author ruoyi team
|
||||||
|
*/
|
||||||
|
@Validated
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/mcp/tool")
|
||||||
|
public class McpToolController extends BaseController {
|
||||||
|
|
||||||
|
private final IMcpToolService mcpToolService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询 MCP 工具列表
|
||||||
|
*/
|
||||||
|
@SaCheckPermission("mcp:tool:list")
|
||||||
|
@GetMapping("/list")
|
||||||
|
public TableDataInfo<McpToolVo> list(McpToolBo bo, PageQuery pageQuery) {
|
||||||
|
return mcpToolService.selectPageList(bo, pageQuery);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询 MCP 工具列表(不分页)
|
||||||
|
*/
|
||||||
|
@SaCheckPermission("mcp:tool:list")
|
||||||
|
@GetMapping("/all")
|
||||||
|
public McpToolListResult listAll(
|
||||||
|
@RequestParam(required = false) String keyword,
|
||||||
|
@RequestParam(required = false) String type,
|
||||||
|
@RequestParam(required = false) String status) {
|
||||||
|
return mcpToolService.listTools(keyword, type, status);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导出 MCP 工具列表
|
||||||
|
*/
|
||||||
|
@SaCheckPermission("mcp:tool:export")
|
||||||
|
@Log(title = "MCP工具管理", businessType = BusinessType.EXPORT)
|
||||||
|
@PostMapping("/export")
|
||||||
|
public void export(McpToolBo bo, HttpServletResponse response) {
|
||||||
|
List<McpToolVo> list = mcpToolService.queryList(bo);
|
||||||
|
ExcelUtil.exportExcel(list, "MCP工具", McpToolVo.class, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据工具ID获取详细信息
|
||||||
|
*
|
||||||
|
* @param id 工具ID
|
||||||
|
*/
|
||||||
|
@SaCheckPermission("mcp:tool:query")
|
||||||
|
@GetMapping("/{id}")
|
||||||
|
public R<McpToolVo> getInfo(@PathVariable Long id) {
|
||||||
|
return R.ok(mcpToolService.selectById(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新增 MCP 工具
|
||||||
|
*/
|
||||||
|
@SaCheckPermission("mcp:tool:add")
|
||||||
|
@Log(title = "MCP工具管理", businessType = BusinessType.INSERT)
|
||||||
|
@RepeatSubmit()
|
||||||
|
@PostMapping
|
||||||
|
public R<Void> add(@Validated @RequestBody McpToolBo bo) {
|
||||||
|
mcpToolService.insert(bo);
|
||||||
|
return R.ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改 MCP 工具
|
||||||
|
*/
|
||||||
|
@SaCheckPermission("mcp:tool:edit")
|
||||||
|
@Log(title = "MCP工具管理", businessType = BusinessType.UPDATE)
|
||||||
|
@RepeatSubmit()
|
||||||
|
@PutMapping
|
||||||
|
public R<Void> edit(@Validated @RequestBody McpToolBo bo) {
|
||||||
|
mcpToolService.update(bo);
|
||||||
|
return R.ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除 MCP 工具
|
||||||
|
*
|
||||||
|
* @param ids 工具ID串
|
||||||
|
*/
|
||||||
|
@SaCheckPermission("mcp:tool:remove")
|
||||||
|
@Log(title = "MCP工具管理", businessType = BusinessType.DELETE)
|
||||||
|
@DeleteMapping("/{ids}")
|
||||||
|
public R<Void> remove(@PathVariable Long[] ids) {
|
||||||
|
mcpToolService.deleteByIds(List.of(ids));
|
||||||
|
return R.ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新工具状态
|
||||||
|
*/
|
||||||
|
@SaCheckPermission("mcp:tool:edit")
|
||||||
|
@Log(title = "MCP工具管理", businessType = BusinessType.UPDATE)
|
||||||
|
@PutMapping("/{id}/status")
|
||||||
|
public R<Void> updateStatus(@PathVariable Long id, @RequestParam String status) {
|
||||||
|
mcpToolService.updateStatus(id, status);
|
||||||
|
return R.ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 测试工具连接
|
||||||
|
*/
|
||||||
|
@SaCheckPermission("mcp:tool:query")
|
||||||
|
@PostMapping("/{id}/test")
|
||||||
|
public R<McpToolTestResult> testTool(@PathVariable Long id) {
|
||||||
|
return R.ok(mcpToolService.testTool(id));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
package org.ruoyi.domain.bo.mcp;
|
||||||
|
|
||||||
|
import io.github.linpeilie.annotations.AutoMapper;
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import jakarta.validation.constraints.Size;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import org.ruoyi.common.mybatis.core.domain.BaseEntity;
|
||||||
|
import org.ruoyi.domain.entity.mcp.McpMarket;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MCP 市场业务对象
|
||||||
|
*
|
||||||
|
* @author ruoyi team
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@AutoMapper(target = McpMarket.class, reverseConvertGenerate = false)
|
||||||
|
public class McpMarketBo extends BaseEntity {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 市场ID
|
||||||
|
*/
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 市场名称
|
||||||
|
*/
|
||||||
|
@NotBlank(message = "市场名称不能为空")
|
||||||
|
@Size(min = 0, max = 200, message = "市场名称不能超过{max}个字符")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 市场 URL
|
||||||
|
*/
|
||||||
|
@NotBlank(message = "市场URL不能为空")
|
||||||
|
@Size(min = 0, max = 500, message = "市场URL不能超过{max}个字符")
|
||||||
|
private String url;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 市场描述
|
||||||
|
*/
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 认证配置(JSON格式)
|
||||||
|
*/
|
||||||
|
private String authConfig;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 状态:ENABLED-启用, DISABLED-禁用
|
||||||
|
*/
|
||||||
|
private String status;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
package org.ruoyi.domain.bo.mcp;
|
||||||
|
|
||||||
|
import io.github.linpeilie.annotations.AutoMapper;
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import jakarta.validation.constraints.Size;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import org.ruoyi.common.mybatis.core.domain.BaseEntity;
|
||||||
|
import org.ruoyi.domain.entity.mcp.McpTool;
|
||||||
|
|
||||||
|
import java.io.Serial;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MCP 工具业务对象
|
||||||
|
*
|
||||||
|
* @author ruoyi team
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@AutoMapper(target = McpTool.class, reverseConvertGenerate = false)
|
||||||
|
public class McpToolBo extends BaseEntity {
|
||||||
|
|
||||||
|
@Serial
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 工具ID
|
||||||
|
*/
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 工具名称
|
||||||
|
*/
|
||||||
|
@NotBlank(message = "工具名称不能为空")
|
||||||
|
@Size(min = 0, max = 200, message = "工具名称不能超过{max}个字符")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 工具描述
|
||||||
|
*/
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 工具类型:LOCAL-本地, REMOTE-远程, BUILTIN-内置
|
||||||
|
*/
|
||||||
|
@NotBlank(message = "工具类型不能为空")
|
||||||
|
private String type;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 状态:ENABLED-启用, DISABLED-禁用
|
||||||
|
*/
|
||||||
|
private String status;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 配置信息(JSON格式)
|
||||||
|
*/
|
||||||
|
private String configJson;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
package org.ruoyi.domain.dto.mcp;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import org.ruoyi.domain.entity.mcp.McpMarket;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MCP 市场列表返回结果
|
||||||
|
*
|
||||||
|
* @author ruoyi team
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Builder
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class McpMarketListResult {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否成功
|
||||||
|
*/
|
||||||
|
private boolean success;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 市场列表
|
||||||
|
*/
|
||||||
|
private List<McpMarket> data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 总数
|
||||||
|
*/
|
||||||
|
private int total;
|
||||||
|
|
||||||
|
public static McpMarketListResult of(List<McpMarket> data) {
|
||||||
|
return McpMarketListResult.builder()
|
||||||
|
.success(true)
|
||||||
|
.data(data)
|
||||||
|
.total(data != null ? data.size() : 0)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
package org.ruoyi.domain.dto.mcp;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MCP 市场工具刷新结果
|
||||||
|
*
|
||||||
|
* @author ruoyi team
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Builder
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class McpMarketRefreshResult {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否成功
|
||||||
|
*/
|
||||||
|
private boolean success;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 消息
|
||||||
|
*/
|
||||||
|
private String message;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新增工具数量
|
||||||
|
*/
|
||||||
|
private int addedCount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新工具数量
|
||||||
|
*/
|
||||||
|
private int updatedCount;
|
||||||
|
}
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
package org.ruoyi.domain.dto.mcp;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import org.ruoyi.domain.entity.mcp.McpMarketTool;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MCP 市场工具列表返回结果(分页)
|
||||||
|
*
|
||||||
|
* @author ruoyi team
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Builder
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class McpMarketToolListResult {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否成功
|
||||||
|
*/
|
||||||
|
private boolean success;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 工具列表
|
||||||
|
*/
|
||||||
|
private List<McpMarketTool> data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 总数
|
||||||
|
*/
|
||||||
|
private long total;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 当前页
|
||||||
|
*/
|
||||||
|
private int page;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 每页大小
|
||||||
|
*/
|
||||||
|
private int size;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 总页数
|
||||||
|
*/
|
||||||
|
private long pages;
|
||||||
|
|
||||||
|
public static McpMarketToolListResult of(List<McpMarketTool> data, long total, int page, int size) {
|
||||||
|
long pages = (total + size - 1) / size;
|
||||||
|
return McpMarketToolListResult.builder()
|
||||||
|
.success(true)
|
||||||
|
.data(data)
|
||||||
|
.total(total)
|
||||||
|
.page(page)
|
||||||
|
.size(size)
|
||||||
|
.pages(pages)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
package org.ruoyi.domain.dto.mcp;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import org.ruoyi.domain.entity.mcp.McpTool;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MCP 工具列表返回结果
|
||||||
|
*
|
||||||
|
* @author ruoyi team
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Builder
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class McpToolListResult {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否成功
|
||||||
|
*/
|
||||||
|
private boolean success;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 工具列表
|
||||||
|
*/
|
||||||
|
private List<McpTool> data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 总数
|
||||||
|
*/
|
||||||
|
private int total;
|
||||||
|
|
||||||
|
public static McpToolListResult of(List<McpTool> data) {
|
||||||
|
return McpToolListResult.builder()
|
||||||
|
.success(true)
|
||||||
|
.data(data)
|
||||||
|
.total(data != null ? data.size() : 0)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
package org.ruoyi.domain.dto.mcp;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MCP 工具测试结果
|
||||||
|
*
|
||||||
|
* @author ruoyi team
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Builder
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class McpToolTestResult {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否成功
|
||||||
|
*/
|
||||||
|
private boolean success;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 消息
|
||||||
|
*/
|
||||||
|
private String message;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发现的工具数量
|
||||||
|
*/
|
||||||
|
private Integer toolCount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 工具名称列表
|
||||||
|
*/
|
||||||
|
private List<String> tools;
|
||||||
|
|
||||||
|
public static McpToolTestResult success(String message, int toolCount, List<String> tools) {
|
||||||
|
return McpToolTestResult.builder()
|
||||||
|
.success(true)
|
||||||
|
.message(message)
|
||||||
|
.toolCount(toolCount)
|
||||||
|
.tools(tools)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static McpToolTestResult fail(String message) {
|
||||||
|
return McpToolTestResult.builder()
|
||||||
|
.success(false)
|
||||||
|
.message(message)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
package org.ruoyi.domain.entity.mcp;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.IdType;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import org.ruoyi.common.tenant.core.TenantEntity;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MCP 市场信息实体
|
||||||
|
*
|
||||||
|
* @author ruoyi team
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@TableName("mcp_market_info")
|
||||||
|
public class McpMarket extends TenantEntity {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 市场ID
|
||||||
|
*/
|
||||||
|
@TableId(value = "id", type = IdType.AUTO)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 市场名称
|
||||||
|
*/
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 市场 URL
|
||||||
|
*/
|
||||||
|
private String url;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 市场描述
|
||||||
|
*/
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 认证配置(JSON格式)
|
||||||
|
*/
|
||||||
|
private String authConfig;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 状态:ENABLED-启用, DISABLED-禁用
|
||||||
|
*/
|
||||||
|
private String status;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
package org.ruoyi.domain.entity.mcp;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.IdType;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import org.ruoyi.common.mybatis.core.domain.BaseEntity;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MCP 市场工具关联实体
|
||||||
|
*
|
||||||
|
* @author ruoyi team
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@TableName("mcp_market_tool")
|
||||||
|
public class McpMarketTool extends BaseEntity {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ID
|
||||||
|
*/
|
||||||
|
@TableId(value = "id", type = IdType.AUTO)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 市场 ID
|
||||||
|
*/
|
||||||
|
private Long marketId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 工具名称
|
||||||
|
*/
|
||||||
|
private String toolName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 工具描述
|
||||||
|
*/
|
||||||
|
private String toolDescription;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 工具版本
|
||||||
|
*/
|
||||||
|
private String toolVersion;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 工具元数据(JSON格式)
|
||||||
|
*/
|
||||||
|
private String toolMetadata;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否已加载到本地
|
||||||
|
*/
|
||||||
|
private Boolean isLoaded;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 关联的本地工具 ID
|
||||||
|
*/
|
||||||
|
private Long localToolId;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
package org.ruoyi.domain.entity.mcp;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.IdType;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import org.ruoyi.common.tenant.core.TenantEntity;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MCP 工具信息实体
|
||||||
|
*
|
||||||
|
* @author ruoyi team
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@TableName("mcp_tool_info")
|
||||||
|
public class McpTool extends TenantEntity {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 工具ID
|
||||||
|
*/
|
||||||
|
@TableId(value = "id", type = IdType.AUTO)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 工具名称
|
||||||
|
*/
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 工具描述
|
||||||
|
*/
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 工具类型:LOCAL-本地, REMOTE-远程, BUILTIN-内置
|
||||||
|
*/
|
||||||
|
private String type;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 状态:ENABLED-启用, DISABLED-禁用
|
||||||
|
*/
|
||||||
|
private String status;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 配置信息(JSON格式)
|
||||||
|
* LOCAL: {"command": "npx", "args": ["-y", "@example/mcp-server"], "env": {...}}
|
||||||
|
* REMOTE: {"baseUrl": "http://localhost:8080/mcp"}
|
||||||
|
* BUILTIN: null
|
||||||
|
*/
|
||||||
|
private String configJson;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
package org.ruoyi.domain.vo.mcp;
|
||||||
|
|
||||||
|
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
|
||||||
|
import cn.idev.excel.annotation.ExcelProperty;
|
||||||
|
import io.github.linpeilie.annotations.AutoMapper;
|
||||||
|
import lombok.Data;
|
||||||
|
import org.ruoyi.domain.entity.mcp.McpMarket;
|
||||||
|
|
||||||
|
import java.io.Serial;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MCP 市场视图对象
|
||||||
|
*
|
||||||
|
* @author ruoyi team
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@ExcelIgnoreUnannotated
|
||||||
|
@AutoMapper(target = McpMarket.class)
|
||||||
|
public class McpMarketVo implements Serializable {
|
||||||
|
|
||||||
|
@Serial
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 市场ID
|
||||||
|
*/
|
||||||
|
@ExcelProperty(value = "市场ID")
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 市场名称
|
||||||
|
*/
|
||||||
|
@ExcelProperty(value = "市场名称")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 市场 URL
|
||||||
|
*/
|
||||||
|
@ExcelProperty(value = "市场URL")
|
||||||
|
private String url;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 市场描述
|
||||||
|
*/
|
||||||
|
@ExcelProperty(value = "市场描述")
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 认证配置
|
||||||
|
*/
|
||||||
|
@ExcelProperty(value = "认证配置")
|
||||||
|
private String authConfig;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 状态
|
||||||
|
*/
|
||||||
|
@ExcelProperty(value = "状态")
|
||||||
|
private String status;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建时间
|
||||||
|
*/
|
||||||
|
@ExcelProperty(value = "创建时间")
|
||||||
|
private Date createTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新时间
|
||||||
|
*/
|
||||||
|
@ExcelProperty(value = "更新时间")
|
||||||
|
private Date updateTime;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
package org.ruoyi.domain.vo.mcp;
|
||||||
|
|
||||||
|
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
|
||||||
|
import cn.idev.excel.annotation.ExcelProperty;
|
||||||
|
import io.github.linpeilie.annotations.AutoMapper;
|
||||||
|
import lombok.Data;
|
||||||
|
import org.ruoyi.domain.entity.mcp.McpTool;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MCP 工具视图对象
|
||||||
|
*
|
||||||
|
* @author ruoyi team
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@ExcelIgnoreUnannotated
|
||||||
|
@AutoMapper(target = McpTool.class)
|
||||||
|
public class McpToolVo implements Serializable {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 工具ID
|
||||||
|
*/
|
||||||
|
@ExcelProperty(value = "工具ID")
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 工具名称
|
||||||
|
*/
|
||||||
|
@ExcelProperty(value = "工具名称")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 工具描述
|
||||||
|
*/
|
||||||
|
@ExcelProperty(value = "工具描述")
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 工具类型
|
||||||
|
*/
|
||||||
|
@ExcelProperty(value = "工具类型")
|
||||||
|
private String type;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 状态
|
||||||
|
*/
|
||||||
|
@ExcelProperty(value = "状态")
|
||||||
|
private String status;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 配置信息
|
||||||
|
*/
|
||||||
|
@ExcelProperty(value = "配置信息")
|
||||||
|
private String configJson;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建时间
|
||||||
|
*/
|
||||||
|
@ExcelProperty(value = "创建时间")
|
||||||
|
private Date createTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新时间
|
||||||
|
*/
|
||||||
|
@ExcelProperty(value = "更新时间")
|
||||||
|
private Date updateTime;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
package org.ruoyi.enums;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MCP 工具状态枚举
|
||||||
|
*
|
||||||
|
* @author ruoyi team
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
public enum McpToolStatus {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 启用状态
|
||||||
|
*/
|
||||||
|
ENABLED("ENABLED", "启用"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 禁用状态
|
||||||
|
*/
|
||||||
|
DISABLED("DISABLED", "禁用");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 状态值(存储到数据库)
|
||||||
|
*/
|
||||||
|
private final String value;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 状态描述
|
||||||
|
*/
|
||||||
|
private final String description;
|
||||||
|
|
||||||
|
McpToolStatus(String value, String description) {
|
||||||
|
this.value = value;
|
||||||
|
this.description = description;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断是否为启用状态
|
||||||
|
*
|
||||||
|
* @param value 状态值
|
||||||
|
* @return 是否启用
|
||||||
|
*/
|
||||||
|
public static boolean isEnabled(String value) {
|
||||||
|
return ENABLED.value.equals(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package org.ruoyi.mapper.mcp;
|
||||||
|
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
import org.ruoyi.common.mybatis.core.mapper.BaseMapperPlus;
|
||||||
|
import org.ruoyi.domain.entity.mcp.McpMarket;
|
||||||
|
import org.ruoyi.domain.vo.mcp.McpMarketVo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MCP 市场信息 Mapper
|
||||||
|
*
|
||||||
|
* @author ruoyi team
|
||||||
|
*/
|
||||||
|
@Mapper
|
||||||
|
public interface McpMarketMapper extends BaseMapperPlus<McpMarket, McpMarketVo> {
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package org.ruoyi.mapper.mcp;
|
||||||
|
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
import org.ruoyi.common.mybatis.core.mapper.BaseMapperPlus;
|
||||||
|
import org.ruoyi.domain.entity.mcp.McpMarketTool;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MCP 市场工具关联 Mapper
|
||||||
|
*
|
||||||
|
* @author ruoyi team
|
||||||
|
*/
|
||||||
|
@Mapper
|
||||||
|
public interface McpMarketToolMapper extends BaseMapperPlus<McpMarketTool, Void> {
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package org.ruoyi.mapper.mcp;
|
||||||
|
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
import org.ruoyi.common.mybatis.core.mapper.BaseMapperPlus;
|
||||||
|
import org.ruoyi.domain.entity.mcp.McpTool;
|
||||||
|
import org.ruoyi.domain.vo.mcp.McpToolVo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MCP 工具信息 Mapper
|
||||||
|
*
|
||||||
|
* @author ruoyi team
|
||||||
|
*/
|
||||||
|
@Mapper
|
||||||
|
public interface McpToolMapper extends BaseMapperPlus<McpTool, McpToolVo> {
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user