v3.0.0 init

This commit is contained in:
ageerle
2026-02-06 03:00:23 +08:00
parent eb2e8f3ff8
commit 7b8cfe02a1
1524 changed files with 53132 additions and 58866 deletions

32
.editorconfig Normal file
View File

@@ -0,0 +1,32 @@
# http://editorconfig.org
root = true
# 所有文件 ([*])
# 使用空格缩进,每级 4 个空格
# UTF-8 编码
# Unix 风格换行符 (LF)
# 自动删除行尾空格
# 文件末尾自动添加空行
# JSON/YAML 文件:
# 缩进改为 2 个空格
# Markdown 文件:
# 不在末尾添加空行
# 保留行尾空格Markdown 语法需要)
# 作用:
# 支持此标准的编辑器VS Code、IDEA、Sublime 等)会自动读取并应用这些规则,确保不同开发者使用不同编辑器时,代码格式保持一致。
[*]
indent_style = space
indent_size = 4
charset = utf-8
end_of_line = lf
trim_trailing_whitespace = true
insert_final_newline = true
[*.{json,yml,yaml}]
indent_size = 2
[*.md]
insert_final_newline = false
trim_trailing_whitespace = false

BIN
.gitignore vendored

Binary file not shown.

20
LICENSE
View File

@@ -1,20 +0,0 @@
The MIT License (MIT)
Copyright (c) 2023 ruoyi-ai
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -15,55 +15,40 @@
</a>
</p>
<img src="image/00.png" alt="RuoYi AI Logo" width="120" height="120">
<img src="docs/image/logo.png" alt="RuoYi AI Logo" width="120" height="120">
### 企业级AI助手平台
*开箱即用的智能AI平台深度集成 FastGPT、扣子(Coze)、DIFY 等主流AI平台提供先进的RAG技术、知识图谱、数字人和AI流程编排能力*
*开箱即用的全栈AI平台集成Coze、DIFY等主流AI平台提供先进的RAG技术、知识图谱、数字人和AI流程编排能力*
**[🇺🇸 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)**
**[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)**
</div>
## ✨ 核心亮点
### 智能AI引擎
- **多模型接入**:支持 OpenAI GPT-4、Azure、ChatGLM、通义千问、智谱AI 等主流模型
- **AI平台集成**深度集成 **FastGPT**、**扣子(Coze)**、**DIFY** 等主流AI应用平台
- **Spring AI MCP 集成**基于模型上下文协议打造可扩展的AI工具生态系统
- **实时流式对话**:采用 SSE/WebSocket 技术,提供丝滑的对话体验
- **AI 编程助手**:内置智能代码分析和项目脚手架生成能力
### AI平台生态集成
- **FastGPT 深度集成**:原生支持 FastGPT API包括知识库检索、工作流编排和上下文管理
- **扣子(Coze) 官方SDK**集成字节跳动扣子平台官方SDK支持Bot对话和流式响应
- **DIFY 完整兼容**:使用 DIFY Java Client支持应用编排、工作流和知识库管理
- **统一聊天接口**:提供统一的聊天服务接口,支持多平台无缝切换和负载均衡
- **多模型接入**:支持 OpenAI、DeepSeek、通义千问、智谱AI 等主流厂商的模型
- **多模态理解**:支持文本、图片、文档等多种格式的智能处理
- **AI平台集成**:集成 **扣子(Coze)**、**DIFY**、**FastGPT** 等主流AI应用平台
- **MCP能力集成**基于模型上下文协议打造可扩展的AI工具生态系统
- **AI编程助手**:内置智能代码分析和项目脚手架生成能力
### 本地化RAG方案
- **私有知识库**:基于 Langchain4j 框架 + BGE-large-zh-v1.5 中文向量模型
- **私有知识库**:基于 Langchain4j 框架 + BGE-large-zh-v1.5 中文向量模型实现本地私有知识库
- **多种向量库**:支持 Milvus、Weaviate、Qdrant 等主流向量数据库
- **数据安全可控**:支持完全本地部署,保护企业数据隐私
- **灵活模型部署**:兼容 Ollama、vLLM 等本地推理框架
### AI创作工具
- **AI 绘画创作**:深度集成 DALL·E-3、MidJourney、Stable Diffusion
- **AI 绘画创作** 集成 MidJourney、GPT-4o-image
- **智能PPT生成**:一键将文本内容转换为精美演示文稿
- **多模态理解**:支持文本、图片、文档等多种格式的智能处理
### 知识图谱与智能编排
- **知识图谱构建**:自动从文档和对话中提取实体关系,构建可视化知识网络
- **AI 流程编排**可视化工作流设计器支持复杂AI任务的编排和自动化执行
- **数字人交互**:集成数字人形象,提供更自然的人机交互体验
- **智能推理引擎**:基于知识图谱的智能推理和问答能力
## 🚀 快速体验
@@ -80,23 +65,15 @@
| 🎨 用户前端 | [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) |
### 合作项目
| 项目介绍 | GitHub 仓库 | Gitee 仓库 |
|:-----:|:----------------------------------------------------------------------:|:----------------------------------------------------------------:|
| 前端简化版 | [ruoyi-element-ai](https://github.com/element-plus-x/ruoyi-element-ai) | [ruoyi-element-ai](https://gitee.com/he-jiayue/ruoyi-element-ai) |
## 🛠️ 技术架构
### 核心框架
- **后端架构**Spring Boot 3.4 + Spring AI + Langchain4j
- **后端架构**Spring Boot 3.5 + Langchain4j
- **数据存储**MySQL 8.0 + Redis + 向量数据库Milvus/Weaviate/Qdrant
- **前端技术**Vue 3 + Vben Admin + Naive UI
- **前端技术**Vue 3 + Vben Admin + Element UI
- **安全认证**Sa-Token + JWT 双重保障
### 系统组件
- **文档处理**PDF、Word、Excel 解析,图像智能分析
- **实时通信**WebSocket 实时通信SSE 流式响应
- **系统监控**:完善的日志体系、性能监控、服务健康检查
@@ -128,21 +105,20 @@
## 🙏 特别鸣谢
感谢以下优秀的开源项目为本项目提供支持:
- [Spring AI Alibaba Copilot](https://github.com/springaialibaba/spring-ai-alibaba-copilot) - 基于spring-ai-alibaba
- [Spring AI Alibaba Copilot](https://github.com/spring-ai-alibaba/copilot) - 基于spring-ai-alibaba
的智能编码助手
- [Spring AI](https://spring.io/projects/spring-ai) - Spring 官方 AI 集成框架
- [Langchain4j](https://github.com/langchain4j/langchain4j) - 强大的 Java LLM 开发框架
- [RuoYi-Vue-Plus](https://gitee.com/dromara/RuoYi-Vue-Plus) - 成熟的企业级快速开发框架
- [Vben Admin](https://github.com/vbenjs/vue-vben-admin) - 现代化的 Vue 后台管理模板
- [chatgpt-java](https://github.com/Grt1228/chatgpt-java) - 优秀的 ChatGPT Java SDK
## 🌐 生态伙伴
- [PPIO 派欧云](https://ppinfra.com/user/register?invited_by=P8QTUY&utm_source=github_ruoyi-ai) - 提供高性价比的 GPU
算力和模型 API 服务
- [优云智算](https://www.compshare.cn/?ytag=GPU_YY-gh_ruoyi) - 万卡RTX40系GPU+海内外主流模型API服务秒级响应按量计费新客免费用。
- [胜算云](https://www.shengsuanyun.com/?from=CH_3WG71ZOS) - AI模型算力聚合超市云服务。
## 优秀开源项目及社区推荐
- [imaiwork](https://gitee.com/tsinghua-open/imaiwork) - AI手机开源版AI获客手机项目基于无障碍模式RPA比豆包AI手机更强大。
## 💬 社区交流
@@ -151,12 +127,12 @@
<table>
<tr>
<td align="center">
<img src="image/wx.png" alt="微信二维码" width="200" height="200"><br>
<img src="docs/image/wx.png" alt="微信二维码" width="200" height="200"><br>
<strong>扫码添加作者微信</strong><br>
<em>邀请进群学习</em>
</td>
<td align="center">
<img src="image/qq.png" alt="QQ群二维码" width="200" height="200"><br>
<img src="docs/image/qq.png" alt="QQ群二维码" width="200" height="200"><br>
<strong>QQ技术交流群</strong><br>
<em>技术讨论</em>
</td>
@@ -170,9 +146,7 @@
<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)**
*用 ❤️ 打造,由 RuoYi AI 开源社区维护*

View File

@@ -8,155 +8,133 @@
[![Issues][issues-shield]][issues-url]
[![MIT License][license-shield]][license-url]
<img src="image/00.png" alt="RuoYi AI Logo" width="120" height="120">
<p align="center">
<a href="https://trendshift.io/repositories/13209">
<img src="https://trendshift.io/api/badge/repositories/13209" alt="GitHub Trending">
</a>
</p>
<img src="docs/image/logo.png" alt="RuoYi AI Logo" width="120" height="120">
### Enterprise-Grade AI Assistant Platform
*Production-ready AI platform with deep integration of FastGPT, Coze, DIFY, featuring advanced RAG technology, knowledge
graphs, digital humans, and AI workflow orchestration*
*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*
**[📖 中文文档](README.md)** | **[📚 Documentation](https://doc.pandarobot.chat)** | *
*[🚀 Live Demo](https://web.pandarobot.chat)** | **[🐛 Report Bug](https://github.com/ageerle/ruoyi-ai/issues)** | *
*[💡 Request Feature](https://github.com/ageerle/ruoyi-ai/issues)**
**[中文](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)**
</div>
## ✨ Key Features
## ✨ Core Features
### 🤖 Advanced AI Engine
### Intelligent AI Engine
- **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
- **AI Platform Integration**: Integrates mainstream AI application platforms like **Coze**, **DIFY**, and **FastGPT**
- **MCP Capability Integration**: Build an extensible AI toolkit ecosystem based on the Model Context Protocol
- **AI Coding Assistant**: Built-in intelligent code analysis and project scaffolding generation capabilities
- **Multi-Model Support**: OpenAI GPT-4, Azure, ChatGLM, Qwen, ZhipuAI
- **AI Platform Integration**: Deep integration with **FastGPT**, **Coze**, **DIFY** and other leading AI platforms
- **Spring AI MCP Integration**: Extensible tool ecosystem with Model Context Protocol
- **Streaming Chat**: Real-time SSE/WebSocket communication
- **AI Copilot**: Intelligent code analysis and project scaffolding
### Local RAG Solution
- **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 Platform Ecosystem
### AI Creative Tools
- **AI Image Generation**: Integrates MidJourney and GPT-4o-image
- **Intelligent PPT Generation**: Convert text content to beautiful presentations with one click
- **FastGPT Deep Integration**: Native FastGPT API support with knowledge base retrieval, workflow orchestration and
context management
- **Coze Official SDK**: Integration with ByteDance Coze platform official SDK, supporting Bot conversations and
streaming responses
- **DIFY Full Compatibility**: Using DIFY Java Client for app orchestration, workflows and knowledge base management
- **Unified Chat Interface**: Standardized chat service interface supporting seamless platform switching and load
balancing
### 🧠 Enterprise RAG Solution
- **Local Knowledge Base**: Langchain4j + BGE-large-zh-v1.5 embeddings
- **Vector Database Support**: Milvus, Weaviate, Qdrant
- **Privacy-First**: On-premise deployment with local LLM support
- **Ollama & vLLM Compatible**: Flexible model deployment options
### 🎨 Creative AI Tools
- **AI Art Generation**: DALL·E-3, MidJourney, Stable Diffusion integration
- **PPT Creation**: Automated slide generation from text input
- **Multi-Modal Processing**: Text, image, and document understanding
### 🧩 Knowledge Graph & Intelligent Orchestration
- **Knowledge Graph Construction**: Automatically extract entities and relationships from documents and conversations to
build visual knowledge networks
- **AI Workflow Orchestration**: Visual workflow designer supporting complex AI task orchestration and automated
execution
- **Digital Human Interaction**: Integrated digital human avatars for more natural human-computer interaction
- **Intelligent Reasoning Engine**: Knowledge graph-based intelligent reasoning and Q&A capabilities
### 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
### Live Demo
- **User Portal**: [web.pandarobot.chat](https://web.pandarobot.chat) (demo/demo123)
- **Admin Panel**: [admin.pandarobot.chat](https://admin.pandarobot.chat) (admin/admin123)
- **User Experience**: [web.pandarobot.chat](https://web.pandarobot.chat) (Username: admin, Password: admin123)
- **Admin Dashboard**: [admin.pandarobot.chat](https://admin.pandarobot.chat) (Username: admin, Password: admin123)
### Source Code
### Project Repositories
| Component | GitHub | Gitee | GitCode |
|----------------|-------------------------------------------------------|------------------------------------------------------|--------------------------------------------------------|
| Backend API | [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) |
| Admin Frontend | [ruoyi-admin](https://github.com/ageerle/ruoyi-admin) | [ruoyi-admin](https://gitee.com/ageerle/ruoyi-admin) | [ruoyi-admin](https://gitcode.com/ageerle/ruoyi-admin) |
| 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) |
| 🎨 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) |
### Collaborative Projects
| Project Description | GitHub Repository | Gitee Repository |
|:-------------------:|:----------------------------------------------------------------------:|:----------------------------------------------------------------:|
| Simplified Frontend | [ruoyi-element-ai](https://github.com/element-plus-x/ruoyi-element-ai) | [ruoyi-element-ai](https://gitee.com/he-jiayue/ruoyi-element-ai) |
## 🛠️ Tech Stack
## 🛠️ Technical Architecture
### Core Framework
- **Backend**: Spring Boot 3.4, Spring AI, Langchain4j
- **Database**: MySQL 8.0, Redis, Vector Databases (Milvus/Weaviate/Qdrant)
- **Frontend**: Vue 3, Vben Admin, Naive UI
- **Authentication**: Sa-Token, JWT
- **Backend**: Spring Boot 3.5 + Langchain4j
- **Data Storage**: MySQL 8.0 + Redis + Vector Databases (Milvus/Weaviate/Qdrant)
- **Frontend**: Vue 3 + Vben Admin + Element UI
- **Security**: Sa-Token + JWT dual-layer security
### System Components
- **File Processing**: PDF, Word, Excel parsing, intelligent image analysis
- **Real-time Communication**: WebSocket real-time communication, SSE streaming
- **System Monitoring**: Comprehensive logging, performance monitoring, health checks
- **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
For detailed setup, configuration, and development guides, visit our comprehensive documentation:
Want to learn more about installation, deployment, configuration, and secondary development?
**[📖 Official Documentation](https://doc.pandarobot.chat)**
**👉 [Complete Documentation](https://doc.pandarobot.chat)**
## 🤝 Contributing
We welcome contributions from developers of all skill levels! Whether you're fixing bugs, adding features, or improving
documentation, your help is appreciated.
We warmly welcome community contributions! Whether you are a seasoned developer or just getting started, you can contribute to the project 💪
### How to Contribute
1. Fork the repository
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
3. Commit your changes (`git commit -m 'Add amazing feature'`)
4. Push to the branch (`git push origin feature/amazing-feature`)
5. Open a Pull Request
1. **Fork** the project to your account
2. **Create a branch** (`git checkout -b feature/new-feature-name`)
3. **Commit your changes** (`git commit -m 'Add new feature'`)
4. **Push to the branch** (`git push origin feature/new-feature-name`)
5. **Create a Pull Request**
*Please submit PRs to GitHub - they will be synchronized to other platforms automatically.*
> 💡 **Tip**: We recommend submitting PRs to GitHub, we will automatically sync to other code hosting platforms
## 📄 License
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
This project is licensed under the **MIT License**. See the [LICENSE](LICENSE) file for details.
## 🙏 Acknowledgments
Special thanks to these amazing open source projects:
- [Spring AI Alibaba Copilot](https://github.com/springaialibaba/spring-ai-alibaba-copilot) - Intelligent coding
assistant based on spring-ai-alibaba with MCP protocol integration for project analysis and code generation
- [Spring AI](https://spring.io/projects/spring-ai) - Spring's AI integration framework
- [Langchain4j](https://github.com/langchain4j/langchain4j) - Java LLM framework
- [RuoYi-Vue-Plus](https://gitee.com/dromara/RuoYi-Vue-Plus) - Enterprise development framework
- [Vben Admin](https://github.com/vbenjs/vue-vben-admin) - Vue admin template
- [chatgpt-java](https://github.com/Grt1228/chatgpt-java) - ChatGPT Java SDK
Thanks to the following excellent open-source projects for their support:
- [Spring AI Alibaba Copilot](https://github.com/spring-ai-alibaba/copilot) - Intelligent coding assistant based on spring-ai-alibaba
- [Langchain4j](https://github.com/langchain4j/langchain4j) - Powerful Java LLM development framework
- [RuoYi-Vue-Plus](https://gitee.com/dromara/RuoYi-Vue-Plus) - Mature enterprise-level rapid development framework
- [Vben Admin](https://github.com/vbenjs/vue-vben-admin) - Modern Vue admin template
## 🌐 Ecosystem Partners
- [PPIO Cloud](https://ppinfra.com/user/register?invited_by=P8QTUY&utm_source=github_ruoyi-ai) - Cost-effective GPU
containers and model APIs
- [PPIO Cloud](https://ppinfra.com/user/register?invited_by=P8QTUY&utm_source=github_ruoyi-ai) - Provides cost-effective GPU computing and model API services
- [Youyun Intelligent Computing](https://www.compshare.cn/?ytag=GPU_YY-gh_ruoyi) - Thousands of RTX40 series GPUs + mainstream models API services, second-level response, pay-per-use, free for new customers.
## 💬 Community
## Outstanding Open-Source Projects and Community Recommendations
- [imaiwork](https://gitee.com/tsinghua-open/imaiwork) - Open-source AI phone, AI customer acquisition phone project, based on accessibility mode and RPA, more powerful than Doubao AI phone.
## 💬 Community Chat
<div align="center">
<table>
<tr>
<td align="center">
<img src="image/wx.png" alt="WeChat" width="200" height="200"><br>
<strong>Add Author WeChat</strong><br>
<em>Scan to join learning group</em>
<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="image/qq.png" alt="QQ Group" width="200" height="200"><br>
<strong>QQ Group</strong><br>
<em>Technical discussion</em>
<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>
@@ -166,10 +144,9 @@ Special thanks to these amazing open source projects:
<div align="center">
**[⭐ Star this repo](https://github.com/ageerle/ruoyi-ai)** • **[🍴 Fork it](https://github.com/ageerle/ruoyi-ai/fork)
****[📖 中文文档](README.md)** • **[📚 Documentation](https://doc.pandarobot.chat)**
**[⭐ Star to Support](https://github.com/ageerle/ruoyi-ai)** • **[Fork to Contribute](https://github.com/ageerle/ruoyi-ai/fork)** • **[📚 中文](README.md)** • **[📖 Complete Documentation](https://doc.pandarobot.chat)**
*Built with ❤️ by the RuoYi AI community*
*Built with ❤️, maintained by the RuoYi AI open-source community*
</div>
@@ -193,8 +170,4 @@ Special thanks to these amazing open source projects:
[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

View File

@@ -1,222 +0,0 @@
## RuoYi-AI 后端部署教程Docker 部署版)
### 一、前置条件
在部署前,请确保系统已满足以下条件:
#### ✅ 系统环境要求
- 操作系统Linux / MacOS推荐 Linux 服务器)
- CPU4 核以上
- 内存:≥ 4GB
- 磁盘空间:≥ 10GB建议 20GB+
#### ✅ 已安装软件
- **Docker**
- **Docker Compose**
验证命令是否可用:
```
docker -v
docker compose version
```
若无输出或提示“command not found”请先安装 Docker 及 Compose。
------
### 二、目录结构配置
#### 1⃣ 创建部署目录
在目标服务器执行以下命令:
```
# 第一级目录
mkdir /ruoyi-ai
cd /ruoyi-ai
# 第二级目录
mkdir deploy
cd deploy
# 第三级目录
mkdir data mysql-init
# 第四级目录
mkdir logs minio minio-config mysql redis weaviate
```
> 💡 `data` 目录用于挂载容器运行期间生成的数据文件。
最终目录结构示例:
```
/ruoyi-ai
└── deploy
├── data/
├── mysql-init/
├── logs/
├── minio/
├── minio-config/
├── mysql/
├── redis/
├── weaviate/
```
------
### 三、上传配置文件
将以下配置文件上传到 `/ruoyi-ai/deploy` 目录:
- `docker-compose.yaml`
- `.env`
- `ruoyi-ai.sql`
- `Dockerfile`
> 📂 这些文件在项目目录 `/script/deploy/deploy` 下。
> 上传后请检查文件路径是否与上方目录结构一致。
------
### 四、构建 Jar 包
1. 打开 IDEA 或其他构建工具
2. 选择 **Maven 构建配置**,勾选 `prod` 环境,取消 `dev` 环境
3. 点击 `package` 进行打包
4. **注意:** 在构建前请将 `application-prod.yml` 拖入
`ruoyi-admin/src/main/resources` 目录中
构建完成后会在:
```
ruoyi-admin/target/ruoyi-admin.jar
```
生成打包文件。
------
### 五、上传 Jar 包至服务器
将生成的 `ruoyi-admin.jar` 上传到服务器 `/ruoyi-ai/deploy` 目录下。
确保与 `Dockerfile` 同目录。
------
### 六、构建 Docker 镜像
`Dockerfile` 内容如下:
```
FROM openjdk:17-jdk
RUN mkdir -p /ruoyi/server/logs \
/ruoyi/server/temp
WORKDIR /ruoyi/server
COPY ruoyi-admin.jar ruoyi-admin.jar
ENTRYPOINT ["java","-jar","ruoyi-admin.jar"]
```
`/ruoyi-ai/deploy` 目录执行以下命令:
```
# 构建镜像
docker build -t ruoyi-ai-backend:v20251013 .
# 查看镜像是否构建成功
docker image ls
```
然后在 `docker-compose.yaml` 文件中,将对应服务的镜像名修改为:
```
image: ruoyi-ai-backend:v20251013
```
------
### 七、启动容器服务
在启动前请确认:
- `.env` 中端口号、数据库密码、环境变量已正确配置
- `docker-compose.yaml` 中 MySQL 的端口已开放(用于导入数据)
如示例:
```
ports:
- "3306:3306"
```
#### 启动命令:
```
cd /ruoyi-ai/deploy
docker compose up -d
```
#### 查看运行状态:
```
docker compose ps
```
#### 查看日志:
```
docker logs -f <容器名称>
```
> ⚠️ 初次启动时可仅运行 `ruoyi-admin`(后端)模块,将前端 `ruoyi-web` 服务暂时注释,确认后端服务正常后再启用前端容器。
------
### 八、数据库初始化
启动 MySQL 容器后,执行以下操作:
```
docker exec -it <mysql_container_name> bash
mysql -uroot -p
source /docker-entrypoint-initdb.d/ruoyi-ai.sql;
```
或手动在客户端中导入 `/ruoyi-ai/deploy/ruoyi-ai.sql` 文件。
------
### 九、常用 Docker 命令
| 功能 | 命令 |
|-----------|-----------------------------------|
| 查看容器状态 | `docker ps -a` |
| 查看日志 | `docker logs -f <容器名>` |
| 停止服务 | `docker compose down` |
| 重启服务 | `docker compose restart` |
| 重新构建镜像 | `docker compose build --no-cache` |
| 清理无用镜像/容器 | `docker system prune -a` |
------
### 🔍 十、部署验证
1. 检查容器是否全部启动成功:
```
docker compose ps
```
2. 访问后端接口:
```
http://<服务器IP>:<后端端口>
```
3. 检查日志输出无异常。

BIN
docs/image/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

BIN
docs/image/qq.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 193 KiB

BIN
docs/image/wx.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 151 KiB

View File

View File

View File

@@ -0,0 +1,161 @@
worker_processes 1;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
# 可以根据业务并发量适当调高
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
# 高效传输文件
sendfile on;
# 长连接超时时间
keepalive_timeout 65;
# 单连接最大请求数,提高长连接复用率
keepalive_requests 100000;
# 限制body大小
client_max_body_size 100m;
client_header_buffer_size 32k;
client_body_buffer_size 512k;
# 开启静态资源压缩
gzip_static on;
# 连接数限制 (防御类配置) 10m 一般够用了,能存储上万 IP 的计数
limit_conn_zone $binary_remote_addr zone=perip:10m;
limit_conn_zone $server_name zone=perserver:10m;
# 隐藏 nginx 版本号,防止暴露版本信息
server_tokens off;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
upstream server {
ip_hash;
server 127.0.0.1:8080;
server 127.0.0.1:8081;
}
upstream monitor-admin {
server 127.0.0.1:9090;
}
upstream snailjob-server {
server 127.0.0.1:8800;
}
server {
listen 80;
server_name localhost;
# https配置参考 start
#listen 443 ssl;
# 证书直接存放 /docker/nginx/cert/ 目录下即可 更改证书名称即可 无需更改证书路径
#ssl on;
#ssl_certificate /etc/nginx/cert/xxx.local.crt; # /etc/nginx/cert/ 为docker映射路径 不允许更改
#ssl_certificate_key /etc/nginx/cert/xxx.local.key; # /etc/nginx/cert/ 为docker映射路径 不允许更改
#ssl_session_timeout 5m;
#ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;
#ssl_protocols TLSv1.3 TLSv1.2 TLSv1.1 TLSv1;
#ssl_prefer_server_ciphers on;
# https配置参考 end
# 演示环境配置 拦截除 GET POST 之外的所有请求
# if ($request_method !~* GET|POST) {
# rewrite ^/(.*)$ /403;
# }
# location = /403 {
# default_type application/json;
# return 200 '{"msg":"演示模式,不允许操作","code":500}';
# }
# 限制外网访问内网 actuator 相关路径
location ~ ^(/[^/]*)?/actuator.*(/.*)?$ {
return 403;
}
location / {
root /usr/share/nginx/html; # docker映射路径 不允许更改
try_files $uri $uri/ /index.html;
index index.html index.htm;
}
location /prod-api/ {
# 设置客户端请求头中的 Host 信息(保持原始 Host
proxy_set_header Host $http_host;
# 获取客户端真实 IP
proxy_set_header X-Real-IP $remote_addr;
# 自定义头 REMOTE-HOST记录客户端 IP
proxy_set_header REMOTE-HOST $remote_addr;
# 获取完整的客户端 IP 链(经过多级代理时)
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# 设置后端响应超时时间(这里是 24 小时,适合长连接/SSE
proxy_read_timeout 86400s;
# SSE (Server-Sent Events) 与 WebSocket 支持参数
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
# 禁用代理缓冲,数据直接传给客户端
proxy_buffering off;
# 禁用代理缓存
proxy_cache off;
# 按 IP 限制连接数(防 CC 攻击) 小型站10~20 就够 中型站50~100
limit_conn perip 20;
# 按 Server 限制总并发连接数 根据服务器的最大并发处理能力来定 太小会限制合法用户访问,太大会占满服务器资源
limit_conn perserver 500;
proxy_pass http://server/;
}
# https 会拦截内链所有的 http 请求 造成功能无法使用
# 解决方案1 将 admin 服务 也配置成 https
# 解决方案2 将菜单配置为外链访问 走独立页面 http 访问
location /admin/ {
# 设置客户端请求头中的 Host 信息(保持原始 Host
proxy_set_header Host $http_host;
# 获取客户端真实 IP
proxy_set_header X-Real-IP $remote_addr;
# 自定义头 REMOTE-HOST记录客户端 IP
proxy_set_header REMOTE-HOST $remote_addr;
# 获取完整的客户端 IP 链(经过多级代理时)
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# 禁用代理缓冲,数据直接传给客户端
proxy_buffering off;
# 禁用代理缓存
proxy_cache off;
proxy_pass http://monitor-admin/admin/;
}
location /snail-job/ {
# 设置客户端请求头中的 Host 信息(保持原始 Host
proxy_set_header Host $http_host;
# 获取客户端真实 IP
proxy_set_header X-Real-IP $remote_addr;
# 自定义头 REMOTE-HOST记录客户端 IP
proxy_set_header REMOTE-HOST $remote_addr;
# 获取完整的客户端 IP 链(经过多级代理时)
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# SSE (Server-Sent Events) 与 WebSocket 支持参数
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
# 禁用代理缓冲,直接传输给客户端
proxy_buffering off;
# 禁用代理缓存
proxy_cache off;
proxy_pass http://snailjob-server/snail-job/;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
}

View File

@@ -0,0 +1,28 @@
# redis 密码
requirepass ruoyi123
# key 监听器配置
# notify-keyspace-events Ex
# 配置持久化文件存储路径
dir /redis/data
# 配置rdb
# 15分钟内有至少1个key被更改则进行快照
save 900 1
# 5分钟内有至少10个key被更改则进行快照
save 300 10
# 1分钟内有至少10000个key被更改则进行快照
save 60 10000
# 开启压缩
rdbcompression yes
# rdb文件名 用默认的即可
dbfilename dump.rdb
# 开启aof
appendonly yes
# 文件名
appendfilename "appendonly.aof"
# 持久化策略,no:不同步,everysec:每秒一次,always:总是同步,速度比较慢
# appendfsync always
appendfsync everysec
# appendfsync no

View File

@@ -0,0 +1 @@
数据目录 请执行 `chmod 777 /docker/redis/data` 赋予读写权限 否则将无法写入数据

View File

@@ -0,0 +1,129 @@
{
"nodeList": [
{
"nodeType": "0",
"nodeCode": "d5ee3ddf-3968-4379-a86f-9ceabde5faac",
"nodeName": "开始",
"permissionFlag": null,
"nodeRatio": "0.000",
"anyNodeSkip": null,
"listenerType": null,
"listenerPath": null,
"formCustom": "N",
"formPath": null,
"ext": "[]",
"coordinate": "200,200|200,200",
"version": "1",
"skipList": [
{
"skipType": "PASS",
"skipCondition": null,
"skipName": null,
"nowNodeCode": "d5ee3ddf-3968-4379-a86f-9ceabde5faac",
"nextNodeCode": "dd515cdd-59f6-446f-94ca-25ca062afb42",
"coordinate": "220,200;310,200"
}
]
},
{
"nodeType": "1",
"nodeCode": "dd515cdd-59f6-446f-94ca-25ca062afb42",
"nodeName": "申请人",
"permissionFlag": "",
"nodeRatio": "0.000",
"anyNodeSkip": null,
"listenerType": "",
"listenerPath": "",
"formCustom": "N",
"formPath": null,
"ext": "[{\"code\":\"ButtonPermissionEnum\",\"value\":\"back,termination,file,copy\"}]",
"coordinate": "360,200|360,200",
"version": "1",
"skipList": [
{
"skipType": "PASS",
"skipCondition": null,
"skipName": null,
"nowNodeCode": "dd515cdd-59f6-446f-94ca-25ca062afb42",
"nextNodeCode": "78fa8e5b-e809-44ed-978a-41092409ebcf",
"coordinate": "410,200;490,200"
}
]
},
{
"nodeType": "1",
"nodeCode": "78fa8e5b-e809-44ed-978a-41092409ebcf",
"nodeName": "组长",
"permissionFlag": "role:1",
"nodeRatio": "0.000",
"anyNodeSkip": null,
"listenerType": "",
"listenerPath": "",
"formCustom": "N",
"formPath": null,
"ext": "[{\"code\":\"ButtonPermissionEnum\",\"value\":\"back,termination,copy,transfer,trust,file\"}]",
"coordinate": "540,200|540,200",
"version": "1",
"skipList": [
{
"skipType": "PASS",
"skipCondition": null,
"skipName": null,
"nowNodeCode": "78fa8e5b-e809-44ed-978a-41092409ebcf",
"nextNodeCode": "a8abf15f-b83e-428a-86cc-033555ea9bbe",
"coordinate": "590,200;670,200"
}
]
},
{
"nodeType": "1",
"nodeCode": "a8abf15f-b83e-428a-86cc-033555ea9bbe",
"nodeName": "部门主管",
"permissionFlag": "role:3@@role:4",
"nodeRatio": "0.000",
"anyNodeSkip": null,
"listenerType": "",
"listenerPath": "",
"formCustom": "N",
"formPath": null,
"ext": "[{\"code\":\"ButtonPermissionEnum\",\"value\":\"back,termination,copy,transfer,trust,file\"}]",
"coordinate": "720,200|720,200",
"version": "1",
"skipList": [
{
"skipType": "PASS",
"skipCondition": null,
"skipName": null,
"nowNodeCode": "a8abf15f-b83e-428a-86cc-033555ea9bbe",
"nextNodeCode": "8b82b7d7-8660-455e-b880-d6d22ea3eb6d",
"coordinate": "770,200;880,200"
}
]
},
{
"nodeType": "2",
"nodeCode": "8b82b7d7-8660-455e-b880-d6d22ea3eb6d",
"nodeName": "结束",
"permissionFlag": null,
"nodeRatio": "0.000",
"anyNodeSkip": null,
"listenerType": null,
"listenerPath": null,
"formCustom": "N",
"formPath": null,
"ext": "[]",
"coordinate": "900,200|900,200",
"version": "1",
"skipList": []
}
],
"flowCode": "leave1",
"flowName": "请假申请-普通",
"modelValue": "CLASSICS",
"category": "103",
"version": "1",
"formCustom": "N",
"formPath": "/workflow/leaveEdit/index",
"listenerType": null,
"listenerPath": null
}

View File

@@ -0,0 +1,187 @@
{
"nodeList": [
{
"nodeType": "0",
"nodeCode": "cef3895c-f7d8-4598-8bf3-8ec2ef6ce84a",
"nodeName": "开始",
"permissionFlag": null,
"nodeRatio": "0.000",
"anyNodeSkip": null,
"listenerType": null,
"listenerPath": null,
"formCustom": "N",
"formPath": null,
"ext": "[]",
"coordinate": "300,240|300,240",
"version": "1",
"skipList": [
{
"skipType": "PASS",
"skipCondition": null,
"skipName": null,
"nowNodeCode": "cef3895c-f7d8-4598-8bf3-8ec2ef6ce84a",
"nextNodeCode": "fdcae93b-b69c-498a-b231-09255e74bcbd",
"coordinate": "320,240;390,240"
}
]
},
{
"nodeType": "1",
"nodeCode": "fdcae93b-b69c-498a-b231-09255e74bcbd",
"nodeName": "申请人",
"permissionFlag": "",
"nodeRatio": "0.000",
"anyNodeSkip": null,
"listenerType": "",
"listenerPath": "",
"formCustom": "N",
"formPath": null,
"ext": "[{\"code\":\"ButtonPermissionEnum\",\"value\":\"back,termination,file\"}]",
"coordinate": "440,240|440,240",
"version": "1",
"skipList": [
{
"skipType": "PASS",
"skipCondition": null,
"skipName": null,
"nowNodeCode": "fdcae93b-b69c-498a-b231-09255e74bcbd",
"nextNodeCode": "7b8c7ead-7dc8-4951-a7f3-f0c41995909e",
"coordinate": "490,240;535,240"
}
]
},
{
"nodeType": "3",
"nodeCode": "7b8c7ead-7dc8-4951-a7f3-f0c41995909e",
"permissionFlag": null,
"nodeRatio": "0.000",
"anyNodeSkip": null,
"listenerType": null,
"listenerPath": null,
"formCustom": "N",
"formPath": null,
"ext": "[]",
"coordinate": "560,240",
"version": "1",
"skipList": [
{
"skipType": "PASS",
"skipCondition": "le@@leaveDays|2",
"skipName": null,
"nowNodeCode": "7b8c7ead-7dc8-4951-a7f3-f0c41995909e",
"nextNodeCode": "b3528155-dcb7-4445-bbdf-3d00e3499e86",
"coordinate": "560,265;560,320;670,320"
},
{
"skipType": "PASS",
"skipCondition": "gt@@leaveDays|2",
"skipName": "大于两天",
"nowNodeCode": "7b8c7ead-7dc8-4951-a7f3-f0c41995909e",
"nextNodeCode": "5ed2362b-fc0c-4d52-831f-95208b830605",
"coordinate": "560,215;560,160;670,160|560,187"
}
]
},
{
"nodeType": "1",
"nodeCode": "b3528155-dcb7-4445-bbdf-3d00e3499e86",
"nodeName": "组长",
"permissionFlag": "3@@4",
"nodeRatio": "0.000",
"anyNodeSkip": null,
"listenerType": "",
"listenerPath": "",
"formCustom": "N",
"formPath": null,
"ext": "[{\"code\":\"ButtonPermissionEnum\",\"value\":\"back,termination,file,transfer,trust,copy\"}]",
"coordinate": "720,320|720,320",
"version": "1",
"skipList": [
{
"skipType": "PASS",
"skipCondition": null,
"skipName": null,
"nowNodeCode": "b3528155-dcb7-4445-bbdf-3d00e3499e86",
"nextNodeCode": "c9fa6d7d-2a74-4e78-b947-0cad8a6af869",
"coordinate": "770,320;860,320;860,280"
}
]
},
{
"nodeType": "1",
"nodeCode": "c9fa6d7d-2a74-4e78-b947-0cad8a6af869",
"nodeName": "总经理",
"permissionFlag": "role:1",
"nodeRatio": "0.000",
"anyNodeSkip": null,
"listenerType": "",
"listenerPath": "",
"formCustom": "N",
"formPath": null,
"ext": "[{\"code\":\"ButtonPermissionEnum\",\"value\":\"back,termination,file,transfer,trust,copy\"}]",
"coordinate": "860,240|860,240",
"version": "1",
"skipList": [
{
"skipType": "PASS",
"skipCondition": null,
"skipName": null,
"nowNodeCode": "c9fa6d7d-2a74-4e78-b947-0cad8a6af869",
"nextNodeCode": "40aa65fd-0712-4d23-b6f7-d0432b920fd1",
"coordinate": "910,240;980,240"
}
]
},
{
"nodeType": "2",
"nodeCode": "40aa65fd-0712-4d23-b6f7-d0432b920fd1",
"nodeName": "结束",
"permissionFlag": null,
"nodeRatio": "0.000",
"anyNodeSkip": null,
"listenerType": null,
"listenerPath": null,
"formCustom": "N",
"formPath": null,
"ext": "[]",
"coordinate": "1000,240|1000,240",
"version": "1",
"skipList": []
},
{
"nodeType": "1",
"nodeCode": "5ed2362b-fc0c-4d52-831f-95208b830605",
"nodeName": "部门领导",
"permissionFlag": "role:1",
"nodeRatio": "0.000",
"anyNodeSkip": null,
"listenerType": "",
"listenerPath": "",
"formCustom": "N",
"formPath": null,
"ext": "[{\"code\":\"ButtonPermissionEnum\",\"value\":\"back,termination,file,transfer,trust,copy\"}]",
"coordinate": "720,160|720,160",
"version": "1",
"skipList": [
{
"skipType": "PASS",
"skipCondition": null,
"skipName": null,
"nowNodeCode": "5ed2362b-fc0c-4d52-831f-95208b830605",
"nextNodeCode": "c9fa6d7d-2a74-4e78-b947-0cad8a6af869",
"nextNodeType": "1",
"coordinate": "770,160;860,160;860,200"
}
]
}
],
"flowCode": "leave2",
"flowName": "请假申请-排他网关",
"modelValue": "CLASSICS",
"category": "103",
"version": "1",
"formCustom": "N",
"formPath": "/workflow/leaveEdit/index",
"listenerType": null,
"listenerPath": null
}

View File

@@ -0,0 +1,211 @@
{
"nodeList": [
{
"nodeType": "0",
"nodeCode": "a80ecf9f-f465-4ae5-a429-e30ec5d0f957",
"nodeName": "开始",
"permissionFlag": null,
"nodeRatio": "0.000",
"anyNodeSkip": null,
"listenerType": null,
"listenerPath": null,
"formCustom": "N",
"formPath": null,
"ext": "[]",
"coordinate": "380,220|380,220",
"version": "1",
"skipList": [
{
"skipType": "PASS",
"skipCondition": null,
"skipName": null,
"nowNodeCode": "a80ecf9f-f465-4ae5-a429-e30ec5d0f957",
"nextNodeCode": "b7bbb571-06de-455c-8083-f83c07bf0b99",
"coordinate": "400,220;470,220"
}
]
},
{
"nodeType": "1",
"nodeCode": "b7bbb571-06de-455c-8083-f83c07bf0b99",
"nodeName": "申请人",
"permissionFlag": "",
"nodeRatio": "0.000",
"anyNodeSkip": null,
"listenerType": "",
"listenerPath": "",
"formCustom": "N",
"formPath": null,
"ext": "[{\"code\":\"ButtonPermissionEnum\",\"value\":\"back,termination,file\"}]",
"coordinate": "520,220|520,220",
"version": "1",
"skipList": [
{
"skipType": "PASS",
"skipCondition": null,
"skipName": null,
"nowNodeCode": "b7bbb571-06de-455c-8083-f83c07bf0b99",
"nextNodeCode": "84d7ed24-bb44-4ba1-bf1f-e6f5092d3f0a",
"coordinate": "570,220;655,220"
}
]
},
{
"nodeType": "4",
"nodeCode": "84d7ed24-bb44-4ba1-bf1f-e6f5092d3f0a",
"permissionFlag": null,
"nodeRatio": "0.000",
"anyNodeSkip": null,
"listenerType": null,
"listenerPath": null,
"formCustom": "N",
"formPath": null,
"ext": "[]",
"coordinate": "680,220",
"version": "1",
"skipList": [
{
"skipType": "PASS",
"skipCondition": null,
"skipName": null,
"nowNodeCode": "84d7ed24-bb44-4ba1-bf1f-e6f5092d3f0a",
"nextNodeCode": "4b7743cd-940c-431b-926f-e7b614fbf1fe",
"coordinate": "680,195;680,140;750,140"
},
{
"skipType": "PASS",
"skipCondition": null,
"skipName": null,
"nowNodeCode": "84d7ed24-bb44-4ba1-bf1f-e6f5092d3f0a",
"nextNodeCode": "762cb975-37d8-4276-b6db-79a4c3606394",
"coordinate": "680,245;680,300;750,300"
}
]
},
{
"nodeType": "1",
"nodeCode": "4b7743cd-940c-431b-926f-e7b614fbf1fe",
"nodeName": "市场部",
"permissionFlag": "role:1",
"nodeRatio": "0.000",
"anyNodeSkip": null,
"listenerType": "",
"listenerPath": "",
"formCustom": "N",
"formPath": null,
"ext": "[{\"code\":\"ButtonPermissionEnum\",\"value\":\"back,termination,file,transfer,trust,copy\"}]",
"coordinate": "800,140|800,140",
"version": "1",
"skipList": [
{
"skipType": "PASS",
"skipCondition": null,
"skipName": null,
"nowNodeCode": "4b7743cd-940c-431b-926f-e7b614fbf1fe",
"nextNodeCode": "b66b6563-f9fe-41cc-a782-f7837bb6f3d2",
"coordinate": "850,140;920,140;920,195"
}
]
},
{
"nodeType": "4",
"nodeCode": "b66b6563-f9fe-41cc-a782-f7837bb6f3d2",
"permissionFlag": null,
"nodeRatio": "0.000",
"anyNodeSkip": null,
"listenerType": null,
"listenerPath": null,
"formCustom": "N",
"formPath": null,
"ext": "[]",
"coordinate": "920,220",
"version": "1",
"skipList": [
{
"skipType": "PASS",
"skipCondition": null,
"skipName": null,
"nowNodeCode": "b66b6563-f9fe-41cc-a782-f7837bb6f3d2",
"nextNodeCode": "23e7429e-2b47-4431-b93e-40db7c431ce6",
"coordinate": "945,220;975,220;975,220;960,220;960,220;990,220"
}
]
},
{
"nodeType": "1",
"nodeCode": "23e7429e-2b47-4431-b93e-40db7c431ce6",
"nodeName": "CEO",
"permissionFlag": "1",
"nodeRatio": "0.000",
"anyNodeSkip": null,
"listenerType": "",
"listenerPath": "",
"formCustom": "N",
"formPath": null,
"ext": "[{\"code\":\"ButtonPermissionEnum\",\"value\":\"back,termination,file,transfer,trust,copy\"}]",
"coordinate": "1040,220|1040,220",
"version": "1",
"skipList": [
{
"skipType": "PASS",
"skipCondition": null,
"skipName": null,
"nowNodeCode": "23e7429e-2b47-4431-b93e-40db7c431ce6",
"nextNodeCode": "f5ace37f-5a5e-4e64-a6f6-913ab9a71cd1",
"coordinate": "1090,220;1140,220"
}
]
},
{
"nodeType": "2",
"nodeCode": "f5ace37f-5a5e-4e64-a6f6-913ab9a71cd1",
"nodeName": "结束",
"permissionFlag": null,
"nodeRatio": "0.000",
"anyNodeSkip": null,
"listenerType": null,
"listenerPath": null,
"formCustom": "N",
"formPath": null,
"ext": "[]",
"coordinate": "1160,220|1160,220",
"version": "1",
"skipList": []
},
{
"nodeType": "1",
"nodeCode": "762cb975-37d8-4276-b6db-79a4c3606394",
"nodeName": "综合部",
"permissionFlag": "role:3@@role:4",
"nodeRatio": "0.000",
"anyNodeSkip": null,
"listenerType": "",
"listenerPath": "",
"formCustom": "N",
"formPath": null,
"ext": "[{\"code\":\"ButtonPermissionEnum\",\"value\":\"back,termination,file,transfer,trust,copy\"}]",
"coordinate": "800,300|800,300",
"version": "1",
"skipList": [
{
"skipType": "PASS",
"skipCondition": null,
"skipName": null,
"nowNodeCode": "762cb975-37d8-4276-b6db-79a4c3606394",
"nextNodeCode": "b66b6563-f9fe-41cc-a782-f7837bb6f3d2",
"nextNodeType": "4",
"coordinate": "850,300;920,300;920,245"
}
]
}
],
"flowCode": "leave3",
"flowName": "请假申请-并行网关",
"modelValue": "CLASSICS",
"category": "103",
"version": "1",
"formCustom": "N",
"formPath": "/workflow/leaveEdit/index",
"listenerType": null,
"listenerPath": null
}

View File

@@ -0,0 +1,154 @@
{
"nodeList": [
{
"nodeType": "0",
"nodeCode": "9ce8bf00-f25b-4fc6-91b8-827082fc4876",
"nodeName": "开始",
"permissionFlag": null,
"nodeRatio": "0.000",
"anyNodeSkip": null,
"listenerType": null,
"listenerPath": null,
"formCustom": "N",
"formPath": null,
"ext": "[]",
"coordinate": "320,240|320,240",
"version": "1",
"skipList": [
{
"skipType": "PASS",
"skipCondition": null,
"skipName": null,
"nowNodeCode": "9ce8bf00-f25b-4fc6-91b8-827082fc4876",
"nextNodeCode": "e90b98ef-35b4-410c-a663-bae8b7624b9f",
"coordinate": "340,240;410,240"
}
]
},
{
"nodeType": "1",
"nodeCode": "e90b98ef-35b4-410c-a663-bae8b7624b9f",
"nodeName": "申请人",
"permissionFlag": "",
"nodeRatio": "0.000",
"anyNodeSkip": null,
"listenerType": "",
"listenerPath": "",
"formCustom": "N",
"formPath": null,
"ext": "[{\"code\":\"ButtonPermissionEnum\",\"value\":\"back,termination,file\"}]",
"coordinate": "460,240|460,240",
"version": "1",
"skipList": [
{
"skipType": "PASS",
"skipCondition": null,
"skipName": null,
"nowNodeCode": "e90b98ef-35b4-410c-a663-bae8b7624b9f",
"nextNodeCode": "768b5b1a-6726-4d67-8853-4cc70d5b1045",
"coordinate": "510,240;590,240"
}
]
},
{
"nodeType": "1",
"nodeCode": "768b5b1a-6726-4d67-8853-4cc70d5b1045",
"nodeName": "百分之60通过",
"permissionFlag": "${userList}",
"nodeRatio": "60.000",
"anyNodeSkip": null,
"listenerType": "",
"listenerPath": "",
"formCustom": "N",
"formPath": null,
"ext": "[{\"code\":\"ButtonPermissionEnum\",\"value\":\"back,termination,file,addSign,subSign\"}]",
"coordinate": "640,240|640,240",
"version": "1",
"skipList": [
{
"skipType": "PASS",
"skipCondition": null,
"skipName": null,
"nowNodeCode": "768b5b1a-6726-4d67-8853-4cc70d5b1045",
"nextNodeCode": "2f9f2e21-9bcf-42a3-a07c-13037aad22d1",
"coordinate": "690,240;770,240"
}
]
},
{
"nodeType": "1",
"nodeCode": "2f9f2e21-9bcf-42a3-a07c-13037aad22d1",
"nodeName": "全部审批通过",
"permissionFlag": "role:1@@role:3",
"nodeRatio": "100.000",
"anyNodeSkip": null,
"listenerType": "",
"listenerPath": "",
"formCustom": "N",
"formPath": null,
"ext": "[{\"code\":\"ButtonPermissionEnum\",\"value\":\"back,termination,file,addSign,subSign\"}]",
"coordinate": "820,240|820,240",
"version": "1",
"skipList": [
{
"skipType": "PASS",
"skipCondition": null,
"skipName": null,
"nowNodeCode": "2f9f2e21-9bcf-42a3-a07c-13037aad22d1",
"nextNodeCode": "27461e01-3d9f-4530-8fe3-bd5ec7f9571f",
"coordinate": "870,240;950,240"
}
]
},
{
"nodeType": "1",
"nodeCode": "27461e01-3d9f-4530-8fe3-bd5ec7f9571f",
"nodeName": "CEO",
"permissionFlag": "1",
"nodeRatio": "0.000",
"anyNodeSkip": null,
"listenerType": "",
"listenerPath": "",
"formCustom": "N",
"formPath": null,
"ext": "[{\"code\":\"ButtonPermissionEnum\",\"value\":\"back,termination,file,transfer,trust,copy\"}]",
"coordinate": "1000,240|1000,240",
"version": "1",
"skipList": [
{
"skipType": "PASS",
"skipCondition": null,
"skipName": null,
"nowNodeCode": "27461e01-3d9f-4530-8fe3-bd5ec7f9571f",
"nextNodeCode": "b62b88c3-8d8d-4969-911e-2aaea219e7fc",
"coordinate": "1050,240;1080,240;1080,240;1070,240;1070,240;1100,240"
}
]
},
{
"nodeType": "2",
"nodeCode": "b62b88c3-8d8d-4969-911e-2aaea219e7fc",
"nodeName": "结束",
"permissionFlag": null,
"nodeRatio": "0.000",
"anyNodeSkip": null,
"listenerType": null,
"listenerPath": null,
"formCustom": "N",
"formPath": null,
"ext": "[]",
"coordinate": "1120,240|1120,240",
"version": "1",
"skipList": []
}
],
"flowCode": "leave4",
"flowName": "请假申请-会签",
"modelValue": "CLASSICS",
"category": "103",
"version": "1",
"formCustom": "N",
"formPath": "/workflow/leaveEdit/index",
"listenerType": null,
"listenerPath": null
}

View File

@@ -0,0 +1,211 @@
{
"nodeList": [
{
"nodeType": "0",
"nodeCode": "ebebaf26-9cb6-497e-8119-4c9fed4c597c",
"nodeName": "开始",
"permissionFlag": null,
"nodeRatio": "0.000",
"anyNodeSkip": null,
"listenerType": null,
"listenerPath": null,
"formCustom": "N",
"formPath": null,
"ext": "[]",
"coordinate": "300,220|300,220",
"version": "1",
"skipList": [
{
"skipType": "PASS",
"skipCondition": null,
"skipName": null,
"nowNodeCode": "ebebaf26-9cb6-497e-8119-4c9fed4c597c",
"nextNodeCode": "e1b04e96-dc81-4858-a309-2fe945d2f374",
"coordinate": "320,220;350,220;350,220;340,220;340,220;370,220"
}
]
},
{
"nodeType": "1",
"nodeCode": "e1b04e96-dc81-4858-a309-2fe945d2f374",
"nodeName": "申请人",
"permissionFlag": "",
"nodeRatio": "0.000",
"anyNodeSkip": null,
"listenerType": "",
"listenerPath": "",
"formCustom": "N",
"formPath": null,
"ext": "[{\"code\":\"ButtonPermissionEnum\",\"value\":\"back,termination,file\"}]",
"coordinate": "420,220|420,220",
"version": "1",
"skipList": [
{
"skipType": "PASS",
"skipCondition": null,
"skipName": null,
"nowNodeCode": "e1b04e96-dc81-4858-a309-2fe945d2f374",
"nextNodeCode": "3e743f4f-51ca-41d4-8e94-21f5dd9b59c9",
"coordinate": "470,220;535,220"
}
]
},
{
"nodeType": "4",
"nodeCode": "3e743f4f-51ca-41d4-8e94-21f5dd9b59c9",
"permissionFlag": null,
"nodeRatio": "0.000",
"anyNodeSkip": null,
"listenerType": null,
"listenerPath": null,
"formCustom": "N",
"formPath": null,
"ext": "[]",
"coordinate": "560,220",
"version": "1",
"skipList": [
{
"skipType": "PASS",
"skipCondition": null,
"skipName": null,
"nowNodeCode": "3e743f4f-51ca-41d4-8e94-21f5dd9b59c9",
"nextNodeCode": "c80f273e-1f17-4bd8-9ad1-04a4a94ea862",
"coordinate": "560,245;560,320;650,320"
},
{
"skipType": "PASS",
"skipCondition": null,
"skipName": null,
"nowNodeCode": "3e743f4f-51ca-41d4-8e94-21f5dd9b59c9",
"nextNodeCode": "1e3e8d3b-18ae-4d6c-a814-ce0d724adfa4",
"coordinate": "560,195;560,120;650,120"
}
]
},
{
"nodeType": "1",
"nodeCode": "c80f273e-1f17-4bd8-9ad1-04a4a94ea862",
"nodeName": "会签",
"permissionFlag": "role:1@@role:3",
"nodeRatio": "100.000",
"anyNodeSkip": null,
"listenerType": "",
"listenerPath": "",
"formCustom": "N",
"formPath": null,
"ext": "[{\"code\":\"ButtonPermissionEnum\",\"value\":\"back,termination,file,addSign,subSign\"}]",
"coordinate": "700,320|700,320",
"version": "1",
"skipList": [
{
"skipType": "PASS",
"skipCondition": null,
"skipName": null,
"nowNodeCode": "c80f273e-1f17-4bd8-9ad1-04a4a94ea862",
"nextNodeCode": "1a20169e-3d82-4926-a151-e2daad28de1b",
"coordinate": "750,320;860,320;860,245"
}
]
},
{
"nodeType": "4",
"nodeCode": "1a20169e-3d82-4926-a151-e2daad28de1b",
"permissionFlag": null,
"nodeRatio": "0.000",
"anyNodeSkip": null,
"listenerType": null,
"listenerPath": null,
"formCustom": "N",
"formPath": null,
"ext": "[]",
"coordinate": "860,220",
"version": "1",
"skipList": [
{
"skipType": "PASS",
"skipCondition": null,
"skipName": null,
"nowNodeCode": "1a20169e-3d82-4926-a151-e2daad28de1b",
"nextNodeCode": "7a8f0473-e409-442e-a843-5c2b813d00e9",
"coordinate": "885,220;950,220"
}
]
},
{
"nodeType": "1",
"nodeCode": "7a8f0473-e409-442e-a843-5c2b813d00e9",
"nodeName": "CEO",
"permissionFlag": "1",
"nodeRatio": "0.000",
"anyNodeSkip": null,
"listenerType": "",
"listenerPath": "",
"formCustom": "N",
"formPath": null,
"ext": "[{\"code\":\"ButtonPermissionEnum\",\"value\":\"back,termination,file,transfer,trust,copy\"}]",
"coordinate": "1000,220|1000,220",
"version": "1",
"skipList": [
{
"skipType": "PASS",
"skipCondition": null,
"skipName": null,
"nowNodeCode": "7a8f0473-e409-442e-a843-5c2b813d00e9",
"nextNodeCode": "03c4d2bc-58b5-4408-a2e4-65afb046f169",
"coordinate": "1050,220;1120,220"
}
]
},
{
"nodeType": "2",
"nodeCode": "03c4d2bc-58b5-4408-a2e4-65afb046f169",
"nodeName": "结束",
"permissionFlag": null,
"nodeRatio": "0.000",
"anyNodeSkip": null,
"listenerType": null,
"listenerPath": null,
"formCustom": "N",
"formPath": null,
"ext": "[]",
"coordinate": "1140,220|1140,220",
"version": "1",
"skipList": []
},
{
"nodeType": "1",
"nodeCode": "1e3e8d3b-18ae-4d6c-a814-ce0d724adfa4",
"nodeName": "百分之60票签",
"permissionFlag": "${userList}",
"nodeRatio": "60.000",
"anyNodeSkip": null,
"listenerType": "",
"listenerPath": "",
"formCustom": "N",
"formPath": null,
"ext": "[{\"code\":\"ButtonPermissionEnum\",\"value\":\"back,termination,file,addSign,subSign\"}]",
"coordinate": "700,120|700,120",
"version": "1",
"skipList": [
{
"skipType": "PASS",
"skipCondition": null,
"skipName": null,
"nowNodeCode": "1e3e8d3b-18ae-4d6c-a814-ce0d724adfa4",
"nextNodeCode": "1a20169e-3d82-4926-a151-e2daad28de1b",
"nextNodeType": "4",
"coordinate": "750,120;860,120;860,195"
}
]
}
],
"flowCode": "leave5",
"flowName": "请假申请-并行会签网关",
"modelValue": "CLASSICS",
"category": "103",
"version": "1",
"formCustom": "N",
"formPath": "/workflow/leaveEdit/index",
"listenerType": null,
"listenerPath": null
}

View File

@@ -0,0 +1,368 @@
{
"nodeList": [
{
"nodeType": "0",
"nodeCode": "122b89a5-7c6f-40a3-aa09-7a263f902054",
"nodeName": "开始",
"permissionFlag": null,
"nodeRatio": "0.000",
"anyNodeSkip": null,
"listenerType": null,
"listenerPath": null,
"formCustom": "N",
"formPath": null,
"ext": "[]",
"coordinate": "240,300|240,300",
"version": "1",
"skipList": [
{
"skipType": "PASS",
"skipCondition": null,
"skipName": null,
"nowNodeCode": "122b89a5-7c6f-40a3-aa09-7a263f902054",
"nextNodeCode": "c25a0e86-fdd1-4f03-8e22-14db70389dbd",
"coordinate": "260,300;350,300"
}
]
},
{
"nodeType": "1",
"nodeCode": "c25a0e86-fdd1-4f03-8e22-14db70389dbd",
"nodeName": "申请人",
"permissionFlag": "",
"nodeRatio": "0.000",
"anyNodeSkip": null,
"listenerType": "",
"listenerPath": "",
"formCustom": "N",
"formPath": null,
"ext": "[{\"code\":\"ButtonPermissionEnum\",\"value\":\"back,termination,file\"}]",
"coordinate": "400,300|400,300",
"version": "1",
"skipList": [
{
"skipType": "PASS",
"skipCondition": null,
"skipName": null,
"nowNodeCode": "c25a0e86-fdd1-4f03-8e22-14db70389dbd",
"nextNodeCode": "07ecda1d-7a0a-47b5-8a91-6186c9473742",
"coordinate": "450,300;510,300"
}
]
},
{
"nodeType": "1",
"nodeCode": "2bfa3919-78cf-4bc1-b59b-df463a4546f9",
"nodeName": "副经理",
"permissionFlag": "role:1@@role:3@@role:4",
"nodeRatio": "0.000",
"anyNodeSkip": null,
"listenerType": "",
"listenerPath": "",
"formCustom": "N",
"formPath": null,
"ext": "[{\"code\":\"ButtonPermissionEnum\",\"value\":\"back,termination\"}]",
"coordinate": "860,200|860,200",
"version": "1",
"skipList": [
{
"skipType": "PASS",
"skipCondition": null,
"skipName": null,
"nowNodeCode": "2bfa3919-78cf-4bc1-b59b-df463a4546f9",
"nextNodeCode": "394e1cc8-b8b2-4189-9f81-44448e88ac32",
"coordinate": "910,200;1000,200;1000,275"
}
]
},
{
"nodeType": "1",
"nodeCode": "ec17f60e-94e0-4d96-a3ce-3417e9d32d60",
"nodeName": "组长",
"permissionFlag": "1",
"nodeRatio": "0.000",
"anyNodeSkip": null,
"listenerType": "",
"listenerPath": "",
"formCustom": "N",
"formPath": null,
"ext": "[{\"code\":\"ButtonPermissionEnum\",\"value\":\"back,termination\"}]",
"coordinate": "860,400|860,400",
"version": "1",
"skipList": [
{
"skipType": "PASS",
"skipCondition": null,
"skipName": null,
"nowNodeCode": "ec17f60e-94e0-4d96-a3ce-3417e9d32d60",
"nextNodeCode": "394e1cc8-b8b2-4189-9f81-44448e88ac32",
"coordinate": "910,400;1000,400;1000,325"
}
]
},
{
"nodeType": "1",
"nodeCode": "07ecda1d-7a0a-47b5-8a91-6186c9473742",
"nodeName": "副组长",
"permissionFlag": "1",
"nodeRatio": "0.000",
"anyNodeSkip": null,
"listenerType": "",
"listenerPath": "",
"formCustom": "N",
"formPath": null,
"ext": "[{\"code\":\"ButtonPermissionEnum\",\"value\":\"back,termination,transfer,copy,pop\"}]",
"coordinate": "560,300|560,300",
"version": "1",
"skipList": [
{
"skipType": "PASS",
"skipCondition": null,
"skipName": null,
"nowNodeCode": "07ecda1d-7a0a-47b5-8a91-6186c9473742",
"nextNodeCode": "48117e2c-6328-406b-b102-c4a9d115bb13",
"coordinate": "610,300;675,300"
}
]
},
{
"nodeType": "3",
"nodeCode": "48117e2c-6328-406b-b102-c4a9d115bb13",
"permissionFlag": null,
"nodeRatio": "0.000",
"anyNodeSkip": null,
"listenerType": null,
"listenerPath": null,
"formCustom": "N",
"formPath": null,
"ext": "[]",
"coordinate": "700,300",
"version": "1",
"skipList": [
{
"skipType": "PASS",
"skipCondition": "default@@${leaveDays > 2}",
"skipName": "大于两天",
"nowNodeCode": "48117e2c-6328-406b-b102-c4a9d115bb13",
"nextNodeCode": "2bfa3919-78cf-4bc1-b59b-df463a4546f9",
"nextNodeType": "1",
"coordinate": "700,275;700,200;810,200|700,237"
},
{
"skipType": "PASS",
"skipCondition": "spel@@#{@testLeaveServiceImpl.eval(#leaveDays)}",
"skipName": null,
"nowNodeCode": "48117e2c-6328-406b-b102-c4a9d115bb13",
"nextNodeCode": "ec17f60e-94e0-4d96-a3ce-3417e9d32d60",
"nextNodeType": "1",
"coordinate": "700,325;700,400;810,400"
}
]
},
{
"nodeType": "3",
"nodeCode": "394e1cc8-b8b2-4189-9f81-44448e88ac32",
"permissionFlag": null,
"nodeRatio": "0.000",
"anyNodeSkip": null,
"listenerType": null,
"listenerPath": null,
"formCustom": "N",
"formPath": null,
"ext": "[]",
"coordinate": "1000,300",
"version": "1",
"skipList": [
{
"skipType": "PASS",
"skipCondition": null,
"skipName": null,
"nowNodeCode": "394e1cc8-b8b2-4189-9f81-44448e88ac32",
"nextNodeCode": "9c93a195-cff2-4e17-ab0a-a4f264191496",
"coordinate": "1025,300;1130,300"
}
]
},
{
"nodeType": "1",
"nodeCode": "9c93a195-cff2-4e17-ab0a-a4f264191496",
"nodeName": "经理会签",
"permissionFlag": "1@@3",
"nodeRatio": "100.000",
"anyNodeSkip": null,
"listenerType": "",
"listenerPath": "",
"formCustom": "N",
"formPath": null,
"ext": "[{\"code\":\"ButtonPermissionEnum\",\"value\":\"back,termination,pop,addSign,subSign\"}]",
"coordinate": "1180,300|1180,300",
"version": "1",
"skipList": [
{
"skipType": "PASS",
"skipCondition": null,
"skipName": null,
"nowNodeCode": "9c93a195-cff2-4e17-ab0a-a4f264191496",
"nextNodeCode": "a1a42056-afd1-4e90-88bc-36cbf5a66992",
"coordinate": "1230,300;1315,300"
}
]
},
{
"nodeType": "4",
"nodeCode": "a1a42056-afd1-4e90-88bc-36cbf5a66992",
"permissionFlag": null,
"nodeRatio": "0.000",
"anyNodeSkip": null,
"listenerType": null,
"listenerPath": null,
"formCustom": "N",
"formPath": null,
"ext": "[]",
"coordinate": "1340,300",
"version": "1",
"skipList": [
{
"skipType": "PASS",
"skipCondition": null,
"skipName": null,
"nowNodeCode": "a1a42056-afd1-4e90-88bc-36cbf5a66992",
"nextNodeCode": "fcfdd9f6-f526-4c1a-b71d-88afa31aebc5",
"coordinate": "1340,325;1340,400;1430,400"
},
{
"skipType": "PASS",
"skipCondition": null,
"skipName": null,
"nowNodeCode": "a1a42056-afd1-4e90-88bc-36cbf5a66992",
"nextNodeCode": "350dfa0c-a77c-4efa-8527-10efa02d8be4",
"coordinate": "1340,275;1340,200;1430,200"
}
]
},
{
"nodeType": "1",
"nodeCode": "350dfa0c-a77c-4efa-8527-10efa02d8be4",
"nodeName": "总经理",
"permissionFlag": "3@@1",
"nodeRatio": "0.000",
"anyNodeSkip": null,
"listenerType": "",
"listenerPath": "",
"formCustom": "N",
"formPath": null,
"ext": "[{\"code\":\"ButtonPermissionEnum\",\"value\":\"back,termination\"}]",
"coordinate": "1480,200|1480,200",
"version": "1",
"skipList": [
{
"skipType": "PASS",
"skipCondition": null,
"skipName": null,
"nowNodeCode": "350dfa0c-a77c-4efa-8527-10efa02d8be4",
"nextNodeCode": "c36a46ef-04f9-463f-bad7-4b395c818519",
"coordinate": "1530,200;1640,200;1640,275"
}
]
},
{
"nodeType": "1",
"nodeCode": "fcfdd9f6-f526-4c1a-b71d-88afa31aebc5",
"nodeName": "副总经理",
"permissionFlag": "1@@3",
"nodeRatio": "0.000",
"anyNodeSkip": null,
"listenerType": "",
"listenerPath": "",
"formCustom": "N",
"formPath": null,
"ext": "[{\"code\":\"ButtonPermissionEnum\",\"value\":\"back,termination\"}]",
"coordinate": "1480,400|1480,400",
"version": "1",
"skipList": [
{
"skipType": "PASS",
"skipCondition": null,
"skipName": null,
"nowNodeCode": "fcfdd9f6-f526-4c1a-b71d-88afa31aebc5",
"nextNodeCode": "c36a46ef-04f9-463f-bad7-4b395c818519",
"coordinate": "1530,400;1640,400;1640,325"
}
]
},
{
"nodeType": "4",
"nodeCode": "c36a46ef-04f9-463f-bad7-4b395c818519",
"permissionFlag": null,
"nodeRatio": "0.000",
"anyNodeSkip": null,
"listenerType": null,
"listenerPath": null,
"formCustom": "N",
"formPath": null,
"ext": "[]",
"coordinate": "1640,300",
"version": "1",
"skipList": [
{
"skipType": "PASS",
"skipCondition": null,
"skipName": null,
"nowNodeCode": "c36a46ef-04f9-463f-bad7-4b395c818519",
"nextNodeCode": "3fcea762-b53a-4ae1-8365-7bec90444828",
"coordinate": "1665,300;1770,300"
}
]
},
{
"nodeType": "1",
"nodeCode": "3fcea762-b53a-4ae1-8365-7bec90444828",
"nodeName": "董事",
"permissionFlag": "1",
"nodeRatio": "0.000",
"anyNodeSkip": null,
"listenerType": "",
"listenerPath": "",
"formCustom": "N",
"formPath": null,
"ext": "[{\"code\":\"ButtonPermissionEnum\",\"value\":\"back,termination\"}]",
"coordinate": "1820,300|1820,300",
"version": "1",
"skipList": [
{
"skipType": "PASS",
"skipCondition": null,
"skipName": null,
"nowNodeCode": "3fcea762-b53a-4ae1-8365-7bec90444828",
"nextNodeCode": "9cfbfd3e-6c04-41d6-9fc2-6787a7d2cd31",
"coordinate": "1870,300;1960,300"
}
]
},
{
"nodeType": "2",
"nodeCode": "9cfbfd3e-6c04-41d6-9fc2-6787a7d2cd31",
"nodeName": "结束",
"permissionFlag": null,
"nodeRatio": "0.000",
"anyNodeSkip": null,
"listenerType": null,
"listenerPath": null,
"formCustom": "N",
"formPath": null,
"ext": "[]",
"coordinate": "1980,300|1980,300",
"version": "1",
"skipList": []
}
],
"flowCode": "leave6",
"flowName": "请假申请-排他并行会签",
"modelValue": "CLASSICS",
"category": "103",
"version": "1",
"formCustom": "N",
"formPath": "/workflow/leaveEdit/index",
"listenerType": null,
"listenerPath": null
}

File diff suppressed because one or more lines are too long

View File

@@ -1,452 +0,0 @@
# Ruoyi-AI 工作流模块详细说明文档
## 概述
Ruoyi-AI 工作流模块是一个基于 LangGraph4j 的智能工作流引擎支持可视化工作流设计、AI 模型集成、条件分支、人机交互等高级功能。该模块采用微服务架构,提供完整的
RESTful API 和流式响应支持。
## 模块架构
### 1. 模块结构
```
ruoyi-ai/
├── ruoyi-modules/
│ └── ruoyi-workflow/ # 工作流核心模块
│ ├── pom.xml
│ └── src/main/java/org/ruoyi/workflow/
│ └── controller/ # 控制器层
│ ├── WorkflowController.java
│ ├── WorkflowRuntimeController.java
│ └── admin/ # 管理端控制器
│ ├── AdminWorkflowController.java
│ └── AdminWorkflowComponentController.java
└── ruoyi-modules-api/
└── ruoyi-workflow-api/ # 工作流API模块
├── pom.xml
└── src/main/java/org/ruoyi/workflow/
├── entity/ # 实体类
├── dto/ # 数据传输对象
├── service/ # 服务接口
├── mapper/ # 数据访问层
├── workflow/ # 工作流核心逻辑
├── enums/ # 枚举类
├── util/ # 工具类
└── exception/ # 异常处理
```
### 2. 核心依赖
- **LangGraph4j**: 1.5.3 - 工作流图执行引擎
- **LangChain4j**: 1.2.0 - AI 模型集成框架
- **Spring Boot**: 3.x - 应用框架
- **MyBatis Plus**: 数据访问层
- **Redis**: 缓存和状态管理
- **Swagger/OpenAPI**: API 文档
## 核心功能
### 1. 工作流管理
#### 1.1 工作流定义
- **创建工作流**: 支持自定义标题、描述、公开性设置
- **编辑工作流**: 可视化节点编辑、连接线配置
- **版本控制**: 支持工作流的版本管理和回滚
- **权限管理**: 支持公开/私有工作流设置
#### 1.2 工作流执行
- **流式执行**: 基于 SSE 的实时流式响应
- **状态管理**: 完整的执行状态跟踪
- **错误处理**: 详细的错误信息和异常处理
- **中断恢复**: 支持工作流中断和恢复执行
### 2. 节点类型
#### 2.1 基础节点
- **Start**: 开始节点,定义工作流入口
- **End**: 结束节点,定义工作流出口
#### 2.2 AI 模型节点
- **Answer**: 大语言模型问答节点
- **Dalle3**: DALL-E 3 图像生成
- **Tongyiwanx**: 通义万相图像生成
- **Classifier**: 内容分类节点
#### 2.3 数据处理节点
- **DocumentExtractor**: 文档信息提取
- **KeywordExtractor**: 关键词提取
- **FaqExtractor**: 常见问题提取
- **KnowledgeRetrieval**: 知识库检索
#### 2.4 控制流节点
- **Switcher**: 条件分支节点
- **HumanFeedback**: 人机交互节点
#### 2.5 外部集成节点
- **Google**: Google 搜索集成
- **MailSend**: 邮件发送
- **HttpRequest**: HTTP 请求
- **Template**: 模板转换
### 3. 数据流管理
#### 3.1 输入输出定义
```java
// 节点输入输出数据结构
public class NodeIOData {
private String name; // 参数名称
private NodeIODataContent content; // 参数内容
}
// 支持的数据类型
public enum WfIODataTypeEnum {
TEXT, // 文本
NUMBER, // 数字
BOOLEAN, // 布尔值
FILES, // 文件
OPTIONS // 选项
}
```
#### 3.2 参数引用
- **节点间引用**: 支持上游节点输出作为下游节点输入
- **参数映射**: 自动处理参数名称映射
- **类型转换**: 自动进行数据类型转换
## 数据库设计
### 1. 核心表结构
#### 1.1 工作流定义表 (t_workflow)
```sql
CREATE TABLE t_workflow (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
uuid VARCHAR(32) NOT NULL DEFAULT '',
title VARCHAR(100) NOT NULL DEFAULT '',
remark TEXT NOT NULL DEFAULT '',
user_id BIGINT NOT NULL DEFAULT 0,
is_public TINYINT(1) NOT NULL DEFAULT 0,
is_enable TINYINT(1) NOT NULL DEFAULT 1,
create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
is_deleted TINYINT(1) NOT NULL DEFAULT 0
);
```
#### 1.2 工作流节点表 (t_workflow_node)
```sql
CREATE TABLE t_workflow_node (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
uuid VARCHAR(32) NOT NULL DEFAULT '',
workflow_id BIGINT NOT NULL DEFAULT 0,
workflow_component_id BIGINT NOT NULL DEFAULT 0,
user_id BIGINT NOT NULL DEFAULT 0,
title VARCHAR(100) NOT NULL DEFAULT '',
remark VARCHAR(500) NOT NULL DEFAULT '',
input_config JSON NOT NULL DEFAULT ('{}'),
node_config JSON NOT NULL DEFAULT ('{}'),
position_x DOUBLE NOT NULL DEFAULT 0,
position_y DOUBLE NOT NULL DEFAULT 0,
create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
is_deleted TINYINT(1) NOT NULL DEFAULT 0
);
```
#### 1.3 工作流边表 (t_workflow_edge)
```sql
CREATE TABLE t_workflow_edge (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
uuid VARCHAR(32) NOT NULL DEFAULT '',
workflow_id BIGINT NOT NULL DEFAULT 0,
source_node_uuid VARCHAR(32) NOT NULL DEFAULT '',
source_handle VARCHAR(32) NOT NULL DEFAULT '',
target_node_uuid VARCHAR(32) NOT NULL DEFAULT '',
create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
is_deleted TINYINT(1) NOT NULL DEFAULT 0
);
```
#### 1.4 工作流运行时表 (t_workflow_runtime)
```sql
CREATE TABLE t_workflow_runtime (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
uuid VARCHAR(32) NOT NULL DEFAULT '',
user_id BIGINT NOT NULL DEFAULT 0,
workflow_id BIGINT NOT NULL DEFAULT 0,
input JSON NOT NULL DEFAULT ('{}'),
output JSON NOT NULL DEFAULT ('{}'),
status SMALLINT NOT NULL DEFAULT 1,
status_remark VARCHAR(250) NOT NULL DEFAULT '',
create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
is_deleted TINYINT(1) NOT NULL DEFAULT 0
);
```
#### 1.5 工作流组件表 (t_workflow_component)
```sql
CREATE TABLE t_workflow_component (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
uuid VARCHAR(32) DEFAULT '' NOT NULL,
name VARCHAR(32) DEFAULT '' NOT NULL,
title VARCHAR(100) DEFAULT '' NOT NULL,
remark TEXT NOT NULL,
display_order INT DEFAULT 0 NOT NULL,
is_enable TINYINT(1) DEFAULT 0 NOT NULL,
create_time DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL,
update_time DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL,
is_deleted TINYINT(1) DEFAULT 0 NOT NULL
);
```
## API 接口
### 1. 工作流管理接口
#### 1.1 基础操作
```http
#
POST /workflow/add
Content-Type: application/json
{
"title": "",
"remark": "",
"isPublic": false
}
#
POST /workflow/update
Content-Type: application/json
{
"uuid": "UUID",
"title": "",
"remark": ""
}
#
POST /workflow/del/{uuid}
# /
POST /workflow/enable/{uuid}?enable=true
```
#### 1.2 搜索和查询
```http
#
GET /workflow/mine/search?keyword=&isPublic=true&currentPage=1&pageSize=10
#
GET /workflow/public/search?keyword=&currentPage=1&pageSize=10
#
GET /workflow/public/component/list
```
### 2. 工作流执行接口
#### 2.1 流式执行
```http
#
POST /workflow/run
Content-Type: application/json
Accept: text/event-stream
{
"uuid": "UUID",
"inputs": [
{
"name": "input",
"content": {
"type": 1,
"textContent": ""
}
}
]
}
```
#### 2.2 运行时管理
```http
#
POST /workflow/runtime/resume/{runtimeUuid}
Content-Type: application/json
{
"feedbackContent": ""
}
#
GET /workflow/runtime/page?wfUuid=UUID&currentPage=1&pageSize=10
#
GET /workflow/runtime/nodes/{runtimeUuid}
#
POST /workflow/runtime/clear?wfUuid=UUID
```
### 3. 管理端接口
#### 3.1 工作流管理
```http
#
POST /admin/workflow/search
Content-Type: application/json
{
"title": "",
"isPublic": true,
"isEnable": true
}
# /
POST /admin/workflow/enable?uuid=UUID&isEnable=true
```
## 核心实现
### 1. 工作流引擎 (WorkflowEngine)
工作流引擎是整个模块的核心,负责:
- 工作流图的构建和编译
- 节点执行调度
- 状态管理和持久化
- 流式输出处理
```java
public class WorkflowEngine {
// 核心执行方法
public void run(User user, List<ObjectNode> userInputs, SseEmitter sseEmitter) {
// 1. 验证工作流状态
// 2. 创建运行时实例
// 3. 构建状态图
// 4. 执行工作流
// 5. 处理流式输出
}
// 恢复执行方法
public void resume(String userInput) {
// 1. 更新状态
// 2. 继续执行
}
}
```
### 2. 节点工厂 (WfNodeFactory)
节点工厂负责根据组件类型创建对应的节点实例:
```java
public class WfNodeFactory {
public static AbstractWfNode create(WorkflowComponent component,
WorkflowNode node,
WfState wfState,
WfNodeState nodeState) {
// 根据组件类型创建对应的节点实例
switch (component.getName()) {
case "Answer":
return new LLMAnswerNode(component, node, wfState, nodeState);
case "Switcher":
return new SwitcherNode(component, node, wfState, nodeState);
// ... 其他节点类型
}
}
}
```
### 3. 图构建器 (WorkflowGraphBuilder)
图构建器负责将工作流定义转换为可执行的状态图:
```java
public class WorkflowGraphBuilder {
public StateGraph<WfNodeState> build(WorkflowNode startNode) {
// 1. 构建编译节点树
// 2. 转换为状态图
// 3. 添加节点和边
// 4. 处理条件分支
// 5. 处理并行执行
}
}
```
## 流式响应机制
### 1. SSE 事件类型
工作流执行过程中会发送多种类型的 SSE 事件:
```javascript
// 节点开始执行
[NODE_RUN_节点UUID] - 节点执行开始事件
// 节点输入数据
[NODE_INPUT_节点UUID] - 节点输入数据事件
// 节点输出数据
[NODE_OUTPUT_节点UUID] - 节点输出数据事件
// 流式内容块
[NODE_CHUNK_节点UUID] - 流式内容块事件
// 等待用户输入
[NODE_WAIT_FEEDBACK_BY_节点UUID] - 等待用户输入事件
```
### 2. 流式处理流程
1. **初始化**: 创建工作流运行时实例
2. **节点执行**: 逐个执行工作流节点
3. **实时输出**: 通过 SSE 实时推送执行结果
4. **状态更新**: 实时更新节点和工作流状态
5. **错误处理**: 捕获并处理执行过程中的错误
## 扩展开发
### 1. 自定义节点开发
要开发自定义工作流节点,需要:
1. **创建节点类**:继承 `AbstractWfNode`
2. **实现处理逻辑**:重写 `onProcess()` 方法
3. **定义配置类**:创建节点配置类
4. **注册组件**:在组件表中注册新组件
```java
public class CustomNode extends AbstractWfNode {
@Override
protected NodeProcessResult onProcess() {
// 实现自定义处理逻辑
List<NodeIOData> outputs = new ArrayList<>();
// ... 处理逻辑
return NodeProcessResult.success(outputs);
}
}
```
### 2. 自定义组件注册
```sql
-- 在 t_workflow_component 表中添加新组件
INSERT INTO t_workflow_component (uuid, name, title, remark, is_enable)
VALUES (REPLACE(UUID(), '-', ''), 'CustomNode', '自定义节点', '自定义节点描述', true);
```

Binary file not shown.

Before

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 267 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 144 KiB

317
pom.xml
View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
@@ -9,58 +9,73 @@
<version>${revision}</version>
<name>ruoyi-ai</name>
<url>https://gitee.com/ageerle/ruoyi-ai</url>
<description>AI助手</description>
<url>>https://gitee.com/ageerle/ruoyi-ai</url>
<description>全栈式AI开发平台</description>
<properties>
<revision>1.0.0</revision>
<spring-boot.version>3.4.4</spring-boot.version>
<revision>3.0.0</revision>
<spring-boot.version>3.5.8</spring-boot.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>17</java.version>
<mysql.version>8.0.33</mysql.version>
<mybatis.version>3.5.16</mybatis.version>
<springdoc.version>2.8.13</springdoc.version>
<therapi-javadoc.version>0.15.0</therapi-javadoc.version>
<poi.version>5.2.3</poi.version>
<easyexcel.version>3.2.1</easyexcel.version>
<fastexcel.version>1.3.0</fastexcel.version>
<velocity.version>2.3</velocity.version>
<satoken.version>1.34.0</satoken.version>
<mybatis-plus.version>3.5.11</mybatis-plus.version>
<satoken.version>1.44.0</satoken.version>
<mybatis-plus.version>3.5.14</mybatis-plus.version>
<p6spy.version>3.9.1</p6spy.version>
<hutool.version>5.8.35</hutool.version>
<okhttp.version>4.10.0</okhttp.version>
<hutool.version>5.8.40</hutool.version>
<spring-boot-admin.version>3.5.5</spring-boot-admin.version>
<redisson.version>3.51.0</redisson.version>
<lock4j.version>2.2.7</lock4j.version>
<dynamic-ds.version>4.3.1</dynamic-ds.version>
<spring-boot-admin.version>3.0.3</spring-boot-admin.version>
<redisson.version>3.20.1</redisson.version>
<lock4j.version>2.2.4</lock4j.version>
<alibaba-ttl.version>2.14.2</alibaba-ttl.version>
<xxl-job.version>2.4.0</xxl-job.version>
<mapstruct-plus.version>1.2.1</mapstruct-plus.version>
<snailjob.version>1.8.0</snailjob.version>
<mapstruct-plus.version>1.5.0</mapstruct-plus.version>
<mapstruct-plus.lombok.version>0.2.0</mapstruct-plus.lombok.version>
<lombok.version>1.18.26</lombok.version>
<bouncycastle.version>1.72</bouncycastle.version>
<!-- Apache Commons Compress 版本 -->
<commons-compress.version>1.26.2</commons-compress.version>
<lombok.version>1.18.40</lombok.version>
<bouncycastle.version>1.80</bouncycastle.version>
<justauth.version>1.16.7</justauth.version>
<!-- 离线IP地址定位库 -->
<ip2region.version>2.7.0</ip2region.version>
<!-- OSS 配置 -->
<aws-java-sdk-s3.version>1.12.400</aws-java-sdk-s3.version>
<aws.sdk.version>2.28.22</aws.sdk.version>
<!-- SMS 配置 -->
<aliyun.sms.version>2.0.23</aliyun.sms.version>
<tencent.sms.version>3.1.687</tencent.sms.version>
<sms4j.version>3.3.5</sms4j.version>
<!-- 限制框架中的fastjson版本 -->
<fastjson.version>1.2.83</fastjson.version>
<!-- 面向运行时的D-ORM依赖 -->
<anyline.version>8.7.2-20250603</anyline.version>
<!-- 工作流配置 -->
<warm-flow.version>1.8.2</warm-flow.version>
<!-- 企业微信SDK -->
<weixin-java-cp.version>4.4.0</weixin-java-cp.version>
<!-- Jackson XML -->
<jackson-dataformat-xml.version>2.20.1</jackson-dataformat-xml.version>
<!-- AI 相关依赖 -->
<langchain4j.version>1.11.0</langchain4j.version>
<langchain4j.community.version>1.11.0-beta19</langchain4j.community.version>
<langgraph4j.version>1.5.3</langgraph4j.version>
<weaviate.version>1.19.6</weaviate.version>
<dify.version>1.0.7</dify.version>
<!-- Neo4j 相关版本 -->
<neo4j-driver.version>5.26.0</neo4j-driver.version>
<neo4j-cypher-dsl.version>2024.1.0</neo4j-cypher-dsl.version>
<langchain4j-neo4j.version>1.2.0-beta8</langchain4j-neo4j.version>
<avatar-generator.version>1.1.0</avatar-generator.version>
<jsoup.version>1.21.2</jsoup.version>
<knife4j.version>4.4.0</knife4j.version>
<swagger-annotations.version>2.2.8</swagger-annotations.version>
<google-api-client.version>2.6.0</google-api-client.version>
<commons-collections4.version>4.5.0</commons-collections4.version>
<!-- 插件版本 -->
<maven-jar-plugin.version>3.2.2</maven-jar-plugin.version>
<maven-war-plugin.version>3.2.2</maven-war-plugin.version>
<maven-compiler-plugin.verison>3.11.0</maven-compiler-plugin.verison>
<maven-surefire-plugin.version>3.0.0</maven-surefire-plugin.version>
<maven-jar-plugin.version>3.4.2</maven-jar-plugin.version>
<maven-war-plugin.version>3.4.0</maven-war-plugin.version>
<maven-compiler-plugin.version>3.14.0</maven-compiler-plugin.version>
<maven-surefire-plugin.version>3.5.3</maven-surefire-plugin.version>
<flatten-maven-plugin.version>1.3.0</flatten-maven-plugin.version>
<!-- 打包默认跳过测试 -->
<skipTests>true</skipTests>
</properties>
<profiles>
@@ -69,7 +84,9 @@
<properties>
<!-- 环境标识,需要与配置文件的名称相对应 -->
<profiles.active>local</profiles.active>
<logging.level>debug</logging.level>
<logging.level>info</logging.level>
<monitor.username>ruoyi</monitor.username>
<monitor.password>123456</monitor.password>
</properties>
</profile>
<profile>
@@ -77,7 +94,9 @@
<properties>
<!-- 环境标识,需要与配置文件的名称相对应 -->
<profiles.active>dev</profiles.active>
<logging.level>debug</logging.level>
<logging.level>info</logging.level>
<monitor.username>ruoyi</monitor.username>
<monitor.password>123456</monitor.password>
</properties>
<activation>
<!-- 默认环境 -->
@@ -89,6 +108,8 @@
<properties>
<profiles.active>prod</profiles.active>
<logging.level>warn</logging.level>
<monitor.username>ruoyi</monitor.username>
<monitor.password>123456</monitor.password>
</properties>
</profile>
</profiles>
@@ -97,12 +118,6 @@
<dependencyManagement>
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
<!-- SpringBoot的依赖配置-->
<dependency>
<groupId>org.springframework.boot</groupId>
@@ -149,25 +164,9 @@
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>${poi.version}</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>${poi.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>${easyexcel.version}</version>
<exclusions>
<exclusion>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml-schemas</artifactId>
</exclusion>
</exclusions>
<groupId>cn.idev.excel</groupId>
<artifactId>fastexcel</artifactId>
<version>${fastexcel.version}</version>
</dependency>
<!-- velocity代码生成使用模板 -->
@@ -201,6 +200,12 @@
<version>${satoken.version}</version>
</dependency>
<!-- dynamic-datasource 多数据源-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot3-starter</artifactId>
<version>${dynamic-ds.version}</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
@@ -226,13 +231,6 @@
<version>${mybatis-plus.version}</version>
</dependency>
<!-- dynamic-datasource 多数据源-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot3-starter</artifactId>
<version>${dynamic-ds.version}</version>
</dependency>
<!-- sql性能分析插件 -->
<dependency>
<groupId>p6spy</groupId>
@@ -240,24 +238,43 @@
<version>${p6spy.version}</version>
</dependency>
<!-- AWS SDK for Java 2.x -->
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk-s3</artifactId>
<version>${aws-java-sdk-s3.version}</version>
<groupId>software.amazon.awssdk</groupId>
<artifactId>s3</artifactId>
<version>${aws.sdk.version}</version>
</dependency>
<!-- 基于 AWS CRT 的 S3 客户端的性能增强的 S3 传输管理器 -->
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>s3-transfer-manager</artifactId>
<version>${aws.sdk.version}</version>
</dependency>
<!-- 将基于 Netty 的 HTTP 客户端从类路径中移除 -->
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>netty-nio-client</artifactId>
<version>${aws.sdk.version}</version>
</dependency>
<!--短信sms4j-->
<dependency>
<groupId>org.dromara.sms4j</groupId>
<artifactId>sms4j-spring-boot-starter</artifactId>
<version>${sms4j.version}</version>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>dysmsapi20170525</artifactId>
<version>${aliyun.sms.version}</version>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-server</artifactId>
<version>${spring-boot-admin.version}</version>
</dependency>
<dependency>
<groupId>com.tencentcloudapi</groupId>
<artifactId>tencentcloud-sdk-java-sms</artifactId>
<version>${tencent.sms.version}</version>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-client</artifactId>
<version>${spring-boot-admin.version}</version>
</dependency>
<!--redisson-->
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
@@ -270,10 +287,16 @@
<version>${lock4j.version}</version>
</dependency>
<!-- SnailJob Client -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>transmittable-thread-local</artifactId>
<version>${alibaba-ttl.version}</version>
<groupId>com.aizuda</groupId>
<artifactId>snail-job-client-starter</artifactId>
<version>${snailjob.version}</version>
</dependency>
<dependency>
<groupId>com.aizuda</groupId>
<artifactId>snail-job-client-job-core</artifactId>
<version>${snailjob.version}</version>
</dependency>
<!-- 加密包引入 -->
@@ -283,19 +306,31 @@
<version>${bouncycastle.version}</version>
</dependency>
<!-- Apache Commons Compress -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-compress</artifactId>
<version>${commons-compress.version}</version>
</dependency>
<dependency>
<groupId>io.github.linpeilie</groupId>
<artifactId>mapstruct-plus-spring-boot-starter</artifactId>
<version>${mapstruct-plus.version}</version>
</dependency>
<!-- Warm-Flow国产工作流引擎, 在线文档http://warm-flow.cn/ -->
<dependency>
<groupId>org.dromara.warm</groupId>
<artifactId>warm-flow-mybatis-plus-sb3-starter</artifactId>
<version>${warm-flow.version}</version>
</dependency>
<dependency>
<groupId>org.dromara.warm</groupId>
<artifactId>warm-flow-plugin-ui-sb-web</artifactId>
<version>${warm-flow.version}</version>
</dependency>
<!-- JustAuth 的依赖配置-->
<dependency>
<groupId>me.zhyd.oauth</groupId>
<artifactId>JustAuth</artifactId>
<version>${justauth.version}</version>
</dependency>
<!-- 离线IP地址定位库 ip2region -->
<dependency>
<groupId>org.lionsoul</groupId>
@@ -303,34 +338,21 @@
<version>${ip2region.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>${fastjson.version}</version>
</dependency>
<dependency>
<groupId>org.ruoyi</groupId>
<artifactId>ruoyi-system</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>org.ruoyi</groupId>
<artifactId>ruoyi-chat</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>org.ruoyi</groupId>
<artifactId>ruoyi-knowledge-api</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>org.ruoyi</groupId>
<artifactId>ruoyi-chat-api</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>org.ruoyi</groupId>
<artifactId>ruoyi-system-api</artifactId>
<artifactId>ruoyi-job</artifactId>
<version>${revision}</version>
</dependency>
@@ -342,61 +364,67 @@
<dependency>
<groupId>org.ruoyi</groupId>
<artifactId>ruoyi-graph</artifactId>
<artifactId>ruoyi-demo</artifactId>
<version>${revision}</version>
</dependency>
<!-- Neo4j Driver -->
<dependency>
<groupId>org.neo4j.driver</groupId>
<artifactId>neo4j-java-driver</artifactId>
<version>${neo4j-driver.version}</version>
</dependency>
<!-- Neo4j Cypher DSL -->
<dependency>
<groupId>org.neo4j</groupId>
<artifactId>neo4j-cypher-dsl</artifactId>
<version>${neo4j-cypher-dsl.version}</version>
</dependency>
<!-- Langchain4j Neo4j 扩展 -->
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-community-neo4j</artifactId>
<version>${langchain4j-neo4j.version}</version>
</dependency>
<dependency>
<groupId>org.ruoyi</groupId>
<artifactId>ruoyi-aihuman</artifactId>
<artifactId>ruoyi-chat</artifactId>
<version>${revision}</version>
</dependency>
<!-- 工作流模块 -->
<dependency>
<groupId>org.ruoyi</groupId>
<artifactId>ruoyi-workflow</artifactId>
<version>${revision}</version>
</dependency>
<!-- 微信模块 -->
<dependency>
<groupId>org.ruoyi</groupId>
<artifactId>ruoyi-workflow-api</artifactId>
<artifactId>ruoyi-wechat</artifactId>
<version>${revision}</version>
</dependency>
<!-- 数字人模块 -->
<dependency>
<groupId>org.ruoyi</groupId>
<artifactId>ruoyi-aihuman</artifactId>
<version>${revision}</version>
</dependency>
<!-- AI流程编排模块 -->
<dependency>
<groupId>org.ruoyi</groupId>
<artifactId>ruoyi-aiflow</artifactId>
<version>${revision}</version>
</dependency>
<!-- 企业微信SDK -->
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-cp</artifactId>
<version>${weixin-java-cp.version}</version>
</dependency>
<!-- Jackson XML -->
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
<version>${jackson-dataformat-xml.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
<modules>
<module>ruoyi-common</module>
<module>ruoyi-modules</module>
<module>ruoyi-modules-api</module>
<module>ruoyi-admin</module>
<module>ruoyi-common</module>
<module>ruoyi-extend</module>
<module>ruoyi-modules</module>
</modules>
<packaging>pom</packaging>
<build>
@@ -404,7 +432,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven-compiler-plugin.verison}</version>
<version>${maven-compiler-plugin.version}</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
@@ -447,6 +475,7 @@
<artifactId>maven-surefire-plugin</artifactId>
<version>${maven-surefire-plugin.version}</version>
<configuration>
<argLine>-Dfile.encoding=UTF-8</argLine>
<!-- 根据打包环境执行对应的@Tag测试方法 -->
<groups>${profiles.active}</groups>
<!-- 排除标签 -->
@@ -503,8 +532,8 @@
<repositories>
<repository>
<id>public</id>
<name>aliyun nexus</name>
<url>https://maven.aliyun.com/repository/public/</url>
<name>huawei nexus</name>
<url>https://mirrors.huaweicloud.com/repository/maven/</url>
<releases>
<enabled>true</enabled>
</releases>
@@ -514,8 +543,8 @@
<pluginRepositories>
<pluginRepository>
<id>public</id>
<name>aliyun nexus</name>
<url>https://maven.aliyun.com/repository/public/</url>
<name>huawei nexus</name>
<url>https://mirrors.huaweicloud.com/repository/maven/</url>
<releases>
<enabled>true</enabled>
</releases>
@@ -525,4 +554,6 @@
</pluginRepository>
</pluginRepositories>
</project>
</project>

31
ruoyi-admin/Dockerfile Normal file
View File

@@ -0,0 +1,31 @@
# 贝尔实验室 Spring 官方推荐镜像 JDK下载地址 https://bell-sw.com/pages/downloads/
FROM bellsoft/liberica-openjdk-rocky:17.0.16-cds
#FROM bellsoft/liberica-openjdk-rocky:21.0.8-cds
#FROM findepi/graalvm:java17-native
LABEL maintainer="Lion Li"
RUN mkdir -p /ruoyi/server/logs \
/ruoyi/server/temp \
/ruoyi/skywalking/agent
WORKDIR /ruoyi/server
ENV SERVER_PORT=8080 SNAIL_PORT=28080 LANG=C.UTF-8 LC_ALL=C.UTF-8 JAVA_OPTS=""
EXPOSE ${SERVER_PORT}
# 暴露 snail job 客户端端口 用于定时任务调度中心通信
EXPOSE ${SNAIL_PORT}
ADD ./target/ruoyi-admin.jar ./app.jar
SHELL ["/bin/bash", "-c"]
ENTRYPOINT java -Djava.security.egd=file:/dev/./urandom -Dserver.port=${SERVER_PORT} \
-Dsnail-job.port=${SNAIL_PORT} \
# 应用名称 如果想区分集群节点监控 改成不同的名称即可
#-Dskywalking.agent.service_name=ruoyi-server \
#-javaagent:/ruoyi/skywalking/agent/skywalking-agent.jar \
-XX:+HeapDumpOnOutOfMemoryError -XX:+UseZGC ${JAVA_OPTS} \
-jar app.jar

View File

@@ -1,12 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>ruoyi-ai</artifactId>
<groupId>org.ruoyi</groupId>
<version>${revision}</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<packaging>jar</packaging>
@@ -24,22 +23,46 @@
<artifactId>mysql-connector-j</artifactId>
</dependency>
<!-- Oracle -->
<!-- &lt;!&ndash; mp支持的数据库均支持 只需要增加对应的jdbc依赖即可 &ndash;&gt;-->
<!-- &lt;!&ndash; Oracle &ndash;&gt;-->
<!-- <dependency>-->
<!-- <groupId>com.oracle.database.jdbc</groupId>-->
<!-- <artifactId>ojdbc8</artifactId>-->
<!-- </dependency>-->
<!-- &lt;!&ndash; 兼容oracle低版本 &ndash;&gt;-->
<!-- <dependency>-->
<!-- <groupId>com.oracle.database.nls</groupId>-->
<!-- <artifactId>orai18n</artifactId>-->
<!-- </dependency>-->
<!-- &lt;!&ndash; PostgreSql &ndash;&gt;-->
<!-- <dependency>-->
<!-- <groupId>org.postgresql</groupId>-->
<!-- <artifactId>postgresql</artifactId>-->
<!-- </dependency>-->
<!-- &lt;!&ndash; SqlServer &ndash;&gt;-->
<!-- <dependency>-->
<!-- <groupId>com.microsoft.sqlserver</groupId>-->
<!-- <artifactId>mssql-jdbc</artifactId>-->
<!-- </dependency>-->
<dependency>
<groupId>com.oracle.database.jdbc</groupId>
<artifactId>ojdbc8</artifactId>
<groupId>org.ruoyi</groupId>
<artifactId>ruoyi-common-doc</artifactId>
</dependency>
<!-- PostgreSql -->
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<groupId>org.ruoyi</groupId>
<artifactId>ruoyi-common-social</artifactId>
</dependency>
<!-- SqlServer -->
<dependency>
<groupId>com.microsoft.sqlserver</groupId>
<artifactId>mssql-jdbc</artifactId>
<groupId>org.ruoyi</groupId>
<artifactId>ruoyi-common-ratelimiter</artifactId>
</dependency>
<dependency>
<groupId>org.ruoyi</groupId>
<artifactId>ruoyi-common-mail</artifactId>
</dependency>
<dependency>
@@ -49,29 +72,73 @@
<dependency>
<groupId>org.ruoyi</groupId>
<artifactId>ruoyi-chat</artifactId>
<artifactId>ruoyi-job</artifactId>
</dependency>
<!-- 代码生成-->
<dependency>
<groupId>org.ruoyi</groupId>
<artifactId>ruoyi-generator</artifactId>
</dependency>
<!-- 知识图谱模块 -->
<!-- demo模块 -->
<dependency>
<groupId>org.ruoyi</groupId>
<artifactId>ruoyi-graph</artifactId>
<artifactId>ruoyi-demo</artifactId>
</dependency>
<dependency>
<groupId>org.ruoyi</groupId>
<artifactId>ruoyi-chat</artifactId>
</dependency>
<!-- 工作流模块 -->
<dependency>
<groupId>org.ruoyi</groupId>
<artifactId>ruoyi-workflow</artifactId>
</dependency>
<!-- 微信模块 -->
<dependency>
<groupId>org.ruoyi</groupId>
<artifactId>ruoyi-wechat</artifactId>
</dependency>
<!-- 数字人模块 -->
<dependency>
<groupId>org.ruoyi</groupId>
<artifactId>ruoyi-aihuman</artifactId>
</dependency>
<!-- AI流程编排模块 -->
<dependency>
<groupId>org.ruoyi</groupId>
<artifactId>ruoyi-aiflow</artifactId>
</dependency>
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- skywalking 整合 logback -->
<!-- <dependency>-->
<!-- <groupId>org.apache.skywalking</groupId>-->
<!-- <artifactId>apm-toolkit-logback-1.x</artifactId>-->
<!-- <version>${与你的agent探针版本保持一致}</version>-->
<!-- </dependency>-->
<!-- <dependency>-->
<!-- <groupId>org.apache.skywalking</groupId>-->
<!-- <artifactId>apm-toolkit-trace</artifactId>-->
<!-- <version>${与你的agent探针版本保持一致}</version>-->
<!-- </dependency>-->
</dependencies>
<build>

View File

@@ -3,23 +3,20 @@ package org.ruoyi;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.metrics.buffering.BufferingApplicationStartup;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
/**
* 启动程序
*
* @author Lion Li
*/
@SpringBootApplication(scanBasePackages = {"org.ruoyi", "org.ruoyi.aihuman"})
@EnableScheduling
@EnableAsync
@SpringBootApplication
public class RuoYiAIApplication {
public static void main(String[] args) {
SpringApplication application = new SpringApplication(RuoYiAIApplication.class);
application.setApplicationStartup(new BufferingApplicationStartup(2048));
application.run(args);
System.out.println("(♥◠‿◠)ノ゙ RuoYiAI启动成功 ლ(´ڡ`ლ)゙");
System.out.println("(♥◠‿◠)ノ゙ RuoYi-AI启动成功 ლ(´ڡ`ლ)゙");
}
}
}

View File

@@ -1,46 +1,70 @@
package org.ruoyi.controller;
import cn.dev33.satoken.annotation.SaIgnore;
import cn.dev33.satoken.exception.NotLoginException;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.codec.Base64;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import org.ruoyi.common.core.constant.Constants;
import lombok.extern.slf4j.Slf4j;
import me.zhyd.oauth.model.AuthResponse;
import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.request.AuthRequest;
import me.zhyd.oauth.utils.AuthStateUtils;
import org.ruoyi.common.core.constant.SystemConstants;
import org.ruoyi.common.core.domain.R;
import org.ruoyi.common.core.domain.model.*;
import org.ruoyi.common.core.utils.MapstructUtils;
import org.ruoyi.common.core.utils.StreamUtils;
import org.ruoyi.common.core.utils.StringUtils;
import org.ruoyi.common.core.domain.model.LoginBody;
import org.ruoyi.common.core.domain.model.RegisterBody;
import org.ruoyi.common.core.domain.model.SocialLoginBody;
import org.ruoyi.common.core.utils.*;
import org.ruoyi.common.encrypt.annotation.ApiEncrypt;
import org.ruoyi.common.json.utils.JsonUtils;
import org.ruoyi.common.ratelimiter.annotation.RateLimiter;
import org.ruoyi.common.ratelimiter.enums.LimitType;
import org.ruoyi.common.satoken.utils.LoginHelper;
import org.ruoyi.common.social.config.properties.SocialLoginConfigProperties;
import org.ruoyi.common.social.config.properties.SocialProperties;
import org.ruoyi.common.social.utils.SocialUtils;
import org.ruoyi.common.sse.dto.SseMessageDto;
import org.ruoyi.common.sse.utils.SseMessageUtils;
import org.ruoyi.common.tenant.helper.TenantHelper;
import org.ruoyi.system.domain.bo.SysTenantBo;
import org.ruoyi.system.domain.vo.LoginTenantVo;
import org.ruoyi.system.domain.vo.LoginVo;
import org.ruoyi.system.domain.vo.SysTenantVo;
import org.ruoyi.system.domain.vo.TenantListVo;
import org.ruoyi.system.service.ISysTenantService;
import org.ruoyi.system.service.SysLoginService;
import org.ruoyi.system.service.SysRegisterService;
import org.ruoyi.system.domain.vo.*;
import org.ruoyi.system.service.*;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
/**
* 认证
*
* @author Lion Li
*/
@Slf4j
@SaIgnore
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/auth")
public class AuthController {
private final SocialProperties socialProperties;
private final SysLoginService loginService;
private final SysRegisterService registerService;
private final ISysConfigService configService;
private final ISysTenantService tenantService;
private final ISysSocialService socialUserService;
private final ISysClientService clientService;
private final ScheduledExecutorService scheduledExecutorService;
/**
* 登录方法
@@ -48,64 +72,97 @@ public class AuthController {
* @param body 登录信息
* @return 结果
*/
@ApiEncrypt
@PostMapping("/login")
public R<LoginVo> login(@Validated @RequestBody LoginBody body) {
body.setTenantId(Constants.TENANT_ID);
LoginVo loginVo = new LoginVo();
// 生成令牌
String token = loginService.login(
body.getTenantId(),
body.getUsername(), body.getPassword(),
body.getCode(), body.getUuid());
loginVo.setToken(token);
// 兼容后台管理登录
loginVo.setAccess_token(token);
loginVo.setUserInfo(LoginHelper.getLoginUser());
public R<LoginVo> login(@RequestBody String body) {
LoginBody loginBody = JsonUtils.parseObject(body, LoginBody.class);
ValidatorUtils.validate(loginBody);
// 授权类型和客户端id
String clientId = loginBody.getClientId();
String grantType = loginBody.getGrantType();
SysClientVo client = clientService.queryByClientId(clientId);
// 查询不到 client 或 client 内不包含 grantType
if (ObjectUtil.isNull(client) || !StringUtils.contains(client.getGrantType(), grantType)) {
log.info("客户端id: {} 认证类型:{} 异常!.", clientId, grantType);
return R.fail(MessageUtils.message("auth.grant.type.error"));
} else if (!SystemConstants.NORMAL.equals(client.getStatus())) {
return R.fail(MessageUtils.message("auth.grant.type.blocked"));
}
// 校验租户
loginService.checkTenant(loginBody.getTenantId());
// 登录
LoginVo loginVo = IAuthStrategy.login(body, client, grantType);
Long userId = LoginHelper.getUserId();
scheduledExecutorService.schedule(() -> {
SseMessageDto dto = new SseMessageDto();
dto.setMessage("欢迎登录ruoyi-ai后台管理系统");
dto.setUserIds(List.of(userId));
SseMessageUtils.publishMessage(dto);
}, 5, TimeUnit.SECONDS);
return R.ok(loginVo);
}
/**
* 短信登录
* 获取跳转URL
*
* @param body 登录信息
* @param source 登录来源
* @return 结果
*/
@PostMapping("/smsLogin")
public R<LoginVo> smsLogin(@Validated @RequestBody SmsLoginBody body) {
LoginVo loginVo = new LoginVo();
// 生成令牌
String token = loginService.smsLogin(body.getTenantId(), body.getPhonenumber(), body.getSmsCode());
loginVo.setToken(token);
return R.ok(loginVo);
@GetMapping("/binding/{source}")
public R<String> authBinding(@PathVariable("source") String source,
@RequestParam String tenantId, @RequestParam String domain) {
SocialLoginConfigProperties obj = socialProperties.getType().get(source);
if (ObjectUtil.isNull(obj)) {
return R.fail(source + "平台账号暂不支持");
}
AuthRequest authRequest = SocialUtils.getAuthRequest(source, socialProperties);
Map<String, String> map = new HashMap<>();
map.put("tenantId", tenantId);
map.put("domain", domain);
map.put("state", AuthStateUtils.createState());
String authorizeUrl = authRequest.authorize(Base64.encode(JsonUtils.toJsonString(map), StandardCharsets.UTF_8));
return R.ok("操作成功", authorizeUrl);
}
/**
* 访客登录
* 前端回调绑定授权(需要token)
*
* @param loginBody 登录信息
* @return token信息
*/
@PostMapping("/visitorLogin")
public R<LoginVo> visitorLogin(@RequestBody VisitorLoginBody loginBody) {
LoginVo loginVo = new LoginVo();
return R.ok(loginVo);
}
/**
* 邮件登录
*
* @param body 登录信息
* @param loginBody 请求体
* @return 结果
*/
@PostMapping("/emailLogin")
public R<LoginVo> emailLogin(@Validated @RequestBody EmailLoginBody body) {
LoginVo loginVo = new LoginVo();
// 生成令牌
String token = loginService.emailLogin(body.getTenantId(), body.getEmail(), body.getEmailCode());
loginVo.setToken(token);
return R.ok(loginVo);
@PostMapping("/social/callback")
public R<Void> socialCallback(@RequestBody SocialLoginBody loginBody) {
// 校验token
StpUtil.checkLogin();
// 获取第三方登录信息
AuthResponse<AuthUser> response = SocialUtils.loginAuth(
loginBody.getSource(), loginBody.getSocialCode(),
loginBody.getSocialState(), socialProperties);
AuthUser authUserData = response.getData();
// 判断授权响应是否成功
if (!response.ok()) {
return R.fail(response.getMsg());
}
loginService.socialRegister(authUserData);
return R.ok();
}
/**
* 取消授权(需要token)
*
* @param socialId socialId
*/
@DeleteMapping(value = "/unlock/{socialId}")
public R<Void> unlockSocial(@PathVariable Long socialId) {
// 校验token
StpUtil.checkLogin();
Boolean rows = socialUserService.deleteWithValidById(socialId);
return rows ? R.ok() : R.fail("取消授权失败");
}
/**
* 退出登录
*/
@@ -118,42 +175,58 @@ public class AuthController {
/**
* 用户注册
*/
@ApiEncrypt
@PostMapping("/register")
public R<Void> register(@Validated @RequestBody RegisterBody user, HttpServletRequest request) {
String domainName = request.getServerName();
user.setDomainName(domainName);
public R<Void> register(@Validated @RequestBody RegisterBody user) {
if (!configService.selectRegisterEnabled(user.getTenantId())) {
return R.fail("当前系统没有开启注册功能!");
}
registerService.register(user);
return R.ok();
}
/**
* 重置密码
*/
@PostMapping("/reset/password")
@SaIgnore
public R<Void> resetPassWord(@Validated @RequestBody RegisterBody user) {
registerService.resetPassWord(user);
return R.ok();
}
/**
* 登录页面租户下拉框
*
* @return 租户列表
*/
@RateLimiter(time = 60, count = 20, limitType = LimitType.IP)
@GetMapping("/tenant/list")
public R<LoginTenantVo> tenantList(HttpServletRequest request) throws Exception {
// 返回对象
LoginTenantVo result = new LoginTenantVo();
boolean enable = TenantHelper.isEnable();
result.setTenantEnabled(enable);
// 如果未开启租户这直接返回
if (!enable) {
return R.ok(result);
}
List<SysTenantVo> tenantList = tenantService.queryList(new SysTenantBo());
List<TenantListVo> voList = MapstructUtils.convert(tenantList, TenantListVo.class);
try {
// 如果只超管返回所有租户
if (LoginHelper.isSuperAdmin()) {
result.setVoList(voList);
return R.ok(result);
}
} catch (NotLoginException ignored) {
}
// 获取域名
String host = new URL(request.getRequestURL().toString()).getHost();
String host;
String referer = request.getHeader("referer");
if (StringUtils.isNotBlank(referer)) {
// 这里从referer中取值是为了本地使用hosts添加虚拟域名方便本地环境调试
host = referer.split("//")[1].split("/")[0];
} else {
host = new URL(request.getRequestURL().toString()).getHost();
}
// 根据域名进行筛选
List<TenantListVo> list = StreamUtils.filter(voList, vo -> StringUtils.equals(vo.getDomain(), host));
// 返回对象
LoginTenantVo vo = new LoginTenantVo();
vo.setVoList(CollUtil.isNotEmpty(list) ? list : voList);
vo.setTenantEnabled(TenantHelper.isEnable());
return R.ok(vo);
List<TenantListVo> list = StreamUtils.filter(voList, vo ->
StringUtils.equalsIgnoreCase(vo.getDomain(), host));
result.setVoList(CollUtil.isNotEmpty(list) ? list : voList);
return R.ok(result);
}
}

View File

@@ -5,38 +5,36 @@ import cn.hutool.captcha.AbstractCaptcha;
import cn.hutool.captcha.generator.CodeGenerator;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.RandomUtil;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotBlank;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.ruoyi.common.core.constant.Constants;
import org.ruoyi.common.core.constant.GlobalConstants;
import org.ruoyi.common.core.domain.R;
import org.ruoyi.common.core.service.ConfigService;
import org.ruoyi.common.core.exception.ServiceException;
import org.ruoyi.common.core.utils.SpringUtils;
import org.ruoyi.common.core.utils.StringUtils;
import org.ruoyi.common.core.utils.reflect.ReflectUtils;
import org.ruoyi.common.mail.config.properties.MailProperties;
import org.ruoyi.common.mail.utils.MailUtils;
import org.ruoyi.common.ratelimiter.annotation.RateLimiter;
import org.ruoyi.common.ratelimiter.enums.LimitType;
import org.ruoyi.common.redis.utils.RedisUtils;
import org.ruoyi.common.sms.config.properties.SmsProperties;
import org.ruoyi.common.sms.core.SmsTemplate;
import org.ruoyi.common.sms.entity.SmsResult;
import org.ruoyi.common.web.config.properties.CaptchaProperties;
import org.ruoyi.common.web.enums.CaptchaType;
import org.ruoyi.system.domain.request.EmailRequest;
import org.dromara.sms4j.api.SmsBlend;
import org.dromara.sms4j.api.entity.SmsResponse;
import org.dromara.sms4j.core.factory.SmsFactory;
import org.ruoyi.system.domain.vo.CaptchaVo;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import java.time.Duration;
import java.util.HashMap;
import java.util.Map;
import java.util.LinkedHashMap;
/**
* 验证码操作处理
@@ -51,31 +49,28 @@ import java.util.Map;
public class CaptchaController {
private final CaptchaProperties captchaProperties;
private final SmsProperties smsProperties;
private final ConfigService configService;
private final MailProperties mailProperties;
/**
* 短信验证码
*
* @param phonenumber 用户手机号
*/
@RateLimiter(key = "#phonenumber", time = 60, count = 1)
@GetMapping("/resource/sms/code")
public R<Void> smsCode(@NotBlank(message = "{user.phonenumber.not.blank}") String phonenumber) {
if (!smsProperties.getEnabled()) {
return R.fail("当前系统没有开启短信功能!");
}
String key = GlobalConstants.CAPTCHA_CODE_KEY + phonenumber;
String code = RandomUtil.randomNumbers(4);
RedisUtils.setCacheObject(key, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION));
// 验证码模板id
// 验证码模板id 自行处理 (查数据库或写死均可)
String templateId = "";
Map<String, String> map = new HashMap<>(1);
LinkedHashMap<String, String> map = new LinkedHashMap<>(1);
map.put("code", code);
SmsTemplate smsTemplate = SpringUtils.getBean(SmsTemplate.class);
SmsResult result = smsTemplate.send(phonenumber, templateId, map);
if (!result.isSuccess()) {
log.error("验证码短信发送异常 => {}", result);
return R.fail(result.getMessage());
SmsBlend smsBlend = SmsFactory.getSmsBlend("config1");
SmsResponse smsResponse = smsBlend.sendMessage(phonenumber, templateId, map);
if (!smsResponse.isSuccess()) {
log.error("验证码短信发送异常 => {}", smsResponse);
return R.fail(smsResponse.getData().toString());
}
return R.ok();
}
@@ -83,24 +78,32 @@ public class CaptchaController {
/**
* 邮箱验证码
*
* @param emailRequest 用户邮箱
* @param email 邮箱
*/
@PostMapping("/resource/email/code")
public R<Void> emailCode(@RequestBody @Valid EmailRequest emailRequest) {
String key = GlobalConstants.CAPTCHA_CODE_KEY + emailRequest.getUsername();
@GetMapping("/resource/email/code")
public R<Void> emailCode(@NotBlank(message = "{user.email.not.blank}") String email) {
if (!mailProperties.getEnabled()) {
return R.fail("当前系统没有开启邮箱功能!");
}
SpringUtils.getAopProxy(this).emailCodeImpl(email);
return R.ok();
}
/**
* 邮箱验证码
* 独立方法避免验证码关闭之后仍然走限流
*/
@RateLimiter(key = "#email", time = 60, count = 1)
public void emailCodeImpl(String email) {
String key = GlobalConstants.CAPTCHA_CODE_KEY + email;
String code = RandomUtil.randomNumbers(4);
RedisUtils.setCacheObject(key, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION));
// 自定义邮箱模板
String model = configService.getConfigValue("mail", "mailModel");
String mailTitle = configService.getConfigValue("mail", "mailTitle");
String replacedModel = model.replace("{code}", code);
try {
MailUtils.sendHtml(emailRequest.getUsername(), mailTitle, replacedModel);
MailUtils.sendText(email, "登录验证码", "您本次验证码为:" + code + ",有效性为" + Constants.CAPTCHA_EXPIRATION + "分钟,请尽快填写。");
} catch (Exception e) {
log.error("邮箱验证码发送异常 => {}", e.getMessage());
return R.fail(e.getMessage());
log.error("验证码短信发送异常 => {}", e.getMessage());
throw new ServiceException(e.getMessage());
}
return R.ok();
}
/**
@@ -108,33 +111,47 @@ public class CaptchaController {
*/
@GetMapping("/auth/code")
public R<CaptchaVo> getCode() {
CaptchaVo captchaVo = new CaptchaVo();
boolean captchaEnabled = captchaProperties.getEnable();
if (!captchaEnabled) {
CaptchaVo captchaVo = new CaptchaVo();
captchaVo.setCaptchaEnabled(false);
return R.ok(captchaVo);
}
return R.ok(SpringUtils.getAopProxy(this).getCodeImpl());
}
/**
* 生成验证码
* 独立方法避免验证码关闭之后仍然走限流
*/
@RateLimiter(time = 60, count = 10, limitType = LimitType.IP)
public CaptchaVo getCodeImpl() {
// 保存验证码信息
String uuid = IdUtil.simpleUUID();
String verifyKey = GlobalConstants.CAPTCHA_CODE_KEY + uuid;
// 生成验证码
CaptchaType captchaType = captchaProperties.getType();
boolean isMath = CaptchaType.MATH == captchaType;
Integer length = isMath ? captchaProperties.getNumberLength() : captchaProperties.getCharLength();
CodeGenerator codeGenerator = ReflectUtils.newInstance(captchaType.getClazz(), length);
CodeGenerator codeGenerator;
if (CaptchaType.MATH == captchaType) {
codeGenerator = ReflectUtils.newInstance(captchaType.getClazz(), captchaProperties.getNumberLength(), false);
} else {
codeGenerator = ReflectUtils.newInstance(captchaType.getClazz(), captchaProperties.getCharLength());
}
AbstractCaptcha captcha = SpringUtils.getBean(captchaProperties.getCategory().getClazz());
captcha.setGenerator(codeGenerator);
captcha.createCode();
// 如果是数学验证码使用SpEL表达式处理验证码结果
String code = captcha.getCode();
if (isMath) {
if (CaptchaType.MATH == captchaType) {
ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression(StringUtils.remove(code, "="));
code = exp.getValue(String.class);
}
RedisUtils.setCacheObject(verifyKey, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION));
CaptchaVo captchaVo = new CaptchaVo();
captchaVo.setUuid(uuid);
captchaVo.setImg(captcha.getImageBase64());
return R.ok(captchaVo);
return captchaVo;
}
}

View File

@@ -1,6 +1,9 @@
package org.ruoyi.controller;
import cn.dev33.satoken.annotation.SaIgnore;
import lombok.RequiredArgsConstructor;
import org.ruoyi.common.core.utils.SpringUtils;
import org.ruoyi.common.core.utils.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@@ -10,6 +13,7 @@ import org.springframework.web.bind.annotation.RestController;
* @author Lion Li
*/
@SaIgnore
@RequiredArgsConstructor
@RestController
public class IndexController {
@@ -18,8 +22,7 @@ public class IndexController {
*/
@GetMapping("/")
public String index() {
return "RuoYi AI启动成功";
return StringUtils.format("欢迎使用{}后台管理框架,请通过前端地址访问。", SpringUtils.getApplicationName());
}
}

View File

@@ -1,3 +1,33 @@
--- # 监控中心配置
spring.boot.admin.client:
# 增加客户端开关
enabled: false
url: http://localhost:9090/admin
instance:
service-host-type: IP
metadata:
username: ${spring.boot.admin.client.username}
userpassword: ${spring.boot.admin.client.password}
username: @monitor.username@
password: @monitor.password@
--- # snail-job 配置
snail-job:
enabled: false
# 需要在 SnailJob 后台组管理创建对应名称的组,然后创建任务的时候选择对应的组,才能正确分派任务
group: "ruoyi_group"
# SnailJob 接入验证令牌 详见 script/sql/ry_job.sql `sj_group_config` 表
token: "SJ_cKqBTPzCsWA3VyuCfFoccmuIEGXjr5KT"
server:
host: 127.0.0.1
port: 17888
# 命名空间UUID 详见 script/sql/ry_job.sql `sj_namespace`表`unique_id`字段
namespace: ${spring.profiles.active}
# 随主应用端口漂移
port: 2${server.port}
# 客户端ip指定
host:
--- # 数据源配置
spring:
datasource:
@@ -15,10 +45,11 @@ spring:
master:
type: ${spring.datasource.type}
driverClassName: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/ruoyi-ai?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true
username: root
password: root
# jdbc 所有参数配置参考 https://lionli.blog.csdn.net/article/details/122018562
# rewriteBatchedStatements=true 批处理优化 大幅提升批量插入更新删除性能(对数据库有性能损耗 使用批量操作应考虑性能问题)
url: jdbc:mysql://127.0.0.1:3306/ruoyi-ai-v3?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
username: ruoyi-ai-v3
password: ruoyi-ai-v3
hikari:
# 最大连接池数量
maxPoolSize: 20
@@ -32,26 +63,32 @@ spring:
idleTimeout: 600000
# 此属性控制池中连接的最长生命周期值0表示无限生命周期默认30分钟
maxLifetime: 1800000
# 连接测试query配置检测连接是否有效
connectionTestQuery: SELECT 1
# 多久检查一次连接的活性
keepaliveTime: 30000
mail:
username: xx
agent:
mysql:
url: jdbc:mysql://localhost:3306/ruoyi-ai-agent?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
username: root
password: root
--- # redis 单机配置(单机与集群只能开启一个另一个需要注释掉)
spring.data:
redis:
# 地址
host: 127.0.0.1
host: localhost
# 端口默认为6379
port: 6379
# 数据库索引
database: 0
# 密码(如没有密码请注释掉)
# password: 123456
# redis 密码必须配置
password: 123456
# 连接超时时间
timeout: 10S
timeout: 10s
# 是否开启ssl
ssl.enabled: false
# redisson 配置
redisson:
# redis key前缀
keyPrefix:
@@ -61,8 +98,8 @@ redisson:
nettyThreads: 8
# 单节点配置
singleServerConfig:
# 客户端名称
clientName: ${ruoyi.name}
# 客户端名称 不能用中文
clientName: ruoyi-ai
# 最小空闲连接数
connectionMinimumIdleSize: 8
# 连接池大小
@@ -74,35 +111,141 @@ redisson:
# 发布和订阅连接池大小
subscriptionConnectionPoolSize: 50
--- # sms 短信
sms:
--- # mail 邮件发送
mail:
enabled: false
# 阿里云 dysmsapi.aliyuncs.com
# 腾讯云 sms.tencentcloudapi.com
endpoint: "dysmsapi.aliyuncs.com"
accessKeyId: xxxxxxx
accessKeySecret: xxxxxx
signName: 测试
# 腾讯专
sdkAppId:
pdf:
extract:
service:
url: http://localhost:8080
ai-api:
url: https://api.pandarobot.chat/v1/chat/completions
key: sk-xxxx
transition:
# 是否开启mineru
enable-minerU: true
# mineru conda环境路径
conda-env-path: "F:\\ProgramData\\Computer\\Anaconda\\envs\\mineru"
# 是否开启图片OCR
enable-ocr: true
host: smtp.163.com
port: 465
# 是否需要用户名密码验证
auth: true
# 发送方遵循RFC-822标准
from: xxx@163.com
# 用户名注意如果使用foxmail邮箱此处user为qq号
user: xxx@163.com
# 密码注意某些邮箱需要为SMTP服务单独设置密码详情查看相关帮助
pass: xxxxxxxxxx
# 使用 STARTTLS安全连接STARTTLS是对纯文本通信协议的扩展。
starttlsEnable: true
# 使用SSL安全连接
sslEnable: true
# SMTP超时时长单位毫秒缺省值不超时
timeout: 0
# Socket连接超时值单位毫秒缺省值不超时
connectionTimeout: 0
--- # sms 短信 支持 阿里云 腾讯云 云片 等等各式各样的短信服务商
# https://sms4j.com/doc3/ 差异配置文档地址 支持单厂商多配置,可以配置多个同时使用
sms:
# 配置源类型用于标定配置来源(interface,yaml)
config-type: yaml
# 用于标定yml中的配置是否开启短信拦截接口配置不受此限制
restricted: true
# 短信拦截限制单手机号每分钟最大发送,只对开启了拦截的配置有效
minute-max: 1
# 短信拦截限制单手机号每日最大发送量,只对开启了拦截的配置有效
account-max: 30
# 以下配置来自于 org.dromara.sms4j.provider.config.BaseConfig类中
blends:
# 唯一ID 用于发送短信寻找具体配置 随便定义别用中文即可
# 可以同时存在两个相同厂商 例如: ali1 ali2 两个不同的阿里短信账号 也可用于区分租户
config1:
# 框架定义的厂商名称标识,标定此配置是哪个厂商,详细请看厂商标识介绍部分
supplier: alibaba
# 有些称为accessKey有些称之为apiKey也有称为sdkKey或者appId。
access-key-id: 您的accessKey
# 称为accessSecret有些称之为apiSecret
access-key-secret: 您的accessKeySecret
signature: 您的短信签名
sdk-app-id: 您的sdkAppId
config2:
# 厂商标识,标定此配置是哪个厂商,详细请看厂商标识介绍部分
supplier: tencent
access-key-id: 您的accessKey
access-key-secret: 您的accessKeySecret
signature: 您的短信签名
sdk-app-id: 您的sdkAppId
--- # 三方授权
justauth:
# 前端外网访问地址
address: http://localhost:80
type:
maxkey:
# maxkey 服务器地址
# 注意 如下均配置均不需要修改 maxkey 已经内置好了数据
server-url: http://sso.maxkey.top
client-id: 876892492581044224
client-secret: x1Y5MTMwNzIwMjMxNTM4NDc3Mzche8
redirect-uri: ${justauth.address}/social-callback?source=maxkey
topiam:
# topiam 服务器地址
server-url: http://127.0.0.1:1898/api/v1/authorize/y0q************spq***********8ol
client-id: 449c4*********937************759
client-secret: ac7***********1e0************28d
redirect-uri: ${justauth.address}/social-callback?source=topiam
scopes: [openid, email, phone, profile]
qq:
client-id: 10**********6
client-secret: 1f7d08**********5b7**********29e
redirect-uri: ${justauth.address}/social-callback?source=qq
union-id: false
weibo:
client-id: 10**********6
client-secret: 1f7d08**********5b7**********29e
redirect-uri: ${justauth.address}/social-callback?source=weibo
gitee:
client-id: 91436b7940090d09c72c7daf85b959cfd5f215d67eea73acbf61b6b590751a98
client-secret: 02c6fcfd70342980cd8dd2f2c06c1a350645d76c754d7a264c4e125f9ba915ac
redirect-uri: ${justauth.address}/social-callback?source=gitee
dingtalk:
client-id: 10**********6
client-secret: 1f7d08**********5b7**********29e
redirect-uri: ${justauth.address}/social-callback?source=dingtalk
baidu:
client-id: 10**********6
client-secret: 1f7d08**********5b7**********29e
redirect-uri: ${justauth.address}/social-callback?source=baidu
csdn:
client-id: 10**********6
client-secret: 1f7d08**********5b7**********29e
redirect-uri: ${justauth.address}/social-callback?source=csdn
coding:
client-id: 10**********6
client-secret: 1f7d08**********5b7**********29e
redirect-uri: ${justauth.address}/social-callback?source=coding
coding-group-name: xx
oschina:
client-id: 10**********6
client-secret: 1f7d08**********5b7**********29e
redirect-uri: ${justauth.address}/social-callback?source=oschina
alipay_wallet:
client-id: 10**********6
client-secret: 1f7d08**********5b7**********29e
redirect-uri: ${justauth.address}/social-callback?source=alipay_wallet
alipay-public-key: MIIB**************DAQAB
wechat_open:
client-id: 10**********6
client-secret: 1f7d08**********5b7**********29e
redirect-uri: ${justauth.address}/social-callback?source=wechat_open
wechat_mp:
client-id: 10**********6
client-secret: 1f7d08**********5b7**********29e
redirect-uri: ${justauth.address}/social-callback?source=wechat_mp
wechat_enterprise:
client-id: 10**********6
client-secret: 1f7d08**********5b7**********29e
redirect-uri: ${justauth.address}/social-callback?source=wechat_enterprise
agent-id: 1000002
gitlab:
client-id: 10**********6
client-secret: 1f7d08**********5b7**********29e
redirect-uri: ${justauth.address}/social-callback?source=gitlab
gitea:
# 前端改动 https://gitee.com/JavaLionLi/plus-ui/pulls/204
# gitea 服务器地址
server-url: https://demo.gitea.com
client-id: 10**********6
client-secret: 1f7d08**********5b7**********29e
redirect-uri: ${justauth.address}/social-callback?source=gitea

View File

@@ -1,29 +1,6 @@
# 项目相关配置
ruoyi:
# 名称
name: "ruoyi-ai"
# 版本
version: ${revision}
# 版权年份
copyrightYear: 2025
# 实例演示开关
demoEnabled: false
captcha:
enable: false
# 页面 <参数设置> 可开启关闭 验证码校验
# 验证码类型 math 数组计算 char 字符验证
type: MATH
# line 线段干扰 circle 圆圈干扰 shear 扭曲干扰
category: CIRCLE
# 数字验证码位数
numberLength: 1
# 字符验证码长度
charLength: 4
# 开发环境配置
server:
# 服务器的HTTP端口默认为8080
# 服务器的HTTP端口默认为6039
port: 6039
servlet:
# 应用的访问路径
@@ -43,6 +20,18 @@ server:
# 阻塞任务线程池, 当执行类似servlet请求阻塞操作, undertow会从这个线程池中取得线程,它的值设置取决于系统的负载
worker: 256
captcha:
# 是否启用验证码校验
enable: false
# 验证码类型 math 数组计算 char 字符验证
type: MATH
# line 线段干扰 circle 圆圈干扰 shear 扭曲干扰
category: CIRCLE
# 数字验证码位数
numberLength: 1
# 字符验证码长度
charLength: 4
# 日志配置
logging:
level:
@@ -63,7 +52,24 @@ user:
# Spring配置
spring:
application:
name: ${ruoyi.name}
name: ruoyi-ai
# ⚠️ 禁用 Spring Boot 的 Neo4j 自动配置
# 默认情况下,如果类路径上存在 neo4j-java-driverSpring Boot 会尝试自动配置
# 这会导致应用在启动时尝试连接到 Neo4j即使我们没有需要它
autoconfigure:
exclude:
- org.springframework.boot.autoconfigure.neo4j.Neo4jAutoConfiguration
threads:
# 开启虚拟线程 仅jdk21可用
virtual:
enabled: false
task:
execution:
# 从 springboot 3.5 开始 spring自带线程池
# 不再需要 AsyncConfig与ThreadPoolConfig 可直接注入线程池使用
thread-name-prefix: async-
# 由spring自己初始化线程池
mode: force
# 资源信息
messages:
# 国际化资源文件路径
@@ -74,10 +80,12 @@ spring:
servlet:
multipart:
# 单个文件大小
max-file-size: 50MB
max-file-size: 10MB
# 设置总上传的文件大小
max-request-size: 200MB
max-request-size: 20MB
mvc:
# 设置静态资源路径 防止所有请求都去查静态资源
static-path-pattern: /static/**
format:
date-time: yyyy-MM-dd HH:mm:ss
jackson:
@@ -96,20 +104,10 @@ spring:
sa-token:
# token名称 (同时也是cookie名称)
token-name: Authorization
# token有效期 设为7天 (必定过期) 单位: 秒
timeout: 604800
# token临时有效期 (指定时间无操作就过期) 单位: 秒
activity-timeout: 604800
# 是否允许同一账号并发登录 (为true时允许一起登录, 为false时新登录挤掉旧登录)
is-concurrent: true
# 在多人登录同一账号时是否共用一个token (为true时所有登录共用一个token, 为false时每次登录新建一个token)
is-share: false
# 是否尝试从header里读取token
is-read-header: true
# 是否尝试从cookie里读取token
is-read-cookie: false
# token前缀
token-prefix: "Bearer"
# jwt秘钥
jwt-secret-key: abcdefghijklmnopqrstuvwxyz
@@ -117,51 +115,20 @@ sa-token:
security:
# 排除路径
excludes:
# 获取模型信息
- /system/model/modelList
# 支付回调
- /pay/returnUrl
- /pay/notifyUrl
# 上传文件
- /resource/oss/upload
# 重置密码
- /auth/reset/password
# 聊天接口
- /chat/send
# 文件上传
- /chat/upload
# 代码生成调用
- /tool/gen/getByTableName
- /tool/gen/batchGenCode
# 静态资源
- /*.html
- /**/*.html
- /**/*.css
- /**/*.js
# 公共路径
- /favicon.ico
- /error
# swagger 文档配置
- /*/api-docs
- /*/api-docs/**
- /v3/api-docs
- /v3/api-docs/**
- /v3/api-docs/swagger-config
- /swagger-ui.html
- /swagger-ui/**
- /doc.html
- /webjars/**
- /swagger-resources
- /swagger-resources/**
# actuator 监控配置
- /actuator
- /actuator/**
- /workflow/**
- /admin/workflow/**
- /warm-flow-ui/config
# 多租户配置
tenant:
# 是否开启
enable: false
enable: true
# 排除表
excludes:
- sys_menu
@@ -171,53 +138,27 @@ tenant:
- sys_role_menu
- sys_user_post
- sys_user_role
knowledge-role:
enable: false
- sys_client
- sys_oss_config
- flow_spel
# MyBatisPlus配置
# https://baomidou.com/config/
mybatis-plus:
# 不支持多包, 如有需要可在注解配置 或 提升扫包等级
# 例如 com.**.**.mapper
# 自定义配置 是否全局开启逻辑删除 关闭后 所有逻辑删除功能将失效
enableLogicDelete: true
# 多包名使用 例如 org.ruoyi.**.mapper,org.xxx.**.mapper
mapperPackage: org.ruoyi.**.mapper
# 对应的 XML 文件位置
mapperLocations: classpath*:mapper/**/*Mapper.xml
# 实体扫描多个package用逗号或者分号分隔
typeAliasesPackage: org.ruoyi.**.domain
# 启动时是否检查 MyBatis XML 文件的存在,默认不检查
checkConfigLocation: false
configuration:
# 自动驼峰命名规则camel case映射
mapUnderscoreToCamelCase: true
# MyBatis 自动映射策略
# NONE不启用 PARTIAL只对非嵌套 resultMap 自动映射 FULL对所有 resultMap 自动映射
autoMappingBehavior: FULL
# MyBatis 自动映射时未知列或未知属性处理策
# NONE不做处理 WARNING打印相关警告 FAILING抛出异常和详细信息
autoMappingUnknownColumnBehavior: NONE
# 更详细的日志输出 会有性能损耗 org.apache.ibatis.logging.stdout.StdOutImpl
# 关闭日志记录 (可单纯使用 p6spy 分析) org.apache.ibatis.logging.nologging.NoLoggingImpl
# 默认日志输出 org.apache.ibatis.logging.slf4j.Slf4jImpl
logImpl: org.apache.ibatis.logging.slf4j.Slf4jImpl
global-config:
# 是否打印 Logo banner
banner: true
dbConfig:
# 主键类型
# AUTO 自增 NONE 空 INPUT 用户输入 ASSIGN_ID 雪花 ASSIGN_UUID 唯一 UUID
# 如需改为自增 需要将数据库表全部设置为自增
idType: ASSIGN_ID
# 逻辑已删除值
logicDeleteValue: 2
# 逻辑未删除值
logicNotDeleteValue: 0
# 字段验证策略之 insert,在 insert 的时候的字段验证策略
# IGNORED 忽略 NOT_NULL 非NULL NOT_EMPTY 非空 DEFAULT 默认 NEVER 不加入 SQL
insertStrategy: NOT_NULL
# 字段验证策略之 update,在 update 的时候的字段验证策略
updateStrategy: NOT_NULL
# 字段验证策略之 select,在 select 的时候的字段验证策略既 wrapper 根据内部 entity 生成的 where 条件
where-strategy: NOT_NULL
# 数据加密
mybatis-encryptor:
@@ -233,57 +174,55 @@ mybatis-encryptor:
publicKey:
privateKey:
# api接口加密
api-decrypt:
# 是否开启全局接口加密
enabled: false
# AES 加密头标识
headerFlag: encrypt-key
# 响应加密公钥 非对称算法的公私钥 如SM2RSA 使用者请自行更换
# 对应前端解密私钥 MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAmc3CuPiGL/LcIIm7zryCEIbl1SPzBkr75E2VMtxegyZ1lYRD+7TZGAPkvIsBcaMs6Nsy0L78n2qh+lIZMpLH8wIDAQABAkEAk82Mhz0tlv6IVCyIcw/s3f0E+WLmtPFyR9/WtV3Y5aaejUkU60JpX4m5xNR2VaqOLTZAYjW8Wy0aXr3zYIhhQQIhAMfqR9oFdYw1J9SsNc+CrhugAvKTi0+BF6VoL6psWhvbAiEAxPPNTmrkmrXwdm/pQQu3UOQmc2vCZ5tiKpW10CgJi8kCIFGkL6utxw93Ncj4exE/gPLvKcT+1Emnoox+O9kRXss5AiAMtYLJDaLEzPrAWcZeeSgSIzbL+ecokmFKSDDcRske6QIgSMkHedwND1olF8vlKsJUGK3BcdtM8w4Xq7BpSBwsloE=
publicKey: MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAJnNwrj4hi/y3CCJu868ghCG5dUj8wZK++RNlTLcXoMmdZWEQ/u02RgD5LyLAXGjLOjbMtC+/J9qofpSGTKSx/MCAwEAAQ==
# 请求解密私钥 非对称算法的公私钥 如SM2RSA 使用者请自行更换
# 对应前端加密公钥 MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoR8mX0rGKLqzcWmOzbfj64K8ZIgOdHnzkXSOVOZbFu/TJhZ7rFAN+eaGkl3C4buccQd/EjEsj9ir7ijT7h96MCAwEAAQ==
privateKey: MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAqhHyZfSsYourNxaY7Nt+PrgrxkiA50efORdI5U5lsW79MmFnusUA355oaSXcLhu5xxB38SMSyP2KvuKNPuH3owIDAQABAkAfoiLyL+Z4lf4Myxk6xUDgLaWGximj20CUf+5BKKnlrK+Ed8gAkM0HqoTt2UZwA5E2MzS4EI2gjfQhz5X28uqxAiEA3wNFxfrCZlSZHb0gn2zDpWowcSxQAgiCstxGUoOqlW8CIQDDOerGKH5OmCJ4Z21v+F25WaHYPxCFMvwxpcw99EcvDQIgIdhDTIqD2jfYjPTY8Jj3EDGPbH2HHuffvflECt3Ek60CIQCFRlCkHpi7hthhYhovyloRYsM+IS9h/0BzlEAuO0ktMQIgSPT3aFAgJYwKpqRYKlLDVcflZFCKY7u3UP8iWi1Qw0Y=
springdoc:
api-docs:
# 是否开启接口文档
enabled: true
# swagger-ui:
# # 持久化认证数据
# persistAuthorization: true
info:
# 标题
title: '标题ruoyi-ai 接口文档'
title: '标题ruoyi-ai管理系统_接口文档'
# 描述
description: ''
description: '描述:用于管理集团旗下公司的人员信息,具体包括XXX,XXX模块...'
# 版本
version: '版本号: ${ruoyi.version}'
version: '版本号: ${project.version}'
# 作者信息
contact:
name: ageerle
name: ageerle
email: ageerle@163.com
url: https://gitee.com/ageerle/ruoyi-ai
components:
# 鉴权方式配置
security-schemes:
apiKey:
type: APIKEY
in: HEADER
name: ${sa-token.token-name}
#这里定义了两个分组,可定义多个,也可以不定义
packages-to-scan: org.ruoyi
knife4j:
enable: true
setting:
language: zh_cn
group-configs:
- group: 1.演示模块
packages-to-scan: org.ruoyi.demo
- group: 2.通用模块
packages-to-scan: org.ruoyi.web
- group: 3.系统模块
packages-to-scan: org.ruoyi.system
- group: 4.代码生成模块
packages-to-scan: org.ruoyi.generator
- group: 5.工作流模块
packages-to-scan: org.ruoyi.workflow
# 防止XSS攻击
xss:
# 过滤开关
enabled: true
# 排除链接(多个用逗号分隔)
excludes: /system/notice,/v3/api-docs/**,/doc.html,/swagger-ui/**,/swagger-ui.html,/swagger-resources/**,/webjars/**,/druid/**,/actuator/**,/favicon.ico
# 匹配链接
urlPatterns: /system/*,/monitor/*,/tool/*
# 全局线程池相关配置
thread-pool:
# 是否开启线程池
enabled: false
# 队列最大长度
queueCapacity: 128
# 线程池维护线程所允许的空闲时间
keepAliveSeconds: 300
# 排除链接
excludeUrls:
- /system/notice
--- # 分布式锁 lock4j 全局配置
lock4j:
@@ -304,42 +243,44 @@ management:
logfile:
external-file: ./logs/sys-console.log
health:
# 禁用 Neo4j 健康检查(当知识图谱功能未启用时)
# ⚠️ 禁用 Neo4j 健康检查
# Spring Boot Actuator 会自动为 Neo4j 创建健康检查器
# 这会导致应用在启动时尝试连接到 Neo4j
neo4j:
enabled: false
# websocket
# websocket
websocket:
--- # 默认/推荐使用sse推送
sse:
enabled: true
path: /resource/sse
--- # websocket
websocket:
# 如果关闭 需要和前端开关一起关闭
enabled: false
# 路径
path: '/resource/websocket'
path: /resource/websocket
# 设置访问源地址
allowedOrigins: '*'
spring:
ai:
openai:
api-key: sk-xx
base-url: https://api.pandarobot.chat/
mcp:
client:
enabled: false
name: ruoyi-ai-mcp
sse:
connections:
server:
url: http://127.0.0.1:8081
stdio:
servers-configuration: classpath:mcp-server.json
request-timeout: 300s
--- # warm-flow工作流配置
warm-flow:
# 是否开启工作流默认true
enabled: true
# 是否开启设计器ui
ui: true
# 是否显示流程图顶部文字
top-text-show: true
# 是否渲染节点悬浮提示默认true
node-tooltip: true
# 默认Authorization如果有多个token用逗号分隔
token-name: ${sa-token.token-name},clientid
# 向量库配置
vector-store:
# 向量存储类型 可选(weaviate/milvus)
# 如需修改向量库类型,请修改此配置值!
type: weaviate
# Weaviate配置
weaviate:
protocol: http
@@ -350,15 +291,29 @@ vector-store:
url: http://localhost:19530
collectionname: LocalKnowledge
chat:
memory:
enabled: true
maxMessages: 20
persistenceEnabled: true
# 企业微信应用
wechat:
cp:
corpId:
appConfigs:
- agentId:
secret: ''
token: ''
aesKey: ''
--- # Neo4j 知识图谱配置
#neo4j:
# uri: bolt://117.72.192.162:7687
# username: neo4j
# password: MySecurePass123!
# database: neo4j
# max-connection-pool-size: 50
# connection-timeout-seconds: 30
neo4j:
uri: bolt://117.72.192.162:7687
username: neo4j
password: MySecurePass123!
database: neo4j
max-connection-pool-size: 50
connection-timeout-seconds: 30
# 知识图谱配置
knowledge:
@@ -401,4 +356,3 @@ knowledge:
cache-enabled: true
# 缓存过期时间(分钟)
cache-expire-minutes: 60

View File

@@ -1,2 +1,8 @@
Application Version: ${revision}
Spring Boot Version: ${spring-boot.version}
__________ _____.___.__ _____ .___
\______ \__ __ ____\__ | |__| / _ \ | |
| _/ | \/ _ \/ | | | ______ / /_\ \| |
| | \ | ( <_> )____ | | /_____/ / | \ |
|____|_ /____/ \____// ______|__| \____|__ /___|
\/ \/ \/

View File

@@ -17,6 +17,7 @@ user.username.length.valid=账户长度必须在{min}到{max}个字符之间
user.password.not.blank=用户密码不能为空
user.password.length.valid=用户密码长度必须在{min}到{max}个字符之间
user.password.not.valid=* 5-50个字符
user.password.format.valid=密码必须包含大写字母、小写字母、数字和特殊字符
user.email.not.valid=邮箱格式错误
user.email.not.blank=邮箱不能为空
user.phonenumber.not.blank=用户手机号不能为空
@@ -28,6 +29,10 @@ user.register.error=注册失败,请联系系统管理人员
user.notfound=请重新登录
user.forcelogout=管理员强制退出,请重新登录
user.unknown.error=未知错误,请重新登录
auth.grant.type.error=认证权限类型错误
auth.grant.type.blocked=认证权限类型已禁用
auth.grant.type.not.blank=认证权限类型不能为空
auth.clientid.not.blank=认证客户端id不能为空
##文件上传消息
upload.exceed.maxSize=上传的文件大小超出限制的文件大小!<br/>允许的文件最大大小是:{0}MB
upload.filename.exceed.length=上传的文件名最长{0}个字符
@@ -46,7 +51,10 @@ sms.code.retry.limit.exceed=短信验证码输入错误{0}次,帐户锁定{1}
email.code.not.blank=邮箱验证码不能为空
email.code.retry.limit.count=邮箱验证码输入错误{0}次
email.code.retry.limit.exceed=邮箱验证码输入错误{0}次,帐户锁定{1}分钟
xcx.code.not.blank=小程序code不能为空
xcx.code.not.blank=小程序[code]不能为空
social.source.not.blank=第三方登录平台[source]不能为空
social.code.not.blank=第三方登录平台[code]不能为空
social.state.not.blank=第三方登录平台[state]不能为空
##租户
tenant.number.not.blank=租户编号不能为空
tenant.not.exists=对不起, 您的租户不存在,请联系管理员

View File

@@ -17,6 +17,7 @@ user.username.length.valid=Account length must be between {min} and {max} charac
user.password.not.blank=Password cannot be empty
user.password.length.valid=Password length must be between {min} and {max} characters
user.password.not.valid=* 5-50 characters
user.password.format.valid=Password must contain uppercase, lowercase, digit, and special character
user.email.not.valid=Mailbox format error
user.email.not.blank=Mailbox cannot be blank
user.phonenumber.not.blank=Phone number cannot be blank
@@ -28,6 +29,10 @@ user.register.error=Register failed, please contact system administrator
user.notfound=Please login again
user.forcelogout=The administrator is forced to exitplease login again
user.unknown.error=Unknown error, please login again
auth.grant.type.error=Auth grant type error
auth.grant.type.blocked=Auth grant type disabled
auth.grant.type.not.blank=Auth grant type cannot be blank
auth.clientid.not.blank=Auth clientid cannot be blank
##文件上传消息
upload.exceed.maxSize=The uploaded file size exceeds the limit file size<br/>the maximum allowed file size is{0}MB
upload.filename.exceed.length=The maximum length of uploaded file name is {0} characters
@@ -46,7 +51,10 @@ sms.code.retry.limit.exceed=Sms code input error {0} times, account locked for {
email.code.not.blank=Email code cannot be blank
email.code.retry.limit.count=Email code input error {0} times
email.code.retry.limit.exceed=Email code input error {0} times, account locked for {1} minutes
xcx.code.not.blank=Mini program code cannot be blank
xcx.code.not.blank=Mini program [code] cannot be blank
social.source.not.blank=Social login platform [source] cannot be blank
social.code.not.blank=Social login platform [code] cannot be blank
social.state.not.blank=Social login platform [state] cannot be blank
##租户
tenant.number.not.blank=Tenant number cannot be blank
tenant.not.exists=Sorry, your tenant does not exist. Please contact the administrator

View File

@@ -17,6 +17,7 @@ user.username.length.valid=账户长度必须在{min}到{max}个字符之间
user.password.not.blank=用户密码不能为空
user.password.length.valid=用户密码长度必须在{min}到{max}个字符之间
user.password.not.valid=* 5-50个字符
user.password.format.valid=密码必须包含大写字母、小写字母、数字和特殊字符
user.email.not.valid=邮箱格式错误
user.email.not.blank=邮箱不能为空
user.phonenumber.not.blank=用户手机号不能为空
@@ -28,6 +29,10 @@ user.register.error=注册失败,请联系系统管理人员
user.notfound=请重新登录
user.forcelogout=管理员强制退出,请重新登录
user.unknown.error=未知错误,请重新登录
auth.grant.type.error=认证权限类型错误
auth.grant.type.blocked=认证权限类型已禁用
auth.grant.type.not.blank=认证权限类型不能为空
auth.clientid.not.blank=认证客户端id不能为空
##文件上传消息
upload.exceed.maxSize=上传的文件大小超出限制的文件大小!<br/>允许的文件最大大小是:{0}MB
upload.filename.exceed.length=上传的文件名最长{0}个字符
@@ -46,7 +51,10 @@ sms.code.retry.limit.exceed=短信验证码输入错误{0}次,帐户锁定{1}
email.code.not.blank=邮箱验证码不能为空
email.code.retry.limit.count=邮箱验证码输入错误{0}次
email.code.retry.limit.exceed=邮箱验证码输入错误{0}次,帐户锁定{1}分钟
xcx.code.not.blank=小程序code不能为空
xcx.code.not.blank=小程序[code]不能为空
social.source.not.blank=第三方登录平台[source]不能为空
social.code.not.blank=第三方登录平台[code]不能为空
social.state.not.blank=第三方登录平台[state]不能为空
##租户
tenant.number.not.blank=租户编号不能为空
tenant.not.exists=对不起, 您的租户不存在,请联系管理员

View File

@@ -2,7 +2,7 @@
<configuration>
<property name="log.path" value="./logs"/>
<property name="console.log.pattern"
value="%red(%d{yyyy-MM-dd HH:mm:ss}) %green([%thread]) %highlight(%-5level) %boldMagenta(%logger{36}%n) - %msg%n"/>
value="%cyan(%d{yyyy-MM-dd HH:mm:ss}) %green([%thread]) %highlight(%-5level) %boldMagenta(%logger{36}%n) - %msg%n"/>
<property name="log.pattern" value="%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"/>
<!-- 控制台输出 -->
@@ -38,7 +38,7 @@
<!-- 循环政策:基于时间创建日志文件 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 日志文件名格式 -->
<fileNamePattern>${log.path}/sys-info.%d{yyyy-MM-dd}.log</fileNamePattern>
<fileNamePattern>${log.path}/sys-info.%d{yyyy-MM-dd}.log.gz</fileNamePattern>
<!-- 日志最大的历史 60天 -->
<maxHistory>60</maxHistory>
</rollingPolicy>
@@ -60,7 +60,7 @@
<!-- 循环政策:基于时间创建日志文件 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 日志文件名格式 -->
<fileNamePattern>${log.path}/sys-error.%d{yyyy-MM-dd}.log</fileNamePattern>
<fileNamePattern>${log.path}/sys-error.%d{yyyy-MM-dd}.log.gz</fileNamePattern>
<!-- 日志最大的历史 60天 -->
<maxHistory>60</maxHistory>
</rollingPolicy>
@@ -98,32 +98,32 @@
</appender>
<!-- 整合 skywalking 控制台输出 tid -->
<!-- <appender name="console" class="ch.qos.logback.core.ConsoleAppender">-->
<!-- <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">-->
<!-- <layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.TraceIdPatternLogbackLayout">-->
<!-- <pattern>[%tid] ${console.log.pattern}</pattern>-->
<!-- </layout>-->
<!-- <charset>utf-8</charset>-->
<!-- </encoder>-->
<!-- </appender>-->
<!-- <appender name="console" class="ch.qos.logback.core.ConsoleAppender">-->
<!-- <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">-->
<!-- <layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.TraceIdPatternLogbackLayout">-->
<!-- <pattern>[%tid] ${console.log.pattern}</pattern>-->
<!-- </layout>-->
<!-- <charset>utf-8</charset>-->
<!-- </encoder>-->
<!-- </appender>-->
<!-- 整合 skywalking 推送采集日志 -->
<!-- <appender name="sky_log" class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.log.GRPCLogClientAppender">-->
<!-- <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">-->
<!-- <layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.TraceIdPatternLogbackLayout">-->
<!-- <pattern>[%tid] ${console.log.pattern}</pattern>-->
<!-- </layout>-->
<!-- <charset>utf-8</charset>-->
<!-- </encoder>-->
<!-- </appender>-->
<!-- <appender name="sky_log" class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.log.GRPCLogClientAppender">-->
<!-- <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">-->
<!-- <layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.TraceIdPatternLogbackLayout">-->
<!-- <pattern>[%tid] ${console.log.pattern}</pattern>-->
<!-- </layout>-->
<!-- <charset>utf-8</charset>-->
<!-- </encoder>-->
<!-- </appender>-->
<!--系统操作日志-->
<root level="info">
<appender-ref ref="console"/>
<appender-ref ref="async_info"/>
<appender-ref ref="async_error"/>
<appender-ref ref="file_console"/>
<!-- <appender-ref ref="sky_log"/>-->
<appender-ref ref="console" />
<appender-ref ref="async_info" />
<appender-ref ref="async_error" />
<appender-ref ref="file_console" />
<!-- <appender-ref ref="sky_log"/>-->
</root>
</configuration>

View File

@@ -1,22 +0,0 @@
{
"mcpServers": {
"fileSystem": {
"command": "C:\\Program Files\\nodejs\\npx.cmd",
"args": [
"-y",
"@modelcontextprotocol/server-filesystem",
"D:\\"
]
},
"search1api": {
"command": "C:\\Program Files\\nodejs\\npx.cmd",
"args": [
"-y",
"search1api-mcp"
],
"env": {
"SEARCH1API_KEY": "xx"
}
}
}
}

View File

@@ -1 +0,0 @@
exclude=SELECT 1

Binary file not shown.

Before

Width:  |  Height:  |  Size: 78 KiB

View File

@@ -1,28 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>ruoyi-ai</artifactId>
<groupId>org.ruoyi</groupId>
<version>${revision}</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>ruoyi-common</artifactId>
<packaging>pom</packaging>
<description>
common 通用模块
</description>
<modelVersion>4.0.0</modelVersion>
<modules>
<module>ruoyi-common-bom</module>
<module>ruoyi-common-social</module>
<module>ruoyi-common-core</module>
<module>ruoyi-common-doc</module>
<module>ruoyi-common-excel</module>
<module>ruoyi-common-idempotent</module>
<module>ruoyi-common-job</module>
<module>ruoyi-common-log</module>
<module>ruoyi-common-mail</module>
<module>ruoyi-common-mybatis</module>
@@ -38,8 +32,15 @@
<module>ruoyi-common-json</module>
<module>ruoyi-common-encrypt</module>
<module>ruoyi-common-tenant</module>
<module>ruoyi-common-chat</module>
<module>ruoyi-common-pay</module>
<module>ruoyi-common-websocket</module>
<module>ruoyi-common-sse</module>
</modules>
<artifactId>ruoyi-common</artifactId>
<packaging>pom</packaging>
<description>
common 通用模块
</description>
</project>

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
@@ -14,7 +14,7 @@
</description>
<properties>
<revision>1.0.0</revision>
<revision>3.0.0</revision>
</properties>
<dependencyManagement>
@@ -47,6 +47,13 @@
<version>${revision}</version>
</dependency>
<!-- 调度模块 -->
<dependency>
<groupId>org.ruoyi</groupId>
<artifactId>ruoyi-common-job</artifactId>
<version>${revision}</version>
</dependency>
<!-- 日志记录 -->
<dependency>
<groupId>org.ruoyi</groupId>
@@ -110,6 +117,12 @@
<version>${revision}</version>
</dependency>
<dependency>
<groupId>org.ruoyi</groupId>
<artifactId>ruoyi-common-social</artifactId>
<version>${revision}</version>
</dependency>
<!-- web服务 -->
<dependency>
<groupId>org.ruoyi</groupId>
@@ -152,29 +165,21 @@
<version>${revision}</version>
</dependency>
<!-- chat模块 -->
<!-- WebSocket模块 -->
<dependency>
<groupId>org.ruoyi</groupId>
<artifactId>ruoyi-common-chat</artifactId>
<artifactId>ruoyi-common-websocket</artifactId>
<version>${revision}</version>
</dependency>
<!-- 微信模块 -->
<!-- SSE模块 -->
<dependency>
<groupId>org.ruoyi</groupId>
<artifactId>ruoyi-common-wechat</artifactId>
<artifactId>ruoyi-common-sse</artifactId>
<version>${revision}</version>
</dependency>
<!-- 支付模块 -->
<dependency>
<groupId>org.ruoyi</groupId>
<artifactId>ruoyi-common-pay</artifactId>
<version>${revision}</version>
</dependency>
</dependencies>
</dependencyManagement>
</project>

View File

@@ -1,97 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>org.ruoyi</groupId>
<artifactId>ruoyi-common</artifactId>
<version>${revision}</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>ruoyi-common-chat</artifactId>
<description>
ruoyi-common-chat 模块
</description>
<properties>
<retrofit2.version>2.9.0</retrofit2.version>
<azure.version>1.0.0-beta.12</azure.version>
<chatglm.version>release-V4-2.3.0</chatglm.version>
<okhttp.version>2.7.5</okhttp.version>
<jtokkit.version>0.5.0</jtokkit.version>
</properties>
<dependencies>
<dependency>
<groupId>org.ruoyi</groupId>
<artifactId>ruoyi-common-core</artifactId>
</dependency>
<!-- 序列化模块 -->
<dependency>
<groupId>org.ruoyi</groupId>
<artifactId>ruoyi-common-json</artifactId>
</dependency>
<!-- redis模块 -->
<dependency>
<groupId>org.ruoyi</groupId>
<artifactId>ruoyi-common-redis</artifactId>
</dependency>
<dependency>
<groupId>com.azure</groupId>
<artifactId>azure-ai-openai</artifactId>
<version>${azure.version}</version>
</dependency>
<dependency>
<groupId>com.squareup.retrofit2</groupId>
<artifactId>retrofit</artifactId>
<version>${retrofit2.version}</version>
</dependency>
<dependency>
<groupId>com.squareup.retrofit2</groupId>
<artifactId>converter-jackson</artifactId>
<version>${retrofit2.version}</version>
</dependency>
<dependency>
<groupId>com.squareup.retrofit2</groupId>
<artifactId>adapter-rxjava2</artifactId>
<version>${retrofit2.version}</version>
</dependency>
<dependency>
<groupId>com.knuddels</groupId>
<artifactId>jtokkit</artifactId>
<version>${jtokkit.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>cn.bigmodel.openapi</groupId>
<artifactId>oapi-java-sdk</artifactId>
<version>${chatglm.version}</version>
</dependency>
<dependency>
<groupId>com.squareup.okhttp</groupId>
<artifactId>okhttp</artifactId>
<version>${okhttp.version}</version>
</dependency>
</dependencies>
</project>

View File

@@ -1,32 +0,0 @@
package org.ruoyi.common.chat.config;
import cn.hutool.cache.CacheUtil;
import cn.hutool.cache.impl.TimedCache;
import cn.hutool.core.date.DateUnit;
import lombok.extern.slf4j.Slf4j;
/**
* @author https:www.unfbx.com
* @date 2023-03-10
*/
@Slf4j
public class LocalCache {
/**
* 缓存时长
*/
public static final long TIMEOUT = 30 * DateUnit.MINUTE.getMillis();
/**
* 缓存对象
*/
public static final TimedCache<String, Object> CACHE = CacheUtil.newTimedCache(TIMEOUT);
/**
* 清理间隔
*/
private static final long CLEAN_TIMEOUT = 30 * DateUnit.MINUTE.getMillis();
static {
//启动定时任务
CACHE.schedulePrune(CLEAN_TIMEOUT);
}
}

View File

@@ -1,15 +0,0 @@
package org.ruoyi.common.chat.constant;
/**
* @author https:www.unfbx.com
* @since 2023-03-06
*/
public class OpenAIConst {
public final static String OPENAI_HOST = "https://api.openai.com/";
public final static String apiUrl = "v1/chat/completions";
public final static int SUCCEED_CODE = 200;
}

View File

@@ -1,48 +0,0 @@
package org.ruoyi.common.chat.entity.Tts;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.*;
@Data
@Builder
@JsonInclude(JsonInclude.Include.NON_NULL)
@NoArgsConstructor
@AllArgsConstructor
public class TextToSpeech {
@Builder.Default
private String model = Model.TTS_1.getName();
/**
* 音频声音源
*
* @see TtsVoice
*/
private String voice;
/**
* 输入内容
*/
private String input;
/**
* 输出音频文件格式
*
* @see TtsFormat
*/
@JsonProperty("response_format")
private String responseFormat;
/**
* 速度调节默认是1取值范围0.25——4.0
*/
private Double speed;
@Getter
@AllArgsConstructor
public enum Model {
TTS_1("tts-1"),
TTS_1_HD("tts-1-hd"),
;
private final String name;
}
}

Some files were not shown because too many files have changed in this diff Show More