diff --git a/src/main/java/com/ai/app/config/AIConfig.java b/src/main/java/com/ai/app/config/AIConfig.java index 6cdbdb8..6889734 100644 --- a/src/main/java/com/ai/app/config/AIConfig.java +++ b/src/main/java/com/ai/app/config/AIConfig.java @@ -6,7 +6,6 @@ import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatModel; import org.springframework.ai.chat.client.ChatClient; import org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor; import org.springframework.ai.chat.client.advisor.SimpleLoggerAdvisor; -import org.springframework.ai.chat.memory.InMemoryChatMemoryRepository; import org.springframework.ai.chat.memory.MessageWindowChatMemory; import org.springframework.ai.deepseek.DeepSeekChatModel; import org.springframework.context.annotation.Bean; @@ -20,15 +19,9 @@ import org.springframework.context.annotation.Configuration; @Configuration public class AIConfig { - @Bean - public InMemoryChatMemoryRepository inMemoryChatMemoryRepository() { - return new InMemoryChatMemoryRepository(); - } - - @Bean - public MessageWindowChatMemory messageWindowChatMemory(InMemoryChatMemoryRepository inMemoryChatMemoryRepository) { - return MessageWindowChatMemory.builder().chatMemoryRepository(inMemoryChatMemoryRepository).build(); + public MessageWindowChatMemory messageWindowChatMemory(MysqlChatMemoryRepository mysqlChatMemoryRepository) { + return MessageWindowChatMemory.builder().chatMemoryRepository(mysqlChatMemoryRepository).build(); } /** diff --git a/src/main/java/com/ai/app/config/MysqlChatMemoryRepository.java b/src/main/java/com/ai/app/config/MysqlChatMemoryRepository.java new file mode 100644 index 0000000..1b6a8c5 --- /dev/null +++ b/src/main/java/com/ai/app/config/MysqlChatMemoryRepository.java @@ -0,0 +1,67 @@ +package com.ai.app.config; + +import com.ai.app.entity.ChatMessageHistory; +import com.ai.app.entity.Msg; +import com.ai.app.mapper.ChatMessageHistoryMapper; +import com.alibaba.fastjson.JSON; +import org.springframework.ai.chat.memory.ChatMemoryRepository; +import org.springframework.ai.chat.messages.AssistantMessage; +import org.springframework.ai.chat.messages.Message; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Propagation; +import org.springframework.transaction.annotation.Transactional; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.UUID; +import java.util.stream.Collectors; + +/** + * @describe mysql存储会话记录 + * @Author kai.liu + * @Date 2025/9/5 16:27 + */ +@Component +public class MysqlChatMemoryRepository implements ChatMemoryRepository { + + @Autowired + private ChatMessageHistoryMapper chatMessageHistoryMapper; + + @Override + public List findConversationIds() { + return List.of(); + } + + @Override + public List findByConversationId(String conversationId) { + List messageHistorieList = chatMessageHistoryMapper.selectByConversationId(conversationId); + return messageHistorieList.stream().map(e -> new Msg(e).toMessage()).collect(Collectors.toList()); + } + + @Override + @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class) + public void saveAll(String conversationId, List messages) { + chatMessageHistoryMapper.deleteByConversationId(conversationId); + List messageHistoryList = messages.stream().map(message -> { + ChatMessageHistory chatMessageHistory = new ChatMessageHistory(); + chatMessageHistory.setId(UUID.randomUUID().toString()); + chatMessageHistory.setConversationId(conversationId); + chatMessageHistory.setMessageType(message.getMessageType().getValue()); + chatMessageHistory.setMessageContent(message.getText()); + chatMessageHistory.setMetadata(JSON.toJSONString(message.getMetadata())); + if (message instanceof AssistantMessage am) { + chatMessageHistory.setToolCalls(JSON.toJSONString(am.getToolCalls())); + } + chatMessageHistory.setCreateTime(LocalDateTime.now()); + return chatMessageHistory; + }).collect(Collectors.toList()); + chatMessageHistoryMapper.insert(messageHistoryList); + } + + @Override + @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class) + public void deleteByConversationId(String conversationId) { + chatMessageHistoryMapper.deleteByConversationId(conversationId); + } +} diff --git a/src/main/java/com/ai/app/controller/DashScopeController.java b/src/main/java/com/ai/app/controller/DashScopeController.java index 33a3d5c..7e23c05 100644 --- a/src/main/java/com/ai/app/controller/DashScopeController.java +++ b/src/main/java/com/ai/app/controller/DashScopeController.java @@ -1,11 +1,11 @@ package com.ai.app.controller; +import com.ai.app.config.MysqlChatMemoryRepository; import com.ai.app.constant.ChatTypeEnum; import com.ai.app.dto.MessageDTO; import com.ai.app.service.ChatHistoryService; import org.springframework.ai.chat.client.ChatClient; import org.springframework.ai.chat.memory.ChatMemory; -import org.springframework.ai.chat.memory.InMemoryChatMemoryRepository; import org.springframework.ai.chat.messages.Message; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; @@ -44,7 +44,7 @@ public class DashScopeController { private ChatHistoryService chatHistoryService; @Autowired - private InMemoryChatMemoryRepository inMemoryChatMemoryRepository; + private MysqlChatMemoryRepository mysqlChatMemoryRepository; /** * 对话聊天 @@ -88,8 +88,9 @@ public class DashScopeController { */ @RequestMapping("/getChatHistory/{chatId}") public List getChatHistory(@PathVariable("chatId") String chatId) { - List messages = inMemoryChatMemoryRepository.findByConversationId(chatId); - return messages.stream().map(e -> new MessageDTO(e.getMessageType().getValue(), e.getText())).collect(Collectors.toList()); + List messages = mysqlChatMemoryRepository.findByConversationId(chatId); + return messages.stream().map(e -> new MessageDTO(e.getMessageType().getValue(), e.getText())) + .collect(Collectors.toList()); } diff --git a/src/main/java/com/ai/app/entity/AIChatMessage.java b/src/main/java/com/ai/app/entity/ChatMessageHistory.java similarity index 67% rename from src/main/java/com/ai/app/entity/AIChatMessage.java rename to src/main/java/com/ai/app/entity/ChatMessageHistory.java index b0b01da..2e68080 100644 --- a/src/main/java/com/ai/app/entity/AIChatMessage.java +++ b/src/main/java/com/ai/app/entity/ChatMessageHistory.java @@ -9,22 +9,24 @@ import java.time.LocalDateTime; /** * @describe * @Author kai.liu - * @Date 2025/8/29 9:20 + * @Date 2025/9/5 10:23 */ @Data -@TableName("ai_chat_message") -public class AIChatMessage { +@TableName("chat_message_history") +public class ChatMessageHistory { @TableId private String id; private String conversationId; - private String conversationType; - private String messageType; - private String message; + private String messageContent; + + private String metadata; + + private String toolCalls; private LocalDateTime createTime; } diff --git a/src/main/java/com/ai/app/entity/Msg.java b/src/main/java/com/ai/app/entity/Msg.java index 988f193..b1097db 100644 --- a/src/main/java/com/ai/app/entity/Msg.java +++ b/src/main/java/com/ai/app/entity/Msg.java @@ -1,5 +1,7 @@ package com.ai.app.entity; +import com.alibaba.fastjson.JSONObject; +import com.alibaba.fastjson.TypeReference; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @@ -26,6 +28,16 @@ public class Msg { } } + public Msg(ChatMessageHistory message) { + this.messageType = MessageType.fromValue(message.getMessageType()); + this.text = message.getMessageContent(); + this.metadata = JSONObject.parseObject(message.getMetadata()); + if (MessageType.ASSISTANT.equals(this.messageType)) { + this.toolCalls = JSONObject.parseObject(message.getToolCalls(), new TypeReference<>() { + }); + } + } + public Message toMessage() { return switch (messageType) { case SYSTEM -> new SystemMessage(text); diff --git a/src/main/java/com/ai/app/mapper/AIChatMessageMapper.java b/src/main/java/com/ai/app/mapper/AIChatMessageMapper.java deleted file mode 100644 index 0aa7aaf..0000000 --- a/src/main/java/com/ai/app/mapper/AIChatMessageMapper.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.ai.app.mapper; - -import com.ai.app.entity.AIChatMessage; -import com.baomidou.mybatisplus.core.mapper.BaseMapper; -import org.apache.ibatis.annotations.Insert; -import org.apache.ibatis.annotations.Mapper; -import org.apache.ibatis.annotations.Select; - -import java.util.List; - -/** - * @describe - * @Author kai.liu - * @Date 2025/8/29 9:18 - */ -@Mapper -public interface AIChatMessageMapper extends BaseMapper { - - @Select("SELECT DISTINCT conversation_id FROM ai_chat_message ") - List selectConversationIds(); - - @Select("SELECT * FROM ai_chat_message WHERE conversation_id = #{conversationId}") - List selectByConversationId(String conversationId); - - void batchInsert(List messageList); - - @Select("DELETE FROM ai_chat_message WHERE conversation_id = #{conversationId}") - void deleteByConversationId(String conversationId); -} diff --git a/src/main/java/com/ai/app/mapper/ChatMessageHistoryMapper.java b/src/main/java/com/ai/app/mapper/ChatMessageHistoryMapper.java new file mode 100644 index 0000000..a686ec1 --- /dev/null +++ b/src/main/java/com/ai/app/mapper/ChatMessageHistoryMapper.java @@ -0,0 +1,22 @@ +package com.ai.app.mapper; + +import com.ai.app.entity.ChatMessageHistory; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Delete; +import org.apache.ibatis.annotations.Select; + +import java.util.List; + +/** + * @describe + * @Author kai.liu + * @Date 2025/9/5 10:43 + */ +public interface ChatMessageHistoryMapper extends BaseMapper { + + @Select("select * from chat_message_history where conversation_id = #{conversationId}") + List selectByConversationId(String conversationId); + + @Delete("delete from chat_message_history where conversation_id = #{conversationId}") + void deleteByConversationId(String conversationId); +} diff --git a/src/main/java/com/ai/app/service/AIChatMessageService.java b/src/main/java/com/ai/app/service/AIChatMessageService.java deleted file mode 100644 index 704fac7..0000000 --- a/src/main/java/com/ai/app/service/AIChatMessageService.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.ai.app.service; - -import org.springframework.ai.chat.messages.Message; - -import java.util.List; - -/** - * @describe - * @Author kai.liu - * @Date 2025/8/29 9:27 - */ -public interface AIChatMessageService { - - List getConversationIds(String type); - - List getConversationIds(); - - List getMessage(String conversationId); - - void saveAll(String conversationId, List messages); - - void delete(String conversationId); -} diff --git a/src/main/java/com/ai/app/service/impl/AIChatMessageServiceImpl.java b/src/main/java/com/ai/app/service/impl/AIChatMessageServiceImpl.java deleted file mode 100644 index f0983d7..0000000 --- a/src/main/java/com/ai/app/service/impl/AIChatMessageServiceImpl.java +++ /dev/null @@ -1,72 +0,0 @@ -package com.ai.app.service.impl; - -import com.ai.app.service.AIChatMessageService; -import com.ai.app.service.RedisTemplateService; -import jakarta.annotation.Resource; -import lombok.extern.slf4j.Slf4j; -import org.springframework.ai.chat.messages.Message; -import org.springframework.stereotype.Service; - -import java.util.ArrayList; -import java.util.List; -import java.util.Set; - -/** - * @describe - * @Author kai.liu - * @Date 2025/8/29 9:27 - */ -@Slf4j -@Service -public class AIChatMessageServiceImpl implements AIChatMessageService { - - @Resource - private RedisTemplateService redisTemplateService; - - public static final String AI_MESSAGE_CONVERSATION_ID = "ai:message:conversationId:"; - public static final String AI_MESSAGE_CONVERSATION_TYPE = "ai:message:conversationType:"; - - @Override - public List getConversationIds(String type) { - String conversationTypeKey = getConversationTypeKey(type); - if (redisTemplateService.hasKey(conversationTypeKey)) { - return (List) redisTemplateService.get(conversationTypeKey); - } - return List.of(); - } - - @Override - public List getConversationIds() { - Set keys = redisTemplateService.getKeys(AI_MESSAGE_CONVERSATION_ID); - return new ArrayList<>(keys); - } - - @Override - public List getMessage(String conversationId) { - String conversationIdKey = getConversationIdKey(conversationId); - if (redisTemplateService.hasKey(conversationIdKey)) { - return (List) redisTemplateService.get(conversationIdKey); - } - return List.of(); - } - - @Override - public void saveAll(String conversationId, List messages) { - String conversationIdKey = getConversationIdKey(conversationId); - redisTemplateService.set(conversationIdKey, messages); - } - - @Override - public void delete(String conversationId) { - String conversationIdKey = getConversationIdKey(conversationId); - redisTemplateService.delete(conversationIdKey); - } - - private String getConversationIdKey(String conversationId) { - return AI_MESSAGE_CONVERSATION_ID + conversationId; - } - - private String getConversationTypeKey(String type) { - return AI_MESSAGE_CONVERSATION_TYPE + type; - } -} diff --git a/src/main/java/com/ai/app/service/impl/ChatHistoryServiceImpl.java b/src/main/java/com/ai/app/service/impl/ChatHistoryServiceImpl.java index 62201f1..59195c7 100644 --- a/src/main/java/com/ai/app/service/impl/ChatHistoryServiceImpl.java +++ b/src/main/java/com/ai/app/service/impl/ChatHistoryServiceImpl.java @@ -20,12 +20,8 @@ import java.util.Map; @Service public class ChatHistoryServiceImpl implements ChatHistoryService { - /*@Autowired - private RedisTemplateService redisTemplateService;*/ - private final Map> chatStore = new HashMap<>(); - /** * 保存会话ID * diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 75d29ed..d0eefdc 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -45,7 +45,8 @@ spring: options: model: deepseek-reasoner dashscope: - api-key: ${DASH_SCOPE_API_KEY} +# api-key: ${DASH_SCOPE_API_KEY} + api-key: sk-1acdcf6f47e7480c9e6bcc5624d4cbfc chat: options: model: qwen-max diff --git a/src/main/resources/mapper/AIChatMessageMapper.xml b/src/main/resources/mapper/AIChatMessageMapper.xml deleted file mode 100644 index ceb303a..0000000 --- a/src/main/resources/mapper/AIChatMessageMapper.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - INSERT INTO ai_chat_message (id,conversation_id,conversation_type,message_type,message,create_time) - VALUES - - (#{message.id}, #{message.conversationId}, #{message.conversationType}, #{message.messageType},#{message.message}, #{message.createTime}) - - - \ No newline at end of file