内存存储会话记录改为mysql存储

This commit is contained in:
liukai
2025-09-05 18:00:00 +08:00
parent 897e2596b0
commit 9d5c2aaf04
12 changed files with 118 additions and 161 deletions

View File

@@ -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();
}
/**

View File

@@ -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<String> findConversationIds() {
return List.of();
}
@Override
public List<Message> findByConversationId(String conversationId) {
List<ChatMessageHistory> 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<Message> messages) {
chatMessageHistoryMapper.deleteByConversationId(conversationId);
List<ChatMessageHistory> 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);
}
}

View File

@@ -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<MessageDTO> getChatHistory(@PathVariable("chatId") String chatId) {
List<Message> messages = inMemoryChatMemoryRepository.findByConversationId(chatId);
return messages.stream().map(e -> new MessageDTO(e.getMessageType().getValue(), e.getText())).collect(Collectors.toList());
List<Message> messages = mysqlChatMemoryRepository.findByConversationId(chatId);
return messages.stream().map(e -> new MessageDTO(e.getMessageType().getValue(), e.getText()))
.collect(Collectors.toList());
}

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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<AIChatMessage> {
@Select("SELECT DISTINCT conversation_id FROM ai_chat_message ")
List<String> selectConversationIds();
@Select("SELECT * FROM ai_chat_message WHERE conversation_id = #{conversationId}")
List<AIChatMessage> selectByConversationId(String conversationId);
void batchInsert(List<AIChatMessage> messageList);
@Select("DELETE FROM ai_chat_message WHERE conversation_id = #{conversationId}")
void deleteByConversationId(String conversationId);
}

View File

@@ -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<ChatMessageHistory> {
@Select("select * from chat_message_history where conversation_id = #{conversationId}")
List<ChatMessageHistory> selectByConversationId(String conversationId);
@Delete("delete from chat_message_history where conversation_id = #{conversationId}")
void deleteByConversationId(String conversationId);
}

View File

@@ -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<String> getConversationIds(String type);
List<String> getConversationIds();
List<Message> getMessage(String conversationId);
void saveAll(String conversationId, List<Message> messages);
void delete(String conversationId);
}

View File

@@ -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<String> getConversationIds(String type) {
String conversationTypeKey = getConversationTypeKey(type);
if (redisTemplateService.hasKey(conversationTypeKey)) {
return (List<String>) redisTemplateService.get(conversationTypeKey);
}
return List.of();
}
@Override
public List<String> getConversationIds() {
Set<String> keys = redisTemplateService.getKeys(AI_MESSAGE_CONVERSATION_ID);
return new ArrayList<>(keys);
}
@Override
public List<Message> getMessage(String conversationId) {
String conversationIdKey = getConversationIdKey(conversationId);
if (redisTemplateService.hasKey(conversationIdKey)) {
return (List<Message>) redisTemplateService.get(conversationIdKey);
}
return List.of();
}
@Override
public void saveAll(String conversationId, List<Message> 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;
}
}

View File

@@ -20,12 +20,8 @@ import java.util.Map;
@Service
public class ChatHistoryServiceImpl implements ChatHistoryService {
/*@Autowired
private RedisTemplateService redisTemplateService;*/
private final Map<String,List<String>> chatStore = new HashMap<>();
/**
* 保存会话ID
*

View File

@@ -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

View File

@@ -1,13 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.ai.app.mapper.AIChatMessageMapper">
<insert id="batchInsert" parameterType="com.ai.app.entity.AIChatMessage">
INSERT INTO ai_chat_message (id,conversation_id,conversation_type,message_type,message,create_time)
VALUES
<foreach collection="messageList" item="message" separator=",">
(#{message.id}, #{message.conversationId}, #{message.conversationType}, #{message.messageType},#{message.message}, #{message.createTime})
</foreach>
</insert>
</mapper>