tsurumure 6 месяцев назад
Родитель
Сommit
ab669f22c9

+ 30 - 0
db/ds_chat.sql

@@ -0,0 +1,30 @@
+/**
+Source Server Version: 8.0.31
+Source Database: backendsys
+Date: 2023/05/23 17:09:22
+*/
+
+DROP TABLE IF EXISTS `ds_chat`;
+CREATE TABLE `ds_chat` (
+    PRIMARY KEY (`id`),
+    `id` BIGINT AUTO_INCREMENT COMMENT 'ID',
+    `model` VARCHAR(255) COMMENT '模型',
+    `history_code` VARCHAR(64) NOT NULL COMMENT '对话历史记录ID',
+    `user_id` BIGINT COMMENT '用户ID',
+    `user_nickname` VARCHAR(255) COMMENT '用户名',
+    `role` VARCHAR(255) COMMENT '对话角色 (user, assistant, system, tool)',
+    `content` VARCHAR(5000) NOT NULL COMMENT '对话内容',
+    `content_type` VARCHAR(255) NOT NULL COMMENT '对话内容类型 (LOADING: 思考中, THINK: 思考, REPLY: 回复)',
+    `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+    INDEX `idx_history_code` (`history_code`),
+    INDEX `idx_user_id` (`user_id`),
+    INDEX `idx_model` (`model`)
+) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='Deepseek对话表';
+
+INSERT INTO ds_chat(model, history_code, user_id, user_nickname, role, content_type, content) VALUES
+    ('deepseek-reasoner', '5670822e-f0f7-4635-b766-c9c61edef0fa', 1, '超人', 'user', 'REPLY', '生成一条关于制作蛋糕的文案,适用于家用,用料是纯天然无添加剂,主打绿色健康美味的理念'),
+    ('deepseek-reasoner', '5670822e-f0f7-4635-b766-c9c61edef0fa', null, null, null, 'THINK', '让我想想用户说这段话的含义'),
+    ('deepseek-reasoner', '5670822e-f0f7-4635-b766-c9c61edef0fa', null, null, 'assistant', 'REPLY', '在家也能享受绿色健康美味!用纯天然无添加剂的食材,带给你和家人最纯净的美味。从选购新鲜食材到烘焙出炉,每一步都充满温馨与乐趣。让我们一起回归自然,享受绿色健康的蛋糕时光!'),
+    ('deepseek-chat', '4e69922c-f0e7-4f82-905d-0b375e094092', 1, '超人', 'user', 'REPLY', '你好'),
+    ('deepseek-chat', '4e69922c-f0e7-4f82-905d-0b375e094092', null, null, 'assistant', 'REPLY', '你好,朋友')
+;

+ 23 - 0
db/ds_chat_history.sql

@@ -0,0 +1,23 @@
+/**
+Source Server Version: 8.0.31
+Source Database: backendsys
+Date: 2023/05/23 17:09:22
+*/
+
+DROP TABLE IF EXISTS `ds_chat_history`;
+CREATE TABLE `ds_chat_history` (
+    PRIMARY KEY (`id`),
+    `id` BIGINT AUTO_INCREMENT COMMENT 'ID',
+    `history_code` VARCHAR(36) NOT NULL COMMENT '对话历史记录ID',
+    `user_id` BIGINT NOT NULL COMMENT '用户ID',
+    `last_content` VARCHAR(255) NOT NULL COMMENT '最后一次的对话内容',
+    `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+    `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+    INDEX `idx_user_id` (`user_id`)
+) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='Deepseek对话历史记录表';
+
+INSERT INTO ds_chat_history(history_code, user_id, last_content) VALUES
+    ('5670822e-f0f7-4635-b766-c9c61edef0fa', 1, '生成一条关于制作蛋糕的文案,适用于家用,用料是纯天然无添加剂,主打绿色健康美味的理念'),
+    ('3e69922c-f0e7-4f82-905d-0b375e09409c', 1, '你好'),
+    ('bd55b4e6-9a4b-4904-99cc-b474bc0a86e4', 2, 'Hello')
+;

+ 1 - 2
db/sys_user_role_permission.sql

@@ -26,8 +26,7 @@ INSERT INTO sys_user_role_permission(id, parent_id, permission_name, sort) VALUE
 
     ('2', -1, '创意中心', 2),
 
-    ('31', -1, 'AI文案', 3),
-        ('31.1', '31', 'AI文案-运营权限', null),
+    ('31', -1, 'AI对话', 3),
     ('32', -1, 'AI商品图', 4),
         ('32.1', '32', 'AI商品图-运营权限', null),
     ('33', -1, 'AI数智人', 5),

+ 0 - 1
db/sys_user_role_permission_relation.sql

@@ -90,7 +90,6 @@ INSERT INTO sys_user_role_permission_relation(role_id, permission_id) VALUES
             (1, '21.3.1'), (1, '21.3.2'), (1, '21.3.3'), (1, '21.3.4'),
 
     (1, '31'),
-        (1, '31.1'),
     (1, '32'),
         (1, '32.1'),
     (1, '33'),

+ 2 - 2
src/main/java/com/backendsys/modules/TestController.java

@@ -17,7 +17,7 @@ import cn.afterturn.easypoi.word.WordExportUtil;
 import com.backendsys.utils.MD5Util;
 
 import com.tencentcloudapi.common.Credential;
-//import io.github.pigmesh.ai.deepseek.core.DeepSeekClient;
+//import io.github.pigmesh.ai.deepseek.core.DeepSeekClientImpl;
 //import io.github.pigmesh.ai.deepseek.core.OpenAiHttpException;
 //import io.github.pigmesh.ai.deepseek.core.chat.ChatCompletionResponse;
 import jakarta.servlet.ServletContext;
@@ -74,7 +74,7 @@ public class TestController {
 
 
 //    @Autowired
-//    private DeepSeekClient deepSeekClient;
+//    private DeepSeekClientImpl deepSeekClient;
 //
 //    // https://javaai.pig4cloud.com/deepseek/quickstart
 //    // sse 流式返回

+ 17 - 0
src/main/java/com/backendsys/modules/ai/deepSeek/controller/DeepSeekChatController.java

@@ -0,0 +1,17 @@
+package com.backendsys.modules.ai.deepSeek.controller;
+
+import com.backendsys.modules.ai.deepSeek.service.DeepSeekChatService;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.RestController;
+
+@Validated
+@RestController
+@Tag(name = "DeepSeekChatService")
+public class DeepSeekChatController {
+
+    @Autowired
+    private DeepSeekChatService deepSeekChatService;
+
+}

+ 23 - 11
src/main/java/com/backendsys/modules/ai/deepSeek/controller/DeepSeekController.java

@@ -1,7 +1,7 @@
 package com.backendsys.modules.ai.deepSeek.controller;
 
 import com.backendsys.modules.ai.deepSeek.entity.DSParam;
-import com.backendsys.modules.ai.deepSeek.utils.DeepSeekClient;
+import com.backendsys.modules.ai.deepSeek.service.impl.DeepSeekClientImpl;
 import com.backendsys.modules.ai.deepSeek.utils.OllamaUtil;
 import com.backendsys.modules.common.config.security.utils.SecurityUtil;
 import com.backendsys.modules.common.utils.Result;
@@ -10,6 +10,7 @@ import io.swagger.v3.oas.annotations.tags.Tag;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.RequestBody;
 import org.springframework.web.bind.annotation.RestController;
@@ -23,29 +24,40 @@ public class DeepSeekController {
     private OllamaUtil ollamaUtil;
 
     @Autowired
-    private DeepSeekClient deepSeekClient;
+    private DeepSeekClientImpl deepSeekClientImpl;
 
     /**
-     * deepseek开放平台
-     * https://platform.deepseek.com/
+     * Deepseek API 开放平台 (https://platform.deepseek.com)
+     * 现有模型:
+     * - deepseek-chat      对话模型
+     * - deepseek-reasoner  推理模型
      */
-
-    @Operation(summary = "Deepseek 提问 (API)")
+    @Operation(summary = "DS-提问 (API)")
     @PreAuthorize("@sr.hasPermission('101')")
     @PostMapping("/api/deepSeek/chat")
     public Result chat(@Validated @RequestBody DSParam param) {
-//        deepSeekClient.chatCompletion(param.getPrompt(), "deepseek-chat");  // 对话模型
-        deepSeekClient.chatCompletion(param.getPrompt(), "deepseek-reasoner");  // 推理模型
+        deepSeekClientImpl.chatCompletion(param.getModel(), param.getPrompt());
         return Result.success();
     }
 
-    @Operation(summary = "Deepseek 提问 (本地部署)")
+    /**
+     * Deepseek 本地部署模型:
+     * - deepseek-r1:1.5b
+     * - deepseek-r1:7b
+     */
+    @Operation(summary = "DS-提问 (本地部署)")
     @PreAuthorize("@sr.hasPermission('101')")
     @PostMapping("/api/deepSeek/chatLocal")
     public Result chatLocal(@Validated @RequestBody DSParam param) {
-        ollamaUtil.chatDeepSeek("deepseek-r1:1.5b", param.getPrompt(), SecurityUtil.getUserId());
-        // ollamaUtil.chatDeepSeek("deepseek-r1:7b", param.getPrompt(), SecurityUtil.getUserId());
+        ollamaUtil.chatDeepSeek(param.getModel(), param.getPrompt(), SecurityUtil.getUserId());
         return Result.success();
     }
 
+    @Operation(summary = "DS-获得模型列表")
+    @PreAuthorize("@sr.hasPermission('101')")
+    @GetMapping("/api/deepSeek/getModels")
+    public Result getModels() {
+        return Result.success().put("data", deepSeekClientImpl.getModels());
+    }
+
 }

+ 28 - 0
src/main/java/com/backendsys/modules/ai/deepSeek/entity/DSChat.java

@@ -0,0 +1,28 @@
+package com.backendsys.modules.ai.deepSeek.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+
+@Data
+@TableName("ds_chat")
+public class DSChat {
+
+    public static interface Detail{}
+    public static interface Create{}
+    public static interface Update{}
+    public static interface Delete{}
+
+    @TableId(type = IdType.AUTO)
+    private Long id;
+    private String model;
+    private String history_code;
+    private Long user_id;
+    private String user_nickname;
+    private String role;
+    private String content;
+    private String content_type;
+    private String create_time;
+
+}

+ 25 - 0
src/main/java/com/backendsys/modules/ai/deepSeek/entity/DSChatHistory.java

@@ -0,0 +1,25 @@
+package com.backendsys.modules.ai.deepSeek.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+
+@Data
+@TableName("ds_chat_history")
+public class DSChatHistory {
+
+    public static interface Detail{}
+    public static interface Create{}
+    public static interface Update{}
+    public static interface Delete{}
+
+    @TableId(type = IdType.AUTO)
+    private Long id;
+    private String history_code;
+    private Long user_id;
+    private String last_content;
+    private String create_time;
+    private String update_time;
+
+}

+ 2 - 0
src/main/java/com/backendsys/modules/ai/deepSeek/entity/DSParam.java

@@ -8,5 +8,7 @@ public class DSParam {
 
     @NotEmpty(message="提示词不能为空")
     private String prompt;
+    @NotEmpty(message="模型不能为空")
+    private String model;
 
 }

+ 4 - 0
src/main/java/com/backendsys/modules/ai/deepSeek/entity/DSRequestMessage.java

@@ -1,8 +1,12 @@
 package com.backendsys.modules.ai.deepSeek.entity;
 
+import lombok.AllArgsConstructor;
 import lombok.Data;
+import lombok.NoArgsConstructor;
 
 @Data
+@NoArgsConstructor
+@AllArgsConstructor
 public class DSRequestMessage {
 
     private String role;

+ 4 - 0
src/main/java/com/backendsys/modules/ai/deepSeek/service/DeepSeekChatService.java

@@ -0,0 +1,4 @@
+package com.backendsys.modules.ai.deepSeek.service;
+
+public interface DeepSeekChatService {
+}

+ 14 - 0
src/main/java/com/backendsys/modules/ai/deepSeek/service/DeepSeekClient.java

@@ -0,0 +1,14 @@
+package com.backendsys.modules.ai.deepSeek.service;
+
+import cn.hutool.json.JSONArray;
+
+import java.util.List;
+import java.util.Map;
+
+public interface DeepSeekClient {
+
+    void chatCompletion(String prompt, String model);
+
+    JSONArray getModels();
+
+}

+ 8 - 0
src/main/java/com/backendsys/modules/ai/deepSeek/service/impl/DeepSeekChatServiceImpl.java

@@ -0,0 +1,8 @@
+package com.backendsys.modules.ai.deepSeek.service.impl;
+
+import com.backendsys.modules.ai.deepSeek.service.DeepSeekChatService;
+import org.springframework.stereotype.Service;
+
+@Service
+public class DeepSeekChatServiceImpl implements DeepSeekChatService {
+}

+ 103 - 45
src/main/java/com/backendsys/modules/ai/deepSeek/utils/DeepSeekClient.java → src/main/java/com/backendsys/modules/ai/deepSeek/service/impl/DeepSeekClientImpl.java

@@ -1,9 +1,14 @@
-package com.backendsys.modules.ai.deepSeek.utils;
+package com.backendsys.modules.ai.deepSeek.service.impl;
 
 import cn.hutool.core.convert.Convert;
+import cn.hutool.json.JSONArray;
+import cn.hutool.json.JSONObject;
+import cn.hutool.json.JSONUtil;
+import com.backendsys.exception.CustException;
 import com.backendsys.modules.ai.deepSeek.entity.DSContent;
 import com.backendsys.modules.ai.deepSeek.entity.DSRequest;
 import com.backendsys.modules.ai.deepSeek.entity.DSRequestMessage;
+import com.backendsys.modules.ai.deepSeek.service.DeepSeekClient;
 import com.backendsys.modules.common.config.security.utils.SecurityUtil;
 import com.backendsys.modules.sse.entity.SseResponse;
 import com.backendsys.modules.sse.entity.SseResponseEnum;
@@ -11,26 +16,32 @@ import com.backendsys.modules.sse.utils.SseUtil;
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpGet;
 import org.apache.http.client.methods.HttpPost;
 import org.apache.http.entity.StringEntity;
 import org.apache.http.impl.client.CloseableHttpClient;
 import org.apache.http.impl.client.HttpClients;
+import org.apache.http.util.EntityUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Component;
+import org.springframework.stereotype.Service;
 
 import java.io.*;
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.concurrent.CompletableFuture;
 
-// 开放平台:https://platform.deepseek.com/
-// 官方文档:https://api-docs.deepseek.com/zh-cn/
-@Component
-public class DeepSeekClient {
+/**
+ * DeepseekAPI 工具类 (by Mure 2025/02/27)
+ *
+ * 开放平台:https://platform.deepseek.com/
+ * 官方文档:https://api-docs.deepseek.com/zh-cn/
+ */
+@Service
+public class DeepSeekClientImpl implements DeepSeekClient {
 
     @Value("${deepseek.url}")
     private String API_URL;
@@ -43,10 +54,13 @@ public class DeepSeekClient {
 
     private final ObjectMapper objectMapper = new ObjectMapper();
 
+
     /**
      * 对话 (文档:https://api-docs.deepseek.com/zh-cn/api/create-chat-completion)
+     *
      * @param prompt
      */
+    @Override
     public void chatCompletion(String prompt, String model) {
 
         Long userId = SecurityUtil.getUserId();
@@ -61,42 +75,24 @@ public class DeepSeekClient {
                 // 【多轮对话】
                 // https://api-docs.deepseek.com/zh-cn/guides/multi_round_chat
 
-
-//                // 获取当前用户的对话历史
-//                List<Map<String, String>> messages = sessionHistory.getOrDefault(userId, new ArrayList<>());
-
-//                // 添加用户的新问题到对话历史
-//                Map<String, String> userMessage = new HashMap<>();
-//                userMessage.put("role", "user");
-//                userMessage.put("content", prompt);
-//
-//                Map<String, String> systemMessage = new HashMap<>();
-//                systemMessage.put("role", "system");
-//                systemMessage.put("content", "聚城网络科技有限公司的物业管理助手");
-//                messages.add(userMessage);
-//                messages.add(systemMessage);
-
-//                // 提取 messages 为单独变量
-//                List<Map<String, String>> messages = new ArrayList<>();
-//                Map<String, String> userMessage = new HashMap<>();
-//                userMessage.put("role", "user");
-//                userMessage.put("content", prompt);
-//                messages.add(userMessage);
+                // - 接口做最大问答限制 (1-字数限制, 2-次数限制)
 
                 List<DSRequestMessage> messages = new ArrayList<>();
-                DSRequestMessage messageItem = new DSRequestMessage();
-                messageItem.setRole("user");
-                messageItem.setContent(prompt);
-                messages.add(messageItem);
+//                messages.add(new DSRequestMessage("user", "你是谁"));
+//                messages.add(new DSRequestMessage("assistant", "xxx"));
+//                messages.add(new DSRequestMessage("user", "你是什么大模型"));
+//                messages.add(new DSRequestMessage("assistant", "xxx"));
+                messages.add(new DSRequestMessage("user", prompt));
 
                 // 构建请求体
-                DSRequest dsRequest = new DSRequest();
-                dsRequest.setModel(model);        // (deepseek-chat: 对话模型, deepseek-reasoner: 推理模型)
-                dsRequest.setMessages(messages);
-                dsRequest.setStream(true);
+                DSRequest body = new DSRequest();
+                // model: (deepseek-chat: 对话模型, deepseek-reasoner: 推理模型)
+                body.setModel(model);
+                body.setMessages(messages);
+                body.setStream(true);
 
                 // 记录请求开始时间
-                long startTime = System.currentTimeMillis();
+                long allStartTime = System.currentTimeMillis();
 
                 // 调用 Deepseek API
                 try (CloseableHttpClient client = HttpClients.createDefault()) {
@@ -105,16 +101,22 @@ public class DeepSeekClient {
                     request.setHeader("Content-Type", "application/json");
                     request.setHeader("Authorization", "Bearer " + API_KEY);
 
-                    String requestBody = objectMapper.writeValueAsString(dsRequest);
+                    String requestBody = objectMapper.writeValueAsString(body);
                     request.setEntity(new StringEntity(requestBody, StandardCharsets.UTF_8));
 
                     try (
-                        CloseableHttpResponse response = client.execute(request);
-                        BufferedReader reader = new BufferedReader(new InputStreamReader(response.getEntity().getContent(), StandardCharsets.UTF_8))
+                            CloseableHttpResponse response = client.execute(request);
+                            BufferedReader reader = new BufferedReader(new InputStreamReader(response.getEntity().getContent(), StandardCharsets.UTF_8))
                     ) {
 
-                        long duration = System.currentTimeMillis() - startTime;            // 计算耗时
-                        System.out.println("API 调用耗时: " + duration + " 毫秒");
+
+                        long apiDuration = System.currentTimeMillis() - allStartTime;   // 接口耗时
+
+                        long thinkStartTime = 0L;   // 开始思考
+                        long thinkDuration = 0L;    // 思考耗时
+                        Boolean isThinking = false;
+
+                        System.out.println("API 调用耗时: " + apiDuration + " 毫秒");
                         System.out.println("-------------------- 开始流式回答: --------------------");
 
                         // [SSE] 发送消息
@@ -152,10 +154,16 @@ public class DeepSeekClient {
                                 JsonNode node = objectMapper.readTree(jsonData);
                                 JsonNode delta = node.path("choices").path(0).path("delta");
 
-                                // [推理] Think
+                                // [推理] Think (仅 deepseek-reasoner: 推理模型 支持)
                                 String reasoning_content = delta.path("reasoning_content").asText("");
                                 if (!reasoning_content.isEmpty()) {
 
+                                    // 开始思考
+                                    if (!isThinking) {
+                                        isThinking = true;
+                                        thinkStartTime = System.currentTimeMillis();
+                                    }
+
                                     System.out.println("reasoning_content: " + reasoning_content);
 
                                     // [SSE] 发送消息
@@ -171,6 +179,15 @@ public class DeepSeekClient {
                                 // [回答] Reply
                                 String content = delta.path("content").asText("");
                                 if (!content.isEmpty()) {
+
+                                    // 停止思考,并计算思考耗时
+                                    if (isThinking) {
+                                        isThinking = false;
+                                        thinkDuration = thinkStartTime - allStartTime;
+                                        System.out.println("推理耗时: " + thinkDuration + "毫秒");
+                                        System.out.println("-----------------------------------------------");
+                                    }
+
                                     System.out.println("content: " + content);
 
                                     // [SSE] 发送消息
@@ -201,11 +218,11 @@ public class DeepSeekClient {
 //                        emitter.complete();
 
                         System.out.println("-------------------- 结束流式回答. --------------------");
-                        long durationOutput = System.currentTimeMillis() - startTime;
-                        System.out.println("输出耗时: " + durationOutput + " 毫秒");
+                        long durationOutput = System.currentTimeMillis() - allStartTime;
 
                         System.out.println("全部推理: " + allReasoning);
                         System.out.println("全部回答: " + allContent);
+                        System.out.println("总输出耗时: " + durationOutput + " 毫秒");
 
                     }
                 } catch (Exception e) {
@@ -223,4 +240,45 @@ public class DeepSeekClient {
 
     }
 
-}
+    @Override
+    public JSONArray getModels() {
+
+        Long userId = SecurityUtil.getUserId();
+        String emitterKey = APPLICATION_NAME + "-userid-" + Convert.toStr(userId);
+
+        // 调用 Deepseek API
+        try (CloseableHttpClient client = HttpClients.createDefault()) {
+
+            HttpGet request = new HttpGet(API_URL + "/models");
+            request.setHeader("Content-Type", "application/json");
+            request.setHeader("Authorization", "Bearer " + API_KEY);
+
+            try (CloseableHttpResponse response = client.execute(request)) {
+
+                // 检查响应状态码
+                if (response.getStatusLine().getStatusCode() == 200) {
+
+                    // 获取响应体
+                    String responseBody = EntityUtils.toString(response.getEntity());
+                    JSONObject jsonResponse = JSONUtil.parseObj(responseBody);
+                    JSONArray models = jsonResponse.getJSONArray("data");
+                    return models;
+
+                } else {
+                    throw new CustException("Failed to retrieve models. Status Code: " + response.getStatusLine().getStatusCode());
+                }
+
+            } catch (Exception e) {
+                String dataStr = (new SseResponse(SseResponseEnum.DEEPSEEK, e.getMessage())).toJsonStr();
+                sseUtil.send(emitterKey, dataStr);
+                throw new CustException(e.getMessage());
+            }
+        } catch (Exception e) {
+            String dataStr = (new SseResponse(SseResponseEnum.DEEPSEEK, e.getMessage())).toJsonStr();
+            sseUtil.send(emitterKey, dataStr);
+            throw new CustException(e.getMessage());
+        }
+
+    }
+
+}

+ 52 - 19
src/main/java/com/backendsys/service/Ai/AiChatServiceImpl.java

@@ -1,17 +1,21 @@
 package com.backendsys.service.Ai;
 
+import cn.hutool.core.convert.Convert;
 import cn.hutool.core.date.DateUtil;
 import cn.hutool.core.util.StrUtil;
 import cn.hutool.json.JSONArray;
 import cn.hutool.json.JSONObject;
 import cn.hutool.json.JSONUtil;
 import com.backendsys.aspect.HttpRequestAspect;
+import com.backendsys.modules.common.config.security.entity.SecurityUserInfo;
+import com.backendsys.modules.common.config.security.utils.SecurityUtil;
 import com.backendsys.modules.common.config.security.utils.TokenUtil;
 import com.backendsys.entity.Ai.AiChatDTO;
 import com.backendsys.mapper.Ai.AiChatHistoryMapper;
 import com.backendsys.mapper.Ai.AiChatMapper;
 import com.backendsys.mapper.System.SysUserMapper;
 import com.backendsys.entity.Ai.AiChatHistoryDTO;
+import com.backendsys.modules.sse.utils.SseUtil;
 import com.backendsys.utils.response.PageInfoResult;
 import com.github.pagehelper.PageHelper;
 import com.google.gson.Gson;
@@ -47,15 +51,20 @@ public class AiChatServiceImpl implements AiChatService {
     @Value("${tencent.hunyuan.region}")
     private String REGION;
 
+    @Value("${spring.application.name}")
+    private String APPLICATION_NAME;
     @Autowired
-    private TokenUtil tokenUtil;
+    private SseUtil sseUtil;
+
+//    @Autowired
+//    private TokenUtil tokenUtil;
 
     private final SimpMessagingTemplate messagingTemplate;
 
     // -----------------------------------------------------------
 
-    @Autowired
-    private HttpRequestAspect httpRequestAspect;
+//    @Autowired
+//    private HttpRequestAspect httpRequestAspect;
 
     @Autowired
     private AiChatMapper aiChatMapper;
@@ -63,8 +72,8 @@ public class AiChatServiceImpl implements AiChatService {
     @Autowired
     private AiChatHistoryMapper aiChatHistoryMapper;
 
-    @Autowired
-    private SysUserMapper sysUserMapper;
+//    @Autowired
+//    private SysUserMapper sysUserMapper;
 
     /**
      * 查询 列表
@@ -89,6 +98,8 @@ public class AiChatServiceImpl implements AiChatService {
      */
     private AiChatDTO receiveAiChatDTO(AiChatDTO aiChatDTO) {
 
+
+
         // [查询] 自身 UserId (并加入)
         Long user_id = aiChatDTO.getUser_id();
         String history_code = aiChatDTO.getHistory_code();
@@ -96,9 +107,17 @@ public class AiChatServiceImpl implements AiChatService {
         /**
          * 使用 Webstock 将 AI回复的内容 发送回给自己
          */
-        // 获得我的用户名
-        Claims loginUserInfo = tokenUtil.getRedisTokenInfo();
-        String receiver = (String) loginUserInfo.get("username");
+        SecurityUserInfo userInfo = SecurityUtil.getUserInfo();
+        String receiver = userInfo.getUsername();
+
+//        // 获得我的用户名
+//        Claims loginUserInfo = tokenUtil.getRedisTokenInfo();
+//        String receiver = (String) loginUserInfo.get("username");
+
+
+        String emitterKey = APPLICATION_NAME + "-userid-" + Convert.toStr(user_id);
+
+
 
         // [发送/接收] 内容
         String sendContent = aiChatDTO.getContent();
@@ -176,6 +195,9 @@ public class AiChatServiceImpl implements AiChatService {
                 // [Webstock] 将消息推送到前台
                 messagingTemplate.convertAndSendToUser(receiver, "/queue/ai-chating", e.Data);
 
+                // [SSE] 发送消息
+                sseUtil.send(emitterKey, e.Data);
+
                 receiveRobotCode = dataObject.getStr("Id");
                 JSONArray choicesArray = dataObject.getJSONArray("Choices");
                 if (choicesArray != null && choicesArray.size() > 0) {
@@ -205,9 +227,12 @@ public class AiChatServiceImpl implements AiChatService {
      */
     private AiChatDTO cheatAiChatDTO(AiChatDTO aiChatDTO, String content) {
 
-        // 获得我的用户名
-        Claims loginUserInfo = tokenUtil.getRedisTokenInfo();
-        String receiver = (String) loginUserInfo.get("username");
+        SecurityUserInfo userInfo = SecurityUtil.getUserInfo();
+        String receiver = userInfo.getUsername();
+
+//        // 获得我的用户名
+//        Claims loginUserInfo = tokenUtil.getRedisTokenInfo();
+//        String receiver = (String) loginUserInfo.get("username");
 
         // [查询] 自身 UserId (并加入)
         Long user_id = aiChatDTO.getUser_id();
@@ -245,15 +270,21 @@ public class AiChatServiceImpl implements AiChatService {
         String[] cheatKeyword = {"你是谁","你是什么","介绍一下自己"};
 
         // [查询] 自身 UserId (并加入)
-        Long user_id = httpRequestAspect.getUserId();
-        aiChatDTO.setUser_id(user_id);
+//        Long user_id = httpRequestAspect.getUserId();
+//        aiChatDTO.setUser_id(user_id);
 
-        // [查询] 自身 用户信息 (并加入)
-        Map<String, Object> userDetail = sysUserMapper.queryUserDetail(user_id);
-        aiChatDTO.setUser_nickname((String) userDetail.get("nickname"));
-        aiChatDTO.setUser_avatar((String) userDetail.get("avatar"));
+        // 添加 当前用户名/对话角色
+        SecurityUserInfo userInfo = SecurityUtil.getUserInfo();
+        aiChatDTO.setUser_id(userInfo.getUser_id());
+        aiChatDTO.setUser_nickname(userInfo.getUsername());
         aiChatDTO.setRole("user");
 
+//        // [查询] 自身 用户信息 (并加入)
+//        Map<String, Object> userDetail = sysUserMapper.queryUserDetail(user_id);
+//        aiChatDTO.setUser_nickname((String) userDetail.get("nickname"));
+//        aiChatDTO.setUser_avatar((String) userDetail.get("avatar"));
+//        aiChatDTO.setRole("user");
+
         // [Create] 如果参数中没有带 { history_code } 时,则在新增的同时创建 chat_history表 记录
         String history_code = aiChatDTO.getHistory_code();
         // 当 { history_code} 为空时
@@ -265,7 +296,8 @@ public class AiChatServiceImpl implements AiChatService {
 
             // -- [新增] 历史记录 -------------------------------------------------------
             AiChatHistoryDTO aiChatHistoryDTO = new AiChatHistoryDTO();
-            aiChatHistoryDTO.setUser_id(user_id);
+//            aiChatHistoryDTO.setUser_id(user_id);
+            aiChatHistoryDTO.setUser_id(userInfo.getUser_id());
             aiChatHistoryDTO.setHistory_code(history_code);
 
             // 最后一次发送的内容 (超过 255 字符时截取)
@@ -304,7 +336,8 @@ public class AiChatServiceImpl implements AiChatService {
                 System.out.println("chatHistoryList is Empty!");
                 // -- [新增] 历史记录 -------------------------------------------------------
                 AiChatHistoryDTO aiChatHistoryDTO = new AiChatHistoryDTO();
-                aiChatHistoryDTO.setUser_id(user_id);
+//                aiChatHistoryDTO.setUser_id(user_id);
+                aiChatHistoryDTO.setUser_id(userInfo.getUser_id());
                 aiChatHistoryDTO.setHistory_code(history_code);
 
                 // 最后一次发送的内容 (超过 255 字符时截取)