From baa664ac4fd1bf72888a59f1099edcf20f22b74f Mon Sep 17 00:00:00 2001
From: ageerle <ageerle@163.com>
Date: 星期四, 05 六月 2025 16:00:06 +0800
Subject: [PATCH] feat: 图片识别功能优化

---
 /dev/null                                                                                             |  140 -----------------------
 ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/chat/impl/ImageServiceImpl.java         |  151 +++++++++++++++++++++++++
 script/sql/update/20250605.sql                                                                        |    7 +
 ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/chat/impl/QianWenAiChatServiceImpl.java |    2 
 ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/chat/impl/SseServiceImpl.java           |    2 
 5 files changed, 161 insertions(+), 141 deletions(-)

diff --git a/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/chat/impl/ImageQianwenServiceImpl.java b/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/chat/impl/ImageQianwenServiceImpl.java
deleted file mode 100644
index aefd62f..0000000
--- a/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/chat/impl/ImageQianwenServiceImpl.java
+++ /dev/null
@@ -1,140 +0,0 @@
-package org.ruoyi.chat.service.chat.impl;
-
-import com.alibaba.dashscope.aigc.multimodalconversation.MultiModalConversation;
-import com.alibaba.dashscope.aigc.multimodalconversation.MultiModalConversationParam;
-import com.alibaba.dashscope.aigc.multimodalconversation.MultiModalConversationResult;
-import com.alibaba.dashscope.common.MultiModalMessage;
-import com.alibaba.dashscope.common.ResultCallback;
-import com.alibaba.dashscope.common.Role;
-import com.alibaba.dashscope.exception.NoApiKeyException;
-import com.alibaba.dashscope.exception.UploadFileException;
-import dev.langchain4j.community.model.dashscope.QwenStreamingChatModel;
-import dev.langchain4j.community.model.dashscope.QwenStreamingResponseBuilder;
-import dev.langchain4j.data.message.ChatMessage;
-import dev.langchain4j.data.message.SystemMessage;
-import dev.langchain4j.data.message.UserMessage;
-import dev.langchain4j.internal.Utils;
-import dev.langchain4j.model.chat.StreamingChatModel;
-import dev.langchain4j.model.chat.response.ChatResponse;
-import dev.langchain4j.model.chat.response.StreamingChatResponseHandler;
-import io.modelcontextprotocol.client.McpSyncClient;
-import io.reactivex.Flowable;
-import lombok.SneakyThrows;
-import lombok.extern.slf4j.Slf4j;
-import org.ruoyi.chat.config.ChatConfig;
-import org.ruoyi.chat.enums.ChatModeType;
-import org.ruoyi.chat.listener.SSEEventSourceListener;
-import org.ruoyi.chat.service.chat.IChatService;
-import org.ruoyi.common.chat.entity.chat.ChatCompletion;
-import org.ruoyi.common.chat.entity.chat.Message;
-import org.ruoyi.common.chat.openai.OpenAiStreamClient;
-import org.ruoyi.common.chat.request.ChatRequest;
-import org.ruoyi.domain.vo.ChatModelVo;
-import org.ruoyi.service.IChatModelService;
-import org.springframework.ai.chat.client.ChatClient;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.stereotype.Service;
-import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
-
-import java.util.*;
-
-/**
- * 鍥剧墖璇嗗埆妯″瀷
- */
-@Service
-@Slf4j
-public class ImageQianwenServiceImpl implements IChatService {
-
-    @Autowired
-    private IChatModelService chatModelService;
-
-    @SneakyThrows
-    @Override
-    public SseEmitter chat(ChatRequest chatRequest, SseEmitter emitter) {
-        ChatModelVo chatModelVo = chatModelService.selectModelByCategory("image");
-
-        // 鍙戦�佹祦寮忔秷鎭�
-
-            MultiModalConversation conv = new MultiModalConversation();
-            MultiModalMessage systemMessage = MultiModalMessage.builder().role(Role.SYSTEM.getValue())
-                    .content(Arrays.asList(
-                            Collections.singletonMap("text",chatRequest.getSysPrompt()))).build();
-            // 鑾峰彇鐢ㄦ埛娑堟伅鍐呭
-            List<Message> messages = chatRequest.getMessages();
-            MultiModalMessage userMessage = null;
-            //婕暱鐨勬牸寮忚浆鎹�
-            // 閬嶅巻娑堟伅鍒楄〃锛屾彁鍙栨枃鏈唴瀹�
-        if (messages != null && !messages.isEmpty()) {
-            Object content = messages.get(messages.size() - 1).getContent();
-            List<Map<String, Object>> contentList = new ArrayList<>();
-            StringBuilder textContent = new StringBuilder();
-            if (content instanceof List<?>) {
-                for (Object item : (List<?>) content) {
-                    if (item instanceof Map<?, ?> mapItem) {
-                        String type = (String) mapItem.get("type");
-                        if ("text".equals(type)) {
-                            String text = (String) mapItem.get("text");
-                            if (text != null) {
-                                textContent.append(text).append(" ");
-                            }
-                        } else if ("image_url".equals(type)) {
-                            Map<String, String> imageUrl  = (Map<String, String>) mapItem.get("image_url");
-                                contentList.add(Collections.singletonMap("image", imageUrl.get("url")));
-                        }
-                    }
-                }
-            }
-            // 灏嗘嫾鎺ュ悗鐨勬枃鏈唴瀹规坊鍔犲埌 contentList
-            if (textContent.length() > 0) {
-                contentList.add(Collections.singletonMap("text", textContent.toString().trim()));
-            }
-            userMessage = MultiModalMessage.builder()
-                    .role(Role.USER.getValue())
-                    .content(contentList)
-                    .build();
-        }
-            MultiModalConversationParam param = MultiModalConversationParam.builder()
-                    .apiKey(chatModelVo.getApiKey())
-                    .model(chatModelVo.getModelName())
-                    .messages(Arrays.asList(systemMessage, userMessage))
-                    .incrementalOutput(true)
-                    .build();
-
-
-        try {
-            final QwenStreamingResponseBuilder responseBuilder = new QwenStreamingResponseBuilder(param.getModel(),param.getIncrementalOutput() );
-            conv.streamCall(param, new ResultCallback<>() {
-                @SneakyThrows
-                public void onEvent(MultiModalConversationResult result) {
-
-                    String delta = responseBuilder.append(result);
-                    if (Utils.isNotNullOrEmpty(delta)) {
-
-                        emitter.send(delta);
-                        log.info("鏀跺埌娑堟伅鐗囨: {}", delta);
-                    }
-                }
-                public void onComplete() {
-                    emitter.complete();
-                    log.info("娑堟伅缁撴潫", responseBuilder.build());
-                }
-                public void onError(Exception e) {
-                    log.info("璇锋眰澶辫触", e.getMessage());
-                }
-            });
-        } catch (NoApiKeyException e) {
-            emitter.send("璇峰厛閰嶇疆API瀵嗛挜");
-            throw new RuntimeException(e);
-        } catch (UploadFileException e) {
-            throw new RuntimeException(e);
-        }
-
-
-        return emitter;
-    }
-        @Override
-    public String getCategory() {
-        return ChatModeType.IMAGE.getCode();
-    }
-}
diff --git a/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/chat/impl/ImageServiceImpl.java b/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/chat/impl/ImageServiceImpl.java
new file mode 100644
index 0000000..a7eee8e
--- /dev/null
+++ b/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/chat/impl/ImageServiceImpl.java
@@ -0,0 +1,151 @@
+package org.ruoyi.chat.service.chat.impl;
+
+import lombok.SneakyThrows;
+import lombok.extern.slf4j.Slf4j;
+import org.ruoyi.chat.config.ChatConfig;
+import org.ruoyi.chat.enums.ChatModeType;
+import org.ruoyi.chat.listener.SSEEventSourceListener;
+import org.ruoyi.chat.service.chat.IChatService;
+import org.ruoyi.common.chat.entity.chat.ChatCompletion;
+import org.ruoyi.common.chat.entity.chat.Message;
+import org.ruoyi.common.chat.openai.OpenAiStreamClient;
+import org.ruoyi.common.chat.request.ChatRequest;
+import org.ruoyi.domain.vo.ChatModelVo;
+import org.ruoyi.service.IChatModelService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
+
+import java.util.*;
+
+/**
+ * 鍥剧墖璇嗗埆妯″瀷
+ */
+@Service
+@Slf4j
+public class ImageServiceImpl implements IChatService {
+
+    @Autowired
+    private IChatModelService chatModelService;
+
+    @SneakyThrows
+//    @Override
+//    public SseEmitter chat(ChatRequest chatRequest, SseEmitter emitter) {
+//        ChatModelVo chatModelVo = chatModelService.selectModelByCategory("image");
+//
+//        // 鍙戦�佹祦寮忔秷鎭�
+//
+//            MultiModalConversation conv = new MultiModalConversation();
+//            MultiModalMessage systemMessage = MultiModalMessage.builder().role(Role.SYSTEM.getValue())
+//                    .content(Arrays.asList(
+//                            Collections.singletonMap("text",chatRequest.getSysPrompt()))).build();
+//            // 鑾峰彇鐢ㄦ埛娑堟伅鍐呭
+//            List<Message> messages = chatRequest.getMessages();
+//            MultiModalMessage userMessage = null;
+//            //婕暱鐨勬牸寮忚浆鎹�
+//            // 閬嶅巻娑堟伅鍒楄〃锛屾彁鍙栨枃鏈唴瀹�
+//        if (messages != null && !messages.isEmpty()) {
+//            Object content = messages.get(messages.size() - 1).getContent();
+//            List<Map<String, Object>> contentList = new ArrayList<>();
+//            StringBuilder textContent = new StringBuilder();
+//            if (content instanceof List<?>) {
+//                for (Object item : (List<?>) content) {
+//                    if (item instanceof Map<?, ?> mapItem) {
+//                        String type = (String) mapItem.get("type");
+//                        if ("text".equals(type)) {
+//                            String text = (String) mapItem.get("text");
+//                            if (text != null) {
+//                                textContent.append(text).append(" ");
+//                            }
+//                        } else if ("image_url".equals(type)) {
+//                            Map<String, String> imageUrl  = (Map<String, String>) mapItem.get("image_url");
+//                                contentList.add(Collections.singletonMap("image", imageUrl.get("url")));
+//                        }
+//                    }
+//                }
+//            }
+//            // 灏嗘嫾鎺ュ悗鐨勬枃鏈唴瀹规坊鍔犲埌 contentList
+//            if (textContent.length() > 0) {
+//                contentList.add(Collections.singletonMap("text", textContent.toString().trim()));
+//            }
+//            userMessage = MultiModalMessage.builder()
+//                    .role(Role.USER.getValue())
+//                    .content(contentList)
+//                    .build();
+//        }
+//            MultiModalConversationParam param = MultiModalConversationParam.builder()
+//                    .apiKey(chatModelVo.getApiKey())
+//                    .model(chatModelVo.getModelName())
+//                    .messages(Arrays.asList(systemMessage, userMessage))
+//                    .incrementalOutput(true)
+//                    .build();
+//
+//
+//        try {
+//            final QwenStreamingResponseBuilder responseBuilder = new QwenStreamingResponseBuilder(param.getModel(),param.getIncrementalOutput() );
+//            conv.streamCall(param, new ResultCallback<>() {
+//                @SneakyThrows
+//                public void onEvent(MultiModalConversationResult result) {
+//
+//                    String delta = responseBuilder.append(result);
+//                    if (Utils.isNotNullOrEmpty(delta)) {
+//
+//                        emitter.send(delta);
+//                        log.info("鏀跺埌娑堟伅鐗囨: {}", delta);
+//                    }
+//                }
+//                public void onComplete() {
+//                    emitter.complete();
+//                    log.info("娑堟伅缁撴潫", responseBuilder.build());
+//                }
+//                public void onError(Exception e) {
+//                    log.info("璇锋眰澶辫触", e.getMessage());
+//                }
+//            });
+//        } catch (NoApiKeyException e) {
+//            emitter.send("璇峰厛閰嶇疆API瀵嗛挜");
+//            throw new RuntimeException(e);
+//        } catch (UploadFileException e) {
+//            throw new RuntimeException(e);
+//        }
+//
+//
+//        return emitter;
+//    }
+
+    @Override
+    public SseEmitter chat(ChatRequest chatRequest, SseEmitter emitter) {
+        // 浠庢暟鎹簱鑾峰彇 image 绫诲瀷鐨勬ā鍨嬮厤缃�
+        ChatModelVo chatModelVo = chatModelService.selectModelByCategory(ChatModeType.IMAGE.getCode());
+        if (chatModelVo == null) {
+            log.error("鏈壘鍒� image 绫诲瀷鐨勬ā鍨嬮厤缃�");
+            emitter.completeWithError(new IllegalStateException("鏈壘鍒� image 绫诲瀷鐨勬ā鍨嬮厤缃�"));
+            return emitter;
+        }
+
+        // 鍒涘缓 OpenAI 娴佸鎴风
+        OpenAiStreamClient openAiStreamClient = ChatConfig.createOpenAiStreamClient(chatModelVo.getApiHost(), chatModelVo.getApiKey());
+        List<Message> messages = chatRequest.getMessages();
+
+        // 鍒涘缓 SSE 浜嬩欢婧愮洃鍚櫒
+        SSEEventSourceListener listener = new SSEEventSourceListener(emitter, chatRequest.getUserId(), chatRequest.getSessionId());
+
+        // 鏋勫缓鑱婂ぉ瀹屾垚璇锋眰
+        ChatCompletion completion = ChatCompletion
+                .builder()
+                .messages(messages)
+                .model(chatModelVo.getModelName()) // 浣跨敤鏁版嵁搴撲腑閰嶇疆鐨勬ā鍨嬪悕绉�
+                .stream(true)
+                .build();
+
+        // 鍙戣捣娴佸紡鑱婂ぉ瀹屾垚璇锋眰
+        openAiStreamClient.streamChatCompletion(completion, listener);
+
+        return emitter;
+    }
+
+    @Override
+    public String getCategory() {
+        return ChatModeType.IMAGE.getCode();
+    }
+}
diff --git a/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/chat/impl/QianWenAiChatServiceImpl.java b/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/chat/impl/QianWenAiChatServiceImpl.java
index 1d6e061..850ebf6 100644
--- a/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/chat/impl/QianWenAiChatServiceImpl.java
+++ b/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/chat/impl/QianWenAiChatServiceImpl.java
@@ -35,6 +35,8 @@
                 .modelName(chatModelVo.getModelName())
                 .build();
 
