Procházet zdrojové kódy

添加Ai模块代码

zhaojinyu před 2 týdny
rodič
revize
50da264568
21 změnil soubory, kde provedl 1336 přidání a 0 odebrání
  1. 3 0
      pom.xml
  2. 19 0
      service-ai/pom.xml
  3. 27 0
      service-ai/service-ai-api/pom.xml
  4. 88 0
      service-ai/service-ai-biz/pom.xml
  5. 40 0
      service-ai/service-ai-biz/src/main/java/com/usky/ai/AIChatApplication.java
  6. 334 0
      service-ai/service-ai-biz/src/main/java/com/usky/ai/controller/web/AiChatController.java
  7. 43 0
      service-ai/service-ai-biz/src/main/java/com/usky/ai/controller/web/AiQuestionController.java
  8. 70 0
      service-ai/service-ai-biz/src/main/java/com/usky/ai/controller/web/AiSessionController.java
  9. 37 0
      service-ai/service-ai-biz/src/main/java/com/usky/ai/mapper/AiQuestionMapper.java
  10. 31 0
      service-ai/service-ai-biz/src/main/java/com/usky/ai/mapper/AiSessionMapper.java
  11. 70 0
      service-ai/service-ai-biz/src/main/java/com/usky/ai/service/AiQuestion.java
  12. 46 0
      service-ai/service-ai-biz/src/main/java/com/usky/ai/service/AiQuestionItem.java
  13. 43 0
      service-ai/service-ai-biz/src/main/java/com/usky/ai/service/AiSession.java
  14. 14 0
      service-ai/service-ai-biz/src/main/java/com/usky/ai/service/config/AiChatConfig.java
  15. 19 0
      service-ai/service-ai-biz/src/main/java/com/usky/ai/service/config/MyGlobalCorsConfig.java
  16. 14 0
      service-ai/service-ai-biz/src/main/java/com/usky/ai/service/config/WebConfig.java
  17. 22 0
      service-ai/service-ai-biz/src/main/java/com/usky/ai/service/vo/AiStreamOutputVO.java
  18. 24 0
      service-ai/service-ai-biz/src/main/resources/bootstrap.yml
  19. 94 0
      service-ai/service-ai-biz/src/main/resources/logback.xml
  20. 149 0
      service-ai/service-ai-biz/src/main/resources/static/dpsk.html
  21. 149 0
      service-ai/service-ai-biz/src/main/resources/static/tyqw.html

+ 3 - 0
pom.xml

@@ -90,6 +90,9 @@
 
     <module>service-oa</module>
 
+
+    <module>service-ai</module>
+
   </modules>
           
   

+ 19 - 0
service-ai/pom.xml

@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>usky-modules</artifactId>
+        <groupId>com.usky</groupId>
+        <version>0.0.1</version>
+    </parent>
+
+    <modelVersion>4.0.0</modelVersion>
+    <artifactId>service-ai</artifactId>
+    <packaging>pom</packaging>
+    <version>0.0.1</version>
+
+    <modules>
+        <module>service-ai-biz</module>
+        <module>service-ai-api</module>
+    </modules>
+</project>

+ 27 - 0
service-ai/service-ai-api/pom.xml

@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>service-ai</artifactId>
+        <groupId>com.usky</groupId>
+        <version>0.0.1</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>service-ai-api</artifactId>
+    <!-- SpringCloud Openfeign -->
+    <dependencies>
+        <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-starter-openfeign</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.usky</groupId>
+            <artifactId>usky-common-core</artifactId>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <finalName>${project.artifactId}</finalName>
+    </build>
+
+</project>

+ 88 - 0
service-ai/service-ai-biz/pom.xml

@@ -0,0 +1,88 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>service-ai</artifactId>
+        <groupId>com.usky</groupId>
+        <version>0.0.1</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>service-ai-biz</artifactId>
+    <dependencies>
+        <dependency>
+            <groupId>com.usky</groupId>
+            <artifactId>common-cloud-starter</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.usky</groupId>
+            <artifactId>service-ai-api</artifactId>
+            <version>0.0.1</version>
+        </dependency>
+
+        <dependency>
+            <groupId>com.usky</groupId>
+            <artifactId>ruoyi-common-swagger</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.mybatis.spring.boot</groupId>
+            <artifactId>mybatis-spring-boot-starter</artifactId>
+            <version>2.1.4</version>
+        </dependency>
+        <dependency>
+            <groupId>mysql</groupId>
+            <artifactId>mysql-connector-java</artifactId>
+            <scope>runtime</scope>
+        </dependency>
+
+        <!-- 阿里dashscope依赖 -->
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>dashscope-sdk-java</artifactId>
+            <version>2.14.0</version>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.slf4j</groupId>
+                    <artifactId>slf4j-simple</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+
+
+    </dependencies>
+
+    <build>
+        <finalName>${project.artifactId}</finalName>
+        <plugins>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+                <version>2.2.6.RELEASE</version>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>repackage</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>com.github.shalousun</groupId>
+                <artifactId>smart-doc-maven-plugin</artifactId>
+                <version>2.1.1</version>
+                <configuration>
+                    <!--指定生成文档的使用的配置文件,配置文件放在自己的项目中-->
+                    <configFile>./src/main/resources/smart-doc.json</configFile>
+                    <!--指定项目名称-->
+                    <projectName>test</projectName>
+                    <!--                    <excludes>-->
+                    <!--                        <exclude>com.bizmatics:product-service-provider</exclude>-->
+                    <!--                        <exclude>cn.afterturn:easypoi-web</exclude>-->
+                    <!--                    </excludes>-->
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>

+ 40 - 0
service-ai/service-ai-biz/src/main/java/com/usky/ai/AIChatApplication.java

