mirror of
https://gitcode.com/ageerle/ruoyi-ai.git
synced 2026-03-13 20:53:42 +08:00
42
docs/文件上传接口文档.md
Normal file
42
docs/文件上传接口文档.md
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
## 接口信息
|
||||||
|
|
||||||
|
**接口路径**: `POST /resource/oss/upload`
|
||||||
|
**请求类型**: `multipart/form-data`
|
||||||
|
**权限要求**: `system:oss:upload`
|
||||||
|
**业务类型**: [INSERT]
|
||||||
|
|
||||||
|
### 接口描述
|
||||||
|
上传OSS对象存储接口,用于将文件上传到对象存储服务。
|
||||||
|
|
||||||
|
### 请求参数
|
||||||
|
| 参数名 | 类型 | 必填 | 说明 |
|
||||||
|
| ---- | ------------- | ---- | ------ |
|
||||||
|
| file | MultipartFile | 是 | 要上传的文件 |
|
||||||
|
|
||||||
|
### 请求头
|
||||||
|
- `Content-Type`: `multipart/form-data`
|
||||||
|
|
||||||
|
### 返回值
|
||||||
|
返回 `R<SysOssUploadVo>` 类型,包含以下字段:
|
||||||
|
| 字段名 | 类型 | 说明 |
|
||||||
|
| -------- | ------ | ------- |
|
||||||
|
| url | String | 文件访问URL |
|
||||||
|
| fileName | String | 原始文件名 |
|
||||||
|
| ossId | String | 文件ID |
|
||||||
|
|
||||||
|
### 响应示例
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"msg": "操作成功",
|
||||||
|
"data": {
|
||||||
|
"url": "fileid://xxx",
|
||||||
|
"fileName": "example.jpg",
|
||||||
|
"ossId": "123"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
### 异常情况
|
||||||
|
- 当上传文件为空时,返回错误信息:"上传文件不能为空"
|
||||||
@@ -22,6 +22,12 @@
|
|||||||
<groupId>org.springframework</groupId>
|
<groupId>org.springframework</groupId>
|
||||||
<artifactId>spring-test</artifactId>
|
<artifactId>spring-test</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.openai</groupId>
|
||||||
|
<artifactId>openai-java</artifactId>
|
||||||
|
<version>4.8.0</version>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
</project>
|
</project>
|
||||||
|
|||||||
@@ -8,16 +8,16 @@ import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
|||||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import okhttp3.*;
|
||||||
import org.ruoyi.common.core.constant.CacheNames;
|
import org.ruoyi.common.core.constant.CacheNames;
|
||||||
import org.ruoyi.common.core.exception.ServiceException;
|
import org.ruoyi.common.core.exception.ServiceException;
|
||||||
|
import org.ruoyi.common.core.service.ConfigService;
|
||||||
import org.ruoyi.common.core.service.OssService;
|
import org.ruoyi.common.core.service.OssService;
|
||||||
import org.ruoyi.common.core.utils.MapstructUtils;
|
|
||||||
import org.ruoyi.common.core.utils.SpringUtils;
|
import org.ruoyi.common.core.utils.SpringUtils;
|
||||||
import org.ruoyi.common.core.utils.StreamUtils;
|
import org.ruoyi.common.core.utils.StreamUtils;
|
||||||
import org.ruoyi.common.core.utils.StringUtils;
|
import org.ruoyi.common.core.utils.StringUtils;
|
||||||
import org.ruoyi.common.core.utils.file.FileUtils;
|
import org.ruoyi.common.core.utils.file.FileUtils;
|
||||||
import org.ruoyi.common.oss.core.OssClient;
|
import org.ruoyi.common.oss.core.OssClient;
|
||||||
import org.ruoyi.common.oss.entity.UploadResult;
|
|
||||||
import org.ruoyi.common.oss.enumd.AccessPolicyType;
|
import org.ruoyi.common.oss.enumd.AccessPolicyType;
|
||||||
import org.ruoyi.common.oss.factory.OssFactory;
|
import org.ruoyi.common.oss.factory.OssFactory;
|
||||||
import org.ruoyi.core.page.PageQuery;
|
import org.ruoyi.core.page.PageQuery;
|
||||||
@@ -27,12 +27,15 @@ import org.ruoyi.system.domain.bo.SysOssBo;
|
|||||||
import org.ruoyi.system.domain.vo.SysOssVo;
|
import org.ruoyi.system.domain.vo.SysOssVo;
|
||||||
import org.ruoyi.system.mapper.SysOssMapper;
|
import org.ruoyi.system.mapper.SysOssMapper;
|
||||||
import org.ruoyi.system.service.ISysOssService;
|
import org.ruoyi.system.service.ISysOssService;
|
||||||
|
import org.ruoyi.system.utils.QwenFileUploadUtils;
|
||||||
|
import org.springframework.beans.BeanUtils;
|
||||||
import org.springframework.cache.annotation.Cacheable;
|
import org.springframework.cache.annotation.Cacheable;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.mock.web.MockMultipartFile;
|
import org.springframework.mock.web.MockMultipartFile;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
@@ -48,6 +51,29 @@ public class SysOssServiceImpl implements ISysOssService, OssService {
|
|||||||
|
|
||||||
private final SysOssMapper baseMapper;
|
private final SysOssMapper baseMapper;
|
||||||
|
|
||||||
|
private final ConfigService configService;
|
||||||
|
|
||||||
|
// 文档解析判断前缀
|
||||||
|
private static final String FILE_ID_PREFIX = "fileid://";
|
||||||
|
|
||||||
|
// 服务名称
|
||||||
|
private static final String DASH_SCOPE = "Qwen";
|
||||||
|
|
||||||
|
// 默认服务
|
||||||
|
private static final String CATEGORY = "file";
|
||||||
|
|
||||||
|
// apiKey 配置名称
|
||||||
|
private static final String CONFIG_NAME_KEY = "apiKey";
|
||||||
|
|
||||||
|
// apiHost 配置名称
|
||||||
|
private static final String CONFIG_NAME_URL = "apiHost";
|
||||||
|
|
||||||
|
// 默认密钥 todo:请在系统配置中设置正确的密钥
|
||||||
|
private static String API_KEY = "";
|
||||||
|
|
||||||
|
// 默认api路径地址
|
||||||
|
private static String API_HOST = "https://dashscope.aliyuncs.com/compatible-mode/v1/files";
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TableDataInfo<SysOssVo> queryPageList(SysOssBo bo, PageQuery pageQuery) {
|
public TableDataInfo<SysOssVo> queryPageList(SysOssBo bo, PageQuery pageQuery) {
|
||||||
LambdaQueryWrapper<SysOss> lqw = buildQueryWrapper(bo);
|
LambdaQueryWrapper<SysOss> lqw = buildQueryWrapper(bo);
|
||||||
@@ -161,26 +187,41 @@ public class SysOssServiceImpl implements ISysOssService, OssService {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SysOssVo upload(MultipartFile file) {
|
public SysOssVo upload(MultipartFile file) {
|
||||||
String originalfileName = file.getOriginalFilename();
|
String originalName = file.getOriginalFilename();
|
||||||
String suffix = StringUtils.substring(originalfileName, originalfileName.lastIndexOf("."),
|
int lastDotIndex = originalName != null ? originalName.lastIndexOf(".") : -1;
|
||||||
originalfileName.length());
|
String prefix = lastDotIndex > 0 ? "" : originalName.substring(0, lastDotIndex);
|
||||||
OssClient storage = OssFactory.instance();
|
String suffix = lastDotIndex > 0 ? "" : originalName.substring(lastDotIndex);
|
||||||
UploadResult uploadResult;
|
File tempFile = null;
|
||||||
try {
|
try {
|
||||||
uploadResult = storage.uploadSuffix(file.getBytes(), suffix, file.getContentType());
|
// 创建临时文件来处理MultipartFile
|
||||||
} catch (IOException e) {
|
tempFile = File.createTempFile("upload_", suffix);
|
||||||
throw new ServiceException(e.getMessage());
|
file.transferTo(tempFile);
|
||||||
|
// 获取配置
|
||||||
|
initConfig();
|
||||||
|
// 使用工具类上传文件到阿里云
|
||||||
|
String fileId = QwenFileUploadUtils.uploadFile(tempFile, API_HOST, API_KEY);
|
||||||
|
if (StringUtils.isEmpty(fileId)) {
|
||||||
|
throw new ServiceException("文件上传失败,未获取到fileId");
|
||||||
}
|
}
|
||||||
// 保存文件信息
|
// 保存文件信息到数据库
|
||||||
SysOss oss = new SysOss();
|
SysOss oss = new SysOss();
|
||||||
oss.setUrl(uploadResult.getUrl());
|
oss.setUrl(FILE_ID_PREFIX + fileId);
|
||||||
oss.setFileSuffix(suffix);
|
oss.setFileSuffix(suffix);
|
||||||
oss.setFileName(uploadResult.getFilename());
|
oss.setFileName(prefix);
|
||||||
oss.setOriginalName(originalfileName);
|
oss.setOriginalName(originalName);
|
||||||
oss.setService(storage.getConfigKey());
|
oss.setService(DASH_SCOPE);
|
||||||
baseMapper.insert(oss);
|
baseMapper.insert(oss);
|
||||||
SysOssVo sysOssVo = MapstructUtils.convert(oss, SysOssVo.class);
|
SysOssVo sysOssVo = new SysOssVo();
|
||||||
return this.matchingUrl(sysOssVo);
|
BeanUtils.copyProperties(oss, sysOssVo);
|
||||||
|
return sysOssVo;
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new ServiceException("文件上传失败: " + e.getMessage());
|
||||||
|
} finally {
|
||||||
|
// 删除临时文件
|
||||||
|
if (tempFile != null) {
|
||||||
|
tempFile.delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -256,4 +297,20 @@ public class SysOssServiceImpl implements ISysOssService, OssService {
|
|||||||
throw new ServiceException("删除文件失败: " + e.getMessage());
|
throw new ServiceException("删除文件失败: " + e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化配置并返回API密钥和主机
|
||||||
|
*/
|
||||||
|
private void initConfig() {
|
||||||
|
String apiKey = configService.getConfigValue(CATEGORY, CONFIG_NAME_KEY);
|
||||||
|
if (StringUtils.isEmpty(apiKey)) {
|
||||||
|
throw new ServiceException("请先配置Qwen上传文件相关API_KEY");
|
||||||
|
}
|
||||||
|
API_KEY = apiKey;
|
||||||
|
String apiHost = configService.getConfigValue(CATEGORY, CONFIG_NAME_URL);
|
||||||
|
if (StringUtils.isEmpty(apiHost)) {
|
||||||
|
throw new ServiceException("请先配置Qwen上传文件相关API_HOST");
|
||||||
|
}
|
||||||
|
API_HOST = apiHost;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,53 @@
|
|||||||
|
package org.ruoyi.system.utils;
|
||||||
|
import okhttp3.MediaType;
|
||||||
|
import okhttp3.MultipartBody;
|
||||||
|
import okhttp3.OkHttpClient;
|
||||||
|
import okhttp3.Request;
|
||||||
|
import okhttp3.RequestBody;
|
||||||
|
import okhttp3.Response;
|
||||||
|
import com.alibaba.fastjson.JSONObject;
|
||||||
|
import org.ruoyi.common.core.utils.StringUtils;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.rmi.ServerException;
|
||||||
|
|
||||||
|
/***
|
||||||
|
* 千问上传文件工具类
|
||||||
|
*/
|
||||||
|
public class QwenFileUploadUtils {
|
||||||
|
|
||||||
|
// 上传本地文件
|
||||||
|
public static String uploadFile(File file, String apiHost, String apiKey) throws IOException {
|
||||||
|
OkHttpClient client = new OkHttpClient();
|
||||||
|
|
||||||
|
// 构建 multipart/form-data 请求体(千问要求的格式)
|
||||||
|
RequestBody requestBody = new MultipartBody.Builder()
|
||||||
|
.setType(MultipartBody.FORM)
|
||||||
|
.addFormDataPart("file", file.getName(), // 参数名必须为 file
|
||||||
|
RequestBody.create(MediaType.parse("application/octet-stream"), file))
|
||||||
|
.addFormDataPart("purpose", "file-extract") // 必须为 file-extract,文档解析专用
|
||||||
|
.build();
|
||||||
|
|
||||||
|
// 构建请求(必须为 POST 方法)
|
||||||
|
Request request = new Request.Builder()
|
||||||
|
.url(apiHost)
|
||||||
|
.post(requestBody)
|
||||||
|
.addHeader("Authorization", apiKey) // 认证头格式正确
|
||||||
|
.build();
|
||||||
|
|
||||||
|
// 发送请求并解析 fileId
|
||||||
|
try (Response response = client.newCall(request).execute()) {
|
||||||
|
if (!response.isSuccessful()) {
|
||||||
|
throw new ServerException("上传失败:" + response.code() + " " + response.message());
|
||||||
|
}
|
||||||
|
// 解析响应体,获取 fileId
|
||||||
|
String responseBody = response.body().string();
|
||||||
|
if (StringUtils.isEmpty(responseBody)){
|
||||||
|
throw new ServerException("上传失败:响应体为空");
|
||||||
|
}
|
||||||
|
JSONObject jsonObject = JSONObject.parseObject(responseBody);
|
||||||
|
return jsonObject.getString("id"); // 千问返回的 fileId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -98,7 +98,7 @@ public class WorkflowComponentService extends ServiceImpl<WorkflowComponentMappe
|
|||||||
return baseMapper.selectPage(new Page<>(currentPage, pageSize), wrapper);
|
return baseMapper.selectPage(new Page<>(currentPage, pageSize), wrapper);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Cacheable(cacheNames = WORKFLOW_COMPONENTS)
|
// @Cacheable(cacheNames = WORKFLOW_COMPONENTS)
|
||||||
public List<WorkflowComponent> getAllEnable() {
|
public List<WorkflowComponent> getAllEnable() {
|
||||||
return ChainWrappers.lambdaQueryChain(baseMapper)
|
return ChainWrappers.lambdaQueryChain(baseMapper)
|
||||||
.eq(WorkflowComponent::getIsEnable, true)
|
.eq(WorkflowComponent::getIsEnable, true)
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import org.ruoyi.workflow.workflow.node.AbstractWfNode;
|
|||||||
import org.ruoyi.workflow.workflow.node.EndNode;
|
import org.ruoyi.workflow.workflow.node.EndNode;
|
||||||
import org.ruoyi.workflow.workflow.node.answer.LLMAnswerNode;
|
import org.ruoyi.workflow.workflow.node.answer.LLMAnswerNode;
|
||||||
import org.ruoyi.workflow.workflow.node.httpRequest.HttpRequestNode;
|
import org.ruoyi.workflow.workflow.node.httpRequest.HttpRequestNode;
|
||||||
|
import org.ruoyi.workflow.workflow.node.humanFeedBack.HumanFeedbackNode;
|
||||||
import org.ruoyi.workflow.workflow.node.keywordExtractor.KeywordExtractorNode;
|
import org.ruoyi.workflow.workflow.node.keywordExtractor.KeywordExtractorNode;
|
||||||
import org.ruoyi.workflow.workflow.node.knowledgeRetrieval.KnowledgeRetrievalNode;
|
import org.ruoyi.workflow.workflow.node.knowledgeRetrieval.KnowledgeRetrievalNode;
|
||||||
import org.ruoyi.workflow.workflow.node.mailSend.MailSendNode;
|
import org.ruoyi.workflow.workflow.node.mailSend.MailSendNode;
|
||||||
@@ -25,6 +26,7 @@ public class WfNodeFactory {
|
|||||||
case MAIL_SEND -> wfNode = new MailSendNode(wfComponent, nodeDefinition, wfState, nodeState);
|
case MAIL_SEND -> wfNode = new MailSendNode(wfComponent, nodeDefinition, wfState, nodeState);
|
||||||
case HTTP_REQUEST -> wfNode = new HttpRequestNode(wfComponent, nodeDefinition, wfState, nodeState);
|
case HTTP_REQUEST -> wfNode = new HttpRequestNode(wfComponent, nodeDefinition, wfState, nodeState);
|
||||||
case SWITCHER -> wfNode = new SwitcherNode(wfComponent, nodeDefinition, wfState, nodeState);
|
case SWITCHER -> wfNode = new SwitcherNode(wfComponent, nodeDefinition, wfState, nodeState);
|
||||||
|
case HUMAN_FEEDBACK -> wfNode = new HumanFeedbackNode(wfComponent, nodeDefinition, wfState, nodeState);
|
||||||
default -> {
|
default -> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,6 +35,12 @@ public class WorkflowUtil {
|
|||||||
@Resource
|
@Resource
|
||||||
private ChatServiceFactory chatServiceFactory;
|
private ChatServiceFactory chatServiceFactory;
|
||||||
|
|
||||||
|
// 添加默认名称的成员变量
|
||||||
|
private static final String DEFAULT_NODE_NAME = "input";
|
||||||
|
|
||||||
|
// 添加文档解析的前缀字段
|
||||||
|
private static final String UPLOAD_FILE_API_PREFIX = "fileid";
|
||||||
|
|
||||||
public static String renderTemplate(String template, List<NodeIOData> values) {
|
public static String renderTemplate(String template, List<NodeIOData> values) {
|
||||||
// 🔒 关键修复:如果 template 为 null,直接返回 null 或空字符串
|
// 🔒 关键修复:如果 template 为 null,直接返回 null 或空字符串
|
||||||
if (template == null) {
|
if (template == null) {
|
||||||
@@ -125,9 +131,9 @@ public class WorkflowUtil {
|
|||||||
// 构建 ruoyi-ai 的 ChatRequest
|
// 构建 ruoyi-ai 的 ChatRequest
|
||||||
List<Message> messages = new ArrayList<>();
|
List<Message> messages = new ArrayList<>();
|
||||||
|
|
||||||
addUserMessage(node, state.getInputs(), messages);
|
List<NodeIOData> inputs = state.getInputs();
|
||||||
|
addUserMessage(node, inputs, messages);
|
||||||
addSystemMessage(systemMessage, messages);
|
addSystemMessage(systemMessage, inputs, messages);
|
||||||
|
|
||||||
ChatRequest chatRequest = new ChatRequest();
|
ChatRequest chatRequest = new ChatRequest();
|
||||||
chatRequest.setModel(modelName);
|
chatRequest.setModel(modelName);
|
||||||
@@ -150,20 +156,44 @@ public class WorkflowUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
WfNodeInputConfig nodeInputConfig = NodeInputConfigTypeHandler.fillNodeInputConfig(node.getInputConfig());
|
WfNodeInputConfig nodeInputConfig = NodeInputConfigTypeHandler.fillNodeInputConfig(node.getInputConfig());
|
||||||
|
|
||||||
List<WfNodeParamRef> refInputs = nodeInputConfig.getRefInputs();
|
List<WfNodeParamRef> refInputs = nodeInputConfig.getRefInputs();
|
||||||
|
|
||||||
Set<String> nameSet = CollStreamUtil.toSet(refInputs, WfNodeParamRef::getName);
|
Set<String> nameSet = CollStreamUtil.toSet(refInputs, WfNodeParamRef::getName);
|
||||||
|
|
||||||
userMessage.stream().filter(item -> nameSet.contains(item.getName()))
|
// 检查是否存在包含fileId的NodeIOData对象
|
||||||
.map(item -> getMessage("user", item.getContent().getValue())).forEach(messages::add);
|
boolean hasFileIdData = hasFileIdData(userMessage);
|
||||||
|
// 构建消息列表
|
||||||
if (CollUtil.isNotEmpty(messages)) {
|
List<Message> messageList = buildMessageList(userMessage, nameSet, hasFileIdData, DEFAULT_NODE_NAME);
|
||||||
return;
|
// 如果没有找到匹配的消息,尝试使用input字段
|
||||||
|
if (CollUtil.isEmpty(messageList)) {
|
||||||
|
messageList = buildMessageList(userMessage, Set.of("input"), hasFileIdData, DEFAULT_NODE_NAME);
|
||||||
|
}
|
||||||
|
messages.addAll(messageList);
|
||||||
}
|
}
|
||||||
|
|
||||||
userMessage.stream().filter(item -> "input".equals(item.getName()))
|
|
||||||
.map(item -> getMessage("user", item.getContent().getValue())).forEach(messages::add);
|
/**
|
||||||
|
* 检查是否包含fileId数据
|
||||||
|
*/
|
||||||
|
private boolean hasFileIdData(List<NodeIOData> userMessage) {
|
||||||
|
return userMessage.stream().anyMatch(item ->
|
||||||
|
item != null &&
|
||||||
|
item.getContent() != null &&
|
||||||
|
item.getContent().getValue() != null &&
|
||||||
|
String.valueOf(item.getContent().getValue()).toLowerCase().contains(UPLOAD_FILE_API_PREFIX)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建消息列表
|
||||||
|
*/
|
||||||
|
private List<Message> buildMessageList(List<NodeIOData> userMessage, Set<String> nameSet, boolean hasFileIdData, String defaultName) {
|
||||||
|
String role = hasFileIdData ? "system" : "user";
|
||||||
|
|
||||||
|
return userMessage.stream()
|
||||||
|
.filter(item -> item != null && item.getName() != null)
|
||||||
|
.filter(item -> nameSet.contains(item.getName()) || defaultName.equals(item.getName()))
|
||||||
|
.map(item -> getMessage(role, item.getContent().getValue()))
|
||||||
|
.toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -187,14 +217,22 @@ public class WorkflowUtil {
|
|||||||
* @param systemMessage
|
* @param systemMessage
|
||||||
* @param messages
|
* @param messages
|
||||||
*/
|
*/
|
||||||
private void addSystemMessage(List<UserMessage> systemMessage, List<Message> messages) {
|
private void addSystemMessage(List<UserMessage> systemMessage, List<NodeIOData> userMessage, List<Message> messages) {
|
||||||
log.info("addSystemMessage received: {}", systemMessage); // 🔥 加这一行
|
log.info("addSystemMessage received: {}", systemMessage);
|
||||||
|
|
||||||
if (CollUtil.isEmpty(systemMessage)) {
|
if (CollUtil.isEmpty(systemMessage)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 检查是否存在包含fileId的NodeIOData对象
|
||||||
|
boolean hasFileIdData = hasFileIdData(userMessage);
|
||||||
|
|
||||||
|
// 根据是否有fileId数据确定消息角色
|
||||||
|
String role = hasFileIdData ? "user" : "system";
|
||||||
|
|
||||||
|
// 添加消息
|
||||||
systemMessage.stream()
|
systemMessage.stream()
|
||||||
.map(userMsg -> getMessage("system", userMsg.singleText()))
|
.map(userMsg -> getMessage(role, userMsg.singleText()))
|
||||||
.forEach(messages::add);
|
.forEach(messages::add);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import lombok.Data;
|
|||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.commons.lang3.SerializationUtils;
|
import org.apache.commons.lang3.SerializationUtils;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
|
||||||
import org.ruoyi.common.core.exception.base.BaseException;
|
import org.ruoyi.common.core.exception.base.BaseException;
|
||||||
import org.ruoyi.workflow.base.NodeInputConfigTypeHandler;
|
import org.ruoyi.workflow.base.NodeInputConfigTypeHandler;
|
||||||
import org.ruoyi.workflow.entity.WorkflowComponent;
|
import org.ruoyi.workflow.entity.WorkflowComponent;
|
||||||
@@ -124,14 +123,6 @@ public abstract class AbstractWfNode {
|
|||||||
log.info("↓↓↓↓↓ node process start,name:{},uuid:{}", node.getTitle(), node.getUuid());
|
log.info("↓↓↓↓↓ node process start,name:{},uuid:{}", node.getTitle(), node.getUuid());
|
||||||
state.setProcessStatus(NODE_PROCESS_STATUS_DOING);
|
state.setProcessStatus(NODE_PROCESS_STATUS_DOING);
|
||||||
initInput();
|
initInput();
|
||||||
//HumanFeedback的情况
|
|
||||||
Object humanFeedbackState = state.data().get(HUMAN_FEEDBACK_KEY);
|
|
||||||
if (null != humanFeedbackState) {
|
|
||||||
String userInput = humanFeedbackState.toString();
|
|
||||||
if (StringUtils.isNotBlank(userInput)) {
|
|
||||||
state.getInputs().add(NodeIOData.createByText(HUMAN_FEEDBACK_KEY, "default", userInput));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (null != inputConsumer) {
|
if (null != inputConsumer) {
|
||||||
inputConsumer.accept(state);
|
inputConsumer.accept(state);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,56 @@
|
|||||||
|
package org.ruoyi.workflow.workflow.node.humanFeedBack;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.ruoyi.workflow.entity.WorkflowComponent;
|
||||||
|
import org.ruoyi.workflow.entity.WorkflowNode;
|
||||||
|
import org.ruoyi.workflow.workflow.NodeProcessResult;
|
||||||
|
import org.ruoyi.workflow.workflow.WfNodeState;
|
||||||
|
import org.ruoyi.workflow.workflow.WfState;
|
||||||
|
import org.ruoyi.workflow.workflow.data.NodeIOData;
|
||||||
|
import org.ruoyi.workflow.workflow.node.AbstractWfNode;
|
||||||
|
|
||||||
|
import static org.ruoyi.workflow.cosntant.AdiConstant.WorkflowConstant.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 人机交互节点实现类
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
public class HumanFeedbackNode extends AbstractWfNode {
|
||||||
|
|
||||||
|
public HumanFeedbackNode(WorkflowComponent component, WorkflowNode nodeDefinition, WfState wfState, WfNodeState nodeState) {
|
||||||
|
super(component, nodeDefinition, wfState, nodeState);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 人机交互节点的处理逻辑
|
||||||
|
@Override
|
||||||
|
public NodeProcessResult onProcess() {
|
||||||
|
log.info("Processing HumanFeedback node: {}", node.getTitle());
|
||||||
|
// 从状态中获取用户输入数据
|
||||||
|
Object humanFeedbackState = state.data().get(HUMAN_FEEDBACK_KEY);
|
||||||
|
if (null != humanFeedbackState) {
|
||||||
|
String userInput = humanFeedbackState.toString();
|
||||||
|
if (StringUtils.isNotBlank(userInput)) {
|
||||||
|
// 用户已提供输入,将用户输入添加到节点输入和输出中
|
||||||
|
NodeIOData feedbackData = NodeIOData.createByText("output", "default", userInput);
|
||||||
|
// 添加到输入列表,这样当前节点处理时可以使用
|
||||||
|
state.getInputs().add(feedbackData);
|
||||||
|
// 添加到输出列表,这样后续节点可以使用
|
||||||
|
state.getOutputs().add(feedbackData);
|
||||||
|
// 设置为成功状态
|
||||||
|
state.setProcessStatus(NODE_PROCESS_STATUS_SUCCESS);
|
||||||
|
log.info("Human feedback processed for node: {}, content: {}", node.getTitle(), userInput);
|
||||||
|
} else {
|
||||||
|
// 用户输入为空,设置等待状态
|
||||||
|
state.setProcessStatus(NODE_PROCESS_STATUS_DOING);
|
||||||
|
log.info("Human feedback is empty for node: {}", node.getTitle());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 没有用户输入,这可能是正常情况(等待用户输入)
|
||||||
|
// 但为了确保流程可以继续,我们仍然标记为成功
|
||||||
|
state.setProcessStatus(NODE_PROCESS_STATUS_SUCCESS);
|
||||||
|
log.info("No human feedback found for node: {}, continuing workflow", node.getTitle());
|
||||||
|
}
|
||||||
|
return new NodeProcessResult();
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user