+
+
         // 鍙戦�佹祦寮忔秷鎭�
         try {
             model.chat(chatRequest.getPrompt(), new StreamingChatResponseHandler() {
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 f7dee9a..d8c61a9 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
@@ -131,7 +131,7 @@
             if (chatModelVo == null) {
                 log.error("鏈壘鍒癷mage绫诲瀷鐨勬ā鍨嬮厤缃�");
                 throw new IllegalStateException("鏈壘鍒癷mage绫诲瀷鐨勬ā鍨嬮厤缃�");
-            }//            chatRequest.setModel(chatModelVo.getModelName());
+            }
         }else{
             chatModelVo = chatModelService.selectModelByName(chatRequest.getModel());
         }
diff --git a/script/sql/update/20250605.sql b/script/sql/update/20250605.sql
new file mode 100644
index 0000000..3d2120d
--- /dev/null
+++ b/script/sql/update/20250605.sql
@@ -0,0 +1,7 @@
+
+/*
+ 涓婁紶鍥剧墖鍚庝細鑷姩鏌ユ壘鍒嗙被涓篿mage鐨勬ā鍨�,瑕佷娇鐢ㄥ浘鐗囪瘑鍒姛鑳�,闇�瑕佹墽琛岃繖鏉ql骞堕厤缃甼ey淇℃伅
+*/
+
+
+INSERT INTO `chat_model` (`id`, `tenant_id`, `category`, `model_name`, `model_describe`, `model_price`, `model_type`, `model_show`, `system_prompt`, `api_host`, `api_key`, `api_url`, `create_dept`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1930184891812147202, '000000', 'image', 'qwen/qwen2.5-vl-72b-instruct', 'qwen/qwen2.5-vl-72b-instruct', 0.003, '2', '0', NULL, 'https://api.ppinfra.com/v3/openai/chat/completions', 'xx', NULL, 103, 1, '2025-06-04 16:48:34', 1, '2025-06-04 16:48:34', '瑙嗚妯″瀷');

--
Gitblit v1.9.3