diff --git a/ruoyi-common/ruoyi-common-chat/src/main/java/org/ruoyi/common/chat/Service/IChatService.java b/ruoyi-common/ruoyi-common-chat/src/main/java/org/ruoyi/common/chat/Service/IChatService.java index 1aeb2456..01a02567 100644 --- a/ruoyi-common/ruoyi-common-chat/src/main/java/org/ruoyi/common/chat/Service/IChatService.java +++ b/ruoyi-common/ruoyi-common-chat/src/main/java/org/ruoyi/common/chat/Service/IChatService.java @@ -1,8 +1,7 @@ package org.ruoyi.common.chat.Service; -import dev.langchain4j.model.chat.response.StreamingChatResponseHandler; -import org.ruoyi.common.chat.domain.dto.request.ChatRequest; -import org.ruoyi.common.chat.domain.vo.chat.ChatModelVo; +import jakarta.validation.Valid; +import org.ruoyi.common.chat.domain.entity.chat.ChatContext; import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; /** @@ -13,12 +12,7 @@ public interface IChatService { /** * 客户端发送对话消息到服务端 */ - SseEmitter chat(ChatModelVo chatModelVo, ChatRequest chatRequest, SseEmitter emitter, Long userId, String tokenValue); - - /** - * 工作流专用对话 - */ - SseEmitter chat(ChatModelVo chatModelVo, ChatRequest chatRequest,SseEmitter emitter,Long userId,String tokenValue, StreamingChatResponseHandler handler); + SseEmitter chat(@Valid ChatContext chatContext); /** * 获取服务提供商名称 diff --git a/ruoyi-common/ruoyi-common-chat/src/main/java/org/ruoyi/common/chat/domain/entity/chat/ChatContext.java b/ruoyi-common/ruoyi-common-chat/src/main/java/org/ruoyi/common/chat/domain/entity/chat/ChatContext.java new file mode 100644 index 00000000..13a5b4b9 --- /dev/null +++ b/ruoyi-common/ruoyi-common-chat/src/main/java/org/ruoyi/common/chat/domain/entity/chat/ChatContext.java @@ -0,0 +1,57 @@ +package org.ruoyi.common.chat.domain.entity.chat; + +import dev.langchain4j.model.chat.response.StreamingChatResponseHandler; +import jakarta.validation.constraints.NotNull; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.ruoyi.common.chat.domain.dto.request.ChatRequest; +import org.ruoyi.common.chat.domain.vo.chat.ChatModelVo; +import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; + +/** + * 聊天对话上下文对象 + * + * @author zengxb + * @date 2026-02-14 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@Builder +public class ChatContext { + + /** + * 模型管理视图对象 + */ + @NotNull(message = "模型管理视图对象不能为空") + private ChatModelVo chatModelVo; + + /** + * 对话请求对象 + */ + @NotNull(message = "对话请求对象不能为空") + private ChatRequest chatRequest; + + /** + * SSe连接对象 + */ + @NotNull(message = "SSe连接对象不能为空") + private SseEmitter emitter; + + /** + * 用户ID + */ + @NotNull(message = "用户ID不能为空") + private Long userId; + + /** + * Token + */ + @NotNull(message = "Token不能为空") + private String tokenValue; + + /** + * 响应处理器 + */ + private StreamingChatResponseHandler handler; +} diff --git a/ruoyi-modules/ruoyi-aiflow/src/main/java/org/ruoyi/workflow/workflow/WorkflowUtil.java b/ruoyi-modules/ruoyi-aiflow/src/main/java/org/ruoyi/workflow/workflow/WorkflowUtil.java index e57b324c..176ed7d6 100644 --- a/ruoyi-modules/ruoyi-aiflow/src/main/java/org/ruoyi/workflow/workflow/WorkflowUtil.java +++ b/ruoyi-modules/ruoyi-aiflow/src/main/java/org/ruoyi/workflow/workflow/WorkflowUtil.java @@ -14,6 +14,7 @@ import org.bsc.langgraph4j.state.AgentState; import org.ruoyi.common.chat.Service.IChatModelService; import org.ruoyi.common.chat.Service.IChatService; import org.ruoyi.common.chat.domain.dto.request.ChatRequest; +import org.ruoyi.common.chat.domain.entity.chat.ChatContext; import org.ruoyi.common.chat.domain.vo.chat.ChatModelVo; import org.ruoyi.common.chat.factory.ChatServiceFactory; import org.ruoyi.workflow.base.NodeInputConfigTypeHandler; @@ -135,9 +136,11 @@ public class WorkflowUtil { .startingState(state) .build(); + // 获取用户信息和Token以及SSe连接对象(对话接口需要使用) Long userId = wfState.getUserId(); String tokenValue = wfState.getTokenValue(); SseEmitter sseEmitter = wfState.getSseEmitter(); + StreamingChatResponseHandler handler = streamingGenerator.handler(); // 构建 ruoyi-ai 的 ChatRequest List chatMessages = new ArrayList<>(); @@ -151,9 +154,18 @@ public class WorkflowUtil { chatRequest.setModel(modelName); chatRequest.setChatMessages(chatMessages); + //构建聊天对话上下文参数 + ChatContext chatContext = ChatContext.builder() + .chatModelVo(chatModelVo) + .chatRequest(chatRequest) + .emitter(sseEmitter) + .userId(userId) + .tokenValue(tokenValue) + .handler(handler) + .build(); + // 使用工作流专用方法 - StreamingChatResponseHandler handler = streamingGenerator.handler(); - chatService.chat(chatModelVo, chatRequest, sseEmitter, userId, tokenValue, handler); + chatService.chat(chatContext); wfState.getNodeToStreamingGenerator().put(node.getUuid(), streamingGenerator); } diff --git a/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/service/chat/impl/AbstractStreamingChatService.java b/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/service/chat/impl/AbstractStreamingChatService.java index c671e679..75894c9a 100644 --- a/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/service/chat/impl/AbstractStreamingChatService.java +++ b/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/service/chat/impl/AbstractStreamingChatService.java @@ -26,6 +26,7 @@ import org.ruoyi.agent.tool.QueryAllTablesTool; import org.ruoyi.agent.tool.QueryTableSchemaTool; import org.ruoyi.common.chat.Service.IChatService; import org.ruoyi.common.chat.domain.dto.request.ChatRequest; +import org.ruoyi.common.chat.domain.entity.chat.ChatContext; import org.ruoyi.common.chat.domain.vo.chat.ChatModelVo; import org.ruoyi.common.core.utils.ObjectUtils; import org.ruoyi.common.core.utils.SpringUtils; @@ -36,6 +37,7 @@ import org.ruoyi.enums.RoleType; import org.ruoyi.service.chat.IChatMessageService; import org.ruoyi.service.chat.impl.memory.PersistentChatMemoryStore; import org.springframework.util.CollectionUtils; +import org.springframework.validation.annotation.Validated; import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; import java.util.*; @@ -55,6 +57,7 @@ import java.util.concurrent.ConcurrentHashMap; * @date 2025/12/13 */ @Slf4j +@Validated public abstract class AbstractStreamingChatService implements IChatService { /** @@ -77,22 +80,19 @@ public abstract class AbstractStreamingChatService implements IChatService { * 定义聊天流程骨架 */ @Override - public SseEmitter chat(ChatModelVo chatModelVo, ChatRequest chatRequest, SseEmitter emitter, Long userId, String tokenValue) { - return executeChat(chatModelVo, chatRequest, emitter, userId, tokenValue, null); - } - - /** - * 定义聊天流程骨架(包含流式回调结构) - */ - @Override - public SseEmitter chat(ChatModelVo chatModelVo, ChatRequest chatRequest, SseEmitter emitter, Long userId, String tokenValue, StreamingChatResponseHandler handler) { - return executeChat(chatModelVo, chatRequest, emitter, userId, tokenValue, handler); - } - - /** - * 定义聊天流程骨架 - */ - public SseEmitter executeChat(ChatModelVo chatModelVo, ChatRequest chatRequest, SseEmitter emitter, Long userId, String tokenValue, StreamingChatResponseHandler handler) { + public SseEmitter chat(ChatContext chatContext) { + // 获取模型管理视图对象 + ChatModelVo chatModelVo = chatContext.getChatModelVo(); + // 获取对话请求对象 + ChatRequest chatRequest = chatContext.getChatRequest(); + // 获取SSe连接对象 + SseEmitter emitter = chatContext.getEmitter(); + // 获取用户ID + Long userId = chatContext.getUserId(); + // 获取Token + String tokenValue = chatContext.getTokenValue(); + // 获取响应处理器 + StreamingChatResponseHandler handler = chatContext.getHandler(); try { String content = Optional.ofNullable(chatRequest.getMessages()).filter(messages -> !messages.isEmpty()) // 对话逻辑:从 messages 筛选第一个元素 @@ -100,11 +100,11 @@ public abstract class AbstractStreamingChatService implements IChatService { .filter(StringUtils::isNotBlank) // 工作流逻辑:从 chatMessages 筛选 UserMessage 的文本 .orElseGet(() -> Optional.ofNullable(chatRequest.getChatMessages()).orElse(List.of()).stream() - .filter(message -> message instanceof UserMessage um) - .map(message -> ((UserMessage) message).singleText()) - .filter(StringUtils::isNotBlank) - .findFirst() - .orElse("")); + .filter(message -> message instanceof UserMessage um) + .map(message -> ((UserMessage) message).singleText()) + .filter(StringUtils::isNotBlank) + .findFirst() + .orElse("")); // 保存用户消息 saveChatMessage(chatRequest, userId, content, RoleType.USER.getName(), chatModelVo); diff --git a/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/service/chat/impl/ChatServiceFacade.java b/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/service/chat/impl/ChatServiceFacade.java index d35f9d9c..5151c35f 100644 --- a/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/service/chat/impl/ChatServiceFacade.java +++ b/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/service/chat/impl/ChatServiceFacade.java @@ -8,6 +8,7 @@ import org.ruoyi.common.chat.Service.IChatModelService; import org.ruoyi.common.chat.Service.IChatService; import org.ruoyi.common.chat.domain.dto.ChatMessageDTO; import org.ruoyi.common.chat.domain.dto.request.ChatRequest; +import org.ruoyi.common.chat.domain.entity.chat.ChatContext; import org.ruoyi.common.chat.domain.vo.chat.ChatModelVo; import org.ruoyi.common.chat.factory.ChatServiceFactory; import org.ruoyi.common.satoken.utils.LoginHelper; @@ -71,7 +72,16 @@ public class ChatServiceFacade { Long userId = LoginHelper.getUserId(); String tokenValue = StpUtil.getTokenValue(); SseEmitter emitter = sseEmitterManager.connect(userId, tokenValue); - return chatService.chat(chatModelVo, chatRequest,emitter,userId, tokenValue); + + // 5. 创建对话上下文对象 + ChatContext chatContext = ChatContext.builder() + .chatModelVo(chatModelVo) + .chatRequest(chatRequest) + .emitter(emitter) + .userId(userId) + .tokenValue(tokenValue) + .build(); + return chatService.chat(chatContext); } /**