@@ -0,0 +1,40 @@
+package com.usky.ai;
+
+import com.ruoyi.common.swagger.annotation.EnableCustomSwagger2;
+import org.mybatis.spring.annotation.MapperScan;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.cloud.openfeign.EnableFeignClients;
+import org.springframework.context.ConfigurableApplicationContext;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.context.annotation.EnableAspectJAutoProxy;
+import org.springframework.core.env.Environment;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+@EnableCustomSwagger2
+@EnableAspectJAutoProxy(proxyTargetClass = true, exposeProxy = true)
+@EnableFeignClients(basePackages = {"com.usky"})
+@MapperScan(value = "com.usky.ai.mapper")
+@ComponentScan(basePackages = {"com.usky"})
+@SpringBootApplication
+public class AIChatApplication {
+        private static final Logger LOGGER = LoggerFactory.getLogger(AIChatApplication.class);
+
+        public static void main (String[]args) throws UnknownHostException {
+        ConfigurableApplicationContext application = SpringApplication.run(AIChatApplication.class, args);
+        Environment env = application.getEnvironment();
+        String ip = InetAddress.getLocalHost().getHostAddress();
+        String port = env.getProperty("server.port");
+        String path = env.getProperty("server.servlet.context-path");
+        LOGGER.info("\n----------------------------------------------------------\n\t" +
+                "Application is running! Access URLs:\n\t" +
+                "Local: \t\thttp://localhost:" + port + (null == path ? "" : path) + "/\n\t" +
+                "External: \thttp://" + ip + ":" + port + (null == path ? "" : path) + "/\n\t" +
+                "Api: \t\thttp://" + ip + ":" + port + (null == path ? "" : path) + "/swagger-ui/index.html\n\t" +
+                "----------------------------------------------------------");
+       }
+}

+ 334 - 0
service-ai/service-ai-biz/src/main/java/com/usky/ai/controller/web/AiChatController.java

