From 37a8b7dad32dbbad4e28ecd15dabd3147be03665 Mon Sep 17 00:00:00 2001
From: ageerle <ageerle@163.com>
Date: 星期五, 11 四月 2025 15:47:05 +0800
Subject: [PATCH] feat: 对话模块重构

---
 ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/chat/impl/SseServiceImpl.java |  174 +++++++++++++++------------------------------------------
 1 files changed, 47 insertions(+), 127 deletions(-)

diff --git a/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/chat/impl/SseServiceImpl.java b/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/chat/impl/SseServiceImpl.java
index 6cf19b4..17a5bd4 100644
--- a/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/chat/impl/SseServiceImpl.java
+++ b/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/chat/impl/SseServiceImpl.java
@@ -6,22 +6,17 @@
 import com.google.protobuf.ServiceException;
 import com.zhipu.oapi.ClientV4;
 import com.zhipu.oapi.service.v4.tools.*;
-import io.github.ollama4j.OllamaAPI;
-import io.github.ollama4j.models.chat.OllamaChatMessage;
-import io.github.ollama4j.models.chat.OllamaChatMessageRole;
-import io.github.ollama4j.models.chat.OllamaChatRequestBuilder;
-import io.github.ollama4j.models.chat.OllamaChatRequestModel;
-import io.github.ollama4j.models.generate.OllamaStreamHandler;
 import jakarta.servlet.http.HttpServletRequest;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import okhttp3.*;
-import org.ruoyi.chat.config.ChatConfig;
-import org.ruoyi.chat.listener.SSEEventSourceListener;
 
 import org.ruoyi.chat.service.chat.IChatCostService;
+import org.ruoyi.chat.service.chat.IChatService;
 import org.ruoyi.chat.service.chat.ISseService;
+import org.ruoyi.chat.factory.SseServiceFactory;
 import org.ruoyi.chat.util.IpUtil;
+import org.ruoyi.chat.util.SSEUtil;
 import org.ruoyi.common.chat.request.ChatRequest;
 import org.ruoyi.common.chat.entity.Tts.TextToSpeech;
 import org.ruoyi.common.chat.entity.chat.ChatCompletion;
@@ -32,15 +27,14 @@
 import org.ruoyi.common.chat.entity.whisper.WhisperResponse;
 import org.ruoyi.common.chat.openai.OpenAiStreamClient;
 import org.ruoyi.common.core.service.ConfigService;
+import org.ruoyi.common.core.utils.DateUtils;
 import org.ruoyi.common.core.utils.StringUtils;
 import org.ruoyi.common.core.utils.file.FileUtils;
 import org.ruoyi.common.core.utils.file.MimeTypeUtils;
 
 import org.ruoyi.common.redis.utils.RedisUtils;
 
-import org.ruoyi.domain.vo.ChatModelVo;
 import org.ruoyi.service.EmbeddingService;
-import org.ruoyi.service.IChatModelService;
 import org.ruoyi.service.VectorStoreService;
 import org.springframework.core.io.InputStreamResource;
 import org.springframework.core.io.Resource;
@@ -60,7 +54,6 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
-import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicReference;
@@ -72,10 +65,6 @@
 
     private final OpenAiStreamClient openAiStreamClient;
 
-    private final ChatConfig chatConfig;
-
-    private final IChatModelService chatModelService;
-
     private final EmbeddingService embeddingService;
 
     private final VectorStoreService vectorStore;
@@ -84,6 +73,8 @@
 
     private final IChatCostService chatCostService;
 
+    private final SseServiceFactory sseServiceFactory;
+
     private static final String requestIdTemplate = "company-%d";
 
     private static final ObjectMapper mapper = new ObjectMapper();
