From 4954d3978cf1967729a5a2d5b90f6baef18974da Mon Sep 17 00:00:00 2001
From: zhou zhou <3272660260@qq.com>
Date: 星期一, 23 三月 2026 09:35:10 +0800
Subject: [PATCH] #ai redis+页面优化
---
rsf-server/src/main/java/com/vincent/rsf/server/ai/service/impl/AiChatMemoryServiceImpl.java | 97 +++++++++++++++++++++++++++++++++++++++++++++---
1 files changed, 90 insertions(+), 7 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 d531a27..a7210c3 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
@@ -16,19 +16,34 @@
import com.vincent.rsf.server.ai.service.AiChatMemoryService;
import com.vincent.rsf.server.system.enums.StatusType;
import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
+import java.util.Set;
+import java.util.concurrent.Executor;
+import java.util.concurrent.ConcurrentHashMap;
@Service
+@Slf4j
@RequiredArgsConstructor
public class AiChatMemoryServiceImpl implements AiChatMemoryService {
private final AiChatSessionMapper aiChatSessionMapper;
private final AiChatMessageMapper aiChatMessageMapper;
+ private final AiRedisSupport aiRedisSupport;
+ @Qualifier("aiMemoryTaskExecutor")
+ private final Executor aiMemoryTaskExecutor;
+
+ /**
+ * 鐢ㄤ袱涓湰鍦伴泦鍚堟妸鈥滃悓涓�涓細璇濈殑鎽樿鍒锋柊鈥濆悎骞舵垚涓茶浠诲姟锛岄伩鍏嶈繛缁秷鎭妸閲嶅浠诲姟濉炴弧绾跨▼姹犮��
+ */
+ private final Set<Long> refreshingSessionIds = ConcurrentHashMap.newKeySet();
+ private final Set<Long> pendingRefreshSessionIds = ConcurrentHashMap.newKeySet();
/**
* 璇诲彇浼氳瘽璁板繂蹇収銆�
@@ -39,11 +54,17 @@
public AiChatMemoryDto getMemory(Long userId, Long tenantId, String promptCode, Long sessionId) {
ensureIdentity(userId, tenantId);
String resolvedPromptCode = requirePromptCode(promptCode);
+ // 浼氳瘽璁板繂灞炰簬鍏稿瀷鈥滆澶氬啓灏戔�濇暟鎹紝鍏堣蛋鐭� TTL 缂撳瓨鑳芥槑鏄惧噺杞绘娊灞夊垵濮嬪寲鍜屽垏浼氳瘽鍘嬪姏銆�
+ AiChatMemoryDto cached = aiRedisSupport.getMemory(tenantId, userId, resolvedPromptCode, sessionId);
+ if (cached != null) {
+ return cached;
+ }
AiChatSession session = sessionId == null
? findLatestSession(userId, tenantId, resolvedPromptCode)
: getSession(sessionId, userId, tenantId, resolvedPromptCode);
+ AiChatMemoryDto memory;
if (session == null) {
- return AiChatMemoryDto.builder()
+ memory = AiChatMemoryDto.builder()
.sessionId(null)
.memorySummary(null)
.memoryFacts(null)
@@ -51,10 +72,12 @@
.persistedMessages(List.of())
.shortMemoryMessages(List.of())
.build();
+ aiRedisSupport.cacheMemory(tenantId, userId, resolvedPromptCode, sessionId, memory);
+ return memory;
}
List<AiChatMessageDto> persistedMessages = listMessages(session.getId());
List<AiChatMessageDto> shortMemoryMessages = tailMessagesByRounds(persistedMessages, AiDefaults.MEMORY_RECENT_ROUNDS);
- return AiChatMemoryDto.builder()
+ memory = AiChatMemoryDto.builder()
.sessionId(session.getId())
.memorySummary(session.getMemorySummary())
.memoryFacts(session.getMemoryFacts())
@@ -62,6 +85,11 @@
.persistedMessages(persistedMessages)
.shortMemoryMessages(shortMemoryMessages)
.build();
+ aiRedisSupport.cacheMemory(tenantId, userId, resolvedPromptCode, session.getId(), memory);
+ if (sessionId == null || !session.getId().equals(sessionId)) {
+ aiRedisSupport.cacheMemory(tenantId, userId, resolvedPromptCode, null, memory);
+ }
+ return memory;
}
/**
@@ -72,6 +100,10 @@
public List<AiChatSessionDto> listSessions(Long userId, Long tenantId, String promptCode, String keyword) {
ensureIdentity(userId, tenantId);
String resolvedPromptCode = requirePromptCode(promptCode);
+ List<AiChatSessionDto> cached = aiRedisSupport.getSessionList(tenantId, userId, resolvedPromptCode, keyword);
+ if (cached != null) {
+ return cached;
+ }
List<AiChatSession> sessions = aiChatSessionMapper.selectList(new LambdaQueryWrapper<AiChatSession>()
.eq(AiChatSession::getUserId, userId)
.eq(AiChatSession::getTenantId, tenantId)
@@ -83,12 +115,14 @@
.orderByDesc(AiChatSession::getLastMessageTime)
.orderByDesc(AiChatSession::getId));
if (Cools.isEmpty(sessions)) {
+ aiRedisSupport.cacheSessionList(tenantId, userId, resolvedPromptCode, keyword, List.of());
return List.of();
}
List<AiChatSessionDto> result = new ArrayList<>();
for (AiChatSession session : sessions) {
result.add(buildSessionDto(session));
}
+ aiRedisSupport.cacheSessionList(tenantId, userId, resolvedPromptCode, keyword, result);
return result;
}
@@ -118,12 +152,14 @@
.setUpdateBy(userId)
.setUpdateTime(now);
aiChatSessionMapper.insert(session);
+ evictConversationCaches(tenantId, userId);
return session;
}
/**
* 钀藉簱淇濆瓨涓�鏁磋疆瀵硅瘽銆�
- * 杩欓噷浼氶『搴忓啓鍏ユ湰杞敤鎴锋秷鎭拰妯″瀷鍥炲锛屽苟鍦ㄦ渶鍚庡埛鏂颁細璇濇爣棰樸�佹渶鍚庢椿璺冩椂闂村拰璁板繂鐢诲儚銆�
+ * 杩欓噷浼氶『搴忓啓鍏ユ湰杞敤鎴锋秷鎭拰妯″瀷鍥炲锛屽苟鍦ㄦ渶鍚庡埛鏂颁細璇濇爣棰樺拰娲昏穬鏃堕棿銆�
+ * 璁板繂鐢诲儚鏀逛负鍚庡彴寮傛鍒锋柊锛岄伩鍏嶆妸鎽樿閲嶇畻鑰楁椂鍘嬪湪鐢ㄦ埛鏈疆鍝嶅簲灏鹃儴銆�
*/
@Override
public void saveRound(AiChatSession session, Long userId, Long tenantId, List<AiChatMessageDto> memoryMessages, String assistantContent) {
@@ -150,7 +186,8 @@
.setUpdateBy(userId)
.setUpdateTime(now);
aiChatSessionMapper.updateById(update);
- refreshMemoryProfile(session.getId(), userId);
+ evictConversationCaches(tenantId, userId);
+ scheduleMemoryProfileRefresh(session.getId(), userId, tenantId);
}
/** 鍒犻櫎鏁翠釜浼氳瘽鍙婂叾娑堟伅銆� */
@@ -185,6 +222,7 @@
.setDeleted(1);
aiChatMessageMapper.updateById(updateMessage);
}
+ evictConversationCaches(tenantId, userId);
}
/** 鏇存柊浼氳瘽鏍囬骞惰繑鍥炴渶鏂颁細璇濇憳瑕併�� */
@@ -202,7 +240,9 @@
.setUpdateBy(userId)
.setUpdateTime(now);
aiChatSessionMapper.updateById(update);
- return buildSessionDto(requireOwnedSession(sessionId, userId, tenantId));
+ AiChatSessionDto sessionDto = buildSessionDto(requireOwnedSession(sessionId, userId, tenantId));
+ evictConversationCaches(tenantId, userId);
+ return sessionDto;
}
/** 鏇存柊浼氳瘽缃《鐘舵�併�� */
@@ -220,7 +260,9 @@
.setUpdateBy(userId)
.setUpdateTime(now);
aiChatSessionMapper.updateById(update);
- return buildSessionDto(requireOwnedSession(sessionId, userId, tenantId));
+ AiChatSessionDto sessionDto = buildSessionDto(requireOwnedSession(sessionId, userId, tenantId));
+ evictConversationCaches(tenantId, userId);
+ return sessionDto;
}
/** 娓呯┖鏌愪釜浼氳瘽鐨勫叏閮ㄦ秷鎭拰娲剧敓璁板繂瀛楁銆� */
@@ -243,6 +285,7 @@
.setUpdateBy(userId)
.setUpdateTime(new Date())
.setLastMessageTime(session.getCreateTime()));
+ evictConversationCaches(tenantId, userId);
}
/** 鍙繚鐣欐渶杩戜竴杞棶绛旓紝鐢ㄤ簬鎵嬪姩瑁佸壀闀夸細璇濄�� */
@@ -263,7 +306,46 @@
.setDeleted(1));
}
}
- refreshMemoryProfile(sessionId, userId);
+ evictConversationCaches(tenantId, userId);
+ scheduleMemoryProfileRefresh(sessionId, userId, tenantId);
+ }
+
+ private void evictConversationCaches(Long tenantId, Long userId) {
+ // 浼氳瘽鏍囬銆佹憳瑕併�佹渶杩戞秷鎭拰 runtime 閮戒細浜掔浉褰卞搷锛岀粺涓�鎸夌敤鎴风淮搴︿竴璧峰け鏁堟洿绋冲Ε銆�
+ aiRedisSupport.evictUserConversationCaches(tenantId, userId);
+ }
+
+ private void scheduleMemoryProfileRefresh(Long sessionId, Long userId, Long tenantId) {
+ if (sessionId == null) {
+ return;
+ }
+ if (!refreshingSessionIds.add(sessionId)) {
+ pendingRefreshSessionIds.add(sessionId);
+ return;
+ }
+ aiMemoryTaskExecutor.execute(() -> runMemoryProfileRefreshLoop(sessionId, userId, tenantId));
+ }
+
+ private void runMemoryProfileRefreshLoop(Long sessionId, Long userId, Long tenantId) {
+ try {
+ boolean shouldContinue;
+ do {
+ pendingRefreshSessionIds.remove(sessionId);
+ try {
+ refreshMemoryProfile(sessionId, userId);
+ evictConversationCaches(tenantId, userId);
+ } catch (Exception e) {
+ log.warn("AI memory profile refresh failed, sessionId={}, userId={}, tenantId={}, message={}",
+ sessionId, userId, tenantId, e.getMessage(), e);
+ }
+ shouldContinue = pendingRefreshSessionIds.remove(sessionId);
+ } while (shouldContinue);
+ } finally {
+ refreshingSessionIds.remove(sessionId);
+ if (pendingRefreshSessionIds.remove(sessionId) && refreshingSessionIds.add(sessionId)) {
+ aiMemoryTaskExecutor.execute(() -> runMemoryProfileRefreshLoop(sessionId, userId, tenantId));
+ }
+ }
}
private AiChatSession findLatestSession(Long userId, Long tenantId, String promptCode) {
@@ -459,6 +541,7 @@
/**
* 閲嶆柊璁$畻浼氳瘽鐨勬憳瑕佽蹇嗗拰鍏抽敭浜嬪疄銆�
* 杩欐槸鈥滄寔涔呭寲娑堟伅鈥濆拰鈥滄ā鍨嬩笂涓嬫枃娌荤悊鈥濅箣闂寸殑妗ユ鏂规硶銆�
+ * 鐜板湪瀹冭繍琛屽湪鍚庡彴绾跨▼閲岋紝鍥犳鍏佽鐭椂闂存渶缁堜竴鑷达紝鑰屼笉鏄己鍒舵湰杞悓姝ュ畬鎴愩��
*/
List<AiChatMessageDto> messages = listMessages(sessionId);
List<AiChatMessageDto> shortMemoryMessages = tailMessagesByRounds(messages, AiDefaults.MEMORY_RECENT_ROUNDS);
--
Gitblit v1.9.1