@@ -0,0 +1,334 @@
+package com.usky.ai.controller.web;
+
+import com.alibaba.dashscope.aigc.generation.Generation;
+import com.alibaba.dashscope.aigc.generation.GenerationParam;
+import com.alibaba.dashscope.common.Message;
+import com.alibaba.dashscope.common.Role;
+import com.alibaba.dashscope.exception.ApiException;
+import com.alibaba.dashscope.exception.InputRequiredException;
+import com.alibaba.dashscope.exception.NoApiKeyException;
+import com.alibaba.fastjson.JSONObject;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.usky.ai.mapper.AiQuestionMapper;
+import com.usky.ai.mapper.AiSessionMapper;
+import com.usky.ai.service.AiQuestion;
+import com.usky.ai.service.AiSession;
+import com.usky.ai.service.vo.AiStreamOutputVO;
+import com.usky.common.core.bean.ApiResult;
+import com.usky.common.security.utils.SecurityUtils;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
+
+import javax.annotation.Resource;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.time.LocalDateTime;
+import java.util.List;
+import java.util.stream.Collectors;
+
+@Slf4j
+@RestController
+@RequestMapping("/ai")
+public class AiChatController {
+
+    @Value("${ai.api.key}")
+    private String apiKey;
+
+    @Value("${airole}")
+    private String aiRole;
+
+    @Value("${aliTyqw.model}")
+    private String tyqwModel;
+
+    @Value("${aliDpsk.model}")
+    private String dpskModel;
+
+    @Value("${ai.historyLimit}")
+    private int Limit;
+
+    @Resource
+    private Generation generation;
+
+    @Autowired
+    private AiQuestionMapper aiQuestionMapper;
+
+    @Autowired
+    private AiSessionMapper aiSessionMapper;
+
+    private final ObjectMapper objectMapper = new ObjectMapper();
+
+    // 阿里百炼通义千问大模型
+    @PostMapping(value = "/aliTyqw", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
+    public ResponseEntity<StreamingResponseBody> send1(@RequestBody JSONObject object )throws NoApiKeyException, InputRequiredException {
+        // 获取当前登录用户的信息
+        Long userId = SecurityUtils.getUserId();
+        String userName = SecurityUtils.getLoginUser().getSysUser().getNickName();
+        String sessionId = null;
+        String content = object.get("content").toString();
+
+        // 如果没有传入 sessionId,则创建一个新的会话ID
+        if (object.containsKey("sessionId")) {
+            sessionId = object.get("sessionId").toString();
+        } else {
+            sessionId = java.util.UUID.randomUUID().toString();
+        }
+
+        // 解析 JSON 并提取 "content" 字段的值
+        String questionText;
+        questionText = content; // 提取 "content" 字段的值
+
+        // 检查是否已经存在相同的 sessionId
+        boolean exists = aiSessionMapper.existsBySessionId(sessionId);
+
+        if (!exists) {
+            // 创建新的 AiSession 实体并存入数据库
+            AiSession aiSession = new AiSession();
+            aiSession.setSessionId(sessionId);
+            aiSession.setUserId(userId);
+            aiSession.setUserName(userName);
+            aiSession.setQuestion(questionText);
+            aiSession.setAskTime(LocalDateTime.now());
+            aiSessionMapper.save(aiSession);
+        }
+
+        // 获取当前用户的对话历史
+        List<AiQuestion> conversationHistory = aiQuestionMapper.findByUserIdAndSessionId(sessionId, userId);
+
+//        // 只保留最近的几轮对话
+//        int historyLimit = Limit;
+//        if (conversationHistory.size() > historyLimit) {
+//            conversationHistory = conversationHistory.subList(conversationHistory.size() - historyLimit, conversationHistory.size());
+//        }
+
+        // 构建对话历史消息
+        List<Message> messages = conversationHistory.stream()
+                .map(q -> Message.builder()
+                        .role(q.getUserId().equals(userId) ? Role.USER.getValue() : Role.ASSISTANT.getValue())
+                        .content(q.getUserId().equals(userId) ? q.getQuestion() : q.getAnswer())
+                        .build())
+                .collect(Collectors.toList());
+
+        // 插入角色定义(在对话历史的开头)
+        Message roleDefinition = Message.builder()
+                .role(Role.SYSTEM.getValue()) // 使用系统角色
+                .content(aiRole) // 定义角色的行为和风格
+                .build();
+        messages.add(0, roleDefinition); // 将角色定义插入到对话历史的开头
+
+        // 添加用户的新消息
+        Message userMessage = Message.builder()
+                .role(Role.USER.getValue())
+                .content(questionText) // 使用提取的文本
+                .build();
+        messages.add(userMessage);
+
+        // 构建模型调用参数
+        GenerationParam param = GenerationParam.builder()
+                .model(tyqwModel)
+                .messages(messages)
+                .resultFormat(GenerationParam.ResultFormat.MESSAGE)
+                .topP(0.8)
+                .apiKey(apiKey)
+                .incrementalOutput(true) // 开启增量输出
+                .build();
+
+        String finalSessionId = sessionId;
+        StringBuilder completeAnswer = new StringBuilder(); // 用于收集完整的回答内容
+        return ResponseEntity.ok()
+                .contentType(MediaType.TEXT_EVENT_STREAM)
+                .body(outputStream -> {
+                    try {
+                        // 调用流式接口
+                        generation.streamCall(param).blockingForEach(chunk -> {
+                            try {
+                                // 获取每次生成的内容
+                                String partialAnswer = chunk.getOutput().getChoices().get(0).getMessage().getContent();
+
+                                // 检查内容是否为空,如果为空则跳过
+                                if (partialAnswer == null || partialAnswer.trim().isEmpty()) {
+                                    return; // 如果内容为空,直接返回,不进行后续操作
+                                }
+
+                                // 构建输出对象
+                                AiStreamOutputVO aiStreamOutputVO = new AiStreamOutputVO();
+                                aiStreamOutputVO.setSessionId(finalSessionId);
+                                aiStreamOutputVO.setReasoningContent(partialAnswer);
+
+                                // 转换为 JSON 字符串
+                                String newString = objectMapper.writeValueAsString(aiStreamOutputVO);
+
+                                // 写入输出流
+                                outputStream.write(("data: " + newString + "\n\n").getBytes(StandardCharsets.UTF_8));
+//                                outputStream.write(ApiResult.success(newString).toString().getBytes(StandardCharsets.UTF_8));
+                                outputStream.flush(); // 确保立即发送到前端
+
+                                // 累加到完整回答内容中
+                                completeAnswer.append(partialAnswer);
+
+                            } catch (Exception e) {
+                                log.error("Error processing chunk", e);
+                                outputStream.write(("data: Error processing chunk\n\n").getBytes(StandardCharsets.UTF_8));
+                                outputStream.flush();
+                            }
+                        });
+
+                        // 流式接口调用完成后,将完整回答存入数据库
+                        AiQuestion aiQuestion = new AiQuestion();
+                        aiQuestion.setModel(tyqwModel);
+                        aiQuestion.setSessionId(finalSessionId);
+                        aiQuestion.setUserId(userId);
+                        aiQuestion.setUserName(userName);
+                        aiQuestion.setQuestion(questionText);
+                        aiQuestion.setAnswer(completeAnswer.toString());
+                        aiQuestion.setAskTime(LocalDateTime.now());
+                        aiQuestionMapper.save(aiQuestion);
+
+                    } catch (ApiException e) {
+                        log.error("Error processing request", e);
+                        outputStream.write(("data: Error processing request\n\n").getBytes(StandardCharsets.UTF_8));
+                        outputStream.flush();
+                    } catch (NoApiKeyException | InputRequiredException e) {
+                        throw new RuntimeException(e);
+                    }
+                });
+    }
+
+    // 阿里百炼DeepSeek大模型
+    @PostMapping(value = "/aliDeepSeek", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
+    public ResponseEntity<StreamingResponseBody> send2(@RequestBody JSONObject object )throws NoApiKeyException, InputRequiredException {
+        // 获取当前登录用户的信息
+        Long userId = SecurityUtils.getUserId();
+        String userName = SecurityUtils.getLoginUser().getSysUser().getNickName();
+        String sessionId = null;
+        String content = object.get("content").toString();
+
+        // 如果没有传入 sessionId,则创建一个新的会话ID
+        if (object.containsKey("sessionId")) {
+            sessionId = object.get("sessionId").toString();
+        } else {
+            sessionId = java.util.UUID.randomUUID().toString();
+        }
+
+        // 解析 JSON 并提取 "content" 字段的值
+        String questionText;
+        questionText = content; // 提取 "content" 字段的值
+
+        // 检查是否已经存在相同的 sessionId
+        boolean exists = aiSessionMapper.existsBySessionId(sessionId);
+
+        if (!exists) {
+            // 创建新的 AiSession 实体并存入数据库
+            AiSession aiSession = new AiSession();
+            aiSession.setSessionId(sessionId);
+            aiSession.setUserId(userId);
+            aiSession.setUserName(userName);
+            aiSession.setQuestion(questionText);
+            aiSession.setAskTime(LocalDateTime.now());
+            aiSessionMapper.save(aiSession);
+        }
+
+        // 获取当前用户的对话历史
+        List<AiQuestion> conversationHistory = aiQuestionMapper.findByUserIdAndSessionId(sessionId, userId);
+
+        // 只保留最近的几轮对话
+//        int historyLimit = Limit;
+//        if (conversationHistory.size() > historyLimit) {
+//            conversationHistory = conversationHistory.subList(conversationHistory.size() - historyLimit, conversationHistory.size());
+//        }
+
+        // 构建对话历史消息
+        List<Message> messages = conversationHistory.stream()
+                .map(q -> Message.builder()
+                        .role(q.getUserId().equals(userId) ? Role.USER.getValue() : Role.ASSISTANT.getValue())
+                        .content(q.getUserId().equals(userId) ? q.getQuestion() : q.getAnswer())
+                        .build())
+                .collect(Collectors.toList());
+
+        // 插入角色定义(在对话历史的开头)
+        Message roleDefinition = Message.builder()
+                .role(Role.SYSTEM.getValue()) // 使用系统角色
+                .content(aiRole) // 定义角色的行为和风格
+                .build();
+        messages.add(0, roleDefinition); // 将角色定义插入到对话历史的开头
+
+        // 添加用户的新消息
+        Message userMessage = Message.builder()
+                .role(Role.USER.getValue())
+                .content(questionText) // 使用提取的文本
+                .build();
+        messages.add(userMessage);
+
+        // 构建模型调用参数
+        GenerationParam param = GenerationParam.builder()
+                .model(dpskModel)
+                .messages(messages)
+                .resultFormat(GenerationParam.ResultFormat.MESSAGE)
+                .apiKey(apiKey)
+                .incrementalOutput(true) // 开启增量输出[^1^]
+                .build();
+
+        String finalSessionId = sessionId;
+        StringBuilder completeAnswer = new StringBuilder(); // 用于收集完整的回答内容
+        return ResponseEntity.ok()
+                .contentType(MediaType.TEXT_EVENT_STREAM)
+                .body(outputStream -> {
+                    try {
+                        // 调用流式接口
+                        generation.streamCall(param).blockingForEach(chunk -> {
+                            try {
+                                // 获取每次生成的内容
+                                String partialAnswer = chunk.getOutput().getChoices().get(0).getMessage().getContent();
+
+                                // 检查内容是否为空,如果为空则跳过
+                                if (partialAnswer == null || partialAnswer.trim().isEmpty()) {
+                                    return; // 如果内容为空,直接返回,不进行后续操作
+                                }
+
+                                // 构建输出对象
+                                AiStreamOutputVO aiStreamOutputVO = new AiStreamOutputVO();
+                                aiStreamOutputVO.setSessionId(finalSessionId);
+                                aiStreamOutputVO.setReasoningContent(partialAnswer);
+
+                                // 转换为 JSON 字符串
+                                String newString = objectMapper.writeValueAsString(aiStreamOutputVO);
+
+                                // 写入输出流
+                                outputStream.write(("data: " + newString + "\n\n").getBytes(StandardCharsets.UTF_8));
+                                outputStream.flush(); // 确保立即发送到前端
+
+                                // 累加到完整回答内容中
+                                completeAnswer.append(partialAnswer);
+
+                            } catch (Exception e) {
+                                log.error("Error processing chunk", e);
+                                outputStream.write(("data: Error processing chunk\n\n").getBytes(StandardCharsets.UTF_8));
+                                outputStream.flush();
+                            }
+                        });
+
+                        // 流式接口调用完成后,将完整回答存入数据库
+                        AiQuestion aiQuestion = new AiQuestion();
+                        aiQuestion.setModel(dpskModel);
+                        aiQuestion.setSessionId(finalSessionId);
+                        aiQuestion.setUserId(userId);
+                        aiQuestion.setUserName(userName);
+                        aiQuestion.setQuestion(questionText);
+                        aiQuestion.setAnswer(completeAnswer.toString());
+                        aiQuestion.setAskTime(LocalDateTime.now());
+                        aiQuestionMapper.save(aiQuestion);
+
+                    } catch (ApiException e) {
+                        log.error("Error processing request", e);
+                        outputStream.write(("data: Error processing request\n\n").getBytes(StandardCharsets.UTF_8));
+                        outputStream.flush();
+                    } catch (NoApiKeyException | InputRequiredException e) {
+                        throw new RuntimeException(e);
+                    }
+                });
+    }
+}

+ 43 - 0
service-ai/service-ai-biz/src/main/java/com/usky/ai/controller/web/AiQuestionController.java

@@ -0,0 +1,43 @@
+package com.usky.ai.controller.web;
+
+import com.usky.ai.mapper.AiQuestionMapper;
+import com.usky.ai.service.AiQuestion;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+@Slf4j
+@RestController
+@RequestMapping("/questions")
+public class AiQuestionController {
+
+    @Autowired
+    private AiQuestionMapper aiQuestionMapper;
+
+    // 查询所有数据
+    @GetMapping("/all")
+    public List<AiQuestion> getAllQuestions() {
+        return aiQuestionMapper.findAll();
+    }
+
+    // 根据 userId 查询数据
+    @GetMapping("/user/{userId}")
+    public List<AiQuestion> getQuestionsByUserId(@PathVariable Long userId) {
+        return aiQuestionMapper.findByUserId(userId);
+    }
+
+    // 根据 id 删除数据
+    @DeleteMapping("/deleted/{id}")
+    public String deleteQuestion(@PathVariable Long id) {
+        aiQuestionMapper.deleteById(id);
+        return "Question with id " + id + " deleted successfully.";
+    }
+
+    // 根据 sessionId 查询数据
+    @GetMapping("/session/{sessionId}")
+    public List<AiQuestion> getQuestionsBySessionId(@PathVariable String sessionId) {
+        return aiQuestionMapper.findBySessionId(sessionId);
+    }
+}

+ 70 - 0
service-ai/service-ai-biz/src/main/java/com/usky/ai/controller/web/AiSessionController.java

@@ -0,0 +1,70 @@
+package com.usky.ai.controller.web;
+
+import com.usky.ai.mapper.AiQuestionMapper;
+import com.usky.ai.mapper.AiSessionMapper;
+import com.usky.ai.service.AiQuestion;
+import com.usky.ai.service.AiQuestionItem;
+import com.usky.ai.service.AiSession;
+import com.usky.common.core.bean.ApiResult;
+import com.usky.common.security.utils.SecurityUtils;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Slf4j
+@RestController
+@RequestMapping("/session")
+public class AiSessionController {
+
+    @Autowired
+    private AiSessionMapper aiSessionMapper;
+
+    @Autowired
+    private AiQuestionMapper aiQuestionMapper;
+
+    @GetMapping("/all")
+    public List<AiSession> getAllSessions() {
+        return aiSessionMapper.findAll();
+    }
+
+    @GetMapping("/current")
+    public List<AiSession> getCurrentSessions(@RequestHeader("Authorization") String token) {
+        // 解析 token 获取 userId
+        Long userId = SecurityUtils.getUserId();
+        return getSessionsByUserId(userId);
+    }
+    public List<AiSession> getSessionsByUserId(@PathVariable Long userId) {
+        List<AiSession> sessions = aiSessionMapper.findByUserId(userId);
+
+        for (AiSession session : sessions) {
+            List<AiQuestion> questions = aiQuestionMapper.findQuestionsBySessionId(session.getSessionId());
+            List<AiQuestionItem> itemList = new ArrayList<>();
+
+            for (AiQuestion question : questions) {
+                AiQuestionItem userItem = new AiQuestionItem("user", question.getQuestion());
+                userItem.setId(question.getId());
+                userItem.setSessionId(question.getSessionId());
+                userItem.setUserId(question.getUserId());
+                userItem.setUserName(question.getUserName());
+                userItem.setAskTime(question.getAskTime());
+
+                AiQuestionItem assistantItem = new AiQuestionItem("assistant", question.getAnswer());
+                assistantItem.setId(question.getId());
+                assistantItem.setSessionId(question.getSessionId());
+                assistantItem.setUserId(question.getUserId());
+                assistantItem.setUserName(question.getUserName());
+                assistantItem.setAskTime(question.getAskTime());
+
+                itemList.add(userItem);
+                itemList.add(assistantItem);
+            }
+
+            session.setItemList(itemList);
+        }
+
+        return ApiResult.success(sessions).getData();
+    }
+}

+ 37 - 0
service-ai/service-ai-biz/src/main/java/com/usky/ai/mapper/AiQuestionMapper.java

@@ -0,0 +1,37 @@
+package com.usky.ai.mapper;
+
+import com.usky.ai.service.AiQuestion;
+import org.apache.ibatis.annotations.*;
+
+import java.util.List;
+
+@Mapper
+public interface AiQuestionMapper {
+    @Insert("INSERT INTO ai_questions (model, session_id, user_id, user_name, question, answer, ask_time) " +
+            "VALUES (#{model},  #{sessionId}, #{userId}, #{userName}, #{question}, #{answer}, #{askTime})")
+    void save(AiQuestion aiQuestion);
+
+    // 查询所有数据
+    @Select("SELECT * FROM ai_questions ORDER BY ask_time ASC")
+    List<AiQuestion> findAll();
+
+    // 根据 userId 查询数据
+    @Select("SELECT * FROM ai_questions WHERE user_id = #{userId} ORDER BY ask_time ASC")
+    List<AiQuestion> findByUserId(Long userId);
+
+    // 根据 id 删除数据
+    @Delete("DELETE FROM ai_questions WHERE id = #{id}")
+    void deleteById(Long id);
+
+    // 根据 sessionId 和 userId 查询数据
+    @Select("SELECT * FROM ai_questions WHERE session_id = #{sessionId} AND user_id = #{userId} ORDER BY ask_time ASC")
+    List<AiQuestion> findByUserIdAndSessionId(@Param("sessionId") String sessionId, @Param("userId") Long userId);
+
+    //根据 sessionId查询数据
+    @Select("SELECT * FROM ai_questions WHERE session_id = #{sessionId} ORDER BY ask_time ASC")
+    List<AiQuestion> findBySessionId(String sessionId);
+
+    // 根据 sessionId 查询 ai_questions 表中的数据
+    @Select("SELECT * FROM ai_questions WHERE session_id = #{sessionId} ORDER BY ask_time ASC")
+    List<AiQuestion> findQuestionsBySessionId(String sessionId);
+}

+ 31 - 0
service-ai/service-ai-biz/src/main/java/com/usky/ai/mapper/AiSessionMapper.java

@@ -0,0 +1,31 @@
+package com.usky.ai.mapper;
+
+import com.usky.ai.service.AiQuestion;
+import com.usky.ai.service.AiSession;
+import org.apache.ibatis.annotations.Insert;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Select;
+
+import java.util.List;
+
+@Mapper
+public interface AiSessionMapper {
+    @Insert("INSERT INTO ai_sessions (session_id, user_id, user_name, question, ask_time) " +
+            "VALUES (#{sessionId}, #{userId}, #{userName}, #{question}, #{askTime})")
+    void save(AiSession aiSession);
+
+    //查询所有数据
+    @Select("SELECT * FROM ai_sessions ORDER BY ask_time ASC")
+    List<AiSession> findAll();
+
+    //根据user_id 查询数据
+    @Select("SELECT * FROM ai_sessions WHERE user_id = #{userId} ORDER BY ask_time ASC")
+    List<AiSession> findByUserId(Long userId);
+
+    // 检查是否存在指定的 session_id
+    @Select("SELECT COUNT(*) FROM ai_sessions WHERE session_id = #{sessionId}")
+    boolean existsBySessionId(String sessionId);
+
+    @Select("SELECT * FROM ai_questions WHERE session_id = #{sessionId} ORDER BY ask_time ASC")
+    List<AiQuestion> findQuestionsBySessionId(String sessionId);
+}

+ 70 - 0
service-ai/service-ai-biz/src/main/java/com/usky/ai/service/AiQuestion.java

@@ -0,0 +1,70 @@
+package com.usky.ai.service;
+
+import java.time.LocalDateTime;
+
+public class AiQuestion {
+    private Long id;
+    private String model;
+    private String sessionId;
+    private Long userId; // 添加用户ID字段
+    private String userName; // 添加用户名字段
+    private String question;
+    private String answer;
+    private LocalDateTime askTime;
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getModel() { return model; }
+
+    public void setModel(String model) { this.model = model; }
+
+    public String getSessionId() { return sessionId; }
+
+    public void setSessionId(String sessionId) { this.sessionId = sessionId; }
+
+    public Long getUserId() {
+        return userId;
+    }
+
+    public void setUserId(Long userId) {
+        this.userId = userId;
+    }
+
+    public String getUserName() {
+        return userName;
+    }
+
+    public void setUserName(String userName) {
+        this.userName = userName;
+    }
+
+    public String getQuestion() {
+        return question;
+    }
+
+    public void setQuestion(String question) {
+        this.question = question;
+    }
+
+    public String getAnswer() {
+        return answer;
+    }
+
+    public void setAnswer(String answer) {
+        this.answer = answer;
+    }
+
+    public LocalDateTime getAskTime() {
+        return askTime;
+    }
+
+    public void setAskTime(LocalDateTime askTime) {
+        this.askTime = askTime;
+    }
+}

+ 46 - 0
service-ai/service-ai-biz/src/main/java/com/usky/ai/service/AiQuestionItem.java

@@ -0,0 +1,46 @@
+package com.usky.ai.service;
+
+import java.time.LocalDateTime;
+
+public class AiQuestionItem {
+    private Long id;
+    private String sessionId;
+    private Long userId;
+    private String userName;
+    private String role;
+    private String content;
+    private LocalDateTime askTime;
+
+    public AiQuestionItem(String role, String content) {
+        this.role = role;
+        this.content = content;
+    }
+
+    public Long getId() { return id; }
+
+    public void setId(Long id) { this.id = id; }
+
+    public String getSessionId() { return sessionId; }
+
+    public void setSessionId(String sessionId) { this.sessionId = sessionId; }
+
+    public Long getUserId() { return userId; }
+
+    public void setUserId(Long userId) { this.userId = userId; }
+
+    public String getUserName() { return userName; }
+
+    public void setUserName(String userName) { this.userName = userName; }
+
+    public String getRole() { return role; }
+
+    public void setRole(String role) { this.role = role; }
+
+    public String getContent() { return content; }
+
+    public void setContent(String content) { this.content = content; }
+
+    public LocalDateTime getAskTime() { return askTime; }
+
+    public void setAskTime(LocalDateTime askTime) { this.askTime = askTime; }
+}

+ 43 - 0
service-ai/service-ai-biz/src/main/java/com/usky/ai/service/AiSession.java

@@ -0,0 +1,43 @@
+package com.usky.ai.service;
+
+import java.time.LocalDateTime;
+import java.util.List;
+
+public class AiSession {
+
+    private Long id;
+    private String sessionId;
+    private Long userId; // 添加用户ID字段
+    private String userName; // 添加用户名字段
+    private String question;
+    private LocalDateTime askTime;
+    private List<AiQuestionItem> itemList;
+
+    public Long getId() { return id; }
+
+    public void setId(Long id) { this.id = id; }
+
+    public String getSessionId() { return sessionId; }
+
+    public void setSessionId(String sessionId) { this.sessionId = sessionId; }
+
+    public Long getUserId() { return userId; }
+
+    public void setUserId(Long userId) { this.userId = userId; }
+
+    public String getUserName() { return userName; }
+
+    public void setUserName(String userName) { this.userName = userName; }
+
+    public String getQuestion() { return question; }
+
+    public void setQuestion(String question) { this.question = question; }
+
+    public LocalDateTime getAskTime() { return askTime; }
+
+    public void setAskTime(LocalDateTime askTime) { this.askTime = askTime; }
+
+    public List<AiQuestionItem> getItemList() { return itemList; }
+
+    public void setItemList(List<AiQuestionItem> itemList) { this.itemList = itemList; }
+}

+ 14 - 0
service-ai/service-ai-biz/src/main/java/com/usky/ai/service/config/AiChatConfig.java

@@ -0,0 +1,14 @@
+package com.usky.ai.service.config;
+
+import com.alibaba.dashscope.aigc.generation.Generation;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class AiChatConfig {
+
+    @Bean
+    public Generation generation() {
+        return new Generation();
+    }
+}

+ 19 - 0
service-ai/service-ai-biz/src/main/java/com/usky/ai/service/config/MyGlobalCorsConfig.java

@@ -0,0 +1,19 @@
+package com.usky.ai.service.config;
+
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.servlet.config.annotation.CorsRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+@Configuration
+public class MyGlobalCorsConfig implements WebMvcConfigurer {
+
+    @Override
+    public void addCorsMappings(CorsRegistry registry) {
+        registry.addMapping("/**") // 对所有的路径允许跨域请求
+                .allowedOrigins("*") // 允许来自任何源的请求
+                .allowedMethods("GET", "POST", "PUT", "DELETE") // 允许的请求方法
+                .allowedHeaders("*") // 允许的请求头
+                .allowCredentials(false) // 是否允许证书(cookies),根据需要设置
+                .maxAge(3600); // 预检请求的缓存时间(秒)
+    }
+}

+ 14 - 0
service-ai/service-ai-biz/src/main/java/com/usky/ai/service/config/WebConfig.java

@@ -0,0 +1,14 @@
+package com.usky.ai.service.config;
+
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.servlet.config.annotation.AsyncSupportConfigurer;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+@Configuration
+public class WebConfig implements WebMvcConfigurer {
+    @Override
+    public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
+        // 设置默认的异步请求超时时间为300秒(单位:毫秒)
+        configurer.setDefaultTimeout(300000);
+    }
+}

+ 22 - 0
service-ai/service-ai-biz/src/main/java/com/usky/ai/service/vo/AiStreamOutputVO.java

@@ -0,0 +1,22 @@
+package com.usky.ai.service.vo;
+
+import lombok.Data;
+
+@Data
+public class AiStreamOutputVO {
+
+    /**
+     * 会话id
+     */
+    private String sessionId;
+
+    /**
+     * 输入内容
+     */
+    private String content;
+
+    /**
+     * 输出内容
+     */
+    private String reasoningContent;
+}

+ 24 - 0
service-ai/service-ai-biz/src/main/resources/bootstrap.yml

@@ -0,0 +1,24 @@
+# Tomcat
+server:
+  port: 9899
+# Spring
+spring: 
+  application:
+    # 应用名称
+    name: service-ai
+  profiles:
+    # 环境配置
+    active: dev
+  cloud:
+    nacos:
+      discovery:
+        # 服务注册地址
+        server-addr: usky-cloud-nacos:8848
+      config:
+        # 配置中心地址
+        server-addr: usky-cloud-nacos:8848
+        # 配置文件格式
+        file-extension: yml
+        # 共享配置
+        shared-configs:
+          - application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}