@@ -91,78 +82,64 @@
     @Override
     public SseEmitter sseChat(ChatRequest chatRequest, HttpServletRequest request) {
         SseEmitter sseEmitter = new SseEmitter(0L);
-        SSEEventSourceListener openAIEventSourceListener = new SSEEventSourceListener(sseEmitter);
-        // 鑾峰彇瀵硅瘽娑堟伅鍒楄〃
-        List<Message> messages = chatRequest.getMessages();
         try {
-            // 鏌ヨ妯″瀷淇℃伅
-            ChatModelVo chatModelVo = chatModelService.selectModelByName(chatRequest.getModel());
-
-            OpenAiStreamClient openAiModelStreamClient;
-            if(chatModelVo!=null){
-                // 寤鸿姹傚鎴风
-                openAiModelStreamClient = chatConfig.createOpenAiStreamClient(chatModelVo.getApiHost(), chatModelVo.getApiKey());
-                // 璁剧疆榛樿鎻愮ず璇�
-                chatRequest.setSysPrompt(chatModelVo.getSystemPrompt());
-            }else {
-                // 浣跨敤榛樿瀹㈡埛绔�
-                openAiModelStreamClient = openAiStreamClient;
-            }
             // 鏋勫缓娑堟伅鍒楄〃澧炲姞鑱旂綉銆佺煡璇嗗簱绛夊唴瀹�
             buildChatMessageList(chatRequest);
-
             // 鏍规嵁妯″瀷鍚嶇О鍓嶇紑璋冪敤涓嶅悓鐨勫鐞嗛�昏緫
-            switchModelAndHandle(chatRequest);
-
+            switchModelAndHandle(chatRequest,sseEmitter);
             // 鏈櫥褰曠敤鎴烽檺鍒跺璇濇鏁�
-            if (!StpUtil.isLogin()) {
-                String clientIp = IpUtil.getClientIp(request);
-                // 璁垮姣忓ぉ榛樿鍙兘瀵硅瘽5娆�
-                int timeWindowInSeconds = 5;
-                String redisKey = "clientIp:" + clientIp;
-                int count = 0;
-                if (RedisUtils.getCacheObject(redisKey) == null) {
-                    // 缂撳瓨鏈夋晥鏃堕棿1澶�
-                    RedisUtils.setCacheObject(redisKey, count, Duration.ofSeconds(86400));
-                }else {
-                    count = RedisUtils.getCacheObject(redisKey);
-                    if (count >= timeWindowInSeconds) {
-                        throw new ServiceException("褰撴棩鍏嶈垂娆℃暟宸茬敤瀹�");
-                    }
-                    count++;
-                    RedisUtils.setCacheObject(redisKey, count);
-                }
-            }
-
-            ChatCompletion completion = ChatCompletion
-                    .builder()
-                    .messages(messages)
-                    .model(chatRequest.getModel())
-                    .stream(chatRequest.getStream())
-                    .build();
-            openAiModelStreamClient.streamChatCompletion(completion, openAIEventSourceListener);
-
+            checkUnauthenticatedUserChatLimit(request);
             // 淇濆瓨娑堟伅璁板綍 骞舵墸闄よ垂鐢�
             chatCostService.deductToken(chatRequest);
         } catch (Exception e) {
             String message = e.getMessage();
-            sendErrorEvent(sseEmitter, message);
+            SSEUtil.sendErrorEvent(sseEmitter, message);
             return sseEmitter;
         }
         return sseEmitter;
     }
 
     /**
+     * 妫�鏌ユ湭鐧诲綍鐢ㄦ埛鏄惁瓒呰繃褰撴棩瀵硅瘽娆℃暟闄愬埗
+     *
+     * @param request 褰撳墠璇锋眰
+     * @throws ServiceException 濡傛灉褰撴棩鍏嶈垂娆℃暟宸茬敤瀹�
+     */
+    public void checkUnauthenticatedUserChatLimit(HttpServletRequest request) throws ServiceException {
+        // 鏈櫥褰曠敤鎴烽檺鍒跺璇濇鏁�
+        if (!StpUtil.isLogin()) {
+            String clientIp = IpUtil.getClientIp(request);
+            // 璁垮姣忓ぉ榛樿鍙兘瀵硅瘽5娆�
+            int timeWindowInSeconds = 5;
+            String redisKey = "clientIp:" + clientIp;
+            int count = 0;
+            // 妫�鏌edis涓殑瀵硅瘽娆℃暟
+            if (RedisUtils.getCacheObject(redisKey) == null) {
+                // 缂撳瓨鏈夋晥鏃堕棿1澶�
+                RedisUtils.setCacheObject(redisKey, count, Duration.ofSeconds(86400));
+            } else {
+                count = RedisUtils.getCacheObject(redisKey);
+                if (count >= timeWindowInSeconds) {
+                    throw new ServiceException("褰撴棩鍏嶈垂娆℃暟宸茬敤瀹�");
+                }
+                count++;
+                RedisUtils.setCacheObject(redisKey, count);
+            }
+        }
+    }
+
+    /**
      *  鏍规嵁妯″瀷鍚嶇О鍓嶇紑璋冪敤涓嶅悓鐨勫鐞嗛�昏緫
      */
