From c2f6a8321a0fa9296818ca89d0f2ab9a53cc93fe Mon Sep 17 00:00:00 2001
From: ageer <ageerle@163.com>
Date: 星期六, 03 五月 2025 10:41:50 +0800
Subject: [PATCH] feat: 会话管理

---
 ruoyi-modules-api/ruoyi-chat-api/src/main/java/org/ruoyi/mapper/ChatSessionMapper.java            |   15 ++
 ruoyi-modules-api/ruoyi-chat-api/src/main/java/org/ruoyi/domain/bo/ChatMessageBo.java             |    5 
 ruoyi-modules-api/ruoyi-chat-api/src/main/java/org/ruoyi/domain/ChatMessage.java                  |    5 
 ruoyi-modules-api/ruoyi-chat-api/src/main/java/org/ruoyi/domain/vo/ChatSessionVo.java             |   59 ++++++++
 ruoyi-common/ruoyi-common-chat/src/main/java/org/ruoyi/common/chat/request/ChatRequest.java       |    6 
 ruoyi-modules-api/ruoyi-chat-api/src/main/java/org/ruoyi/service/impl/ChatSessionServiceImpl.java |  111 +++++++++++++++
 ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/chat/impl/SseServiceImpl.java       |   31 ++++
 ruoyi-modules-api/ruoyi-chat-api/src/main/java/org/ruoyi/domain/vo/ChatMessageVo.java             |    5 
 ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/chat/impl/ChatCostServiceImpl.java  |    2 
 ruoyi-modules-api/ruoyi-chat-api/src/main/java/org/ruoyi/domain/ChatSession.java                  |   51 +++++++
 ruoyi-modules-api/ruoyi-chat-api/src/main/java/org/ruoyi/domain/bo/ChatSessionBo.java             |   54 +++++++
 ruoyi-modules-api/ruoyi-chat-api/src/main/java/org/ruoyi/service/IChatSessionService.java         |   48 ++++++
 12 files changed, 392 insertions(+), 0 deletions(-)

diff --git a/ruoyi-common/ruoyi-common-chat/src/main/java/org/ruoyi/common/chat/request/ChatRequest.java b/ruoyi-common/ruoyi-common-chat/src/main/java/org/ruoyi/common/chat/request/ChatRequest.java
index 0172fa5..c611c74 100644
--- a/ruoyi-common/ruoyi-common-chat/src/main/java/org/ruoyi/common/chat/request/ChatRequest.java
+++ b/ruoyi-common/ruoyi-common-chat/src/main/java/org/ruoyi/common/chat/request/ChatRequest.java
@@ -56,6 +56,12 @@
      */
     private Long userId;
 
+
+    /**
+     * 浼氳瘽id
+     */
+    private Long sessionId;
+
     /**
      * 搴旂敤ID
      */
diff --git a/ruoyi-modules-api/ruoyi-chat-api/src/main/java/org/ruoyi/domain/ChatMessage.java b/ruoyi-modules-api/ruoyi-chat-api/src/main/java/org/ruoyi/domain/ChatMessage.java
index 691f98f..a139b7b 100644
--- a/ruoyi-modules-api/ruoyi-chat-api/src/main/java/org/ruoyi/domain/ChatMessage.java
+++ b/ruoyi-modules-api/ruoyi-chat-api/src/main/java/org/ruoyi/domain/ChatMessage.java
@@ -35,6 +35,11 @@
     private Long userId;
 
     /**
+     * 浼氳瘽id
+     */
+    private Long sessionId;
+
+    /**
      * 娑堟伅鍐呭
      */
     private String content;