+ 94 - 0
service-ai/service-ai-biz/src/main/resources/logback.xml

@@ -0,0 +1,94 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<configuration scan="true" scanPeriod="60 seconds" debug="false">
+    <!-- 日志存放路径 -->
+    <property name="log.path" value="/var/log/uskycloud/service-ai" />
+    <!-- 日志输出格式 -->
+    <property name="log.pattern" value="%d{MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{26}:%line: %msg%n" />
+    <!--    	<property name="log.pattern" value="%gray(%d{MM-dd HH:mm:ss.SSS}) %highlight(%-5level) &#45;&#45; [%gray(%thread)] %cyan(%logger{26}:%line): %msg%n" />-->
+
+
+    <property name="SQL_PACKAGE" value="com.usky.ai.mapper"/>
+
+    <!-- 控制台输出 -->
+    <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
+        <encoder>
+            <pattern>${log.pattern}</pattern>
+        </encoder>
+    </appender>
+
+    <appender name="file_sql" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <file>${log.path}/sql.log</file>
+        <!-- 循环政策:基于时间创建日志文件 -->
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <!-- 日志文件名格式 -->
+            <fileNamePattern>${log.path}/sql.%d{yyyy-MM-dd}.log</fileNamePattern>
+            <!-- 日志最大的历史 60天 -->
+            <maxHistory>3</maxHistory>
+        </rollingPolicy>
+        <encoder>
+            <pattern>${log.pattern}</pattern>
+        </encoder>
+    </appender>
+
+    <!-- 系统日志输出 -->
+    <appender name="file_info" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <file>${log.path}/info.log</file>
+        <!-- 循环政策:基于时间创建日志文件 -->
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <!-- 日志文件名格式 -->
+            <fileNamePattern>${log.path}/info.%d{yyyy-MM-dd}.log</fileNamePattern>
+            <!-- 日志最大的历史 60天 -->
+            <maxHistory>3</maxHistory>
+        </rollingPolicy>
+        <encoder>
+            <pattern>${log.pattern}</pattern>
+        </encoder>
+        <filter class="ch.qos.logback.classic.filter.LevelFilter">
+            <!-- 过滤的级别 -->
+            <level>INFO</level>
+            <!-- 匹配时的操作:接收(记录) -->
+            <onMatch>ACCEPT</onMatch>
+            <!-- 不匹配时的操作:拒绝(不记录) -->
+            <onMismatch>DENY</onMismatch>
+        </filter>
+    </appender>
+
+    <appender name="file_error" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <file>${log.path}/error.log</file>
+        <!-- 循环政策:基于时间创建日志文件 -->
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <!-- 日志文件名格式 -->
+            <fileNamePattern>${log.path}/error.%d{yyyy-MM-dd}.log</fileNamePattern>
+            <!-- 日志最大的历史 60天 -->
+            <maxHistory>60</maxHistory>
+        </rollingPolicy>
+        <encoder>
+            <pattern>${log.pattern}</pattern>
+        </encoder>
+        <filter class="ch.qos.logback.classic.filter.LevelFilter">
+            <!-- 过滤的级别 -->
+            <level>ERROR</level>
+            <!-- 匹配时的操作:接收(记录) -->
+            <onMatch>ACCEPT</onMatch>
+            <!-- 不匹配时的操作:拒绝(不记录) -->
+            <onMismatch>DENY</onMismatch>
+        </filter>
+    </appender>
+
+    <!-- 系统模块日志级别控制  -->
+    <!--	<logger name="com.usky" level="info" />-->
+    <!-- Spring日志级别控制  -->
+    <!--	<logger name="org.springframework" level="warn" />-->
+
+    <logger name="${SQL_PACKAGE}" additivity="false" level="debug">
+        <appender-ref ref="console"/>
+        <appender-ref ref="file_sql"/>
+    </logger>
+
+    <!--系统操作日志-->
+    <root level="info">
+        <appender-ref ref="file_info" />
+        <appender-ref ref="file_error" />
+        <appender-ref ref="console" />
+    </root>
+</configuration>