-    private void switchModelAndHandle(ChatRequest chatRequest) {
+    private void switchModelAndHandle(ChatRequest chatRequest,SseEmitter emitter) {
         String model = chatRequest.getModel();
         // 濡傛灉妯″瀷鍚嶇О浠llama寮�澶达紝鍒欒皟鐢╫llama涓儴缃茬殑鏈湴妯″瀷
         if (model.startsWith("ollama-")) {
             String[] parts = chatRequest.getModel().split("ollama-", 2); // 闄愬埗鍒嗗壊娆℃暟涓�2
             if (parts.length > 1) {
                 chatRequest.setModel(parts[1]);
-                ollamaChat(chatRequest);
+                IChatService chatService = sseServiceFactory.getSseService("ollama");
+                chatService.chat(chatRequest,emitter);
             } else {
                 throw new IllegalArgumentException("Invalid ollama model name: " + chatRequest.getModel());
             }
@@ -177,8 +154,13 @@
     private void buildChatMessageList(ChatRequest chatRequest){
         // 鑾峰彇瀵硅瘽娑堟伅鍒楄〃
         List<Message> messages = chatRequest.getMessages();
+        String sysPrompt = chatRequest.getSysPrompt();
+        if(StringUtils.isEmpty(sysPrompt)){
+            sysPrompt ="浣犳槸涓�涓敱RuoYI-AI寮�鍙戠殑浜哄伐鏅鸿兘鍔╂墜锛屽悕瀛楀彨鐔婄尗鍔╂墜銆備綘鎿呴暱涓嫳鏂囧璇濓紝鑳藉鐞嗚В骞跺鐞嗗悇绉嶉棶棰橈紝鎻愪緵瀹夊叏銆佹湁甯姪銆佸噯纭殑鍥炵瓟銆�" +
+                    "褰撳墠鏃堕棿锛�"+ DateUtils.getDate();
+        }
         // 璁剧疆绯荤粺榛樿鎻愮ず璇�
-        Message sysMessage = Message.builder().content(chatRequest.getSysPrompt()).role(Message.Role.SYSTEM).build();
+        Message sysMessage = Message.builder().content(sysPrompt).role(Message.Role.SYSTEM).build();
         messages.add(0,sysMessage);
 
         // 鏌ヨ鍚戦噺搴撶浉鍏充俊鎭姞鍏ュ埌涓婁笅鏂�
@@ -216,23 +198,6 @@
         }
     }
 
-    /**
-     * 鍙戦�丼SE閿欒浜嬩欢鐨勫皝瑁呮柟娉�
-     *
-     * @param sseEmitter
-     * @param errorMessage
-     */
-    private void sendErrorEvent(SseEmitter sseEmitter, String errorMessage) {
-        SseEmitter.SseEventBuilder event = SseEmitter.event()
-                .name("error")
-                .data(errorMessage);
-        try {
-            sseEmitter.send(event);
-        } catch (IOException e) {
-            log.error("SSE鍙戦�佸け璐�: {}", e.getMessage());
-        }
-        sseEmitter.complete();
-    }
 
     /**
      * 鏂囧瓧杞闊�
@@ -323,51 +288,6 @@
         return file;
     }
 
-    @Override
-    public SseEmitter ollamaChat(ChatRequest chatRequest) {
-
-        ChatModelVo chatModelVo = chatModelService.selectModelByName(chatRequest.getModel());
-        final SseEmitter emitter = new SseEmitter();
-        String host = chatModelVo.getApiHost();
-        List<Message> msgList = chatRequest.getMessages();
-
-        List<OllamaChatMessage> messages = new ArrayList<>();
-        for (Message message : msgList) {
-            OllamaChatMessage ollamaChatMessage = new OllamaChatMessage();
-            ollamaChatMessage.setRole(OllamaChatMessageRole.USER);
-            ollamaChatMessage.setContent(message.getContent().toString());
-            messages.add(ollamaChatMessage);
-        }
-        OllamaAPI api = new OllamaAPI(host);
-        api.setRequestTimeoutSeconds(100);
-        OllamaChatRequestBuilder builder = OllamaChatRequestBuilder.getInstance(chatRequest.getModel());
-
-        OllamaChatRequestModel requestModel = builder
-            .withMessages(messages)
-            .build();
-
-        // 寮傛鎵ц OllAma API 璋冪敤
-        CompletableFuture.runAsync(() -> {
-            try {
-                StringBuilder response = new StringBuilder();
-                OllamaStreamHandler streamHandler = (s) -> {
-                    String substr = s.substring(response.length());
-                    response.append(substr);
-                    System.out.println(substr);
-                    try {
-                        emitter.send(substr);
-                    } catch (IOException e) {
-                        sendErrorEvent(emitter, e.getMessage());
-                    }
-                };
-                api.chat(requestModel, streamHandler);
-                emitter.complete();
-            } catch (Exception e) {
-                sendErrorEvent(emitter, e.getMessage());
-            }
-        });
-        return emitter;
-    }
 
     @Override
     public String wxCpChat(String prompt) {

--
Gitblit v1.9.3