diff --git a/ruoyi-oa/src/main/java/com/ruoyi/oa/controller/SysOaAiController.java b/ruoyi-oa/src/main/java/com/ruoyi/oa/controller/SysOaAiController.java index 2551c80..f0bc958 100644 --- a/ruoyi-oa/src/main/java/com/ruoyi/oa/controller/SysOaAiController.java +++ b/ruoyi-oa/src/main/java/com/ruoyi/oa/controller/SysOaAiController.java @@ -200,9 +200,29 @@ public class SysOaAiController extends BaseController { */ @PostMapping("/conversation/{conversationId}/end") public R endConversation(@PathVariable Long conversationId) { + // 清除对话缓存 + aiServiceUtil.clearConversationCache(conversationId); return toAjax(conversationService.endConversation(conversationId)); } + /** + * 清除对话缓存 + */ + @DeleteMapping("/conversation/{conversationId}/cache") + public R clearConversationCache(@PathVariable Long conversationId) { + aiServiceUtil.clearConversationCache(conversationId); + return R.ok("缓存已清除"); + } + + /** + * 清除AI配置缓存 + */ + @DeleteMapping("/cache/config") + public R clearAiConfigCache() { + aiServiceUtil.clearAiConfigCache(); + return R.ok("AI配置缓存已清除"); + } + /** * 发送消息给AI */ @@ -212,8 +232,14 @@ public class SysOaAiController extends BaseController { // 1. 保存用户消息 messageService.addUserMessage(conversationId, message); - // 2. 获取对话历史 - List conversationHistory = messageService.queryByConversationId(conversationId); + // 2. 获取对话历史(优先从Redis缓存获取) + List conversationHistory = aiServiceUtil.getCachedConversationHistory(conversationId); + if (conversationHistory == null) { + // 缓存中没有,从数据库获取 + conversationHistory = messageService.queryByConversationId(conversationId); + // 缓存对话历史 + aiServiceUtil.cacheConversationHistory(conversationId, conversationHistory); + } // 3. 调用AI服务获取回复(传递对话历史) String aiResponse = callAiServiceWithHistory(message, conversationHistory); @@ -221,6 +247,10 @@ public class SysOaAiController extends BaseController { // 4. 保存AI回复 messageService.addAiMessage(conversationId, aiResponse, 0, java.math.BigDecimal.ZERO); + // 5. 更新对话历史缓存 + List updatedHistory = messageService.queryByConversationId(conversationId); + aiServiceUtil.cacheConversationHistory(conversationId, updatedHistory); + Map result = new HashMap<>(); result.put("conversationId", conversationId); result.put("response", aiResponse); diff --git a/ruoyi-oa/src/main/java/com/ruoyi/oa/utils/AiServiceUtil.java b/ruoyi-oa/src/main/java/com/ruoyi/oa/utils/AiServiceUtil.java index bf3958c..cad7391 100644 --- a/ruoyi-oa/src/main/java/com/ruoyi/oa/utils/AiServiceUtil.java +++ b/ruoyi-oa/src/main/java/com/ruoyi/oa/utils/AiServiceUtil.java @@ -2,6 +2,7 @@ package com.ruoyi.oa.utils; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; +import com.ruoyi.common.utils.redis.RedisUtils; import com.ruoyi.oa.domain.vo.SysOaAiConfigVo; import com.ruoyi.oa.domain.vo.SysOaAiMessageVo; import com.ruoyi.oa.service.ISysOaAiConfigService; @@ -12,6 +13,7 @@ import org.springframework.stereotype.Component; import org.springframework.web.client.RestTemplate; import java.math.BigDecimal; +import java.time.Duration; import java.util.*; /** @@ -29,6 +31,11 @@ public class AiServiceUtil { private final RestTemplate restTemplate; private final ObjectMapper objectMapper; + // Redis缓存key前缀 + private static final String AI_CONFIG_CACHE_KEY = "ai:config:active"; + private static final String AI_CONVERSATION_CACHE_KEY = "ai:conversation:"; + private static final String AI_RESPONSE_CACHE_KEY = "ai:response:"; + /** * 调用DeepSeek AI服务(单次对话) * @@ -48,18 +55,26 @@ public class AiServiceUtil { */ public String callDeepSeekWithHistory(String message, List conversationHistory) { try { - // 获取AI配置 - SysOaAiConfigVo config = configService.getActiveConfig(); + // 1. 检查缓存中是否有相同的请求 + String cacheKey = generateCacheKey(message, conversationHistory); + String cachedResponse = RedisUtils.getCacheObject(cacheKey); + if (cachedResponse != null) { + log.info("从Redis缓存获取AI回复"); + return cachedResponse; + } + + // 2. 获取AI配置(优先从缓存获取) + SysOaAiConfigVo config = getCachedAiConfig(); if (config == null) { throw new RuntimeException("未找到可用的AI配置"); } - // 构建请求头 + // 3. 构建请求头 HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); headers.set("Authorization", "Bearer " + config.getApiKey()); - // 构建请求体 + // 4. 构建请求体 Map requestBody = new HashMap<>(); requestBody.put("model", config.getModelName()); requestBody.put("temperature", config.getTemperature()); @@ -67,7 +82,7 @@ public class AiServiceUtil { List> messages = new ArrayList<>(); - // 添加对话历史 + // 5. 添加对话历史 if (conversationHistory != null && !conversationHistory.isEmpty()) { for (SysOaAiMessageVo historyMessage : conversationHistory) { Map historyMsg = new HashMap<>(); @@ -77,7 +92,7 @@ public class AiServiceUtil { } } - // 添加当前用户消息 + // 6. 添加当前用户消息 Map userMessage = new HashMap<>(); userMessage.put("role", "user"); userMessage.put("content", message); @@ -85,7 +100,7 @@ public class AiServiceUtil { requestBody.put("messages", messages); - // 发送请求 + // 7. 发送请求 HttpEntity> entity = new HttpEntity<>(requestBody, headers); String url = config.getBaseUrl() + "/chat/completions"; @@ -96,10 +111,13 @@ public class AiServiceUtil { JsonNode jsonNode = objectMapper.readTree(response.getBody()); String aiResponse = jsonNode.path("choices").path(0).path("message").path("content").asText(); - // 记录token使用情况 + // 8. 记录token使用情况 int totalTokens = jsonNode.path("usage").path("total_tokens").asInt(); log.info("AI回复成功,消耗token: {}", totalTokens); + // 9. 缓存AI回复(缓存5分钟) + RedisUtils.setCacheObject(cacheKey, aiResponse, Duration.ofDays(1)); + return aiResponse; } else { log.error("DeepSeek API调用失败: {}", response.getStatusCode()); @@ -111,6 +129,82 @@ public class AiServiceUtil { } } + /** + * 获取缓存的AI配置 + */ + private SysOaAiConfigVo getCachedAiConfig() { + // 先从Redis缓存获取 + SysOaAiConfigVo cachedConfig = RedisUtils.getCacheObject(AI_CONFIG_CACHE_KEY); + if (cachedConfig != null) { + log.debug("从Redis缓存获取AI配置"); + return cachedConfig; + } + + // 缓存中没有,从数据库获取 + SysOaAiConfigVo config = configService.getActiveConfig(); + if (config != null) { + // 缓存配置(缓存30分钟) + RedisUtils.setCacheObject(AI_CONFIG_CACHE_KEY, config, Duration.ofMinutes(30)); + log.debug("AI配置已缓存到Redis"); + } + + return config; + } + + /** + * 生成缓存key + */ + private String generateCacheKey(String message, List conversationHistory) { + StringBuilder keyBuilder = new StringBuilder(AI_RESPONSE_CACHE_KEY); + keyBuilder.append(message.hashCode()); + + if (conversationHistory != null && !conversationHistory.isEmpty()) { + for (SysOaAiMessageVo history : conversationHistory) { + keyBuilder.append(":").append(history.getContent().hashCode()); + } + } + + return keyBuilder.toString(); + } + + /** + * 缓存对话历史 + */ + public void cacheConversationHistory(Long conversationId, List messages) { + String cacheKey = AI_CONVERSATION_CACHE_KEY + conversationId; + RedisUtils.setCacheObject(cacheKey, messages, Duration.ofMinutes(10)); + log.debug("对话历史已缓存到Redis, conversationId: {}", conversationId); + } + + /** + * 获取缓存的对话历史 + */ + public List getCachedConversationHistory(Long conversationId) { + String cacheKey = AI_CONVERSATION_CACHE_KEY + conversationId; + List cachedMessages = RedisUtils.getCacheObject(cacheKey); + if (cachedMessages != null) { + log.debug("从Redis缓存获取对话历史, conversationId: {}", conversationId); + } + return cachedMessages; + } + + /** + * 清除对话历史缓存 + */ + public void clearConversationCache(Long conversationId) { + String cacheKey = AI_CONVERSATION_CACHE_KEY + conversationId; + RedisUtils.deleteObject(cacheKey); + log.debug("已清除对话历史缓存, conversationId: {}", conversationId); + } + + /** + * 清除AI配置缓存 + */ + public void clearAiConfigCache() { + RedisUtils.deleteObject(AI_CONFIG_CACHE_KEY); + log.debug("已清除AI配置缓存"); + } + /** * 计算费用(示例:每1000个token 0.002美元) *