Browse Source

feat:完成数据库建模,新增获历史对话接口,新增获取对话消息接口

yangyi 5 months ago
parent
commit
a528dc5cab
21 changed files with 508 additions and 67 deletions
  1. 28 0
      ruoyi-modules/ruoyi-ai/src/main/java/org/dromara/ai/controller/AIController.java
  2. 20 0
      ruoyi-modules/ruoyi-ai/src/main/java/org/dromara/ai/controller/ConversationController.java
  3. 22 0
      ruoyi-modules/ruoyi-ai/src/main/java/org/dromara/ai/controller/MessageController.java
  4. 0 56
      ruoyi-modules/ruoyi-ai/src/main/java/org/dromara/ai/controller/TestController.java
  5. 60 0
      ruoyi-modules/ruoyi-ai/src/main/java/org/dromara/ai/domain/Conversation.java
  6. 31 0
      ruoyi-modules/ruoyi-ai/src/main/java/org/dromara/ai/domain/DTO/MessageDTO.java
  7. 42 0
      ruoyi-modules/ruoyi-ai/src/main/java/org/dromara/ai/domain/Message.java
  8. 8 2
      ruoyi-modules/ruoyi-ai/src/main/java/org/dromara/ai/domain/MessageContext.java
  9. 27 0
      ruoyi-modules/ruoyi-ai/src/main/java/org/dromara/ai/domain/VO/ConversationVO.java
  10. 48 0
      ruoyi-modules/ruoyi-ai/src/main/java/org/dromara/ai/domain/VO/MessageVO.java
  11. 27 9
      ruoyi-modules/ruoyi-ai/src/main/java/org/dromara/ai/handler/AIHandlerImpl.java
  12. 7 0
      ruoyi-modules/ruoyi-ai/src/main/java/org/dromara/ai/mapper/ConversationMapper.java
  13. 7 0
      ruoyi-modules/ruoyi-ai/src/main/java/org/dromara/ai/mapper/MessageMapper.java
  14. 8 0
      ruoyi-modules/ruoyi-ai/src/main/java/org/dromara/ai/service/AIService.java
  15. 10 0
      ruoyi-modules/ruoyi-ai/src/main/java/org/dromara/ai/service/ConversationService.java
  16. 12 0
      ruoyi-modules/ruoyi-ai/src/main/java/org/dromara/ai/service/MessageService.java
  17. 76 0
      ruoyi-modules/ruoyi-ai/src/main/java/org/dromara/ai/service/service/AIServiceImpl.java
  18. 24 0
      ruoyi-modules/ruoyi-ai/src/main/java/org/dromara/ai/service/service/ConversationServiceImpl.java
  19. 37 0
      ruoyi-modules/ruoyi-ai/src/main/java/org/dromara/ai/service/service/MessageServiceImpl.java
  20. 7 0
      ruoyi-modules/ruoyi-ai/src/main/resources/mapper/MessageMapper.xml
  21. 7 0
      ruoyi-modules/ruoyi-ai/src/main/resources/mapper/conversationMapper.xml

+ 28 - 0
ruoyi-modules/ruoyi-ai/src/main/java/org/dromara/ai/controller/AIController.java

@@ -0,0 +1,28 @@
+package org.dromara.ai.controller;
+
+import lombok.RequiredArgsConstructor;
+import org.dromara.ai.domain.DTO.MessageDTO;
+import org.dromara.ai.domain.Message;
+import org.dromara.ai.service.AIService;
+import org.springframework.beans.BeanUtils;
+import org.springframework.http.MediaType;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
+
+import java.nio.charset.StandardCharsets;
+
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/ai")
+public class AIController {
+    private final AIService aiService;
+    @GetMapping(produces = MediaType.TEXT_EVENT_STREAM_VALUE)
+    public SseEmitter streamAnswer(MessageDTO messageDTO){
+        Message message = new Message();
+        BeanUtils.copyProperties(messageDTO,message);
+        message.setContent(messageDTO.getContent().getBytes(StandardCharsets.UTF_8));
+        return aiService.answer(message);
+    }
+}

+ 20 - 0
ruoyi-modules/ruoyi-ai/src/main/java/org/dromara/ai/controller/ConversationController.java