diff --git a/ruoyi-modules-api/ruoyi-chat-api/src/main/java/org/ruoyi/domain/ChatSession.java b/ruoyi-modules-api/ruoyi-chat-api/src/main/java/org/ruoyi/domain/ChatSession.java
new file mode 100644
index 0000000..3271141
--- /dev/null
+++ b/ruoyi-modules-api/ruoyi-chat-api/src/main/java/org/ruoyi/domain/ChatSession.java
@@ -0,0 +1,51 @@
+package org.ruoyi.domain;
+
+import com.baomidou.mybatisplus.annotation.*;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.ruoyi.core.domain.BaseEntity;
+
+import java.io.Serial;
+
+/**
+ * 浼氳瘽绠$悊瀵硅薄 chat_session
+ *
+ * @author ageerle
+ * @date 2025-05-03
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@TableName("chat_session")
+public class ChatSession extends BaseEntity {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 涓婚敭
+     */
+    @TableId(value = "id")
+    private Long id;
+
+    /**
+     * 鐢ㄦ埛id
+     */
+    private Long userId;
+
+    /**
+     * 浼氳瘽鏍囬
+     */
+    private String sessionTitle;
+
+    /**
+     * 浼氳瘽鍐呭
+     */
+    private String sessionContent;
+
+    /**
+     * 澶囨敞
+     */
+    private String remark;
+
+
+}
diff --git a/ruoyi-modules-api/ruoyi-chat-api/src/main/java/org/ruoyi/domain/bo/ChatMessageBo.java b/ruoyi-modules-api/ruoyi-chat-api/src/main/java/org/ruoyi/domain/bo/ChatMessageBo.java
index 484d041..012d526 100644
--- a/ruoyi-modules-api/ruoyi-chat-api/src/main/java/org/ruoyi/domain/bo/ChatMessageBo.java
+++ b/ruoyi-modules-api/ruoyi-chat-api/src/main/java/org/ruoyi/domain/bo/ChatMessageBo.java
@@ -41,6 +41,11 @@
     private String content;
 
     /**
+     * 浼氳瘽id
+     */
+    private Long sessionId;
+
+    /**
      * 瀵硅瘽瑙掕壊
      */
     @NotBlank(message = "瀵硅瘽瑙掕壊涓嶈兘涓虹┖", groups = { AddGroup.class, EditGroup.class })
diff --git a/ruoyi-modules-api/ruoyi-chat-api/src/main/java/org/ruoyi/domain/bo/ChatSessionBo.java b/ruoyi-modules-api/ruoyi-chat-api/src/main/java/org/ruoyi/domain/bo/ChatSessionBo.java
new file mode 100644
index 0000000..a6b23ba
--- /dev/null
+++ b/ruoyi-modules-api/ruoyi-chat-api/src/main/java/org/ruoyi/domain/bo/ChatSessionBo.java
@@ -0,0 +1,54 @@
+package org.ruoyi.domain.bo;
+
+import org.ruoyi.common.core.validate.AddGroup;
+import org.ruoyi.common.core.validate.EditGroup;
+import org.ruoyi.core.domain.BaseEntity;
+import io.github.linpeilie.annotations.AutoMapper;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import jakarta.validation.constraints.*;
+import org.ruoyi.domain.ChatSession;
+
+/**
+ * 浼氳瘽绠$悊涓氬姟瀵硅薄 chat_session
+ *
+ * @author ageerle
+ * @date 2025-05-03
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@AutoMapper(target = ChatSession.class, reverseConvertGenerate = false)
+public class ChatSessionBo extends BaseEntity {
+
+    /**
+     * 涓婚敭
+     */
+    @NotNull(message = "涓婚敭涓嶈兘涓虹┖", groups = { EditGroup.class })
+    private Long id;
+
+    /**
+     * 鐢ㄦ埛id
+     */
+    @NotNull(message = "鐢ㄦ埛id涓嶈兘涓虹┖", groups = { AddGroup.class, EditGroup.class })
+    private Long userId;
+
+    /**
+     * 浼氳瘽鏍囬
+     */
+    @NotBlank(message = "浼氳瘽鏍囬涓嶈兘涓虹┖", groups = { AddGroup.class, EditGroup.class })
+    private String sessionTitle;
+
+    /**
+     * 浼氳瘽鍐呭
+     */
+    @NotBlank(message = "浼氳瘽鍐呭涓嶈兘涓虹┖", groups = { AddGroup.class, EditGroup.class })
+    private String sessionContent;
+
+    /**
+     * 澶囨敞
+     */
+    @NotBlank(message = "澶囨敞涓嶈兘涓虹┖", groups = { AddGroup.class, EditGroup.class })
+    private String remark;
+
+
+}
diff --git a/ruoyi-modules-api/ruoyi-chat-api/src/main/java/org/ruoyi/domain/vo/ChatMessageVo.java b/ruoyi-modules-api/ruoyi-chat-api/src/main/java/org/ruoyi/domain/vo/ChatMessageVo.java
index 501a1b6..23d092a 100644
--- a/ruoyi-modules-api/ruoyi-chat-api/src/main/java/org/ruoyi/domain/vo/ChatMessageVo.java
+++ b/ruoyi-modules-api/ruoyi-chat-api/src/main/java/org/ruoyi/domain/vo/ChatMessageVo.java
@@ -42,6 +42,11 @@
     private Long userId;
 
     /**
+     * 浼氳瘽id
+     */
+    private Long sessionId;
+
+    /**
      * 娑堟伅鍐呭
      */
     @ExcelProperty(value = "娑堟伅鍐呭")