+ 149 - 0
service-ai/service-ai-biz/src/main/resources/static/dpsk.html

@@ -0,0 +1,149 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <title>小天-AI</title>
+    <style>
+        body {
+            font-family: 'Roboto', Arial, sans-serif;
+            margin: 0;
+            padding: 0;
+            background-color: #f4f4f9;
+            display: flex;
+            justify-content: center;
+            align-items: center;
+            height: 100vh;
+        }
+        .container {
+            width: 500px;
+            background-color: #fff;
+            box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
+            border-radius: 8px;
+            overflow: hidden;
+        }
+        h1 {
+            background-color: #007bff;
+            color: #fff;
+            text-align: center;
+            padding: 15px;
+            margin: 0;
+            font-size: 1.5em;
+        }
+        form {
+            padding: 20px;
+        }
+        label {
+            display: block;
+            margin-bottom: 10px;
+            font-weight: bold;
+        }
+        textarea {
+            width: 100%;
+            height: 100px;
+            border: 1px solid #ccc;
+            border-radius: 4px;
+            padding: 10px;
+            resize: none;
+        }
+        button {
+            width: 100%;
+            padding: 10px;
+            background-color: #007bff;
+            color: #fff;
+            border: none;
+            border-radius: 4px;
+            cursor: pointer;
+            transition: background-color 0.3s ease;
+        }
+        button:hover {
+            background-color: #0056b3;
+        }
+        #response {
+            margin-top: 20px;
+            padding: 10px;
+            background-color: #f9f9f9;
+            border-top: 1px solid #ccc;
+            max-height: 200px;
+            overflow-y: auto;
+            font-family: monospace;
+            white-space: pre-wrap;
+        }
+    </style>
+</head>
+<body>
+<div class="container">
+    <h1>小天-AI</h1>
+    <form id="chatForm">
+        <label for="content">你的问题:</label>
+        <textarea id="content" name="content" placeholder="请输入你的问题在这里..."></textarea>
+        <button type="submit">发送</button>
+    </form>
+    <div id="response"></div>
+    <div id="sessionId" style="margin-top: 10px; font-size: 0.9em; color: #666;"></div>
+</div>
+
+<script>
+    document.getElementById('chatForm').addEventListener('submit', function(event) {
+        event.preventDefault();
+
+        const content = document.getElementById('content').value;
+
+        const requestBody = JSON.stringify({content: content});
+
+        const token = "eyJhbGciOiJIUzUxMiJ9.eyIiOjEwMDMsInVzZXJfaWQiOjIxMywidXNlcl9rZXkiOiJlYzUxODMzNjdmYTk0ODgwOGQwZjEwODEyOWVmNjgwOSIsInVzZXJuYW1lIjoi6LW16YeR6ZuoIn0.zWulXcesI1TRcDmiAHuQ9P2WHDE2l7mDmuunx13TmVl6E5Yvs8nZvu1ddtINdw0lrnnR3Q5lZaRH3mJJTaDhig";
+
+        fetch('/ai/aliDeepSeek', {
+            method: 'POST',
+            headers: {
+                'Content-Type': 'application/json',
+                'Authorization': `Bearer ${token}`
+            },
+            body: requestBody
+        })
+            .then(response => {
+                if (!response.ok) {
+                    throw new Error('Network response was not ok');
+                }
+                return response.text();
+            })
+            .then(data => {
+                document.getElementById('response').innerText = '';
+
+                // 解析数据,提取 reasoningContent
+                const lines = data.split('\n');
+                let responseLines = [];
+
+                for (let line of lines) {
+                    try {
+                        const parsedLine = JSON.parse(line.replace('data: ', '').trim());
+                        if (parsedLine.reasoningContent !== null) {
+                            responseLines.push(parsedLine.reasoningContent);
+                        }
+                    } catch (e) {
+                        console.error('Error parsing line:', e);
+                    }
+                }
+
+                // 将 reasoningContent 的内容拼接成完整字符串
+                const fullResponse = responseLines.join('');
+
+                // 逐字显示响应内容
+                let index = 0;
+                const responseElement = document.getElementById('response');
+                const interval = setInterval(() => {
+                    if (index < fullResponse.length) {
+                        responseElement.innerText += fullResponse[index];
+                        index++;
+                    } else {
+                        clearInterval(interval);
+                    }
+                }, 50);
+            })
+            .catch(error => {
+                document.getElementById('response').innerText = 'Error: ' + error.message;
+            });
+    })
+</script>
+</body>
+</html>