@@ -0,0 +1,20 @@
+package org.dromara.ai.controller;
+
+import lombok.RequiredArgsConstructor;
+import org.dromara.ai.service.ConversationService;
+import org.dromara.common.core.domain.R;
+import org.dromara.common.mybatis.core.page.PageQuery;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/ai/conversation")
+public class ConversationController {
+    private final ConversationService conversationService;
+    @GetMapping("/history")
+    public R history(PageQuery pageQuery){
+        return conversationService.history(pageQuery);
+    }
+}

+ 22 - 0
ruoyi-modules/ruoyi-ai/src/main/java/org/dromara/ai/controller/MessageController.java

@@ -0,0 +1,22 @@
+package org.dromara.ai.controller;
+
+import lombok.RequiredArgsConstructor;
+import org.dromara.ai.domain.VO.MessageVO;
+import org.dromara.ai.service.MessageService;
+import org.dromara.common.core.domain.R;
+import org.dromara.common.mybatis.core.page.PageQuery;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+@RequiredArgsConstructor
+@RequestMapping("/ai/message")
+public class MessageController {
+    private final MessageService messageService;
+    @GetMapping
+    public R<TableDataInfo<MessageVO>> getMessage(PageQuery pageQuery, Long conversationId) {
+        return messageService.getMessage(pageQuery,conversationId);
+    }
+}

+ 0 - 56
ruoyi-modules/ruoyi-ai/src/main/java/org/dromara/ai/controller/TestController.java