diff --git a/ruoyi-modules-api/ruoyi-chat-api/src/main/java/org/ruoyi/domain/vo/ChatSessionVo.java b/ruoyi-modules-api/ruoyi-chat-api/src/main/java/org/ruoyi/domain/vo/ChatSessionVo.java
new file mode 100644
index 0000000..2a25f5c
--- /dev/null
+++ b/ruoyi-modules-api/ruoyi-chat-api/src/main/java/org/ruoyi/domain/vo/ChatSessionVo.java
@@ -0,0 +1,59 @@
+package org.ruoyi.domain.vo;
+
+import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
+import com.alibaba.excel.annotation.ExcelProperty;
+import io.github.linpeilie.annotations.AutoMapper;
+import lombok.Data;
+import org.ruoyi.domain.ChatSession;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+
+
+/**
+ * 浼氳瘽绠$悊瑙嗗浘瀵硅薄 chat_session
+ *
+ * @author ageerle
+ * @date 2025-05-03
+ */
+@Data
+@ExcelIgnoreUnannotated
+@AutoMapper(target = ChatSession.class)
+public class ChatSessionVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 涓婚敭
+     */
+    @ExcelProperty(value = "涓婚敭")
+    private Long id;
+
+    /**
+     * 鐢ㄦ埛id
+     */
+    @ExcelProperty(value = "鐢ㄦ埛id")
+    private Long userId;
+
+    /**
+     * 浼氳瘽鏍囬
+     */
+    @ExcelProperty(value = "浼氳瘽鏍囬")
+    private String sessionTitle;
+
+    /**
+     * 浼氳瘽鍐呭
+     */
+    @ExcelProperty(value = "浼氳瘽鍐呭")
+    private String sessionContent;
+
+    /**
+     * 澶囨敞
+     */
+    @ExcelProperty(value = "澶囨敞")
+    private String remark;
+
+
+}
diff --git a/ruoyi-modules-api/ruoyi-chat-api/src/main/java/org/ruoyi/mapper/ChatSessionMapper.java b/ruoyi-modules-api/ruoyi-chat-api/src/main/java/org/ruoyi/mapper/ChatSessionMapper.java
new file mode 100644
index 0000000..7833fdc
--- /dev/null
+++ b/ruoyi-modules-api/ruoyi-chat-api/src/main/java/org/ruoyi/mapper/ChatSessionMapper.java
@@ -0,0 +1,15 @@
+package org.ruoyi.mapper;
+
+import org.ruoyi.core.mapper.BaseMapperPlus;
+import org.ruoyi.domain.ChatSession;
+import org.ruoyi.domain.vo.ChatSessionVo;
+
+/**
+ * 浼氳瘽绠$悊Mapper鎺ュ彛
+ *
+ * @author ageerle
+ * @date 2025-05-03
+ */
+public interface ChatSessionMapper extends BaseMapperPlus<ChatSession, ChatSessionVo> {
+
+}
diff --git a/ruoyi-modules-api/ruoyi-chat-api/src/main/java/org/ruoyi/service/IChatSessionService.java b/ruoyi-modules-api/ruoyi-chat-api/src/main/java/org/ruoyi/service/IChatSessionService.java
new file mode 100644
index 0000000..3ce60ad
--- /dev/null
+++ b/ruoyi-modules-api/ruoyi-chat-api/src/main/java/org/ruoyi/service/IChatSessionService.java
@@ -0,0 +1,48 @@
+package org.ruoyi.service;
+
+import org.ruoyi.core.page.PageQuery;
+import org.ruoyi.core.page.TableDataInfo;
+import org.ruoyi.domain.bo.ChatSessionBo;
+import org.ruoyi.domain.vo.ChatSessionVo;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * 浼氳瘽绠$悊Service鎺ュ彛
+ *
+ * @author ageerle
+ * @date 2025-05-03
+ */
+public interface IChatSessionService {
+
+    /**
+     * 鏌ヨ浼氳瘽绠$悊
+     */
+    ChatSessionVo queryById(Long id);
+
+    /**
+     * 鏌ヨ浼氳瘽绠$悊鍒楄〃
+     */
+    TableDataInfo<ChatSessionVo> queryPageList(ChatSessionBo bo, PageQuery pageQuery);
+
+    /**
+     * 鏌ヨ浼氳瘽绠$悊鍒楄〃
+     */
+    List<ChatSessionVo> queryList(ChatSessionBo bo);
+
+    /**
+     * 鏂板浼氳瘽绠$悊
+     */
+    Boolean insertByBo(ChatSessionBo bo);
+
+    /**
+     * 淇敼浼氳瘽绠$悊
+     */
+    Boolean updateByBo(ChatSessionBo bo);
+
+    /**
+     * 鏍¢獙骞舵壒閲忓垹闄や細璇濈鐞嗕俊鎭�
+     */
+    Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
+}
diff --git a/ruoyi-modules-api/ruoyi-chat-api/src/main/java/org/ruoyi/service/impl/ChatSessionServiceImpl.java b/ruoyi-modules-api/ruoyi-chat-api/src/main/java/org/ruoyi/service/impl/ChatSessionServiceImpl.java
new file mode 100644
index 0000000..247de45
--- /dev/null
+++ b/ruoyi-modules-api/ruoyi-chat-api/src/main/java/org/ruoyi/service/impl/ChatSessionServiceImpl.java
@@ -0,0 +1,111 @@
+package org.ruoyi.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import org.ruoyi.common.core.utils.MapstructUtils;
+import org.ruoyi.common.core.utils.StringUtils;
+import org.ruoyi.core.page.TableDataInfo;
+import org.ruoyi.core.page.PageQuery;
+import lombok.RequiredArgsConstructor;
+import org.ruoyi.domain.ChatSession;
+import org.ruoyi.domain.bo.ChatSessionBo;
+import org.ruoyi.domain.vo.ChatSessionVo;
+import org.ruoyi.mapper.ChatSessionMapper;
+import org.ruoyi.service.IChatSessionService;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Collection;
+
+/**
+ * 浼氳瘽绠$悊Service涓氬姟灞傚鐞�
+ *
+ * @author ageerle
+ * @date 2025-05-03
+ */
+@RequiredArgsConstructor
+@Service
+public class ChatSessionServiceImpl implements IChatSessionService {
+
+    private final ChatSessionMapper baseMapper;
+
+    /**
+     * 鏌ヨ浼氳瘽绠$悊
+     */
+    @Override
+    public ChatSessionVo queryById(Long id){
+        return baseMapper.selectVoById(id);
+    }
+
+    /**
+     * 鏌ヨ浼氳瘽绠$悊鍒楄〃
+     */
+    @Override
+    public TableDataInfo<ChatSessionVo> queryPageList(ChatSessionBo bo, PageQuery pageQuery) {
+        LambdaQueryWrapper<ChatSession> lqw = buildQueryWrapper(bo);
+        Page<ChatSessionVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
+        return TableDataInfo.build(result);
+    }
+
+    /**
+     * 鏌ヨ浼氳瘽绠$悊鍒楄〃
+     */
+    @Override
+    public List<ChatSessionVo> queryList(ChatSessionBo bo) {
+        LambdaQueryWrapper<ChatSession> lqw = buildQueryWrapper(bo);
+        return baseMapper.selectVoList(lqw);
+    }
+
+    private LambdaQueryWrapper<ChatSession> buildQueryWrapper(ChatSessionBo bo) {
+        Map<String, Object> params = bo.getParams();
+        LambdaQueryWrapper<ChatSession> lqw = Wrappers.lambdaQuery();
+        lqw.eq(bo.getUserId() != null, ChatSession::getUserId, bo.getUserId());
+        lqw.eq(StringUtils.isNotBlank(bo.getSessionTitle()), ChatSession::getSessionTitle, bo.getSessionTitle());
+        lqw.eq(StringUtils.isNotBlank(bo.getSessionContent()), ChatSession::getSessionContent, bo.getSessionContent());
+        return lqw;
+    }
+
+    /**
+     * 鏂板浼氳瘽绠$悊
+     */
+    @Override
+    public Boolean insertByBo(ChatSessionBo bo) {
+        ChatSession add = MapstructUtils.convert(bo, ChatSession.class);
+        validEntityBeforeSave(add);
+        boolean flag = baseMapper.insert(add) > 0;
+        if (flag) {
+            bo.setId(add.getId());
+        }
+        return flag;
+    }
+
+    /**
+     * 淇敼浼氳瘽绠$悊
+     */
+    @Override
+    public Boolean updateByBo(ChatSessionBo bo) {
+        ChatSession update = MapstructUtils.convert(bo, ChatSession.class);
+        validEntityBeforeSave(update);
+        return baseMapper.updateById(update) > 0;
+    }
+
+    /**
+     * 淇濆瓨鍓嶇殑鏁版嵁鏍¢獙
+     */
+    private void validEntityBeforeSave(ChatSession entity){
+        //TODO 鍋氫竴浜涙暟鎹牎楠�,濡傚敮涓�绾︽潫
+    }
+
+    /**
+     * 鎵归噺鍒犻櫎浼氳瘽绠$悊
+     */
+    @Override
+    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
+        if(isValid){
+            //TODO 鍋氫竴浜涗笟鍔′笂鐨勬牎楠�,鍒ゆ柇鏄惁闇�瑕佹牎楠�
+        }
+        return baseMapper.deleteBatchIds(ids) > 0;
+    }
+}
diff --git a/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/chat/impl/ChatCostServiceImpl.java b/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/chat/impl/ChatCostServiceImpl.java
index 2a4e5f0..be99044 100644
--- a/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/chat/impl/ChatCostServiceImpl.java
+++ b/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/chat/impl/ChatCostServiceImpl.java
@@ -46,6 +46,7 @@
     /**
      * 鎵i櫎鐢ㄦ埛浣欓
      */
