From 5e40dee0e0a4e4cff4a1aafca2444f61c39cbf32 Mon Sep 17 00:00:00 2001
From: zhou zhou <3272660260@qq.com>
Date: 星期四, 19 三月 2026 11:17:14 +0800
Subject: [PATCH] #AI.会话能力增强

---
 rsf-server/src/main/java/com/vincent/rsf/server/ai/service/impl/AiChatMemoryServiceImpl.java |  119 +++++++++++++++++++++++++++++++++++++++++++++++++++++++----
 1 files changed, 110 insertions(+), 9 deletions(-)

diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/ai/service/impl/AiChatMemoryServiceImpl.java b/rsf-server/src/main/java/com/vincent/rsf/server/ai/service/impl/AiChatMemoryServiceImpl.java
index b663a6a..c0a4a61 100644
--- a/rsf-server/src/main/java/com/vincent/rsf/server/ai/service/impl/AiChatMemoryServiceImpl.java
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/ai/service/impl/AiChatMemoryServiceImpl.java
@@ -5,6 +5,8 @@
 import com.vincent.rsf.framework.exception.CoolException;
 import com.vincent.rsf.server.ai.dto.AiChatMemoryDto;
 import com.vincent.rsf.server.ai.dto.AiChatMessageDto;
+import com.vincent.rsf.server.ai.dto.AiChatSessionPinRequest;
+import com.vincent.rsf.server.ai.dto.AiChatSessionRenameRequest;
 import com.vincent.rsf.server.ai.dto.AiChatSessionDto;
 import com.vincent.rsf.server.ai.entity.AiChatMessage;
 import com.vincent.rsf.server.ai.entity.AiChatSession;
@@ -19,6 +21,7 @@
 import java.util.ArrayList;
 import java.util.Date;
 import java.util.List;
+import java.util.Locale;
 
 @Service
 @RequiredArgsConstructor
@@ -47,7 +50,7 @@
     }
 
     @Override
-    public List<AiChatSessionDto> listSessions(Long userId, Long tenantId, String promptCode) {
+    public List<AiChatSessionDto> listSessions(Long userId, Long tenantId, String promptCode, String keyword) {
         ensureIdentity(userId, tenantId);
         String resolvedPromptCode = requirePromptCode(promptCode);
         List<AiChatSession> sessions = aiChatSessionMapper.selectList(new LambdaQueryWrapper<AiChatSession>()
@@ -56,6 +59,8 @@
                 .eq(AiChatSession::getPromptCode, resolvedPromptCode)
                 .eq(AiChatSession::getDeleted, 0)
                 .eq(AiChatSession::getStatus, StatusType.ENABLE.val)
+                .like(StringUtils.hasText(keyword), AiChatSession::getTitle, keyword == null ? null : keyword.trim())
+                .orderByDesc(AiChatSession::getPinned)
                 .orderByDesc(AiChatSession::getLastMessageTime)
                 .orderByDesc(AiChatSession::getId));
         if (Cools.isEmpty(sessions)) {
@@ -63,12 +68,7 @@
         }
         List<AiChatSessionDto> result = new ArrayList<>();
         for (AiChatSession session : sessions) {
-            result.add(AiChatSessionDto.builder()
-                    .sessionId(session.getId())
-                    .title(session.getTitle())
-                    .promptCode(session.getPromptCode())
-                    .lastMessageTime(session.getLastMessageTime())
-                    .build());
+            result.add(buildSessionDto(session));
         }
         return result;
     }
@@ -87,6 +87,7 @@
                 .setUserId(userId)
                 .setTenantId(tenantId)
                 .setLastMessageTime(now)
+                .setPinned(0)
                 .setStatus(StatusType.ENABLE.val)
                 .setDeleted(0)
                 .setCreateBy(userId)
@@ -157,6 +158,40 @@
         }
     }
 