@@ -1,56 +0,0 @@
-package org.dromara.ai.controller;
-
-import lombok.RequiredArgsConstructor;
-import lombok.extern.slf4j.Slf4j;
-import org.dromara.ai.domain.MessageContext;
-import org.dromara.ai.handler.AIHandler;
-import org.dromara.common.core.domain.R;
-import org.springframework.http.MediaType;
-import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
-import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
-
-import java.io.IOException;
-
-@Slf4j
-@RequiredArgsConstructor
-@RestController
-@RequestMapping("/ai")
-public class TestController {
-    private final AIHandler aiHandler;
-    private final ThreadPoolTaskExecutor taskExecutor;
-    @GetMapping("/get")
-    public R test(String message) {
-        return R.ok(aiHandler.handle(MessageContext.createDefault(message)));
-    }
-
-    @GetMapping(value = "/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
-    public SseEmitter streamData(String message) {
-        // 创建 SSE 发射器,设置超时时间(例如 1 分钟)
-        SseEmitter emitter = new SseEmitter(60_000L);
-        emitter.onCompletion(()->{
-            log.info("sse 完成");
-        });
-        emitter.onError(e ->{
-            log.error("sse 出错",e);
-            try {
-                emitter.send("服务器发生错误");
-            } catch (IOException ex) {
-                log.error("SSE 推动数据发生错误");
-                throw new RuntimeException(ex);
-            }
-            emitter.completeWithError(e);
-        });
-        emitter.onTimeout(()->{
-            log.info("sse 超时");
-            emitter.complete();
-        });
-        // 创建新线程,防止主程序阻塞
-        taskExecutor.execute(()->{
-            aiHandler.handle(MessageContext.createDefault(message),emitter);
-        });
-        return emitter;
-    }
-}

+ 60 - 0
ruoyi-modules/ruoyi-ai/src/main/java/org/dromara/ai/domain/Conversation.java

@@ -0,0 +1,60 @@
+package org.dromara.ai.domain;
+
+import cn.hutool.core.util.IdUtil;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+/**
+ * 对话实体
+ */
+@TableName("ai_conversation")
+@Data
+public class Conversation {
+    public Conversation(){
+        this.id = IdUtil.getSnowflakeNextId();
+        this.title = "未命名的对话" + System.currentTimeMillis();
+    }
+    public Conversation(Long id,String title){
+        this.id = id;
+        this.title = title;
+    }
+    {
+//        this.messages = new ArrayList<>();
+        this.timestamp = LocalDateTime.now();
+    }
+    /**
+     * 对话id
+     */
+    private Long id;
+    /**
+     * 用户ID
+     */
+    private Long userId;
+    /**
+     * 对话标题
+     */
+    private String title;
+    /**
+     * 对话信息列表
+     */
+//    private List<Message> messages;
+    /**
+     * 创建时间
+     */
+    private LocalDateTime timestamp;
+    /**
+     * 删除标志
+     * 0:未删除
+     * 1:已删除
+     */
+    private Integer delFlag = 0;
+
+//    public void addMessage(Message message) {
+//        if (Objects.isNull(messages)) {
+//            this.messages = new ArrayList<>();
+//        }
+//        messages.add(message);
+//    }
+}

+ 31 - 0
ruoyi-modules/ruoyi-ai/src/main/java/org/dromara/ai/domain/DTO/MessageDTO.java

@@ -0,0 +1,31 @@
+package org.dromara.ai.domain.DTO;
+
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+@Data
+public class MessageDTO {
+    /**
+     * 消息ID
+     */
+    private Long id;
+    /**
+     * 会话ID
+     */
+    private Long conversationId;
+    /**
+     * 消息内容
+     */
+    private String content;
+    /**
+     * 消息角色
+     * 1:用户(user)
+     * 2:助手(assistant)
+     */
+    private Integer role;
+    /**
+     * 消息时间戳
+     */
+    private LocalDateTime timestamp;
+}

+ 42 - 0
ruoyi-modules/ruoyi-ai/src/main/java/org/dromara/ai/domain/Message.java

@@ -0,0 +1,42 @@
+package org.dromara.ai.domain;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+/**
+ * 消息实体
+ */
+@TableName("ai_message")
+@Data
+public class Message{
+    /**
+     * 消息ID
+     */
+    private Long id;
+    /**
+     * 会话ID
+     */
+    private Long conversationId;
+    /**
+     * 消息内容
+     */
+    private byte[] content;
+    /**
+     * 消息角色
+     * 1:用户(user)
+     * 2:助手(assistant)
+     */
+    private Integer role = 1;
+    /**
+     * 删除标志
+     * 0:未删除
+     * 1:已删除
+     */
+    private Integer delFlag = 0;
+    /**
+     * 消息时间戳
+     */
+    private LocalDateTime timestamp = LocalDateTime.now();
+}

+ 8 - 2
ruoyi-modules/ruoyi-ai/src/main/java/org/dromara/ai/domain/MessageContext.java

@@ -1,6 +1,7 @@
 package org.dromara.ai.domain;
 
 import lombok.Data;
+import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
 
 import java.util.List;
 
@@ -16,7 +17,7 @@ public class MessageContext {
     /**
      * 信息内容
      */
-    private String message;
+    private Message message;
     /**
      * 状态
      */
@@ -41,8 +42,13 @@ public class MessageContext {
      * 最终的结果
      */
     private Object result;
+    /**
+     * sseEmitter
+     * SSE推流对象
+     */
+    private SseEmitter sseEmitter;
 
-    public static MessageContext createDefault(String message){
+    public static MessageContext createDefault(Message message){
         MessageContext messageContext = new MessageContext();
         messageContext.setMessage(message);
         return messageContext;

+ 27 - 0
ruoyi-modules/ruoyi-ai/src/main/java/org/dromara/ai/domain/VO/ConversationVO.java

@@ -0,0 +1,27 @@
+package org.dromara.ai.domain.VO;
+
+import lombok.Data;
+
+import java.time.LocalDateTime;
+import java.util.List;
+
+@Data
+public class ConversationVO {
+    /**
+     * 对话id
+     */
+    private Long id;
+    /**
+     * 对话标题
+     */
+    private String title;
+    /**
+     * 对话信息列表
+     */
+    private List<MessageVO> messages;
+    /**
+     * 创建时间
+     */
+    private LocalDateTime timestamp;
+
+}

+ 48 - 0
ruoyi-modules/ruoyi-ai/src/main/java/org/dromara/ai/domain/VO/MessageVO.java

@@ -0,0 +1,48 @@
+package org.dromara.ai.domain.VO;
+
+import lombok.Data;
+
+import java.time.LocalDateTime;
+@Data
+public class MessageVO {
+    /**
+     * 消息ID
+     */
+    private Long id;
+    /**
+     * 会话ID
+     */
+    private Long conversationId;
+    /**
+     * 消息内容
+     */
+    private String content;
+    /**
+     * 消息角色
+     * 1:用户(user)
+     * 2:助手(assistant)
+     */
+    private Integer role = 1;
+    /**
+     * 消息时间戳
+     */
+    private LocalDateTime timestamp = LocalDateTime.now();
+    /**
+     * 状态,表示处理中的流程
+     * 1:正在分析涉及的数据表
+     * 2:正在组织sql
+     * 3:正在查询数据
+     * 4:正在分析数据
+     * 0:表示最终结果
+     */
+    private Integer status = -1;
+
+    public MessageVO(int status, String content) {
+        this.status = status;
+        this.content = content;
+    }
+
+    public MessageVO() {
+
+    }
+}

+ 27 - 9
ruoyi-modules/ruoyi-ai/src/main/java/org/dromara/ai/handler/AIHandlerImpl.java

@@ -1,13 +1,18 @@
 package org.dromara.ai.handler;
 
+import cn.hutool.core.util.IdUtil;
 import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
+import org.dromara.ai.domain.Message;
 import org.dromara.ai.domain.MessageContext;
+import org.dromara.ai.domain.VO.MessageVO;
 import org.dromara.ai.domain.deepseek.DeepSeekHttpResponseData;
+import org.dromara.ai.service.MessageService;
 import org.dromara.ai.util.DeepSeekAIUtil;
 import org.dromara.common.redis.utils.RedisUtils;
+import org.springframework.beans.BeanUtils;
 import org.springframework.jdbc.core.JdbcTemplate;
 import org.springframework.stereotype.Component;
 import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
@@ -22,6 +27,7 @@ import java.util.*;
 public class AIHandlerImpl implements AIHandler {
     private final ObjectMapper objectMapper;
     private final JdbcTemplate jdbcTemplate;
+    private final MessageService messageService;
     @Override
     public List<String> getTableNames(MessageContext messageContext) {
         messageContext.setStatus(1);
@@ -35,7 +41,7 @@ public class AIHandlerImpl implements AIHandler {
         for (String tableName : tableNamesList) {
             tableNamesStringBuilder.append(tableName).append("\n");
         }
-        String prompt = new StringBuilder("用户需求:\n").append(messageContext.getMessage())
+        String prompt = new StringBuilder("用户需求:\n").append(messageContext.getMessage().getContent())
             .append("\n")
             .append(tableNamesStringBuilder)
             .append("请你根据用户需求和现有的数据表,选择满足用户需求的数据表,返回数据表的名称,以json的格式返回{'tableNames':[]}").toString();
@@ -77,7 +83,7 @@ public class AIHandlerImpl implements AIHandler {
         }
         String struct = structBuilder.toString();
         String prompt = new StringBuilder("用户需求:\n")
-            .append(messageContext.getMessage())
+            .append(messageContext.getMessage().getContent())
             .append("涉及的表和结构如下:\n")
             .append(struct)
             .append("\n")
@@ -128,7 +134,7 @@ public class AIHandlerImpl implements AIHandler {
         messageContext.setStatus(4);
         String data = messageContext.getData();
         log.info("data = \n{}", data);
-        String prompt = new StringBuilder("用户需求:\n").append(messageContext.getMessage())
+        String prompt = new StringBuilder("用户需求:\n").append(messageContext.getMessage().getContent())
             .append("涉及的数据如下:\n").append(data)
             .append("\n")
 //            .append("请根据用户需求和数据给出结论(中文的),并选择表格或者图表(折线图、柱状图、饼图、热力图)将数据展示出来,表格状态为1;图标状态为2,表格或者图表使用svg格式的xml,分辨率为1920*1080,以json的格式返回.{'conclusion':'','data':'','status':0}").toString();
@@ -154,25 +160,37 @@ public class AIHandlerImpl implements AIHandler {
 
     @Override
     public void handle(MessageContext messageContext, SseEmitter emitter) {
+        //todo:优化逻辑,提供更丰富的上下文
         try {
             //获取涉及的表名,通过LLM
-            emitter.send("1正在分析涉及的数据表");
+            emitter.send(objectMapper.writeValueAsString(new MessageVO(1,"正在分析涉及的数据表")));
             List<String> tableNames = getTableNames(messageContext);
             //获取sql,通过LLM
-            emitter.send("2正在组织sql");
+            emitter.send(objectMapper.writeValueAsString(new MessageVO(2,"正在组织sql")));
             String sql = getSQL(messageContext);
             //查数据,通过JDBCTemplate
-            emitter.send("3正在查询数据");
+            emitter.send(objectMapper.writeValueAsString(new MessageVO(3,"正在查询数据")));
             String data = getData(messageContext);
             //处理数据,通过LLM
-            emitter.send("4正在处理数据");
+            emitter.send(objectMapper.writeValueAsString(new MessageVO(4,"正在处理数据")));
             Object object = dataHandler(messageContext);
-            emitter.send(object);
+            //构建ai的相应消息
+            Message message = new Message();
+            message.setId(IdUtil.getSnowflakeNextId());
+            message.setConversationId(messageContext.getMessage().getConversationId());
+            message.setRole(2);
+            message.setTimestamp(LocalDateTime.now());
+            message.setContent(object.toString().getBytes(StandardCharsets.UTF_8));
+            messageService.save(message);
+            MessageVO messageVO = new MessageVO(0,"");
+            BeanUtils.copyProperties(message, messageVO);
+            emitter.send(messageVO);
         } catch (IOException e) {
             log.error("sseEmitter error: {}", e.getMessage());
             throw new RuntimeException(e);
+        }finally {
+            emitter.complete();
         }
-        emitter.complete();
     }
 
     /**

+ 7 - 0
ruoyi-modules/ruoyi-ai/src/main/java/org/dromara/ai/mapper/ConversationMapper.java

@@ -0,0 +1,7 @@
+package org.dromara.ai.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.dromara.ai.domain.Conversation;
+
+public interface ConversationMapper extends BaseMapper<Conversation> {
+}

+ 7 - 0
ruoyi-modules/ruoyi-ai/src/main/java/org/dromara/ai/mapper/MessageMapper.java

@@ -0,0 +1,7 @@
+package org.dromara.ai.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.dromara.ai.domain.Message;
+
+public interface MessageMapper extends BaseMapper<Message> {
+}

+ 8 - 0
ruoyi-modules/ruoyi-ai/src/main/java/org/dromara/ai/service/AIService.java

@@ -0,0 +1,8 @@
+package org.dromara.ai.service;
+
+import org.dromara.ai.domain.Message;
+import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
+
+public interface AIService {
+    SseEmitter answer(Message message);
+}

+ 10 - 0
ruoyi-modules/ruoyi-ai/src/main/java/org/dromara/ai/service/ConversationService.java

@@ -0,0 +1,10 @@
+package org.dromara.ai.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import org.dromara.ai.domain.Conversation;
+import org.dromara.common.core.domain.R;
+import org.dromara.common.mybatis.core.page.PageQuery;
+
+public interface ConversationService extends IService<Conversation> {
+    R history(PageQuery pageQuery);
+}

+ 12 - 0
ruoyi-modules/ruoyi-ai/src/main/java/org/dromara/ai/service/MessageService.java

@@ -0,0 +1,12 @@
+package org.dromara.ai.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import org.dromara.ai.domain.Message;
+import org.dromara.ai.domain.VO.MessageVO;
+import org.dromara.common.core.domain.R;
+import org.dromara.common.mybatis.core.page.PageQuery;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+
+public interface MessageService extends IService<Message> {
+    R<TableDataInfo<MessageVO>> getMessage(PageQuery pageQuery, Long conversationId);
+}

+ 76 - 0
ruoyi-modules/ruoyi-ai/src/main/java/org/dromara/ai/service/service/AIServiceImpl.java

@@ -0,0 +1,76 @@
+package org.dromara.ai.service.service;
+
+import cn.hutool.core.util.IdUtil;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.dromara.ai.domain.Conversation;
+import org.dromara.ai.domain.Message;
+import org.dromara.ai.domain.MessageContext;
+import org.dromara.ai.handler.AIHandler;
+import org.dromara.ai.service.AIService;
+import org.dromara.ai.service.ConversationService;
+import org.dromara.ai.service.MessageService;
+import org.dromara.common.satoken.utils.LoginHelper;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
+
+import java.io.IOException;
+import java.time.LocalDateTime;
+import java.util.Objects;
+
+@Slf4j
+@RequiredArgsConstructor
+@Service
+public class AIServiceImpl implements AIService {
+    private final AIHandler aiHandler;
+    private final ThreadPoolTaskExecutor taskExecutor;
+    private final ConversationService conversationService;
+    private final MessageService messageService;
+    @Transactional
+    @Override
+    public SseEmitter answer(Message message) {
+        message.setId(IdUtil.getSnowflakeNextId());
+        message.setRole(1);
+        Long conversationId = message.getConversationId();
+        // 如果没有会话id,则创建新的会话
+        if (Objects.isNull(conversationId) || conversationId < 0L) {
+            conversationId = IdUtil.getSnowflakeNextId();
+            Conversation conversation = new Conversation();
+            conversation.setId(conversationId);
+            conversation.setUserId(LoginHelper.getUserId());
+            conversationService.save(conversation);
+        }
+
+        message.setConversationId(conversationId);
+        message.setTimestamp(LocalDateTime.now());
+        messageService.save(message);
+        SseEmitter emitter = new SseEmitter(100_000L);
+        emitter.onCompletion(()->{
+            log.info("sse 完成");
+        });
+        emitter.onError(e ->{
+            log.error("sse 出错",e);
+            try {
+                emitter.send("服务器发生错误");
+            } catch (IOException ex) {
+                log.error("SSE 推动数据发生错误");
+                throw new RuntimeException(ex);
+            }finally {
+                emitter.completeWithError(e);
+            }
+        });
+        emitter.onTimeout(()->{
+            log.info("sse 超时");
+            emitter.complete();
+        });
+        // 创建新线程,防止主程序阻塞
+        taskExecutor.execute(()->{
+            MessageContext context = MessageContext.createDefault(message);
+            context.setSseEmitter(emitter);
+            aiHandler.handle(context,emitter);
+        });
+        return emitter;
+    }
+}

+ 24 - 0
ruoyi-modules/ruoyi-ai/src/main/java/org/dromara/ai/service/service/ConversationServiceImpl.java

@@ -0,0 +1,24 @@
+package org.dromara.ai.service.service;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import org.dromara.ai.domain.Conversation;
+import org.dromara.ai.mapper.ConversationMapper;
+import org.dromara.ai.service.ConversationService;
+import org.dromara.common.core.domain.R;
+import org.dromara.common.mybatis.core.page.PageQuery;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.common.satoken.utils.LoginHelper;
+import org.springframework.stereotype.Service;
+
+@Service
+public class ConversationServiceImpl extends ServiceImpl<ConversationMapper, Conversation> implements ConversationService {
+    @Override
+    public R history(PageQuery pageQuery) {
+        Long userId = LoginHelper.getUserId();
+        LambdaQueryWrapper<Conversation> queryWrapper = new LambdaQueryWrapper<Conversation>().eq(Conversation::getUserId, userId).orderByDesc(Conversation::getTimestamp);
+        Page<Conversation> page = page(pageQuery.build(), queryWrapper);
+        return R.ok(TableDataInfo.build(page));
+    }
+}

+ 37 - 0
ruoyi-modules/ruoyi-ai/src/main/java/org/dromara/ai/service/service/MessageServiceImpl.java

@@ -0,0 +1,37 @@
+package org.dromara.ai.service.service;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import org.dromara.ai.domain.Message;
+import org.dromara.ai.domain.VO.MessageVO;
+import org.dromara.ai.mapper.MessageMapper;
+import org.dromara.ai.service.MessageService;
+import org.dromara.common.core.domain.R;
+import org.dromara.common.mybatis.core.page.PageQuery;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.springframework.beans.BeanUtils;
+import org.springframework.stereotype.Service;
+
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+import java.util.stream.Collectors;
+
+@Service
+public class MessageServiceImpl extends ServiceImpl<MessageMapper, Message> implements MessageService {
+    @Override
+    public R<TableDataInfo<MessageVO>> getMessage(PageQuery pageQuery, Long conversationId) {
+        LambdaQueryWrapper<Message> queryWrapper = new QueryWrapper<Message>().lambda().eq(Message::getConversationId, conversationId).orderByDesc(Message::getTimestamp);
+        Page<Message> page = page(pageQuery.build(), queryWrapper);
+        List<MessageVO> messageVOList = page.getRecords().stream()
+            .map(message -> {
+                MessageVO messageVO = new MessageVO();
+                BeanUtils.copyProperties(message, messageVO);
+                messageVO.setContent(new String(message.getContent(), StandardCharsets.UTF_8));
+                return messageVO;
+            }).collect((Collectors.toList()));
+        TableDataInfo<MessageVO> tableDataInfo = TableDataInfo.build(messageVOList);
+        return R.ok(tableDataInfo);
+    }
+}

+ 7 - 0
ruoyi-modules/ruoyi-ai/src/main/resources/mapper/MessageMapper.xml

@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+    PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+    "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="org.dromara.ai.mapper.MessageMapper">
+
+</mapper>

+ 7 - 0
ruoyi-modules/ruoyi-ai/src/main/resources/mapper/conversationMapper.xml

@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+    PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+    "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="org.dromara.ai.mapper.ConversationMapper">
+
+</mapper>