+    @Override
     public void deductToken(ChatRequest chatRequest) {
 
         int tokens = TikTokensUtil.tokens(chatRequest.getModel(), chatRequest.getPrompt());
@@ -53,6 +54,7 @@
         String modelName = chatRequest.getModel();
 
         ChatMessageBo chatMessageBo = new ChatMessageBo();
+        chatMessageBo.setSessionId(chatRequest.getSessionId());
 
         Object userId = LocalCache.CACHE.get("userId");
         if(userId!=null){
diff --git a/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/chat/impl/SseServiceImpl.java b/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/chat/impl/SseServiceImpl.java
index b6759f5..ccf9f3a 100644
--- a/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/chat/impl/SseServiceImpl.java
+++ b/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/chat/impl/SseServiceImpl.java
@@ -24,9 +24,12 @@
 import org.ruoyi.common.core.utils.file.FileUtils;
 import org.ruoyi.common.core.utils.file.MimeTypeUtils;
 import org.ruoyi.common.redis.utils.RedisUtils;
+import org.ruoyi.domain.ChatSession;
+import org.ruoyi.domain.bo.ChatSessionBo;
 import org.ruoyi.domain.vo.ChatModelVo;
 import org.ruoyi.service.EmbeddingService;
 import org.ruoyi.service.IChatModelService;
+import org.ruoyi.service.IChatSessionService;
 import org.ruoyi.service.VectorStoreService;
 import org.springframework.core.io.InputStreamResource;
 import org.springframework.core.io.Resource;
@@ -65,6 +68,8 @@
 
     private final OllamaServiceImpl ollamaService;
 
+    private final IChatSessionService chatSessionService;
+
     private ChatModelVo chatModelVo;
 
 
@@ -80,6 +85,15 @@
             }else {
                 LocalCache.CACHE.put("userId", chatCostService.getUserId());
                 chatRequest.setUserId(chatCostService.getUserId());
+                // 淇濆瓨浼氳瘽淇℃伅
+                if(chatRequest.getSessionId()==null){
+                    ChatSessionBo chatSessionBo = new ChatSessionBo();
+                    chatSessionBo.setUserId(chatCostService.getUserId());
+                    chatSessionBo.setSessionTitle(getFirst10Characters(chatRequest.getPrompt()));
+                    chatSessionBo.setSessionContent(chatRequest.getPrompt());
+                    chatSessionService.insertByBo(chatSessionBo);
+                    chatRequest.setSessionId(chatSessionBo.getId());
+                }
                 // 淇濆瓨娑堟伅璁板綍 骞舵墸闄よ垂鐢�
                 chatCostService.deductToken(chatRequest);
             }
@@ -93,6 +107,23 @@
     }
 
     /**
+     * 鑾峰彇瀵硅瘽鏍囬
+     *
+     * @param str 鍘熷瓧绗�
+     * @return 鎴彇鍚庣殑瀛楃
+     */
+    public static String getFirst10Characters(String str) {
+        // 鍒ゆ柇瀛楃涓查暱搴�
+        if (str.length() > 10) {
+            // 濡傛灉闀垮害澶т簬10锛屾埅鍙栧墠10涓瓧绗�
+            return str.substring(0, 10);
+        } else {
+            // 濡傛灉闀垮害涓嶈冻10锛岃繑鍥炴暣涓瓧绗︿覆
+            return str;
+        }
+    }
+
+    /**
      * 妫�鏌ユ湭鐧诲綍鐢ㄦ埛鏄惁瓒呰繃褰撴棩瀵硅瘽娆℃暟闄愬埗
      *
      * @param request 褰撳墠璇锋眰

--
Gitblit v1.9.3