+    @Override
+    public AiChatSessionDto renameSession(Long userId, Long tenantId, Long sessionId, AiChatSessionRenameRequest request) {
+        ensureIdentity(userId, tenantId);
+        if (request == null || !StringUtils.hasText(request.getTitle())) {
+            throw new CoolException("浼氳瘽鏍囬涓嶈兘涓虹┖");
+        }
+        AiChatSession session = requireOwnedSession(sessionId, userId, tenantId);
+        Date now = new Date();
+        AiChatSession update = new AiChatSession()
+                .setId(sessionId)
+                .setTitle(buildSessionTitle(request.getTitle()))
+                .setUpdateBy(userId)
+                .setUpdateTime(now);
+        aiChatSessionMapper.updateById(update);
+        return buildSessionDto(requireOwnedSession(sessionId, userId, tenantId));
+    }
+
+    @Override
+    public AiChatSessionDto pinSession(Long userId, Long tenantId, Long sessionId, AiChatSessionPinRequest request) {
+        ensureIdentity(userId, tenantId);
+        if (request == null || request.getPinned() == null) {
+            throw new CoolException("缃《鐘舵�佷笉鑳戒负绌�");
+        }
+        AiChatSession session = requireOwnedSession(sessionId, userId, tenantId);
+        Date now = new Date();
+        AiChatSession update = new AiChatSession()
+                .setId(sessionId)
+                .setPinned(Boolean.TRUE.equals(request.getPinned()) ? 1 : 0)
+                .setUpdateBy(userId)
+                .setUpdateTime(now);
+        aiChatSessionMapper.updateById(update);
+        return buildSessionDto(requireOwnedSession(sessionId, userId, tenantId));
+    }
+
     private AiChatSession findLatestSession(Long userId, Long tenantId, String promptCode) {
         return aiChatSessionMapper.selectOne(new LambdaQueryWrapper<AiChatSession>()
                 .eq(AiChatSession::getUserId, userId)
@@ -175,6 +210,23 @@
                 .eq(AiChatSession::getUserId, userId)
                 .eq(AiChatSession::getTenantId, tenantId)
                 .eq(AiChatSession::getPromptCode, promptCode)
+                .eq(AiChatSession::getDeleted, 0)
+                .eq(AiChatSession::getStatus, StatusType.ENABLE.val)
+                .last("limit 1"));
+        if (session == null) {
+            throw new CoolException("AI 浼氳瘽涓嶅瓨鍦ㄦ垨鏃犳潈璁块棶");
+        }
+        return session;
+    }
+
+    private AiChatSession requireOwnedSession(Long sessionId, Long userId, Long tenantId) {
+        if (sessionId == null) {
+            throw new CoolException("AI 浼氳瘽 ID 涓嶈兘涓虹┖");
+        }
+        AiChatSession session = aiChatSessionMapper.selectOne(new LambdaQueryWrapper<AiChatSession>()
+                .eq(AiChatSession::getId, sessionId)
+                .eq(AiChatSession::getUserId, userId)
+                .eq(AiChatSession::getTenantId, tenantId)
                 .eq(AiChatSession::getDeleted, 0)
                 .eq(AiChatSession::getStatus, StatusType.ENABLE.val)
                 .last("limit 1"));
@@ -266,8 +318,57 @@
         if (!StringUtils.hasText(titleSeed)) {
             throw new CoolException("AI 浼氳瘽鏍囬涓嶈兘涓虹┖");
         }
-        String title = titleSeed.trim().replace("\r", " ").replace("\n", " ");
-        return title.length() > 60 ? title.substring(0, 60) : title;
+        String title = titleSeed.trim()
+                .replace("\r", " ")
+                .replace("\n", " ")
+                .replaceAll("\\s+", " ");
+        int punctuationIndex = findSummaryBreakIndex(title);
+        if (punctuationIndex > 0) {
+            title = title.substring(0, punctuationIndex).trim();
+        }
+        return title.length() > 48 ? title.substring(0, 48) : title;
+    }
+
+    private int findSummaryBreakIndex(String title) {
+        String[] separators = {"銆�", "锛�", "锛�", ".", "!", "?"};
+        int result = -1;
+        for (String separator : separators) {
+            int index = title.indexOf(separator);
+            if (index > 0 && (result < 0 || index < result)) {
+                result = index;
+            }
+        }
+        return result;
+    }
+
+    private AiChatSessionDto buildSessionDto(AiChatSession session) {
+        AiChatMessage lastMessage = aiChatMessageMapper.selectOne(new LambdaQueryWrapper<AiChatMessage>()
+                .eq(AiChatMessage::getSessionId, session.getId())
+                .eq(AiChatMessage::getDeleted, 0)
+                .orderByDesc(AiChatMessage::getSeqNo)
+                .orderByDesc(AiChatMessage::getId)
+                .last("limit 1"));
+        return AiChatSessionDto.builder()
+                .sessionId(session.getId())
+                .title(session.getTitle())
+                .promptCode(session.getPromptCode())
+                .pinned(session.getPinned() != null && session.getPinned() == 1)
+                .lastMessagePreview(buildLastMessagePreview(lastMessage))
+                .lastMessageTime(session.getLastMessageTime())
+                .build();
+    }
+
+    private String buildLastMessagePreview(AiChatMessage message) {
+        if (message == null || !StringUtils.hasText(message.getContent())) {
+            return null;
+        }
+        String preview = message.getContent().trim()
+                .replace("\r", " ")
+                .replace("\n", " ")
+                .replaceAll("\\s+", " ");
+        String prefix = "assistant".equalsIgnoreCase(message.getRole()) ? "AI: " : "浣�: ";
+        String normalized = prefix + preview;
+        return normalized.length() > 80 ? normalized.substring(0, 80) : normalized;
     }
 
     private void ensureIdentity(Long userId, Long tenantId) {

--
Gitblit v1.9.1