mirror of
https://gitcode.com/ageerle/ruoyi-ai.git
synced 2026-04-07 00:37:33 +00:00
Merge pull request #227 from MuSan-Li/feature_20250930_work_flow
Feature 20250930 work flow
This commit is contained in:
432
docs/工作流模块说明.md
Normal file
432
docs/工作流模块说明.md
Normal file
@@ -0,0 +1,432 @@
|
|||||||
|
# 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¤tPage=1&pageSize=10
|
||||||
|
|
||||||
|
# 搜索公开工作流
|
||||||
|
GET /workflow/public/search?keyword=关键词¤tPage=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¤tPage=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);
|
||||||
|
```
|
||||||
22
pom.xml
22
pom.xml
@@ -20,7 +20,7 @@
|
|||||||
<java.version>17</java.version>
|
<java.version>17</java.version>
|
||||||
<mysql.version>8.0.33</mysql.version>
|
<mysql.version>8.0.33</mysql.version>
|
||||||
<mybatis.version>3.5.16</mybatis.version>
|
<mybatis.version>3.5.16</mybatis.version>
|
||||||
<springdoc.version>2.8.5</springdoc.version>
|
<springdoc.version>2.8.13</springdoc.version>
|
||||||
<therapi-javadoc.version>0.15.0</therapi-javadoc.version>
|
<therapi-javadoc.version>0.15.0</therapi-javadoc.version>
|
||||||
<poi.version>5.2.3</poi.version>
|
<poi.version>5.2.3</poi.version>
|
||||||
<easyexcel.version>3.2.1</easyexcel.version>
|
<easyexcel.version>3.2.1</easyexcel.version>
|
||||||
@@ -265,13 +265,6 @@
|
|||||||
<version>${lock4j.version}</version>
|
<version>${lock4j.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!-- xxl-job-core -->
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.xuxueli</groupId>
|
|
||||||
<artifactId>xxl-job-core</artifactId>
|
|
||||||
<version>${xxl-job.version}</version>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.alibaba</groupId>
|
<groupId>com.alibaba</groupId>
|
||||||
<artifactId>transmittable-thread-local</artifactId>
|
<artifactId>transmittable-thread-local</artifactId>
|
||||||
@@ -348,6 +341,19 @@
|
|||||||
<version>${revision}</version>
|
<version>${revision}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.ruoyi</groupId>
|
||||||
|
<artifactId>ruoyi-workflow</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.ruoyi</groupId>
|
||||||
|
<artifactId>ruoyi-workflow-api</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</dependencyManagement>
|
</dependencyManagement>
|
||||||
|
|
||||||
|
|||||||
@@ -57,6 +57,11 @@
|
|||||||
<artifactId>ruoyi-generator</artifactId>
|
<artifactId>ruoyi-generator</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.ruoyi</groupId>
|
||||||
|
<artifactId>ruoyi-workflow</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.ruoyi</groupId>
|
<groupId>org.ruoyi</groupId>
|
||||||
<artifactId>ruoyi-aihuman</artifactId>
|
<artifactId>ruoyi-aihuman</artifactId>
|
||||||
|
|||||||
@@ -16,9 +16,9 @@ spring:
|
|||||||
master:
|
master:
|
||||||
type: ${spring.datasource.type}
|
type: ${spring.datasource.type}
|
||||||
driverClassName: com.mysql.cj.jdbc.Driver
|
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
|
url: jdbc:mysql://47.112.190.27:3306/ruoyi-ai?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true
|
||||||
username: root
|
username: ruoyi-ai
|
||||||
password: root
|
password: 5YEAWhSFZXKaMGxi
|
||||||
|
|
||||||
hikari:
|
hikari:
|
||||||
# 最大连接池数量
|
# 最大连接池数量
|
||||||
@@ -37,6 +37,8 @@ spring:
|
|||||||
connectionTestQuery: SELECT 1
|
connectionTestQuery: SELECT 1
|
||||||
# 多久检查一次连接的活性
|
# 多久检查一次连接的活性
|
||||||
keepaliveTime: 30000
|
keepaliveTime: 30000
|
||||||
|
mail:
|
||||||
|
username: xx
|
||||||
|
|
||||||
--- # redis 单机配置(单机与集群只能开启一个另一个需要注释掉)
|
--- # redis 单机配置(单机与集群只能开启一个另一个需要注释掉)
|
||||||
spring.data:
|
spring.data:
|
||||||
@@ -102,5 +104,13 @@ pdf:
|
|||||||
#百炼模型配置
|
#百炼模型配置
|
||||||
dashscope:
|
dashscope:
|
||||||
key: sk-xxxx
|
key: sk-xxxx
|
||||||
model: qvq-max
|
|
||||||
|
local:
|
||||||
|
images: xx
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
files: xx
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -156,6 +156,8 @@ security:
|
|||||||
# actuator 监控配置
|
# actuator 监控配置
|
||||||
- /actuator
|
- /actuator
|
||||||
- /actuator/**
|
- /actuator/**
|
||||||
|
- /workflow/**
|
||||||
|
- /admin/workflow/**
|
||||||
# 多租户配置
|
# 多租户配置
|
||||||
tenant:
|
tenant:
|
||||||
# 是否开启
|
# 是否开启
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
<module>ruoyi-chat-api</module>
|
<module>ruoyi-chat-api</module>
|
||||||
<module>ruoyi-knowledge-api</module>
|
<module>ruoyi-knowledge-api</module>
|
||||||
<module>ruoyi-system-api</module>
|
<module>ruoyi-system-api</module>
|
||||||
|
<module>ruoyi-workflow-api</module>
|
||||||
</modules>
|
</modules>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import java.io.Serializable;
|
|||||||
*/
|
*/
|
||||||
@Data
|
@Data
|
||||||
@ExcelIgnoreUnannotated
|
@ExcelIgnoreUnannotated
|
||||||
@AutoMapper(target = ChatConfig.class)
|
@AutoMapper(target = ChatConfig.class)
|
||||||
public class ChatConfigVo implements Serializable {
|
public class ChatConfigVo implements Serializable {
|
||||||
|
|
||||||
@Serial
|
@Serial
|
||||||
|
|||||||
133
ruoyi-modules-api/ruoyi-workflow-api/pom.xml
Normal file
133
ruoyi-modules-api/ruoyi-workflow-api/pom.xml
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
<?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">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<parent>
|
||||||
|
<groupId>org.ruoyi</groupId>
|
||||||
|
<artifactId>ruoyi-modules-api</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
<relativePath>../pom.xml</relativePath>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<artifactId>ruoyi-workflow-api</artifactId>
|
||||||
|
|
||||||
|
<description>
|
||||||
|
工作流API模块
|
||||||
|
</description>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<maven.compiler.source>17</maven.compiler.source>
|
||||||
|
<maven.compiler.target>17</maven.compiler.target>
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.fasterxml.jackson.core</groupId>
|
||||||
|
<artifactId>jackson-databind</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework</groupId>
|
||||||
|
<artifactId>spring-web</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.ruoyi</groupId>
|
||||||
|
<artifactId>ruoyi-system-api</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.ruoyi</groupId>
|
||||||
|
<artifactId>ruoyi-common-satoken</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.ruoyi</groupId>
|
||||||
|
<artifactId>ruoyi-common-mail</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.ruoyi</groupId>
|
||||||
|
<artifactId>ruoyi-chat</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>dev.langchain4j</groupId>
|
||||||
|
<artifactId>langchain4j-core</artifactId>
|
||||||
|
<version>1.2.0</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>cn.hutool</groupId>
|
||||||
|
<artifactId>hutool-all</artifactId>
|
||||||
|
<version>5.8.12</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.bsc.langgraph4j</groupId>
|
||||||
|
<artifactId>langgraph4j-core</artifactId>
|
||||||
|
<version>1.5.3</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.bsc.langgraph4j</groupId>
|
||||||
|
<artifactId>langgraph4j-langchain4j</artifactId>
|
||||||
|
<version>1.5.3</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.swagger.core.v3</groupId>
|
||||||
|
<artifactId>swagger-annotations</artifactId>
|
||||||
|
<version>2.2.8</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>dev.langchain4j</groupId>
|
||||||
|
<artifactId>langchain4j-open-ai</artifactId>
|
||||||
|
<version>1.2.0</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>dev.langchain4j</groupId>
|
||||||
|
<artifactId>langchain4j-community-dashscope</artifactId>
|
||||||
|
<version>1.2.0-beta8</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.baomidou</groupId>
|
||||||
|
<artifactId>mybatis-plus-generator</artifactId>
|
||||||
|
<version>3.5.3.1</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>dev.langchain4j</groupId>
|
||||||
|
<artifactId>langchain4j-http-client-jdk</artifactId>
|
||||||
|
<version>1.2.0</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>dev.langchain4j</groupId>
|
||||||
|
<artifactId>langchain4j-document-parser-apache-poi</artifactId>
|
||||||
|
<version>1.2.0-beta8</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.google.api-client</groupId>
|
||||||
|
<artifactId>google-api-client</artifactId>
|
||||||
|
<version>2.6.0</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
</project>
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
package org.ruoyi.workflow;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.generator.FastAutoGenerator;
|
||||||
|
import com.baomidou.mybatisplus.generator.config.OutputFile;
|
||||||
|
import com.baomidou.mybatisplus.generator.config.rules.DbColumnType;
|
||||||
|
|
||||||
|
import java.sql.Types;
|
||||||
|
import java.util.Collections;
|
||||||
|
|
||||||
|
public class CodeGenerator {
|
||||||
|
public static void main(String[] args) {
|
||||||
|
FastAutoGenerator.create("jdbc:postgres://172.17.30.40:5432/aideepin?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&tinyInt1isBit=false&allowMultiQueries=true", "postgres", "postgres")
|
||||||
|
.globalConfig(builder -> {
|
||||||
|
builder.author("moyz") // 设置作者
|
||||||
|
.enableSwagger() // 开启 swagger 模式
|
||||||
|
.fileOverride() // 覆盖已生成文件
|
||||||
|
.outputDir("D://"); // 指定输出目录
|
||||||
|
})
|
||||||
|
.dataSourceConfig(builder -> builder.typeConvertHandler((globalConfig, typeRegistry, metaInfo) -> {
|
||||||
|
int typeCode = metaInfo.getJdbcType().TYPE_CODE;
|
||||||
|
if (typeCode == Types.SMALLINT) {
|
||||||
|
// 自定义类型转换
|
||||||
|
return DbColumnType.INTEGER;
|
||||||
|
}
|
||||||
|
return typeRegistry.getColumnType(metaInfo);
|
||||||
|
|
||||||
|
}))
|
||||||
|
.packageConfig(builder -> {
|
||||||
|
builder.mapper("com.adi.common.mapper")
|
||||||
|
.parent("")
|
||||||
|
.moduleName("")
|
||||||
|
.entity("po")
|
||||||
|
.serviceImpl("service.impl")
|
||||||
|
.pathInfo(Collections.singletonMap(OutputFile.xml, "D://mybatisplus-generatorcode")); // 设置mapperXml生成路径
|
||||||
|
})
|
||||||
|
.strategyConfig(builder -> {
|
||||||
|
builder.addInclude("adi_knowledge_base_qa_record") // 设置需要生成的表名
|
||||||
|
.addTablePrefix("adi_");
|
||||||
|
builder.mapperBuilder().enableBaseResultMap().enableMapperAnnotation().build();
|
||||||
|
})
|
||||||
|
.execute();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
package org.ruoyi.workflow.base;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import org.ruoyi.workflow.enums.ErrorEnum;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class BaseResponse<T> implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
/**
|
||||||
|
* 是否成功
|
||||||
|
*/
|
||||||
|
private boolean success;
|
||||||
|
/**
|
||||||
|
* 状态码
|
||||||
|
*/
|
||||||
|
private String code;
|
||||||
|
/**
|
||||||
|
* 提示
|
||||||
|
*/
|
||||||
|
private String message;
|
||||||
|
/**
|
||||||
|
* 数据
|
||||||
|
*/
|
||||||
|
private T data;
|
||||||
|
|
||||||
|
public BaseResponse() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public BaseResponse(boolean success) {
|
||||||
|
this.success = success;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BaseResponse(boolean success, T data) {
|
||||||
|
this.data = data;
|
||||||
|
this.success = success;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BaseResponse(String code, String message, T data) {
|
||||||
|
this.code = code;
|
||||||
|
this.success = false;
|
||||||
|
this.message = message;
|
||||||
|
this.data = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BaseResponse success(String message) {
|
||||||
|
return new BaseResponse(ErrorEnum.SUCCESS.getCode(), message, "");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,118 @@
|
|||||||
|
package org.ruoyi.workflow.base;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
import com.fasterxml.jackson.databind.node.ArrayNode;
|
||||||
|
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.commons.collections4.CollectionUtils;
|
||||||
|
import org.apache.ibatis.type.BaseTypeHandler;
|
||||||
|
import org.apache.ibatis.type.JdbcType;
|
||||||
|
import org.apache.ibatis.type.MappedJdbcTypes;
|
||||||
|
import org.apache.ibatis.type.MappedTypes;
|
||||||
|
import org.ruoyi.workflow.enums.WfIODataTypeEnum;
|
||||||
|
import org.ruoyi.workflow.util.JsonUtil;
|
||||||
|
import org.ruoyi.workflow.workflow.WfNodeInputConfig;
|
||||||
|
import org.ruoyi.workflow.workflow.def.WfNodeIO;
|
||||||
|
import org.ruoyi.workflow.workflow.def.WfNodeParamRef;
|
||||||
|
|
||||||
|
import java.sql.CallableStatement;
|
||||||
|
import java.sql.PreparedStatement;
|
||||||
|
import java.sql.ResultSet;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.ruoyi.workflow.workflow.WfNodeIODataUtil.INPUT_TYPE_TO_NODE_IO_DEF;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@MappedJdbcTypes({JdbcType.JAVA_OBJECT})
|
||||||
|
@MappedTypes({WfNodeInputConfig.class})
|
||||||
|
public class NodeInputConfigTypeHandler extends BaseTypeHandler<WfNodeInputConfig> {
|
||||||
|
|
||||||
|
public static WfNodeInputConfig fillNodeInputConfig(String jsonSource) {
|
||||||
|
ObjectNode jsonNode = (ObjectNode) JsonUtil.toJsonNode(jsonSource);
|
||||||
|
return createNodeInputConfig(jsonNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static WfNodeInputConfig createNodeInputConfig(ObjectNode jsonNode) {
|
||||||
|
List<WfNodeIO> userInputs = new ArrayList<>();
|
||||||
|
WfNodeInputConfig result = new WfNodeInputConfig();
|
||||||
|
result.setUserInputs(userInputs);
|
||||||
|
result.setRefInputs(new ArrayList<>());
|
||||||
|
if (null == jsonNode) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
ArrayNode userInputsJson = jsonNode.withArray("user_inputs");
|
||||||
|
ArrayNode refInputs = jsonNode.withArray("ref_inputs");
|
||||||
|
if (!userInputsJson.isEmpty()) {
|
||||||
|
for (JsonNode userInput : userInputsJson) {
|
||||||
|
if (userInput instanceof ObjectNode objectNode) {
|
||||||
|
int type = objectNode.get("type").asInt();
|
||||||
|
Class<? extends WfNodeIO> nodeIOClass = INPUT_TYPE_TO_NODE_IO_DEF.get(WfIODataTypeEnum.getByValue(type));
|
||||||
|
WfNodeIO wfNodeIO = JsonUtil.fromJson(objectNode, nodeIOClass);
|
||||||
|
if (null != wfNodeIO) {
|
||||||
|
userInputs.add(wfNodeIO);
|
||||||
|
} else {
|
||||||
|
log.warn("用户输入格式不正确:{}", userInput);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!refInputs.isEmpty()) {
|
||||||
|
List<WfNodeParamRef> list = JsonUtil.fromArrayNode(refInputs, WfNodeParamRef.class);
|
||||||
|
if (CollectionUtils.isNotEmpty(list)) {
|
||||||
|
result.setRefInputs(list);
|
||||||
|
} else {
|
||||||
|
log.warn("引用输入格式不正确:{}", refInputs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setNonNullParameter(PreparedStatement ps, int i, WfNodeInputConfig parameter, JdbcType jdbcType) {
|
||||||
|
// PGobject jsonObject = new PGobject();
|
||||||
|
// jsonObject.setType("jsonb");
|
||||||
|
// try {
|
||||||
|
// jsonObject.setValue(JsonUtil.toJson(parameter));
|
||||||
|
// ps.setObject(i, jsonObject);
|
||||||
|
// } catch (Exception e) {
|
||||||
|
// throw new RuntimeException(e);
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public WfNodeInputConfig getNullableResult(ResultSet rs, String columnName) throws SQLException {
|
||||||
|
String jsonSource = rs.getString(columnName);
|
||||||
|
if (jsonSource != null) {
|
||||||
|
try {
|
||||||
|
return fillNodeInputConfig(jsonSource);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public WfNodeInputConfig getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
|
||||||
|
String jsonSource = rs.getString(columnIndex);
|
||||||
|
if (jsonSource != null) {
|
||||||
|
return fillNodeInputConfig(jsonSource);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public WfNodeInputConfig getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
|
||||||
|
String jsonSource = cs.getString(columnIndex);
|
||||||
|
if (jsonSource != null) {
|
||||||
|
try {
|
||||||
|
return fillNodeInputConfig(jsonSource);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,122 @@
|
|||||||
|
package org.ruoyi.workflow.base;
|
||||||
|
|
||||||
|
import cn.dev33.satoken.stp.StpUtil;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.ruoyi.common.core.domain.model.LoginUser;
|
||||||
|
import org.ruoyi.common.core.exception.base.BaseException;
|
||||||
|
import org.ruoyi.common.satoken.utils.LoginHelper;
|
||||||
|
import org.ruoyi.workflow.entity.User;
|
||||||
|
import org.ruoyi.workflow.enums.UserStatusEnum;
|
||||||
|
|
||||||
|
import static org.ruoyi.workflow.enums.ErrorEnum.A_USER_NOT_FOUND;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 线程上下文适配器,统一接入 Sa-Token 登录态。
|
||||||
|
*/
|
||||||
|
public class ThreadContext {
|
||||||
|
|
||||||
|
private static final ThreadLocal<User> CURRENT_USER = new ThreadLocal<>();
|
||||||
|
private static final ThreadLocal<String> CURRENT_TOKEN = new ThreadLocal<>();
|
||||||
|
|
||||||
|
private ThreadContext() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前登录的工作流用户。
|
||||||
|
*/
|
||||||
|
public static User getCurrentUser() {
|
||||||
|
User cached = CURRENT_USER.get();
|
||||||
|
if (cached != null) {
|
||||||
|
return cached;
|
||||||
|
}
|
||||||
|
LoginUser loginUser = LoginHelper.getLoginUser();
|
||||||
|
if (loginUser == null) {
|
||||||
|
throw new BaseException(A_USER_NOT_FOUND.getInfo());
|
||||||
|
}
|
||||||
|
User mapped = mapToWorkflowUser(loginUser);
|
||||||
|
CURRENT_USER.set(mapped);
|
||||||
|
return mapped;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 允许在测试或特殊场景下显式设置当前用户。
|
||||||
|
*/
|
||||||
|
public static void setCurrentUser(User user) {
|
||||||
|
if (user == null) {
|
||||||
|
CURRENT_USER.remove();
|
||||||
|
} else {
|
||||||
|
CURRENT_USER.set(user);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前登录用户 ID。
|
||||||
|
*/
|
||||||
|
public static Long getCurrentUserId() {
|
||||||
|
Long userId = LoginHelper.getUserId();
|
||||||
|
if (userId != null) {
|
||||||
|
return userId;
|
||||||
|
}
|
||||||
|
return getCurrentUser().getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前访问 token。
|
||||||
|
*/
|
||||||
|
public static String getToken() {
|
||||||
|
String token = CURRENT_TOKEN.get();
|
||||||
|
if (StringUtils.isNotBlank(token)) {
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
token = StpUtil.getTokenValue();
|
||||||
|
} catch (Exception ignore) {
|
||||||
|
token = null;
|
||||||
|
}
|
||||||
|
if (StringUtils.isNotBlank(token)) {
|
||||||
|
CURRENT_TOKEN.set(token);
|
||||||
|
}
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setToken(String token) {
|
||||||
|
if (StringUtils.isBlank(token)) {
|
||||||
|
CURRENT_TOKEN.remove();
|
||||||
|
} else {
|
||||||
|
CURRENT_TOKEN.set(token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isLogin() {
|
||||||
|
return LoginHelper.isLogin();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static User getExistCurrentUser() {
|
||||||
|
return getCurrentUser();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void unload() {
|
||||||
|
CURRENT_USER.remove();
|
||||||
|
CURRENT_TOKEN.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static User mapToWorkflowUser(LoginUser loginUser) {
|
||||||
|
User user = new User();
|
||||||
|
user.setId(loginUser.getUserId());
|
||||||
|
String nickname = loginUser.getNickName();
|
||||||
|
user.setName(StringUtils.defaultIfBlank(nickname, loginUser.getUsername()));
|
||||||
|
user.setEmail(loginUser.getUsername());
|
||||||
|
user.setUuid(String.valueOf(loginUser.getUserId()));
|
||||||
|
user.setUserStatus(UserStatusEnum.NORMAL);
|
||||||
|
user.setIsAdmin(LoginHelper.isSuperAdmin(loginUser.getUserId()));
|
||||||
|
user.setUnderstandContextMsgPairNum(0);
|
||||||
|
user.setQuotaByTokenDaily(0);
|
||||||
|
user.setQuotaByTokenMonthly(0);
|
||||||
|
user.setQuotaByRequestDaily(0);
|
||||||
|
user.setQuotaByRequestMonthly(0);
|
||||||
|
user.setQuotaByImageDaily(0);
|
||||||
|
user.setQuotaByImageMonthly(0);
|
||||||
|
user.setIsDeleted(false);
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
package org.ruoyi.workflow.config;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
|
||||||
|
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.ruoyi.workflow.util.LocalDateTimeUtil;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.context.annotation.Primary;
|
||||||
|
import org.springframework.core.task.AsyncTaskExecutor;
|
||||||
|
import org.springframework.http.client.BufferingClientHttpRequestFactory;
|
||||||
|
import org.springframework.http.client.SimpleClientHttpRequestFactory;
|
||||||
|
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
|
||||||
|
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||||
|
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
|
||||||
|
import org.springframework.web.client.RestTemplate;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Configuration
|
||||||
|
public class BeanConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public RestTemplate restTemplate() {
|
||||||
|
log.info("Configuration:create restTemplate");
|
||||||
|
SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
|
||||||
|
// 设置建立连接超时时间 毫秒
|
||||||
|
requestFactory.setConnectTimeout(60000);
|
||||||
|
// 设置读取数据超时时间 毫秒
|
||||||
|
requestFactory.setReadTimeout(60000);
|
||||||
|
RestTemplate restTemplate = new RestTemplate();
|
||||||
|
// 注册LOG拦截器
|
||||||
|
// restTemplate.setInterceptors(Lists.newArrayList(new LogClientHttpRequestInterceptor()));
|
||||||
|
restTemplate.setRequestFactory(new BufferingClientHttpRequestFactory(requestFactory));
|
||||||
|
|
||||||
|
return restTemplate;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@Primary
|
||||||
|
public ObjectMapper objectMapper() {
|
||||||
|
log.info("Configuration:create objectMapper");
|
||||||
|
ObjectMapper objectMapper = new Jackson2ObjectMapperBuilder().createXmlMapper(false).build();
|
||||||
|
objectMapper.registerModules(LocalDateTimeUtil.getSimpleModule(), new JavaTimeModule(), new Jdk8Module());
|
||||||
|
//设置null值不参与序列化(字段不被显示)
|
||||||
|
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
|
||||||
|
return objectMapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean(name = "mainExecutor")
|
||||||
|
@Primary
|
||||||
|
public AsyncTaskExecutor mainExecutor() {
|
||||||
|
int processorsNum = Runtime.getRuntime().availableProcessors();
|
||||||
|
log.info("mainExecutor,processorsNum:{}", processorsNum);
|
||||||
|
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
|
||||||
|
executor.setCorePoolSize(processorsNum * 2);
|
||||||
|
executor.setMaxPoolSize(100);
|
||||||
|
return executor;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean(name = "imagesExecutor")
|
||||||
|
public AsyncTaskExecutor imagesExecutor() {
|
||||||
|
int processorsNum = Runtime.getRuntime().availableProcessors();
|
||||||
|
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
|
||||||
|
log.info("imagesExecutor corePoolSize:{},maxPoolSize:{}", processorsNum, processorsNum * 2);
|
||||||
|
executor.setCorePoolSize(processorsNum);
|
||||||
|
executor.setMaxPoolSize(processorsNum * 2);
|
||||||
|
return executor;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean(name = "beanValidator")
|
||||||
|
public LocalValidatorFactoryBean validator() {
|
||||||
|
return new LocalValidatorFactoryBean();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,407 @@
|
|||||||
|
package org.ruoyi.workflow.cosntant;
|
||||||
|
|
||||||
|
import dev.langchain4j.model.input.PromptTemplate;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
public class AdiConstant {
|
||||||
|
public static final int DEFAULT_PAGE_SIZE = 10;
|
||||||
|
/**
|
||||||
|
* 验证码id过期时间:1小时
|
||||||
|
*/
|
||||||
|
public static final int AUTH_CAPTCHA_ID_EXPIRE = 1;
|
||||||
|
/**
|
||||||
|
* 验证码过期时间,5分钟
|
||||||
|
*/
|
||||||
|
public static final int AUTH_CAPTCHA_EXPIRE = 5;
|
||||||
|
/**
|
||||||
|
* 注册激活码有效时长,8小时
|
||||||
|
*/
|
||||||
|
public static final int AUTH_ACTIVE_CODE_EXPIRE = 8;
|
||||||
|
/**
|
||||||
|
* token存活时间(8小时)
|
||||||
|
*/
|
||||||
|
public static final int USER_TOKEN_EXPIRE = 8;
|
||||||
|
public static final String DEFAULT_PASSWORD = "123456";
|
||||||
|
public static final int LOGIN_MAX_FAIL_TIMES = 3;
|
||||||
|
public static final String[] WEB_RESOURCES = {
|
||||||
|
"/swagger-ui/index.html",
|
||||||
|
"/swagger-ui",
|
||||||
|
"/swagger-resources",
|
||||||
|
"/v3/api-docs",
|
||||||
|
"/favicon.ico",
|
||||||
|
".css",
|
||||||
|
".js",
|
||||||
|
"/doc.html"
|
||||||
|
};
|
||||||
|
public static final int SECRET_KEY_TYPE_SYSTEM = 1;
|
||||||
|
public static final int SECRET_KEY_TYPE_CUSTOM = 2;
|
||||||
|
public static final String OPENAI_MESSAGE_DONE_FLAG = "[DONE]";
|
||||||
|
public static final String DEFAULT_MODEL = "gpt-3.5-turbo";
|
||||||
|
public static final String CREATE_IMAGE_RESP_FORMATS_B64JSON = "b64_json";
|
||||||
|
public static final String OPENAI_CREATE_IMAGE_RESP_FORMATS_URL = "url";
|
||||||
|
public static final List<String> DALLE2_CREATE_IMAGE_SIZES = List.of("256x256", "512x512", "1024x1024");
|
||||||
|
public static final List<String> DALLE3_CREATE_IMAGE_SIZES = List.of("1024x1024", "1024x1792", "1792x1024");
|
||||||
|
public static final PromptTemplate PROMPT_EXTRA_TEMPLATE = PromptTemplate.from("""
|
||||||
|
## 要求
|
||||||
|
尽可能准确地回答用户的问题
|
||||||
|
|
||||||
|
## 用户的问题
|
||||||
|
{{question}}
|
||||||
|
|
||||||
|
## 注意
|
||||||
|
{{extraInfo}}
|
||||||
|
""");
|
||||||
|
public static final PromptTemplate PROMPT_INFO_TEMPLATE = PromptTemplate.from("""
|
||||||
|
## 要求
|
||||||
|
根据已知信息,尽可能准确地回答用户的问题
|
||||||
|
|
||||||
|
## 用户的问题
|
||||||
|
{{question}}
|
||||||
|
|
||||||
|
## 已知信息
|
||||||
|
{{information}}
|
||||||
|
|
||||||
|
## 注意
|
||||||
|
回答的内容不能让用户感知到已知信息的存在
|
||||||
|
""");
|
||||||
|
/**
|
||||||
|
* 可能的 extraInfo 如适用转音频的要求: 2. 回答的内容要尽量口语化,以方便将内容转成语音
|
||||||
|
*/
|
||||||
|
public static final PromptTemplate PROMPT_INFO_EXTRA_TEMPLATE = PromptTemplate.from("""
|
||||||
|
## 要求
|
||||||
|
根据已知信息,尽可能准确地回答用户的问题
|
||||||
|
|
||||||
|
## 用户的问题
|
||||||
|
{{question}}
|
||||||
|
|
||||||
|
## 已知信息
|
||||||
|
{{information}}
|
||||||
|
|
||||||
|
## 注意
|
||||||
|
1. 回答的内容不能让用户感知到已知信息的存在
|
||||||
|
{{extraInfo}}
|
||||||
|
""");
|
||||||
|
public static final String PROMPT_EXTRA_AUDIO = "2. 回答的内容要尽量口语化,以方便将内容转成语音";
|
||||||
|
public static final Double LLM_TEMPERATURE_DEFAULT = 0.7D;
|
||||||
|
public static final Double RAG_RETRIEVE_MIN_SCORE_DEFAULT = 0.6D;
|
||||||
|
public static final int tts_ = 1;
|
||||||
|
public static final String[] POI_DOC_TYPES = {"doc", "docx", "ppt", "pptx", "xls", "xlsx"};
|
||||||
|
public static final long SSE_TIMEOUT = (2 * 60 + 30) * 1000L; // 2.5分钟
|
||||||
|
public static final int RAG_TYPE_KB = 1;
|
||||||
|
public static final int RAG_TYPE_SEARCH = 2;
|
||||||
|
/**
|
||||||
|
* 每块文档长度(按token算)
|
||||||
|
*/
|
||||||
|
public static final int RAG_MAX_SEGMENT_SIZE_IN_TOKENS = 1000;
|
||||||
|
/**
|
||||||
|
* 文档召回默认数量
|
||||||
|
*/
|
||||||
|
public static final int RAG_RETRIEVE_NUMBER_DEFAULT = 3;
|
||||||
|
/**
|
||||||
|
* 文档召回最大数量
|
||||||
|
*/
|
||||||
|
public static final int RAG_RETRIEVE_NUMBER_MAX = 5;
|
||||||
|
/**
|
||||||
|
* 向量搜索时命中所需的最低分数
|
||||||
|
*/
|
||||||
|
public static final double RAG_MIN_SCORE = 0.6;
|
||||||
|
/**
|
||||||
|
* 默认的最大输入token数
|
||||||
|
*/
|
||||||
|
public static final int LLM_MAX_INPUT_TOKENS_DEFAULT = 4096;
|
||||||
|
public static final String LLM_INPUT_TYPE_TEXT = "text";
|
||||||
|
public static final String LLM_INPUT_TYPE_IMAGE = "image";
|
||||||
|
public static final String LLM_INPUT_TYPE_AUDIO = "audio";
|
||||||
|
public static final String LLM_INPUT_TYPE_VIDEO = "video";
|
||||||
|
public static final String[] GRAPH_ENTITY_EXTRACTION_ENTITY_TYPES = {"organization", "person", "geo", "event"};
|
||||||
|
public static final String GRAPH_TUPLE_DELIMITER = "<|>";
|
||||||
|
public static final String GRAPH_RECORD_DELIMITER = "##";
|
||||||
|
public static final String GRAPH_COMPLETION_DELIMITER = "<|COMPLETE|>";
|
||||||
|
public static final List<String> GRAPH_STORE_MAIN_FIELDS = List.of("name", "label", "text_segment_id", "description");
|
||||||
|
/**
|
||||||
|
* 唯一标识字段,如果该字段有指定,则根据该配置判断Vertex或Edge是否唯一,如知识库中根据 name、metadata->>kb_uuid 来做判断
|
||||||
|
*/
|
||||||
|
public static final String GRAPH_METADATA_IDENTIFY_COLUMNS = "graph_metadata_identify_columns";
|
||||||
|
/**
|
||||||
|
* 内容追加字段
|
||||||
|
* 更新数据时,如遇到该标识中的字段,追加内容而不是替换
|
||||||
|
*/
|
||||||
|
public static final String GRAPH_METADATA_APPEND_COLUMNS = "graph_metadata_append_columns_if_exist";
|
||||||
|
public static final int AI_IMAGE_TYPE_REGULAR = 1;
|
||||||
|
public static final int AI_IMAGE_TYPE_THUMBNAIL = 2;
|
||||||
|
public static final int AI_IMAGE_TYPE_REGULAR_MARK = 3;
|
||||||
|
public static final int AI_IMAGE_TYPE_THUMBNAIL_MARK = 4;
|
||||||
|
public static final String DOC_INDEX_TYPE_EMBEDDING = "embedding";
|
||||||
|
public static final String DOC_INDEX_TYPE_GRAPHICAL = "graphical";
|
||||||
|
public static final String DRAW_TYPE_PUBLIC = "public";
|
||||||
|
public static final String DRAW_TYPE_STARRED = "starred";
|
||||||
|
public static final String DRAW_TYPE_MINE = "mine";
|
||||||
|
public static final String MP_LIMIT_1 = "limit 1";
|
||||||
|
/**
|
||||||
|
* 文件存储在本地
|
||||||
|
*/
|
||||||
|
public static final int STORAGE_LOCATION_LOCAL = 1;
|
||||||
|
/**
|
||||||
|
* 文件存储到阿里云OSS
|
||||||
|
*/
|
||||||
|
public static final int STORAGE_LOCATION_ALI_OSS = 2;
|
||||||
|
public static final String URL_PREFIX_FILE = "/file/";
|
||||||
|
public static final String URL_PREFIX_IMAGE = "/image/";
|
||||||
|
public static final String URL_PREFIX_MY_IMAGE = "/my-image/";
|
||||||
|
public static final String URL_PREFIX_MY_THUMBNAIL = "/my-thumbnail/";
|
||||||
|
public static final List<String> IMAGE_EXTENSIONS = List.of("jpg", "jpeg", "png", "gif", "bmp", "webp");
|
||||||
|
public static final String W_FAILED = "FAILED";
|
||||||
|
public static final String COLUMN_NAME_IS_DELETE = "is_deleted";
|
||||||
|
public static final String COLUMN_NAME_USER_ID = "user_id";
|
||||||
|
public static final String COLUMN_NAME_ID = "id";
|
||||||
|
public static final String COLUMN_NAME_UUID = "uuid";
|
||||||
|
public static final String FORM_DATA_BOUNDARY_PRE = "----WebKitFormBoundary";
|
||||||
|
|
||||||
|
private AdiConstant() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class ConversationConstant {
|
||||||
|
public static final String DEFAULT_NAME = "通用智能助手";
|
||||||
|
public static final int ANSWER_CONTENT_TYPE_AUTO = 1;
|
||||||
|
public static final int ANSWER_CONTENT_TYPE_TEXT = 2;
|
||||||
|
public static final int ANSWER_CONTENT_TYPE_AUDIO = 3;
|
||||||
|
public static final String AUDIO_CONFIG_FIELD_ANSWER_VOICE = "answer_voice";
|
||||||
|
public static final String AUDIO_CONFIG_FIELD_VOICE_PLATFORM = "platform";
|
||||||
|
|
||||||
|
private ConversationConstant() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class GenerateImage {
|
||||||
|
public static final int INTERACTING_METHOD_GENERATE_IMAGE = 1;
|
||||||
|
public static final int INTERACTING_METHOD_EDIT_IMAGE = 2;
|
||||||
|
public static final int INTERACTING_METHOD_VARIATION = 3;
|
||||||
|
public static final int INTERACTING_METHOD_BACKGROUND_GENERATION = 4;
|
||||||
|
public static final int STATUS_DOING = 1;
|
||||||
|
public static final int STATUS_FAIL = 2;
|
||||||
|
public static final int STATUS_SUCCESS = 3;
|
||||||
|
|
||||||
|
private GenerateImage() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class MetadataKey {
|
||||||
|
public static final String KB_UUID = "kb_uuid";
|
||||||
|
public static final String KB_ITEM_UUID = "kb_item_uuid";
|
||||||
|
public static final String ENGINE_NAME = "engine_name";
|
||||||
|
public static final String SEARCH_UUID = "search_uuid";
|
||||||
|
|
||||||
|
private MetadataKey() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class SysConfigKey {
|
||||||
|
public static final String DEEPSEEK_SETTING = "deepseek_setting";
|
||||||
|
public static final String OPENAI_SETTING = "openai_setting";
|
||||||
|
public static final String DASHSCOPE_SETTING = "dashscope_setting";
|
||||||
|
public static final String QIANFAN_SETTING = "qianfan_setting";
|
||||||
|
public static final String OLLAMA_SETTING = "ollama_setting";
|
||||||
|
public static final String SILICONFLOW_SETTING = "siliconflow_setting";
|
||||||
|
public static final String GOOGLE_SETTING = "google_setting";
|
||||||
|
public static final String BING_SETTING = "bing_setting";
|
||||||
|
public static final String BAIDU_SETTING = "baidu_setting";
|
||||||
|
public static final String REQUEST_TEXT_RATE_LIMIT = "request_text_rate_limit";
|
||||||
|
public static final String REQUEST_IMAGE_RATE_LIMIT = "request_image_rate_limit";
|
||||||
|
public static final String CONVERSATION_MAX_NUM = "conversation_max_num";
|
||||||
|
public static final String QUOTA_BY_TOKEN_DAILY = "quota_by_token_daily";
|
||||||
|
public static final String QUOTA_BY_TOKEN_MONTHLY = "quota_by_token_monthly";
|
||||||
|
public static final String QUOTA_BY_REQUEST_DAILY = "quota_by_request_daily";
|
||||||
|
public static final String QUOTA_BY_REQUEST_MONTHLY = "quota_by_request_monthly";
|
||||||
|
public static final String QUOTA_BY_IMAGE_DAILY = "quota_by_image_daily";
|
||||||
|
public static final String QUOTA_BY_IMAGE_MONTHLY = "quota_by_image_monthly";
|
||||||
|
public static final String QUOTA_BY_QA_ASK_DAILY = "quota_by_qa_ask_daily";
|
||||||
|
public static final String STORAGE_LOCATION = "storage_location";
|
||||||
|
public static final String STORAGE_LOCATION_ALI_OSS = "storage_location_ali_oss";
|
||||||
|
public static final String ASR_SETTING = "asr_setting";
|
||||||
|
public static final String TTS_SETTING = "tts_setting";
|
||||||
|
|
||||||
|
private SysConfigKey() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class ModelPlatform {
|
||||||
|
public static final String DEEPSEEK = "deepseek";
|
||||||
|
public static final String OPENAI = "openai";
|
||||||
|
public static final String DASHSCOPE = "dashscope";
|
||||||
|
public static final String QIANFAN = "qianfan";
|
||||||
|
public static final String OLLAMA = "ollama";
|
||||||
|
public static final String SILICONFLOW = "siliconflow";
|
||||||
|
|
||||||
|
private ModelPlatform() {
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取所有公共静态常量(String类型的值)的列表
|
||||||
|
public static List<String> getModelConstants() {
|
||||||
|
List<String> list = new ArrayList<>();
|
||||||
|
Class<ModelPlatform> clazz = ModelPlatform.class;
|
||||||
|
for (Field field : clazz.getDeclaredFields()) {
|
||||||
|
try {
|
||||||
|
String value = (String) field.get(null);
|
||||||
|
list.add(value);
|
||||||
|
} catch (ReflectiveOperationException e) {
|
||||||
|
log.error("error", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class ModelType {
|
||||||
|
public static final String TEXT = "text";
|
||||||
|
public static final String IMAGE = "image";
|
||||||
|
public static final String EMBEDDING = "embedding";
|
||||||
|
public static final String RERANK = "rerank";
|
||||||
|
public static final String ASR = "asr";
|
||||||
|
public static final String TTS = "tts";
|
||||||
|
|
||||||
|
private ModelType() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<String> getModelType() {
|
||||||
|
List<String> list = new ArrayList<>();
|
||||||
|
Class<ModelType> clazz = ModelType.class;
|
||||||
|
for (Field field : clazz.getDeclaredFields()) {
|
||||||
|
try {
|
||||||
|
String value = (String) field.get(null);
|
||||||
|
list.add(value);
|
||||||
|
} catch (ReflectiveOperationException e) {
|
||||||
|
log.error("error", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class SearchEngineName {
|
||||||
|
public static final String GOOGLE = "google";
|
||||||
|
public static final String BING = "bing";
|
||||||
|
public static final String BAIDU = "baidu";
|
||||||
|
public static final String[] GOOGLE_COUNTRIES = {"cn", "af", "al", "dz", "as", "ad", "ao", "ai", "aq", "ag", "ar", "am", "aw", "au", "at", "az", "bs", "bh", "bd", "bb", "by", "be", "bz", "bj", "bm", "bt", "bo", "ba", "bw", "bv", "br", "io", "bn", "bg", "bf", "bi", "kh", "cm", "ca", "cv", "ky", "cf", "td", "cl", "cx", "cc", "co", "km", "cg", "cd", "ck", "cr", "ci", "hr", "cu", "cy", "cz", "dk", "dj", "dm", "do", "ec", "eg", "sv", "gq", "er", "ee", "et", "fk", "fo", "fj", "fi", "fr", "gf", "pf", "tf", "ga", "gm", "ge", "de", "gh", "gi", "gr", "gl", "gd", "gp", "gu", "gt", "gn", "gw", "gy", "ht", "hm", "va", "hn", "hk", "hu", "is", "in", "id", "ir", "iq", "ie", "il", "it", "jm", "jp", "jo", "kz", "ke", "ki", "kp", "kr", "kw", "kg", "la", "lv", "lb", "ls", "lr", "ly", "li", "lt", "lu", "mo", "mk", "mg", "mw", "my", "mv", "ml", "mt", "mh", "mq", "mr", "mu", "yt", "mx", "fm", "md", "mc", "mn", "ms", "ma", "mz", "mm", "na", "nr", "np", "nl", "an", "nc", "nz", "ni", "ne", "ng", "nu", "nf", "mp", "no", "om", "pk", "pw", "ps", "pa", "pg", "py", "pe", "ph", "pn", "pl", "pt", "pr", "qa", "re", "ro", "ru", "rw", "sh", "kn", "lc", "pm", "vc", "ws", "sm", "st", "sa", "sn", "rs", "sc", "sl", "sg", "sk", "si", "sb", "so", "za", "gs", "es", "lk", "sd", "sr", "sj", "sz", "se", "ch", "sy", "tw", "tj", "tz", "th", "tl", "tg", "tk", "to", "tt", "tn", "tr", "tm", "tc", "tv", "ug", "ua", "ae", "uk", "gb", "us", "um", "uy", "uz", "vu", "ve", "vn", "vg", "vi", "wf", "eh", "ye", "zm", "zw"};
|
||||||
|
public static final String[] GOOGLE_LANGUAGES = {"zh-cn", "zh-tw", "af", "ak", "sq", "ws", "am", "ar", "hy", "az", "eu", "be", "bem", "bn", "bh", "xx-bork", "bs", "br", "bg", "bt", "km", "ca", "chr", "ny", "co", "hr", "cs", "da", "nl", "xx-elmer", "en", "eo", "et", "ee", "fo", "tl", "fi", "fr", "fy", "gaa", "gl", "ka", "de", "el", "kl", "gn", "gu", "xx-hacker", "ht", "ha", "haw", "iw", "hi", "hu", "is", "ig", "id", "ia", "ga", "it", "ja", "jw", "kn", "kk", "rw", "rn", "xx-klingon", "kg", "ko", "kri", "ku", "ckb", "ky", "lo", "la", "lv", "ln", "lt", "loz", "lg", "ach", "mk", "mg", "ms", "ml", "mt", "mv", "mi", "mr", "mfe", "mo", "mn", "sr-me", "my", "ne", "pcm", "nso", "no", "nn", "oc", "or", "om", "ps", "fa", "xx-pirate", "pl", "pt", "pt-br", "pt-pt", "pa", "qu", "ro", "rm", "nyn", "ru", "gd", "sr", "sh", "st", "tn", "crs", "sn", "sd", "si", "sk", "sl", "so", "es", "es-419", "su", "sw", "sv", "tg", "ta", "tt", "te", "th", "ti", "to", "lua", "tum", "tr", "tk", "tw", "ug", "uk", "ur", "uz", "vu", "vi", "cy", "wo", "xh", "yi", "yo", "zu"};
|
||||||
|
|
||||||
|
private SearchEngineName() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class SSEEventName {
|
||||||
|
public static final String START = "[START]";
|
||||||
|
public static final String DONE = "[DONE]";
|
||||||
|
public static final String ERROR = "[ERROR]";
|
||||||
|
public static final String META = "[META]";
|
||||||
|
public static final String AUDIO = "[AUDIO]";
|
||||||
|
public static final String THINKING = "[THINKING]";
|
||||||
|
public static final String AI_SEARCH_SOURCE_LINKS = "[SOURCE_LINKS]";
|
||||||
|
public static final String WF_NODE_CHUNK = "[WF_NODE_CHUNK]";
|
||||||
|
public static final String WF_NODE_OUTPUT = "[WF_NODE_OUTPUT]";
|
||||||
|
public static final String STATE_CHANGED = "[STATE_CHANGED]";
|
||||||
|
|
||||||
|
private SSEEventName() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class SSEEventData {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 状态:问题分析中
|
||||||
|
* 如敏感词校验等
|
||||||
|
*/
|
||||||
|
public static final String STATE_QUESTION_ANALYSING = """
|
||||||
|
{"state":"question_analysing","remark":"问题分析中"}
|
||||||
|
""";
|
||||||
|
|
||||||
|
public static final String STATE_KNOWLEDGE_SEARCHING = """
|
||||||
|
{"state":"knowledge_searching","remark":"知识库搜索中"}
|
||||||
|
""";
|
||||||
|
//使用 THINKING 事件代替
|
||||||
|
public static final String STATE_THINKING = """
|
||||||
|
{"state":"thinking","remark":"推理中"}
|
||||||
|
""";
|
||||||
|
public static final String STATE_RESPONDING = """
|
||||||
|
{"state":"responding","remark":"回答中"}
|
||||||
|
""";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class WorkflowConstant {
|
||||||
|
public static final String DEFAULT_INPUT_PARAM_NAME = "input";
|
||||||
|
public static final String DEFAULT_OUTPUT_PARAM_NAME = "output";
|
||||||
|
public static final String DEFAULT_ERROR_OUTPUT_PARAM_NAME = "error_msg";
|
||||||
|
public static final String HUMAN_FEEDBACK_KEY = "human_feedback";
|
||||||
|
public static final int NODE_PROCESS_STATUS_READY = 1;
|
||||||
|
public static final int NODE_PROCESS_STATUS_DOING = 2;
|
||||||
|
public static final int NODE_PROCESS_STATUS_SUCCESS = 3;
|
||||||
|
public static final int NODE_PROCESS_STATUS_FAIL = 4;
|
||||||
|
|
||||||
|
public static final int WORKFLOW_PROCESS_STATUS_READY = 1;
|
||||||
|
public static final int WORKFLOW_PROCESS_STATUS_DOING = 2;
|
||||||
|
public static final int WORKFLOW_PROCESS_STATUS_SUCCESS = 3;
|
||||||
|
public static final int WORKFLOW_PROCESS_STATUS_FAIL = 4;
|
||||||
|
public static final int WORKFLOW_PROCESS_STATUS_WAITING_INPUT = 5;
|
||||||
|
|
||||||
|
public static final int WORKFLOW_NODE_PROCESS_TYPE_NORMAL = 1;
|
||||||
|
public static final int WORKFLOW_NODE_PROCESS_TYPE_CONDITIONAL = 2;
|
||||||
|
public static final int WORKFLOW_NODE_PROCESS_TYPE_PARALLEL = 3;
|
||||||
|
|
||||||
|
public static final int MAIL_SENDER_TYPE_SYS = 1;
|
||||||
|
public static final int MAIL_SENDER_TYPE_CUSTOM = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class TokenEstimator {
|
||||||
|
public static String OPENAI = "openai";
|
||||||
|
public static String HUGGING_FACE = "huggingface";
|
||||||
|
public static String QWEN = "qwen";
|
||||||
|
|
||||||
|
public static List<String> ALL = List.of(OPENAI, HUGGING_FACE, QWEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class EmbeddingModel {
|
||||||
|
public static String ALL_MINILM_L6 = "local:all-minilm-l6-v2";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class McpConstant {
|
||||||
|
public static final String TRANSPORT_TYPE_SSE = "sse";
|
||||||
|
public static final String TRANSPORT_TYPE_STDIO = "stdio";
|
||||||
|
public static final String INSTALL_TYPE_REMOTE = "remote";
|
||||||
|
public static final String INSTALL_TYPE_WASM = "wasm";
|
||||||
|
public static final String INSTALL_TYPE_LOCAL = "local";
|
||||||
|
public static final String INSTALL_TYPE_DOCKER = "docker";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class TtsConstant {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 语音合成器位置-客户端
|
||||||
|
*/
|
||||||
|
public static final String SYNTHESIZER_CLIENT = "client";
|
||||||
|
/**
|
||||||
|
* 语音合成器位置-服务端
|
||||||
|
*/
|
||||||
|
public static final String SYNTHESIZER_SERVER = "server";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通义默认语音音色-龙应严(义正严辞女声)
|
||||||
|
*/
|
||||||
|
public static final String DASHSCOPE_DEFAULT_VOICE = "longyingyan";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class CustomChatRequestParameterKeys {
|
||||||
|
/**
|
||||||
|
* 是否开启思考模式,默认不开启
|
||||||
|
*/
|
||||||
|
public static final String ENABLE_THINKING = "enable_thinking";
|
||||||
|
|
||||||
|
private CustomChatRequestParameterKeys() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,116 @@
|
|||||||
|
package org.ruoyi.workflow.cosntant;
|
||||||
|
|
||||||
|
public class RedisKeyConstant {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 账号激活码的key
|
||||||
|
*/
|
||||||
|
public static final String AUTH_ACTIVE_CODE = "auth:activeCode:{0}";
|
||||||
|
/**
|
||||||
|
* 注册时使用的验证码
|
||||||
|
* 参数:验证码id
|
||||||
|
* 值:验证码
|
||||||
|
*/
|
||||||
|
public static final String AUTH_REGISTER_CAPTCHA_ID = "auth:register:captcha:{0}";
|
||||||
|
/**
|
||||||
|
* 登录时使用的验证码id缓存
|
||||||
|
* 参数:验证码id
|
||||||
|
* 值:验证码
|
||||||
|
*/
|
||||||
|
public static final String AUTH_LOGIN_CAPTCHA_ID = "auth:login:captcha:{0}";
|
||||||
|
/**
|
||||||
|
* 注册验证码缓存
|
||||||
|
* 参数:验证码
|
||||||
|
* 值:1
|
||||||
|
*/
|
||||||
|
public static final String AUTH_CAPTCHA = "auth:register:captcha:{0}";
|
||||||
|
/**
|
||||||
|
* 登录token
|
||||||
|
* {0}:用户token
|
||||||
|
* 值:json.format(user)
|
||||||
|
*/
|
||||||
|
public static final String USER_TOKEN = "user:token:{0}";
|
||||||
|
/**
|
||||||
|
* 参数:游客的uuid
|
||||||
|
* 值:json.format(guest)
|
||||||
|
*/
|
||||||
|
public static final String GUEST_UUID = "guest:uuid:{0}";
|
||||||
|
/**
|
||||||
|
* 登录失败次数
|
||||||
|
* 参数:用户邮箱
|
||||||
|
* 值: 失效次数
|
||||||
|
*/
|
||||||
|
public static final String LOGIN_FAIL_COUNT = "user:login:fail:{0}";
|
||||||
|
/**
|
||||||
|
* 用户是否请求ai中
|
||||||
|
* 参数:用户id
|
||||||
|
* 值: 1或者0
|
||||||
|
*/
|
||||||
|
public static final String USER_ASKING = "user:asking:{0}";
|
||||||
|
/**
|
||||||
|
* 用户是否画画中
|
||||||
|
* 参数:用户id
|
||||||
|
* 值: 1或者0
|
||||||
|
*/
|
||||||
|
public static final String USER_DRAWING = "user:drawing:{0}";
|
||||||
|
/**
|
||||||
|
* 用户提问限流计数
|
||||||
|
* 参数:用户id
|
||||||
|
* 值: 当前时间窗口访问量
|
||||||
|
*/
|
||||||
|
public static final String USER_REQUEST_TEXT_TIMES = "user:request-text:times:{0}";
|
||||||
|
public static final String USER_REQUEST_IMAGE_TIMES = "user:request-image:times:{0}";
|
||||||
|
/**
|
||||||
|
* 用户信息缓存
|
||||||
|
* 参数:用户id
|
||||||
|
* 值: user object
|
||||||
|
*/
|
||||||
|
public static final String USER_INFO = "user:info:";
|
||||||
|
/**
|
||||||
|
* 找回密码的请求绑在
|
||||||
|
* 参数:随机数
|
||||||
|
* 值: 用户id,用于校验后续流程中的重置密码使用
|
||||||
|
*/
|
||||||
|
public static final String FIND_MY_PASSWORD = "user:find:password:{0}";
|
||||||
|
/**
|
||||||
|
* qa提问次数(每天)
|
||||||
|
* 参数:用户id:日期yyyyMMdd
|
||||||
|
* 值:提问数量
|
||||||
|
*/
|
||||||
|
public static final String AQ_ASK_TIMES = "qa:ask:limit:{0}:{1}";
|
||||||
|
/**
|
||||||
|
* 知识库知识点生成数量
|
||||||
|
* 值: 用户id
|
||||||
|
*/
|
||||||
|
public static final String QA_ITEM_CREATE_LIMIT = "aq:item:create:{0}";
|
||||||
|
/**
|
||||||
|
* 信号(重新生成知识库统计数据)
|
||||||
|
* 值:知识库uuid
|
||||||
|
*/
|
||||||
|
public static final String KB_STATISTIC_RECALCULATE_SIGNAL = "kb:statistic:recalculate:signal";
|
||||||
|
public static final String STATISTIC = "statistic";
|
||||||
|
public static final String STATISTIC_USER = "user";
|
||||||
|
public static final String STATISTIC_KNOWLEDGE_BASE = "kb";
|
||||||
|
public static final String STATISTIC_TOKEN_COST = "token-cost";
|
||||||
|
public static final String STATISTIC_CONVERSATION = "conversation";
|
||||||
|
public static final String STATISTIC_IMAGE_COST = "image-cost";
|
||||||
|
public static final String TOKEN_USAGE_KEY = "token:usage:{0}";
|
||||||
|
/**
|
||||||
|
* 用户正在对文档进行索引
|
||||||
|
* 值:用户id
|
||||||
|
*/
|
||||||
|
public static final String USER_INDEXING = "user:indexing:{0}";
|
||||||
|
/**
|
||||||
|
* 用户评论并发限制
|
||||||
|
* 值:用户id
|
||||||
|
*/
|
||||||
|
public static final String DRAW_COMMENT_LIMIT_KEY = "user:draw:comment-submitting:{0}";
|
||||||
|
public static final String WORKFLOW_KEY = "workflow";
|
||||||
|
public static final String WORKFLOW_COMPONENTS = "workflow:components";
|
||||||
|
public static final String WORKFLOW_COMPONENT_START_KEY = "workflow:component:start";
|
||||||
|
public static final String WORKFLOW_COMPONENT_KEY = "workflow:component";
|
||||||
|
public static final String WORKFLOW_COPY_DOING = "workflow:copy:doing:{0}";
|
||||||
|
|
||||||
|
private RedisKeyConstant() {
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package org.ruoyi.workflow.dto.workflow;
|
||||||
|
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import lombok.Data;
|
||||||
|
import org.springframework.validation.annotation.Validated;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Validated
|
||||||
|
public class WfAddReq {
|
||||||
|
|
||||||
|
@NotBlank
|
||||||
|
private String title;
|
||||||
|
|
||||||
|
private String remark;
|
||||||
|
|
||||||
|
private Boolean isPublic;
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package org.ruoyi.workflow.dto.workflow;
|
||||||
|
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import lombok.Data;
|
||||||
|
import org.springframework.validation.annotation.Validated;
|
||||||
|
|
||||||
|
@Validated
|
||||||
|
@Data
|
||||||
|
public class WfBaseInfoUpdateReq {
|
||||||
|
@NotBlank
|
||||||
|
private String uuid;
|
||||||
|
private String title;
|
||||||
|
private String remark;
|
||||||
|
private Boolean isPublic;
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package org.ruoyi.workflow.dto.workflow;
|
||||||
|
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import lombok.Data;
|
||||||
|
import org.springframework.validation.annotation.Validated;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Validated
|
||||||
|
public class WfComponentReq {
|
||||||
|
private String uuid;
|
||||||
|
@NotBlank(message = "标题不能为空")
|
||||||
|
private String name;
|
||||||
|
@NotBlank(message = "标题不能为空")
|
||||||
|
private String title;
|
||||||
|
private String remark;
|
||||||
|
private Boolean isEnable;
|
||||||
|
private Integer displayOrder;
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package org.ruoyi.workflow.dto.workflow;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class WfComponentSearchReq {
|
||||||
|
private String title;
|
||||||
|
private Boolean isEnable;
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package org.ruoyi.workflow.dto.workflow;
|
||||||
|
|
||||||
|
import jakarta.validation.constraints.Min;
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import lombok.Data;
|
||||||
|
import org.springframework.validation.annotation.Validated;
|
||||||
|
|
||||||
|
@Validated
|
||||||
|
@Data
|
||||||
|
public class WfEdgeReq {
|
||||||
|
private Long id;
|
||||||
|
@NotBlank
|
||||||
|
private String uuid;
|
||||||
|
@Min(1)
|
||||||
|
private Long workflowId;
|
||||||
|
@NotBlank
|
||||||
|
private String sourceNodeUuid;
|
||||||
|
private String sourceHandle;
|
||||||
|
@NotBlank
|
||||||
|
private String targetNodeUuid;
|
||||||
|
/**
|
||||||
|
* 是否新增
|
||||||
|
*/
|
||||||
|
private Boolean isNew;
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
package org.ruoyi.workflow.dto.workflow;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||||
|
import jakarta.validation.constraints.Min;
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
import jakarta.validation.constraints.Size;
|
||||||
|
import lombok.Data;
|
||||||
|
import org.springframework.validation.annotation.Validated;
|
||||||
|
|
||||||
|
@Validated
|
||||||
|
@Data
|
||||||
|
public class WfNodeDto {
|
||||||
|
private Long id;
|
||||||
|
@NotBlank
|
||||||
|
@Size(min = 32, max = 32)
|
||||||
|
private String uuid;
|
||||||
|
private Long workflowId;
|
||||||
|
@Min(1)
|
||||||
|
private Long workflowComponentId;
|
||||||
|
@NotBlank
|
||||||
|
private String title;
|
||||||
|
private String remark;
|
||||||
|
@NotNull
|
||||||
|
private ObjectNode inputConfig;
|
||||||
|
@NotNull
|
||||||
|
private ObjectNode nodeConfig;
|
||||||
|
@NotNull
|
||||||
|
private Double positionX;
|
||||||
|
@NotNull
|
||||||
|
private Double positionY;
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package org.ruoyi.workflow.dto.workflow;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||||
|
import lombok.Data;
|
||||||
|
import org.springframework.validation.annotation.Validated;
|
||||||
|
|
||||||
|
@Validated
|
||||||
|
@Data
|
||||||
|
public class WfRuntimeNodeDto {
|
||||||
|
private Long id;
|
||||||
|
private String uuid;
|
||||||
|
private Long workflowRuntimeId;
|
||||||
|
private Long nodeId;
|
||||||
|
private ObjectNode input;
|
||||||
|
private ObjectNode output;
|
||||||
|
private Integer status;
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package org.ruoyi.workflow.dto.workflow;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||||
|
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class WfRuntimeResp {
|
||||||
|
private Long id;
|
||||||
|
private String uuid;
|
||||||
|
private Long workflowId;
|
||||||
|
private ObjectNode input;
|
||||||
|
private ObjectNode output;
|
||||||
|
private Integer status;
|
||||||
|
private String statusRemark;
|
||||||
|
|
||||||
|
private String workflowUuid;
|
||||||
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
|
private LocalDateTime createTime;
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
package org.ruoyi.workflow.dto.workflow;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class WfSearchReq {
|
||||||
|
private String title;
|
||||||
|
private Boolean isEnable;
|
||||||
|
private Boolean isPublic;
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package org.ruoyi.workflow.dto.workflow;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class WorkflowResp {
|
||||||
|
private Long id;
|
||||||
|
private String uuid;
|
||||||
|
private String title;
|
||||||
|
private String remark;
|
||||||
|
private Boolean isPublic;
|
||||||
|
private Long userId;
|
||||||
|
private String userUuid;
|
||||||
|
private String userName;
|
||||||
|
private List<WfNodeDto> nodes;
|
||||||
|
private List<WfEdgeReq> edges;
|
||||||
|
private LocalDateTime createTime;
|
||||||
|
private LocalDateTime updateTime;
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package org.ruoyi.workflow.dto.workflow;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class WorkflowResumeReq {
|
||||||
|
private String feedbackContent;
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package org.ruoyi.workflow.dto.workflow;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class WorkflowRunReq {
|
||||||
|
private List<ObjectNode> inputs;
|
||||||
|
private String uuid;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package org.ruoyi.workflow.dto.workflow;
|
||||||
|
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
import jakarta.validation.constraints.Size;
|
||||||
|
import lombok.Data;
|
||||||
|
import org.springframework.validation.annotation.Validated;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Validated
|
||||||
|
@Data
|
||||||
|
public class WorkflowUpdateReq {
|
||||||
|
@NotBlank
|
||||||
|
private String uuid;
|
||||||
|
@Size(min = 1)
|
||||||
|
private List<WfNodeDto> nodes;
|
||||||
|
@NotNull
|
||||||
|
private List<WfEdgeReq> edges;
|
||||||
|
|
||||||
|
private List<String> deleteNodes;
|
||||||
|
|
||||||
|
private List<String> deleteEdges;
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package org.ruoyi.workflow.entity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.IdType;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableField;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class BaseEntity implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@TableId(type = IdType.AUTO)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@TableField(value = "create_time")
|
||||||
|
private LocalDateTime createTime;
|
||||||
|
|
||||||
|
@TableField(value = "update_time")
|
||||||
|
private LocalDateTime updateTime;
|
||||||
|
|
||||||
|
@Schema(title = "是否删除(0:未删除,1:已删除)")
|
||||||
|
@TableField(value = "is_deleted")
|
||||||
|
private Boolean isDeleted;
|
||||||
|
}
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
package org.ruoyi.workflow.entity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableField;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import org.ruoyi.workflow.enums.UserStatusEnum;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@Data
|
||||||
|
@TableName("adi_user")
|
||||||
|
@Schema(title = "User对象")
|
||||||
|
public class User extends BaseEntity {
|
||||||
|
|
||||||
|
@Schema(name = "用户名称")
|
||||||
|
@TableField("name")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@TableField("email")
|
||||||
|
private String email;
|
||||||
|
|
||||||
|
@TableField("password")
|
||||||
|
private String password;
|
||||||
|
|
||||||
|
@TableField("uuid")
|
||||||
|
private String uuid;
|
||||||
|
|
||||||
|
@Schema(name = "上下文理解中需要携带的消息对数量(提示词及回复)")
|
||||||
|
@TableField("understand_context_msg_pair_num")
|
||||||
|
private Integer understandContextMsgPairNum;
|
||||||
|
|
||||||
|
@Schema(name = "token quota in one day")
|
||||||
|
@TableField("quota_by_token_daily")
|
||||||
|
private Integer quotaByTokenDaily;
|
||||||
|
|
||||||
|
@Schema(name = "token quota in one month")
|
||||||
|
@TableField("quota_by_token_monthly")
|
||||||
|
private Integer quotaByTokenMonthly;
|
||||||
|
|
||||||
|
@Schema(name = "request quota in one day")
|
||||||
|
@TableField("quota_by_request_daily")
|
||||||
|
private Integer quotaByRequestDaily;
|
||||||
|
|
||||||
|
@Schema(name = "request quota in one month")
|
||||||
|
@TableField("quota_by_request_monthly")
|
||||||
|
private Integer quotaByRequestMonthly;
|
||||||
|
|
||||||
|
@TableField("quota_by_image_daily")
|
||||||
|
private Integer quotaByImageDaily;
|
||||||
|
|
||||||
|
@TableField("quota_by_image_monthly")
|
||||||
|
private Integer quotaByImageMonthly;
|
||||||
|
|
||||||
|
@TableField("user_status")
|
||||||
|
private UserStatusEnum userStatus;
|
||||||
|
|
||||||
|
@TableField("active_time")
|
||||||
|
private LocalDateTime activeTime;
|
||||||
|
|
||||||
|
@Schema(title = "是否管理员(0:否,1:是)")
|
||||||
|
@TableField(value = "is_admin")
|
||||||
|
private Boolean isAdmin;
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
package org.ruoyi.workflow.entity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableField;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
import java.io.Serial;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@TableName("t_workflow")
|
||||||
|
@Schema(title = "工作流定义 | workflow definition")
|
||||||
|
public class Workflow extends BaseEntity {
|
||||||
|
|
||||||
|
@Serial
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@TableField("uuid")
|
||||||
|
private String uuid;
|
||||||
|
|
||||||
|
@TableField("title")
|
||||||
|
private String title;
|
||||||
|
|
||||||
|
@TableField("remark")
|
||||||
|
private String remark;
|
||||||
|
|
||||||
|
@TableField("user_id")
|
||||||
|
private Long userId;
|
||||||
|
|
||||||
|
@TableField("is_public")
|
||||||
|
private Boolean isPublic;
|
||||||
|
|
||||||
|
@TableField("is_enable")
|
||||||
|
private Boolean isEnable;
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
package org.ruoyi.workflow.entity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableField;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
import java.io.Serial;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@TableName(value = "t_workflow_component", autoResultMap = true)
|
||||||
|
@Schema(title = "工作流组件")
|
||||||
|
public class WorkflowComponent extends BaseEntity {
|
||||||
|
|
||||||
|
@Serial
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@TableField("uuid")
|
||||||
|
private String uuid;
|
||||||
|
|
||||||
|
@TableField("name")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@TableField("title")
|
||||||
|
private String title;
|
||||||
|
|
||||||
|
@TableField("remark")
|
||||||
|
private String remark;
|
||||||
|
|
||||||
|
@TableField("display_order")
|
||||||
|
private Integer displayOrder;
|
||||||
|
|
||||||
|
@TableField("is_enable")
|
||||||
|
private Boolean isEnable;
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
package org.ruoyi.workflow.entity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableField;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
import java.io.Serial;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@TableName("t_workflow_edge")
|
||||||
|
@Schema(title = "工作流定义-边 | workflow definition edge")
|
||||||
|
public class WorkflowEdge extends BaseEntity {
|
||||||
|
|
||||||
|
@Serial
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@TableField("uuid")
|
||||||
|
private String uuid;
|
||||||
|
|
||||||
|
@TableField("workflow_id")
|
||||||
|
private Long workflowId;
|
||||||
|
|
||||||
|
@TableField("source_node_uuid")
|
||||||
|
private String sourceNodeUuid;
|
||||||
|
|
||||||
|
@TableField("source_handle")
|
||||||
|
private String sourceHandle;
|
||||||
|
|
||||||
|
@TableField("target_node_uuid")
|
||||||
|
private String targetNodeUuid;
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
package org.ruoyi.workflow.entity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableField;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
import java.io.Serial;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@TableName(value = "t_workflow_node", autoResultMap = true)
|
||||||
|
@Schema(title = "工作流定义-节点 | workflow definition node")
|
||||||
|
public class WorkflowNode extends BaseEntity {
|
||||||
|
|
||||||
|
@Serial
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@TableField("uuid")
|
||||||
|
private String uuid;
|
||||||
|
|
||||||
|
@TableField("workflow_id")
|
||||||
|
private Long workflowId;
|
||||||
|
|
||||||
|
@TableField("workflow_component_id")
|
||||||
|
private Long workflowComponentId;
|
||||||
|
|
||||||
|
@TableField("title")
|
||||||
|
private String title;
|
||||||
|
|
||||||
|
@TableField("remark")
|
||||||
|
private String remark;
|
||||||
|
|
||||||
|
@TableField(value = "input_config")
|
||||||
|
private String inputConfig;
|
||||||
|
|
||||||
|
@TableField(value = "node_config")
|
||||||
|
private String nodeConfig;
|
||||||
|
|
||||||
|
@TableField("position_x")
|
||||||
|
private Double positionX;
|
||||||
|
|
||||||
|
@TableField("position_y")
|
||||||
|
private Double positionY;
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
package org.ruoyi.workflow.entity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableField;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
import java.io.Serial;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@TableName(value = "t_workflow_runtime", autoResultMap = true)
|
||||||
|
@Schema(title = "工作流运行时 | Workflow runtime")
|
||||||
|
public class WorkflowRuntime extends BaseEntity {
|
||||||
|
|
||||||
|
@Serial
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@TableField("uuid")
|
||||||
|
private String uuid;
|
||||||
|
|
||||||
|
@TableField("user_id")
|
||||||
|
private Long userId;
|
||||||
|
|
||||||
|
@TableField("workflow_id")
|
||||||
|
private Long workflowId;
|
||||||
|
|
||||||
|
@TableField(value = "input")
|
||||||
|
private String input;
|
||||||
|
|
||||||
|
@TableField(value = "output")
|
||||||
|
private String output;
|
||||||
|
|
||||||
|
@TableField("status")
|
||||||
|
private Integer status;
|
||||||
|
|
||||||
|
@TableField("status_remark")
|
||||||
|
private String statusRemark;
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
package org.ruoyi.workflow.entity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableField;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
import java.io.Serial;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@TableName(value = "t_workflow_runtime_node", autoResultMap = true)
|
||||||
|
@Schema(title = "工作流实例-节点 | Workflow runtime - node")
|
||||||
|
public class WorkflowRuntimeNode extends BaseEntity {
|
||||||
|
@Serial
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@TableField("uuid")
|
||||||
|
private String uuid;
|
||||||
|
|
||||||
|
@TableField("user_id")
|
||||||
|
private Long userId;
|
||||||
|
|
||||||
|
@TableField("workflow_runtime_id")
|
||||||
|
private Long workflowRuntimeId;
|
||||||
|
|
||||||
|
@TableField("node_id")
|
||||||
|
private Long nodeId;
|
||||||
|
|
||||||
|
@TableField(value = "input")
|
||||||
|
private String input;
|
||||||
|
|
||||||
|
@TableField(value = "output")
|
||||||
|
private String output;
|
||||||
|
|
||||||
|
@TableField("status")
|
||||||
|
private Integer status;
|
||||||
|
|
||||||
|
@TableField("status_remark")
|
||||||
|
private String statusRemark;
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package org.ruoyi.workflow.enums;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@AllArgsConstructor
|
||||||
|
public enum AiModelStatus implements BaseEnum {
|
||||||
|
ACTIVE(1, "启用"),
|
||||||
|
INACTIVE(2, "停用");
|
||||||
|
|
||||||
|
private final Integer value;
|
||||||
|
private final String desc;
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package org.ruoyi.workflow.enums;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.IEnum;
|
||||||
|
|
||||||
|
public interface BaseEnum extends IEnum<Integer> {
|
||||||
|
/**
|
||||||
|
* 获取对应名称
|
||||||
|
*
|
||||||
|
* @return String
|
||||||
|
*/
|
||||||
|
String getDesc();
|
||||||
|
}
|
||||||
@@ -0,0 +1,127 @@
|
|||||||
|
package org.ruoyi.workflow.enums;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
public enum ErrorEnum {
|
||||||
|
SUCCESS("00000", "成功"),
|
||||||
|
A_URL_NOT_FOUND("A0001", "地址不存在"),
|
||||||
|
A_PARAMS_ERROR("A0002", "参数校验不通过"),
|
||||||
|
A_REQUEST_TOO_MUCH("A0003", "访问次数太多"),
|
||||||
|
A_LOGIN_ERROR("A0004", "登陆失败,账号或密码错误"),
|
||||||
|
A_LOGIN_ERROR_MAX("A0005", "失败次数太多,请输入验证码重试"),
|
||||||
|
A_LOGIN_CAPTCHA_ERROR("A0006", "验证码不正确"),
|
||||||
|
A_USER_NOT_EXIST("A0007", "用户不存在"),
|
||||||
|
A_CONVERSATION_NOT_EXIST("A0008", "对话不存在"),
|
||||||
|
A_IMAGE_NUMBER_ERROR("A0009", "图片数量不对"),
|
||||||
|
A_IMAGE_SIZE_ERROR("A0010", "图片尺寸不对"),
|
||||||
|
A_FILE_NOT_EXIST("A0011", "文件不存在"),
|
||||||
|
A_DRAWING("A0012", "作图还未完成"),
|
||||||
|
A_USER_EXIST("A0013", "账号已经存在,请使用账号密码登录"),
|
||||||
|
A_FIND_PASSWORD_CODE_ERROR("A0014", "重置码已过期或不存在"),
|
||||||
|
A_USER_WAIT_CONFIRM("A0015", "用户未激活"),
|
||||||
|
A_USER_NOT_AUTH("A0016", "用户无权限"),
|
||||||
|
A_DATA_NOT_FOUND("A0017", "数据不存在"),
|
||||||
|
A_UPLOAD_FAIL("A0018", "上传失败"),
|
||||||
|
A_QA_ASK_LIMIT("A0019", "请求次数太多"),
|
||||||
|
A_QA_ITEM_LIMIT("A0020", "知识点生成已超额度"),
|
||||||
|
A_CONVERSATION_EXIST("A0021", "会话(角色)已存在"),
|
||||||
|
A_MODEL_NOT_FOUND("A0022", "模型不存在"),
|
||||||
|
A_MODEL_ALREADY_EXIST("A0023", "模型已存在"),
|
||||||
|
A_CONVERSATION_NOT_FOUND("A0024", "会话(角色)找不到"),
|
||||||
|
A_AI_IMAGE_NOT_FOUND("A0024", "图片找不到"),
|
||||||
|
A_ENABLE_MODEL_NOT_FOUND("A0025", "没有可用的模型"),
|
||||||
|
A_DOC_INDEX_DOING("A0026", "文档索引正在进行中,请稍后重试"),
|
||||||
|
A_PRESET_CONVERSATION_NOT_EXIST("A0027", "预设会话或角色不存在"),
|
||||||
|
A_CONVERSATION_TITLE_EXIST("A0028", "会话(角色)标题已存在"),
|
||||||
|
A_AI_IMAGE_NO_AUTH("A0029", "无权限查看该图片"),
|
||||||
|
A_USER_NOT_FOUND("A0030", "用户不存在"),
|
||||||
|
A_ACTIVE_CODE_INVALID("A0031", "激活码已失效"),
|
||||||
|
A_OLD_PASSWORD_INVALID("A0032", "原密码不正确"),
|
||||||
|
A_OPT_TOO_FREQUENTLY("A0032", "操作太频繁"),
|
||||||
|
A_DRAW_NOT_FOUND("A00033", "绘图记录找不到"),
|
||||||
|
A_WF_NOT_FOUND("A00034", "工作流找不到"),
|
||||||
|
A_WF_DISABLED("A0035", "工作流已停用"),
|
||||||
|
A_WF_NODE_NOT_FOUND("A0036", "工作流节点找不到"),
|
||||||
|
A_WF_NODE_CONFIG_NOT_FOUND("A0037", "工作流节点配置找不到"),
|
||||||
|
A_WF_NODE_CONFIG_ERROR("A0038", "工作流节点配置异常"),
|
||||||
|
A_WF_INPUT_INVALID("A0039", "工作流输入参数错误"),
|
||||||
|
A_WF_INPUT_MISSING("A0040", "工作流输入缺少参数"),
|
||||||
|
A_WF_MULTIPLE_START_NODE("A0041", "多个开始节点"),
|
||||||
|
A_WF_START_NODE_NOT_FOUND("A0042", "没有开始节点"),
|
||||||
|
A_WF_END_NODE_NOT_FOUND("A0043", "没有结束节点"),
|
||||||
|
A_WF_EDGE_NOT_FOUND("A0044", "工作流的边找不到"),
|
||||||
|
A_WF_RUNTIME_NOT_FOUND("A00045", "工作流运行时数据找不到"),
|
||||||
|
A_SEARCH_QUERY_IS_EMPTY("A00046", "搜索内容不能为空"),
|
||||||
|
A_WF_COMPONENT_NOT_FOUND("A00047", "工作流基础组件找不到"),
|
||||||
|
A_WF_RESUME_FAIL("A00048", "工作流恢复执行时失败"),
|
||||||
|
A_MAIL_SENDER_EMPTY("A00049", "邮件发送人不能为空"),
|
||||||
|
A_MAIL_SENDER_CONFIG_ERROR("A00050", "邮件发送人配置错误"),
|
||||||
|
A_MAIL_RECEIVER_EMPTY("A00051", "邮件接收人不能为空"),
|
||||||
|
A_MCP_SERVER_NOT_FOUND("A00052", "MCP服务找不到"),
|
||||||
|
A_USER_MCP_SERVER_NOT_FOUND("A00053", "用户的MCP服务找不到"),
|
||||||
|
A_PARAMS_INVALID_BY_("A00054", "参数校验异常:{0}"),
|
||||||
|
A_AI_MESSAGE_NOT_FOUND("A00055", "找不到AI的消息"),
|
||||||
|
A_USER_QUESTION_NOT_FOUND("A00056", "用户问题不存在"),
|
||||||
|
A_PLATFORM_NOT_MATCH("A0057", "平台不匹配"),
|
||||||
|
B_UNCAUGHT_ERROR("B0001", "未捕捉异常"),
|
||||||
|
B_COMMON_ERROR("B0002", "业务出错"),
|
||||||
|
B_GLOBAL_ERROR("B0003", "全局异常"),
|
||||||
|
B_SAVE_IMAGE_ERROR("B0004", "保存图片异常"),
|
||||||
|
B_FIND_IMAGE_404("B0005", "无法找到图片"),
|
||||||
|
B_DAILY_QUOTA_USED("B0006", "今天额度已经用完"),
|
||||||
|
B_MONTHLY_QUOTA_USED("B0007", "当月额度已经用完"),
|
||||||
|
B_LLM_NOT_SUPPORT("B0008", "LLM不支持该功能"),
|
||||||
|
B_LLM_SECRET_KEY_NOT_SET("B0009", "LLM的secret key没设置"),
|
||||||
|
B_MESSAGE_NOT_FOUND("B0008", "消息不存在"),
|
||||||
|
B_LLM_SERVICE_DISABLED("B0009", "LLM服务不可用"),
|
||||||
|
B_KNOWLEDGE_BASE_IS_EMPTY("B0010", "知识库内容为空"),
|
||||||
|
B_NO_ANSWER("B0011", "[无答案]"),
|
||||||
|
B_SAVE_FILE_ERROR("B0012", "保存文件异常"),
|
||||||
|
B_BREAK_SEARCH("B0013", "中断搜索"),
|
||||||
|
B_GRAPH_FILTER_NOT_FOUND("B0014", "图过滤器未定义"),
|
||||||
|
B_DB_ERROR("B0015", "数据库查询异常"),
|
||||||
|
B_ACTIVE_USER_ERROR("B0016", "激活用户失败"),
|
||||||
|
B_RESET_PASSWORD_ERROR("B0017", "重置密码失败"),
|
||||||
|
B_IMAGE_LOAD_ERROR("B0018", "加载图片失败"),
|
||||||
|
B_IO_EXCEPTION("B0019", "IO异常"),
|
||||||
|
B_SERVER_EXCEPTION("B0020", "服务端异常"),
|
||||||
|
B_DELETE_FILE_ERROR("B0021", "删除文件异常"),
|
||||||
|
B_WF_RUN_ERROR("B0022", "工作流运行异常"),
|
||||||
|
B_WF_NODE_DEFINITION_NOT_FOUND("B0023", "工作流节点定义找不到"),
|
||||||
|
B_DIR_CREATE_FAIL("B0024", "创建目录失败"),
|
||||||
|
B_LLM_TEMPERATURE_ERROR("B0025", "采样温度应该在 0.1-1之间"),
|
||||||
|
B_ASR_SETTING_NOT_FOUND("B0026", "语音识别设置未找到"),
|
||||||
|
B_URL_INVALID("B0027", "不是有效的网络地址"),
|
||||||
|
B_ASR_MODEL_NOT_FOUND("B0028", "语音识别模型未找到"),
|
||||||
|
B_TTS_SETTING_NOT_FOUND("B0029", "语音合成设置未找到"),
|
||||||
|
B_TTS_MODEL_NOT_FOUND("B0030", "语音合成模型未找到"),
|
||||||
|
B_VOICE_NOT_FOUND("B0031", "声音不存在"),
|
||||||
|
C_DRAW_FAIL("C0001", "大模型生成图片失败,原因:{0}"),
|
||||||
|
C_ALI_OSS_CONFIG_ERROR("C0002", "阿里云OSS初始化失败,原因:{0}"),
|
||||||
|
C_LLM_RESPONSE_INVALID("C0003", "大模型生成结果内容无效"),
|
||||||
|
C_WF_COMPONENT_DELETED_FAIL_BY_USED("C0004", "工作流组件已经被使用,无法被删除,可先停用");
|
||||||
|
|
||||||
|
private final String code;
|
||||||
|
private final String info;
|
||||||
|
|
||||||
|
ErrorEnum(String code, String info) {
|
||||||
|
this.code = code;
|
||||||
|
this.info = info;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ErrorEnum getErrorEnum(String code) {
|
||||||
|
ErrorEnum result = null;
|
||||||
|
for (ErrorEnum c : ErrorEnum.values()) {
|
||||||
|
if (c.getCode().equals(code)) {
|
||||||
|
result = c;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (null == result) {
|
||||||
|
result = B_COMMON_ERROR;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package org.ruoyi.workflow.enums;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@AllArgsConstructor
|
||||||
|
public enum UserStatusEnum implements BaseEnum {
|
||||||
|
|
||||||
|
WAIT_CONFIRM(1, "待验证"),
|
||||||
|
NORMAL(2, "正常"),
|
||||||
|
FREEZE(3, "冻结");
|
||||||
|
|
||||||
|
private final Integer value;
|
||||||
|
private final String desc;
|
||||||
|
|
||||||
|
public static UserStatusEnum getByValue(Integer val) {
|
||||||
|
return Arrays.stream(UserStatusEnum.values()).filter(item -> item.value.equals(val)).findFirst().orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package org.ruoyi.workflow.enums;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@AllArgsConstructor
|
||||||
|
public enum WfIODataTypeEnum implements BaseEnum {
|
||||||
|
TEXT(1, "文本"),
|
||||||
|
NUMBER(2, "数字"),
|
||||||
|
OPTIONS(3, "下拉选项"),
|
||||||
|
FILES(4, "文件列表"),
|
||||||
|
BOOL(5, "布尔值"),
|
||||||
|
REF_INPUT(6, "引用节点的输入参数"),
|
||||||
|
REF_OUTPUT(7, "引用节点的输出参数");
|
||||||
|
|
||||||
|
private final Integer value;
|
||||||
|
private final String desc;
|
||||||
|
|
||||||
|
public static WfIODataTypeEnum getByValue(Integer val) {
|
||||||
|
return Arrays.stream(WfIODataTypeEnum.values()).filter(item -> item.value.equals(val)).findFirst().orElse(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,138 @@
|
|||||||
|
package org.ruoyi.workflow.helper;
|
||||||
|
|
||||||
|
import com.google.common.cache.Cache;
|
||||||
|
import com.google.common.cache.CacheBuilder;
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.ruoyi.workflow.cosntant.AdiConstant;
|
||||||
|
import org.ruoyi.workflow.cosntant.RedisKeyConstant;
|
||||||
|
import org.ruoyi.workflow.entity.User;
|
||||||
|
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.text.MessageFormat;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Service
|
||||||
|
public class SSEEmitterHelper {
|
||||||
|
|
||||||
|
private static final Cache<SseEmitter, Boolean> COMPLETED_SSE = CacheBuilder.newBuilder()
|
||||||
|
.expireAfterWrite(10, TimeUnit.MINUTES).build();
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private StringRedisTemplate stringRedisTemplate;
|
||||||
|
|
||||||
|
public static void parseAndSendPartialMsg(SseEmitter sseEmitter, String name, String content) {
|
||||||
|
if (Boolean.TRUE.equals(COMPLETED_SSE.getIfPresent(sseEmitter))) {
|
||||||
|
log.warn("sseEmitter already completed,name:{}", name);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String[] lines = content.split("[\\r\\n]", -1);
|
||||||
|
if (lines.length > 1) {
|
||||||
|
sendPartial(sseEmitter, name, " " + lines[0]);
|
||||||
|
for (int i = 1; i < lines.length; i++) {
|
||||||
|
sendPartial(sseEmitter, name, "-_wrap_-");
|
||||||
|
sendPartial(sseEmitter, name, " " + lines[i]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
sendPartial(sseEmitter, name, " " + content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void sendPartial(SseEmitter sseEmitter, String name, String msg) {
|
||||||
|
if (Boolean.TRUE.equals(COMPLETED_SSE.getIfPresent(sseEmitter))) {
|
||||||
|
log.warn("sseEmitter already completed,name:{}", name);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
if (StringUtils.isNotBlank(name)) {
|
||||||
|
sseEmitter.send(SseEmitter.event().name(name).data(msg));
|
||||||
|
} else {
|
||||||
|
sseEmitter.send(msg);
|
||||||
|
}
|
||||||
|
} catch (IOException ioException) {
|
||||||
|
log.error("stream onNext error", ioException);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public boolean checkOrComplete(User user, SseEmitter sseEmitter) {
|
||||||
|
//Check: If still waiting response
|
||||||
|
String askingKey = MessageFormat.format(RedisKeyConstant.USER_ASKING, user.getId());
|
||||||
|
String askingVal = stringRedisTemplate.opsForValue().get(askingKey);
|
||||||
|
if (StringUtils.isNotBlank(askingVal)) {
|
||||||
|
sendErrorAndComplete(user.getId(), sseEmitter, "正在回复中...");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void startSse(User user, SseEmitter sseEmitter, String data) {
|
||||||
|
|
||||||
|
String askingKey = MessageFormat.format(RedisKeyConstant.USER_ASKING, user.getId());
|
||||||
|
stringRedisTemplate.opsForValue().set(askingKey, "1", 15, TimeUnit.SECONDS);
|
||||||
|
|
||||||
|
try {
|
||||||
|
SseEmitter.SseEventBuilder builder = SseEmitter.event().name(AdiConstant.SSEEventName.START);
|
||||||
|
if (StringUtils.isNotBlank(data)) {
|
||||||
|
builder.data(data);
|
||||||
|
}
|
||||||
|
sseEmitter.send(builder);
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.error("startSse error", e);
|
||||||
|
sseEmitter.completeWithError(e);
|
||||||
|
COMPLETED_SSE.put(sseEmitter, Boolean.TRUE);
|
||||||
|
stringRedisTemplate.delete(askingKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void sendComplete(long userId, SseEmitter sseEmitter, String msg) {
|
||||||
|
if (Boolean.TRUE.equals(COMPLETED_SSE.getIfPresent(sseEmitter))) {
|
||||||
|
log.warn("sseEmitter already completed,userId:{}", userId);
|
||||||
|
delSseRequesting(userId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
sseEmitter.send(SseEmitter.event().name(AdiConstant.SSEEventName.DONE).data(msg));
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
} finally {
|
||||||
|
COMPLETED_SSE.put(sseEmitter, Boolean.TRUE);
|
||||||
|
delSseRequesting(userId);
|
||||||
|
sseEmitter.complete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void sendErrorAndComplete(long userId, SseEmitter sseEmitter, String errorMsg) {
|
||||||
|
if (Boolean.TRUE.equals(COMPLETED_SSE.getIfPresent(sseEmitter))) {
|
||||||
|
log.warn("sseEmitter already completed,ignore error:{}", errorMsg);
|
||||||
|
delSseRequesting(userId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
SseEmitter.SseEventBuilder event = SseEmitter.event();
|
||||||
|
event.name(AdiConstant.SSEEventName.ERROR);
|
||||||
|
event.data(Objects.toString(errorMsg, ""));
|
||||||
|
sseEmitter.send(event);
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.warn("sendErrorAndComplete userId:{},errorMsg:{}", userId, errorMsg);
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
} finally {
|
||||||
|
COMPLETED_SSE.put(sseEmitter, Boolean.TRUE);
|
||||||
|
delSseRequesting(userId);
|
||||||
|
sseEmitter.complete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void delSseRequesting(long userId) {
|
||||||
|
String askingKey = MessageFormat.format(RedisKeyConstant.USER_ASKING, userId);
|
||||||
|
stringRedisTemplate.delete(askingKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package org.ruoyi.workflow.mapper;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
import org.ruoyi.workflow.entity.WorkflowComponent;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public interface WorkflowComponentMapper extends BaseMapper<WorkflowComponent> {
|
||||||
|
Integer countRefNodes(@Param("uuid") String uuid);
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package org.ruoyi.workflow.mapper;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
import org.ruoyi.workflow.entity.WorkflowEdge;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public interface WorkflowEdgeMapper extends BaseMapper<WorkflowEdge> {
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package org.ruoyi.workflow.mapper;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
import org.ruoyi.workflow.entity.Workflow;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public interface WorkflowMapper extends BaseMapper<Workflow> {
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
package org.ruoyi.workflow.mapper;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
import org.ruoyi.workflow.entity.WorkflowNode;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public interface WorkflowNodeMapper extends BaseMapper<WorkflowNode> {
|
||||||
|
WorkflowNode getStartNode(long workflowId);
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package org.ruoyi.workflow.mapper;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
import org.ruoyi.workflow.entity.WorkflowRuntime;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public interface WorkflowRunMapper extends BaseMapper<WorkflowRuntime> {
|
||||||
|
|
||||||
|
Page<WorkflowRuntime> pageByWfUuid(Page<WorkflowRuntime> page, @Param("wfUuid") String wfUuid, @Param("userId") Long userId);
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package org.ruoyi.workflow.mapper;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
import org.ruoyi.workflow.entity.WorkflowRuntimeNode;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public interface WorkflowRuntimeNodeMapper extends BaseMapper<WorkflowRuntimeNode> {
|
||||||
|
}
|
||||||
@@ -0,0 +1,126 @@
|
|||||||
|
package org.ruoyi.workflow.service;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
|
import com.baomidou.mybatisplus.extension.toolkit.ChainWrappers;
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.ruoyi.common.core.exception.base.BaseException;
|
||||||
|
import org.ruoyi.workflow.dto.workflow.WfComponentReq;
|
||||||
|
import org.ruoyi.workflow.dto.workflow.WfComponentSearchReq;
|
||||||
|
import org.ruoyi.workflow.entity.WorkflowComponent;
|
||||||
|
import org.ruoyi.workflow.enums.ErrorEnum;
|
||||||
|
import org.ruoyi.workflow.mapper.WorkflowComponentMapper;
|
||||||
|
import org.ruoyi.workflow.util.PrivilegeUtil;
|
||||||
|
import org.ruoyi.workflow.util.UuidUtil;
|
||||||
|
import org.ruoyi.workflow.workflow.WfComponentNameEnum;
|
||||||
|
import org.springframework.beans.BeanUtils;
|
||||||
|
import org.springframework.cache.annotation.CacheEvict;
|
||||||
|
import org.springframework.cache.annotation.Cacheable;
|
||||||
|
import org.springframework.context.annotation.Lazy;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.ruoyi.workflow.cosntant.RedisKeyConstant.WORKFLOW_COMPONENTS;
|
||||||
|
import static org.ruoyi.workflow.cosntant.RedisKeyConstant.WORKFLOW_COMPONENT_START_KEY;
|
||||||
|
import static org.ruoyi.workflow.enums.ErrorEnum.C_WF_COMPONENT_DELETED_FAIL_BY_USED;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Service
|
||||||
|
public class WorkflowComponentService extends ServiceImpl<WorkflowComponentMapper, WorkflowComponent> {
|
||||||
|
|
||||||
|
@Lazy
|
||||||
|
@Resource
|
||||||
|
private WorkflowComponentService self;
|
||||||
|
|
||||||
|
@CacheEvict(cacheNames = {WORKFLOW_COMPONENTS, WORKFLOW_COMPONENT_START_KEY})
|
||||||
|
public WorkflowComponent addOrUpdate(WfComponentReq req) {
|
||||||
|
WorkflowComponent wfComponent;
|
||||||
|
if (StringUtils.isNotBlank(req.getUuid())) {
|
||||||
|
wfComponent = PrivilegeUtil.checkAndGetByUuid(req.getUuid(), this.query(), ErrorEnum.A_WF_COMPONENT_NOT_FOUND);
|
||||||
|
|
||||||
|
WorkflowComponent update = new WorkflowComponent();
|
||||||
|
BeanUtils.copyProperties(req, update, "id", "uuid");
|
||||||
|
update.setId(wfComponent.getId());
|
||||||
|
update.setName(req.getName());
|
||||||
|
update.setTitle(req.getTitle());
|
||||||
|
update.setRemark(req.getRemark());
|
||||||
|
update.setIsEnable(req.getIsEnable());
|
||||||
|
update.setDisplayOrder(req.getDisplayOrder());
|
||||||
|
this.baseMapper.updateById(update);
|
||||||
|
|
||||||
|
return update;
|
||||||
|
} else {
|
||||||
|
wfComponent = new WorkflowComponent();
|
||||||
|
BeanUtils.copyProperties(req, wfComponent, "id", "uuid");
|
||||||
|
wfComponent.setUuid(UuidUtil.createShort());
|
||||||
|
this.baseMapper.insert(wfComponent);
|
||||||
|
|
||||||
|
return wfComponent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@CacheEvict(cacheNames = {WORKFLOW_COMPONENTS, WORKFLOW_COMPONENT_START_KEY})
|
||||||
|
public void enable(String uuid, Boolean isEnable) {
|
||||||
|
WorkflowComponent wfComponent = PrivilegeUtil.checkAndGetByUuid(uuid, this.query(), ErrorEnum.A_WF_COMPONENT_NOT_FOUND);
|
||||||
|
WorkflowComponent update = new WorkflowComponent();
|
||||||
|
update.setIsEnable(isEnable);
|
||||||
|
update.setId(wfComponent.getId());
|
||||||
|
this.baseMapper.updateById(update);
|
||||||
|
}
|
||||||
|
|
||||||
|
@CacheEvict(cacheNames = {WORKFLOW_COMPONENTS, WORKFLOW_COMPONENT_START_KEY})
|
||||||
|
public void deleteByUuid(String uuid) {
|
||||||
|
WorkflowComponent component = PrivilegeUtil.checkAndGetByUuid(uuid, this.query(), ErrorEnum.A_WF_COMPONENT_NOT_FOUND);
|
||||||
|
Integer refNodeCount = baseMapper.countRefNodes(uuid);
|
||||||
|
if (refNodeCount != null && refNodeCount > 0) {
|
||||||
|
throw new BaseException(C_WF_COMPONENT_DELETED_FAIL_BY_USED.getInfo());
|
||||||
|
}
|
||||||
|
boolean updated = ChainWrappers.lambdaUpdateChain(baseMapper)
|
||||||
|
.eq(WorkflowComponent::getId, component.getId())
|
||||||
|
.set(WorkflowComponent::getIsDeleted, true)
|
||||||
|
.set(WorkflowComponent::getIsEnable, false)
|
||||||
|
.update();
|
||||||
|
if (!updated) {
|
||||||
|
throw new BaseException(ErrorEnum.A_WF_COMPONENT_NOT_FOUND.getInfo());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Page<WorkflowComponent> search(WfComponentSearchReq searchReq, Integer currentPage, Integer pageSize) {
|
||||||
|
LambdaQueryWrapper<WorkflowComponent> wrapper = new LambdaQueryWrapper<>();
|
||||||
|
wrapper.eq(WorkflowComponent::getIsDeleted, false);
|
||||||
|
wrapper.eq(null != searchReq.getIsEnable(), WorkflowComponent::getIsEnable, searchReq.getIsEnable());
|
||||||
|
wrapper.like(StringUtils.isNotBlank(searchReq.getTitle()), WorkflowComponent::getTitle, searchReq.getTitle());
|
||||||
|
wrapper.orderByAsc(List.of(WorkflowComponent::getDisplayOrder, WorkflowComponent::getId));
|
||||||
|
return baseMapper.selectPage(new Page<>(currentPage, pageSize), wrapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Cacheable(cacheNames = WORKFLOW_COMPONENTS)
|
||||||
|
public List<WorkflowComponent> getAllEnable() {
|
||||||
|
return ChainWrappers.lambdaQueryChain(baseMapper)
|
||||||
|
.eq(WorkflowComponent::getIsEnable, true)
|
||||||
|
.eq(WorkflowComponent::getIsDeleted, false)
|
||||||
|
.orderByAsc(List.of(WorkflowComponent::getDisplayOrder, WorkflowComponent::getId))
|
||||||
|
.list();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Cacheable(cacheNames = WORKFLOW_COMPONENT_START_KEY)
|
||||||
|
public WorkflowComponent getStartComponent() {
|
||||||
|
List<WorkflowComponent> components = self.getAllEnable();
|
||||||
|
return components.stream()
|
||||||
|
.filter(component -> WfComponentNameEnum.START.getName().equals(component.getName()))
|
||||||
|
.findFirst()
|
||||||
|
.orElseThrow(() -> new BaseException(ErrorEnum.B_WF_NODE_DEFINITION_NOT_FOUND.getInfo()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public WorkflowComponent getComponent(Long id) {
|
||||||
|
List<WorkflowComponent> components = self.getAllEnable();
|
||||||
|
return components.stream()
|
||||||
|
.filter(component -> component.getId().equals(id))
|
||||||
|
.findFirst()
|
||||||
|
.orElseThrow(() -> new BaseException(ErrorEnum.B_WF_NODE_DEFINITION_NOT_FOUND.getInfo()));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,118 @@
|
|||||||
|
package org.ruoyi.workflow.service;
|
||||||
|
|
||||||
|
import cn.hutool.core.collection.CollUtil;
|
||||||
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
|
import com.baomidou.mybatisplus.extension.toolkit.ChainWrappers;
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.commons.collections4.CollectionUtils;
|
||||||
|
import org.ruoyi.common.core.exception.base.BaseException;
|
||||||
|
import org.ruoyi.workflow.dto.workflow.WfEdgeReq;
|
||||||
|
import org.ruoyi.workflow.entity.WorkflowEdge;
|
||||||
|
import org.ruoyi.workflow.enums.ErrorEnum;
|
||||||
|
import org.ruoyi.workflow.mapper.WorkflowEdgeMapper;
|
||||||
|
import org.ruoyi.workflow.util.MPPageUtil;
|
||||||
|
import org.ruoyi.workflow.util.UuidUtil;
|
||||||
|
import org.springframework.beans.BeanUtils;
|
||||||
|
import org.springframework.context.annotation.Lazy;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Service
|
||||||
|
public class WorkflowEdgeService extends ServiceImpl<WorkflowEdgeMapper, WorkflowEdge> {
|
||||||
|
|
||||||
|
@Lazy
|
||||||
|
@Resource
|
||||||
|
private WorkflowEdgeService self;
|
||||||
|
|
||||||
|
public List<WfEdgeReq> listDtoByWfId(long workflowId) {
|
||||||
|
List<WorkflowEdge> edges = ChainWrappers.lambdaQueryChain(baseMapper)
|
||||||
|
.eq(WorkflowEdge::getWorkflowId, workflowId)
|
||||||
|
.eq(WorkflowEdge::getIsDeleted, false)
|
||||||
|
.list();
|
||||||
|
return MPPageUtil.convertToList(edges, WfEdgeReq.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public void createOrUpdateEdges(Long workflowId, List<WfEdgeReq> edges) {
|
||||||
|
List<String> uuidList = new ArrayList<>();
|
||||||
|
for (WfEdgeReq edge : edges) {
|
||||||
|
WorkflowEdge newOne = new WorkflowEdge();
|
||||||
|
BeanUtils.copyProperties(edge, newOne);
|
||||||
|
newOne.setWorkflowId(workflowId);
|
||||||
|
|
||||||
|
WorkflowEdge old = self.getByUuid(edge.getUuid());
|
||||||
|
if (null != old) {
|
||||||
|
log.info("更新边,id:{},uuid:{},source:{},sourceHandle:{},target:{}",
|
||||||
|
edge.getId(), edge.getUuid(), edge.getSourceNodeUuid(), edge.getSourceHandle(), edge.getTargetNodeUuid());
|
||||||
|
newOne.setId(old.getId());
|
||||||
|
} else {
|
||||||
|
newOne.setId(null);
|
||||||
|
log.info("新增边,uuid:{},source:{},sourceHandle:{},target:{}",
|
||||||
|
edge.getUuid(), edge.getSourceNodeUuid(), edge.getSourceHandle(), edge.getTargetNodeUuid());
|
||||||
|
}
|
||||||
|
uuidList.add(edge.getUuid());
|
||||||
|
self.saveOrUpdate(newOne);
|
||||||
|
}
|
||||||
|
ChainWrappers.lambdaUpdateChain(baseMapper)
|
||||||
|
.eq(WorkflowEdge::getWorkflowId, workflowId)
|
||||||
|
.notIn(CollUtil.isNotEmpty(uuidList), WorkflowEdge::getUuid, uuidList)
|
||||||
|
.set(WorkflowEdge::getIsDeleted, true)
|
||||||
|
.update();
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<WorkflowEdge> listByWorkflowId(Long workflowId) {
|
||||||
|
return ChainWrappers.lambdaQueryChain(baseMapper)
|
||||||
|
.eq(WorkflowEdge::getWorkflowId, workflowId)
|
||||||
|
.eq(WorkflowEdge::getIsDeleted, false)
|
||||||
|
.list();
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<WorkflowEdge> copyByWorkflowId(long workflowId, long targetWorkflow) {
|
||||||
|
List<WorkflowEdge> result = new ArrayList<>();
|
||||||
|
self.listByWorkflowId(workflowId).forEach(edge -> {
|
||||||
|
result.add(self.copyEdge(targetWorkflow, edge));
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public WorkflowEdge copyEdge(long targetWorkflow, WorkflowEdge sourceEdge) {
|
||||||
|
WorkflowEdge newEdge = new WorkflowEdge();
|
||||||
|
BeanUtils.copyProperties(sourceEdge, newEdge, "id", "uuid", "createTime", "updateTime");
|
||||||
|
newEdge.setUuid(UuidUtil.createShort());
|
||||||
|
newEdge.setWorkflowId(targetWorkflow);
|
||||||
|
baseMapper.insert(newEdge);
|
||||||
|
return getById(newEdge.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public void deleteEdges(Long workflowId, List<String> uuids) {
|
||||||
|
if (CollectionUtils.isEmpty(uuids)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (String uuid : uuids) {
|
||||||
|
WorkflowEdge old = self.getByUuid(uuid);
|
||||||
|
if (null != old && !old.getWorkflowId().equals(workflowId)) {
|
||||||
|
log.error("该边不属于指定的工作流,删除失败,workflowId:{},node workflowId:{}", workflowId, workflowId);
|
||||||
|
throw new BaseException(ErrorEnum.A_PARAMS_ERROR.getInfo());
|
||||||
|
}
|
||||||
|
ChainWrappers.lambdaUpdateChain(baseMapper)
|
||||||
|
.eq(WorkflowEdge::getWorkflowId, workflowId)
|
||||||
|
.eq(WorkflowEdge::getUuid, uuid)
|
||||||
|
.set(WorkflowEdge::getIsDeleted, true)
|
||||||
|
.update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public WorkflowEdge getByUuid(String uuid) {
|
||||||
|
return ChainWrappers.lambdaQueryChain(baseMapper)
|
||||||
|
.eq(WorkflowEdge::getUuid, uuid)
|
||||||
|
.eq(WorkflowEdge::getIsDeleted, false)
|
||||||
|
.last("limit 1")
|
||||||
|
.one();
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user