+ 149 - 0
service-ai/service-ai-biz/src/main/resources/static/tyqw.html

@@ -0,0 +1,149 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <title>小天-AI</title>
+    <style>
+        body {
+            font-family: 'Roboto', Arial, sans-serif;
+            margin: 0;
+            padding: 0;
+            background-color: #f4f4f9;
+            display: flex;
+            justify-content: center;
+            align-items: center;
+            height: 100vh;
+        }
+        .container {
+            width: 500px;
+            background-color: #fff;
+            box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
+            border-radius: 8px;
+            overflow: hidden;
+        }
+        h1 {
+            background-color: #007bff;
+            color: #fff;
+            text-align: center;
+            padding: 15px;
+            margin: 0;
+            font-size: 1.5em;
+        }
+        form {
+            padding: 20px;
+        }
+        label {
+            display: block;
+            margin-bottom: 10px;
+            font-weight: bold;
+        }
+        textarea {
+            width: 100%;
+            height: 100px;
+            border: 1px solid #ccc;
+            border-radius: 4px;
+            padding: 10px;
+            resize: none;
+        }
+        button {
+            width: 100%;
+            padding: 10px;
+            background-color: #007bff;
+            color: #fff;
+            border: none;
+            border-radius: 4px;
+            cursor: pointer;
+            transition: background-color 0.3s ease;
+        }
+        button:hover {
+            background-color: #0056b3;
+        }
+        #response {
+            margin-top: 20px;
+            padding: 10px;
+            background-color: #f9f9f9;
+            border-top: 1px solid #ccc;
+            max-height: 200px;
+            overflow-y: auto;
+            font-family: monospace;
+            white-space: pre-wrap;
+        }
+    </style>
+</head>
+<body>
+<div class="container">
+    <h1>小天-AI</h1>
+    <form id="chatForm">
+        <label for="content">你的问题:</label>
+        <textarea id="content" name="content" placeholder="请输入你的问题在这里..."></textarea>
+        <button type="submit">发送</button>
+    </form>
+    <div id="response"></div>
+    <div id="sessionId" style="margin-top: 10px; font-size: 0.9em; color: #666;"></div>
+</div>
+
+<script>
+    document.getElementById('chatForm').addEventListener('submit', function(event) {
+        event.preventDefault();
+
+        const content = document.getElementById('content').value;
+
+        const requestBody = JSON.stringify({content: content});
+
+        const token = "eyJhbGciOiJIUzUxMiJ9.eyIiOjEwMDMsInVzZXJfaWQiOjIxMywidXNlcl9rZXkiOiJlYzUxODMzNjdmYTk0ODgwOGQwZjEwODEyOWVmNjgwOSIsInVzZXJuYW1lIjoi6LW16YeR6ZuoIn0.zWulXcesI1TRcDmiAHuQ9P2WHDE2l7mDmuunx13TmVl6E5Yvs8nZvu1ddtINdw0lrnnR3Q5lZaRH3mJJTaDhig";
+
+        fetch('/ai/aliTyqw', {
+            method: 'POST',
+            headers: {
+                'Content-Type': 'application/json',
+                'Authorization': `Bearer ${token}`
+            },
+            body: requestBody
+        })
+            .then(response => {
+                if (!response.ok) {
+                    throw new Error('Network response was not ok');
+                }
+                return response.text();
+            })
+            .then(data => {
+                document.getElementById('response').innerText = '';
+
+                // 解析数据,提取 reasoningContent
+                const lines = data.split('\n');
+                let responseLines = [];
+
+                for (let line of lines) {
+                    try {
+                        const parsedLine = JSON.parse(line.replace('data: ', '').trim());
+                        if (parsedLine.reasoningContent !== null) {
+                            responseLines.push(parsedLine.reasoningContent);
+                        }
+                    } catch (e) {
+                        console.error('Error parsing line:', e);
+                    }
+                }
+
+                // 将 reasoningContent 的内容拼接成完整字符串
+                const fullResponse = responseLines.join('');
+
+                // 逐字显示响应内容
+                let index = 0;
+                const responseElement = document.getElementById('response');
+                const interval = setInterval(() => {
+                    if (index < fullResponse.length) {
+                        responseElement.innerText += fullResponse[index];
+                        index++;
+                    } else {
+                        clearInterval(interval);
+                    }
+                }, 50);
+            })
+            .catch(error => {
+                document.getElementById('response').innerText = 'Error: ' + error.message;
+            });
+    })
+</script>
+</body>
+</html>