SpringBoot3 整合 SpringAI 实现ai助手(记忆)

SpringBoot3 整合 SpringAI 实现ai助手(记忆)

    • [1. 项目概述](#1. 项目概述)
    • [2. 环境准备](#2. 环境准备)
      • [2.1 安装必要软件](#2.1 安装必要软件)
      • [2.2 配置环境变量](#2.2 配置环境变量)
    • [3. 项目初始化](#3. 项目初始化)
      • [3.1 创建 Spring Boot 项目](#3.1 创建 Spring Boot 项目)
      • [3.2 配置 Maven 依赖](#3.2 配置 Maven 依赖)
    • [4. 核心配置](#4. 核心配置)
      • [4.1 应用配置文件 (`application.yml`)](#4.1 应用配置文件 (application.yml))
      • [4.2 Redis 配置 (`RedisConfiguration.java`)](#4.2 Redis 配置 (RedisConfiguration.java))
    • [5. 核心功能实现](#5. 核心功能实现)
      • [5.1 创建模型类](#5.1 创建模型类)
        • [5.1.1 创建 MessageVO.java](#5.1.1 创建 MessageVO.java)
        • [5.1.2 创建 SerializableMessage.java](#5.1.2 创建 SerializableMessage.java)
      • [5.2 定义仓库接口 (`ChatHistoryRepository.java`)](#5.2 定义仓库接口 (ChatHistoryRepository.java))
      • [5.3 实现 Redis 聊天历史仓库 (`RedisChatHistoryRepository.java`)](#5.3 实现 Redis 聊天历史仓库 (RedisChatHistoryRepository.java))
      • [5.4 实现聊天记忆 (`RedisChatMemory.java`)](#5.4 实现聊天记忆 (RedisChatMemory.java))
      • [5.5 配置 ChatClient (`CommonConfiguration.java`)](#5.5 配置 ChatClient (CommonConfiguration.java))
      • [5.6 实现聊天控制器 (`ChatController.java`)](#5.6 实现聊天控制器 (ChatController.java))
      • [5.7 实现聊天历史控制器 (`ChatHistoryController.java`)](#5.7 实现聊天历史控制器 (ChatHistoryController.java))
    • [6. 测试和验证](#6. 测试和验证)
      • [6.1 启动服务](#6.1 启动服务)
      • [6.2 测试聊天记忆功能](#6.2 测试聊天记忆功能)
      • [6.3 测试用户隔离功能](#6.3 测试用户隔离功能)
      • [6.4 测试历史记录持久化](#6.4 测试历史记录持久化)
      • [6.5 测试历史记录管理](#6.5 测试历史记录管理)

1. 项目概述

本教程详细介绍如何使用 Spring Boot 3 整合 Spring AI 实现一个具有记忆功能的 AI 助手。该实现使用 Redis 作为存储介质,支持用户级别的会话隔离和 30 天的对话历史持久化。

技术栈

  • Spring Boot 3.3.0
  • Java 17
  • Spring AI
  • Redis 6.0+
  • MyBatis Plus
  • MySQL 8.0
  • Sa-Token(用户认证)

2. 环境准备

2.1 安装必要软件

2.2 配置环境变量

确保 JAVA_HOMEMAVEN_HOME 已正确配置。

3. 项目初始化

3.1 创建 Spring Boot 项目

使用 Spring Initializr 创建项目:

  • 访问 Spring Initializr
  • 选择 Spring Boot 3.3.0
  • 选择 Java 17
  • 添加依赖:Spring Web, Spring Data Redis, MyBatis Plus, MySQL Driver, Spring Boot DevTools

3.2 配置 Maven 依赖

pom.xml 文件中添加以下依赖:

xml 复制代码
<dependencies>
    <!-- Spring Boot 核心依赖 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    
    <!-- Spring Data Redis -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    
    <!-- MyBatis Plus -->
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>3.5.5</version>
    </dependency>
    
    <!-- MySQL 驱动 -->
    <dependency>
        <groupId>com.mysql</groupId>
        <artifactId>mysql-connector-j</artifactId>
        <scope>runtime</scope>
    </dependency>
    
    <!-- Spring AI -->
    <dependency>
        <groupId>org.springframework.ai</groupId>
        <artifactId>spring-ai-openai</artifactId>
        <version>1.0.0</version>
    </dependency>
    
    <!-- Sa-Token 认证 -->
    <dependency>
        <groupId>cn.dev33</groupId>
        <artifactId>sa-token-spring-boot-starter</artifactId>
        <version>1.38.1</version>
    </dependency>
    
    <!-- Jackson 序列化 -->
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
    </dependency>
    
    <!-- Lombok -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    
    <!-- Spring Boot 测试 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

4. 核心配置

4.1 应用配置文件 (application.yml)

创建 src/main/resources/application.yml 文件,配置应用信息:

yaml 复制代码
server:
  port: 9527
  servlet:
    context-path: /api

spring:
  application:
    name: smart-pic-community-backend
  # Redis 配置
  data:
    redis:
      database: 2
      host: localhost
      port: 6379
      timeout: 5000
  # 数据库配置
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/smart_pic_community
    username: root
    password: your_password
  # Spring AI 配置
  ai:
    openai:
      base-url: https://api.deepseek.com/  # 使用 DeepSeek API
      api-key: your_api_key
      chat:
        options:
          model: deepseek-chat

4.2 Redis 配置 (RedisConfiguration.java)

创建 Redis 配置类,确保正确序列化对象:

java 复制代码
package com.spc.smartpiccommunitybackend.config;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

@Configuration
@Slf4j
public class RedisConfiguration {

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        log.info("开始创建redis模板对象...");
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        
        // 设置连接工厂
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        
        // 使用 StringRedisSerializer 来序列化和反序列化 redis 的 key
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        
        // key 采用 String 的序列化方式
        redisTemplate.setKeySerializer(stringRedisSerializer);
        redisTemplate.setHashKeySerializer(stringRedisSerializer);
        
        // 使用 Jackson2JsonRedisSerializer 来序列化和反序列化 redis 的 value
        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
        
        // value 采用 JSON 的序列化方式
        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
        redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
        
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }
}

注意事项

  • 使用 Jackson2JsonRedisSerializer 而不是 StringRedisSerializer 可以避免 ClassCastException
  • 启用默认类型可以确保反序列化时能正确识别对象类型

5. 核心功能实现

5.1 创建模型类

5.1.1 创建 MessageVO.java

创建消息视图对象,用于前端展示:

java 复制代码
package com.spc.smartpiccommunitybackend.model.vo.ai;

import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.ai.chat.messages.Message;

@NoArgsConstructor
@Data
public class MessageVO {
    private String role;
    private String content;

    public MessageVO(Message message) {
        this.role = switch (message.getMessageType()) {
            case USER -> "user";
            case ASSISTANT -> "assistant";
            case SYSTEM -> "system";
            default -> "";
        };
        this.content = message.getText();
    }
}

功能说明

  • 将 Spring AI 的 Message 对象转换为前端可识别的格式
  • 根据消息类型设置不同的角色
  • 提取消息内容用于展示
5.1.2 创建 SerializableMessage.java

创建可序列化的消息对象,用于 Redis 存储:

java 复制代码
package com.spc.smartpiccommunitybackend.model.entity.ai;

import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;

@Data
@NoArgsConstructor
public class SerializableMessage implements Serializable {
    private static final long serialVersionUID = 1L;

    private String role;
    private String content;
    private String messageType;
    private Long timestamp;

    public SerializableMessage(String role, String content, String messageType) {
        this.role = role;
        this.content = content;
        this.messageType = messageType;
        this.timestamp = System.currentTimeMillis();
    }

    public SerializableMessage(String role, String content) {
        this(role, content, "user");
    }
}

功能说明

  • 实现 Serializable 接口,支持 Redis 序列化
  • 包含角色、内容、消息类型和时间戳字段
  • 提供多个构造方法,方便使用

5.2 定义仓库接口 (ChatHistoryRepository.java)

创建聊天历史仓库接口:

java 复制代码
package com.spc.smartpiccommunitybackend.repository;

import java.util.List;
import java.util.Map;

public interface ChatHistoryRepository {
    /**
     * 保存会话记录
     */
    void save(String type, String chatId, Long userId);
    
    /**
     * 获取用户的会话ID列表
     */
    List<String> getChatIds(Long userId, String type);
    
    /**
     * 保存聊天消息
     */
    void saveMessage(String chatId, String message, String sender);
    
    /**
     * 获取聊天消息历史
     */
    List<String> getMessages(String chatId);
    
    /**
     * 删除会话
     */
    void deleteChat(Long userId, String type, String chatId);
    
    /**
     * 获取会话信息
     */
    Map<Object, Object> getSessionInfo(String chatId);
}

5.3 实现 Redis 聊天历史仓库 (RedisChatHistoryRepository.java)

实现基于 Redis 的聊天历史仓库:

java 复制代码
package com.spc.smartpiccommunitybackend.repository;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.core.JsonProcessingException;
import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Component
@RequiredArgsConstructor
public class RedisChatHistoryRepository implements ChatHistoryRepository {

    private final RedisTemplate<String, Object> redisTemplate;

    // Redis key前缀
    private static final String CHAT_HISTORY_PREFIX = "chat:history:";
    private static final String CHAT_SESSION_PREFIX = "chat:session:";
    private static final String CHAT_MESSAGES_PREFIX = "chat:messages:";

    /**
     * 保存会话记录
     */
    @Override
    public void save(String type, String chatId, Long userId) {
        // 保存会话信息
        String sessionKey = CHAT_SESSION_PREFIX + chatId;
        Map<String, Object> sessionInfo = new HashMap<>();
        sessionInfo.put("userId", String.valueOf(userId));
        sessionInfo.put("type", type);
        sessionInfo.put("createTime", System.currentTimeMillis());
        sessionInfo.put("lastUpdateTime", System.currentTimeMillis());
        redisTemplate.opsForHash().putAll(sessionKey, sessionInfo);
        // 设置过期时间为30天
        redisTemplate.expire(sessionKey, 30, TimeUnit.DAYS);

        // 将chatId添加到用户的聊天历史列表中
        String historyKey = CHAT_HISTORY_PREFIX + userId + ":" + type;
        redisTemplate.opsForSet().add(historyKey, chatId);
        // 设置过期时间为30天
        redisTemplate.expire(historyKey, 30, TimeUnit.DAYS);
    }

    /**
     * 获取用户的会话ID列表
     */
    @Override
    public List<String> getChatIds(Long userId, String type) {
        String historyKey = CHAT_HISTORY_PREFIX + userId + ":" + type;
        Set<Object> chatIds = redisTemplate.opsForSet().members(historyKey);
        if (chatIds == null || chatIds.isEmpty()) {
            return Collections.emptyList();
        }
        return chatIds.stream()
                .map(Object::toString)
                .collect(Collectors.toList());
    }

    /**
     * 保存聊天消息
     */
    @Override
    public void saveMessage(String chatId, String message, String sender) {
        String messagesKey = CHAT_MESSAGES_PREFIX + chatId;
        // 创建消息对象
        Map<String, Object> messageInfo = new HashMap<>();
        messageInfo.put("content", message);
        messageInfo.put("sender", sender);
        messageInfo.put("timestamp", System.currentTimeMillis());
        // 使用JSON格式保存消息
        ObjectMapper objectMapper = new ObjectMapper();
        try {
            String jsonMessage = objectMapper.writeValueAsString(messageInfo);
            redisTemplate.opsForList().rightPush(messagesKey, jsonMessage);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
            // 如果JSON序列化失败,使用原始消息
            redisTemplate.opsForList().rightPush(messagesKey, message);
        }
        // 设置过期时间为30天
        redisTemplate.expire(messagesKey, 30, TimeUnit.DAYS);

        // 更新会话的最后更新时间
        String sessionKey = CHAT_SESSION_PREFIX + chatId;
        redisTemplate.opsForHash().put(sessionKey, "lastUpdateTime", System.currentTimeMillis());
        // 确保会话信息也有过期时间
        redisTemplate.expire(sessionKey, 30, TimeUnit.DAYS);
    }

    /**
     * 获取聊天消息历史
     */
    @Override
    public List<String> getMessages(String chatId) {
        String messagesKey = CHAT_MESSAGES_PREFIX + chatId;
        List<Object> messages = redisTemplate.opsForList().range(messagesKey, 0, -1);
        if (messages == null || messages.isEmpty()) {
            return Collections.emptyList();
        }
        return messages.stream()
                .map(Object::toString)
                .collect(Collectors.toList());
    }

    /**
     * 删除会话
     */
    @Override
    public void deleteChat(Long userId, String type, String chatId) {
        // 从用户的聊天历史列表中删除
        String historyKey = CHAT_HISTORY_PREFIX + userId + ":" + type;
        redisTemplate.opsForSet().remove(historyKey, chatId);

        // 删除会话信息
        String sessionKey = CHAT_SESSION_PREFIX + chatId;
        redisTemplate.delete(sessionKey);

        // 删除聊天消息
        String messagesKey = CHAT_MESSAGES_PREFIX + chatId;
        redisTemplate.delete(messagesKey);
    }

    /**
     * 获取会话信息
     */
    @Override
    public Map<Object, Object> getSessionInfo(String chatId) {
        String sessionKey = CHAT_SESSION_PREFIX + chatId;
        return redisTemplate.opsForHash().entries(sessionKey);
    }
}

注意事项

  • 使用不同的 Redis key 前缀区分不同类型的数据
  • 为所有 Redis 键设置过期时间,避免内存泄漏
  • 处理 JSON 序列化失败的情况,提高系统健壮性

5.4 实现聊天记忆 (RedisChatMemory.java)

实现基于 Redis 的聊天记忆,支持 Spring AI 的 ChatMemory 接口:

java 复制代码
package com.spc.smartpiccommunitybackend.config;

import org.springframework.ai.chat.messages.Message;
import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

@Component
public class RedisChatMemory implements ChatMemory {

    private final RedisTemplate<String, Object> redisTemplate;
    private static final String MEMORY_KEY_PREFIX = "chat:memory:";
    private static final long EXPIRATION_DAYS = 30;

    public RedisChatMemory(RedisTemplate<String, Object> redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    @Override
    public void add(String key, List<Message> messages) {
        // 为特定会话添加多条消息
        for (Message message : messages) {
            addMessage(key, message);
        }
    }

    @Override
    public List<Message> get(String key, int maxCount) {
        // 实现 get 方法,根据 key 获取消息
        List<Message> messages = getMessages(key);
        // 如果指定了最大数量,返回不超过该数量的消息
        if (maxCount > 0 && messages.size() > maxCount) {
            return messages.subList(messages.size() - maxCount, messages.size());
        }
        return messages;
    }

    @Override
    public void clear() {
        // 清理所有会话记忆
        // 注意:这个操作会删除所有聊天记忆,谨慎使用
    }

    /**
     * 为特定会话添加消息
     */
    public void addMessage(String chatId, Message message) {
        String key = MEMORY_KEY_PREFIX + chatId;
        redisTemplate.opsForList().rightPush(key, message);
        redisTemplate.expire(key, EXPIRATION_DAYS, TimeUnit.DAYS);
    }

    /**
     * 获取特定会话的消息
     */
    public List<Message> getMessages(String chatId) {
        String key = MEMORY_KEY_PREFIX + chatId;
        List<Object> objects = redisTemplate.opsForList().range(key, 0, -1);
        List<Message> messages = new ArrayList<>();
        if (objects != null) {
            for (Object obj : objects) {
                if (obj instanceof Message) {
                    messages.add((Message) obj);
                }
            }
        }
        return messages;
    }

    /**
     * 清理特定会话的记忆
     */
    public void clear(String chatId) {
        String key = MEMORY_KEY_PREFIX + chatId;
        redisTemplate.delete(key);
    }
}

注意事项

  • 实现 ChatMemory 接口以支持 Spring AI 的消息记忆功能
  • 为每条消息设置过期时间,确保内存使用合理
  • 提供批量添加和获取消息的方法,提高性能

5.5 配置 ChatClient (CommonConfiguration.java)

配置 Spring AI 的 ChatClient

java 复制代码
package com.spc.smartpiccommunitybackend.config;

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.ChatMemory;
import org.springframework.ai.openai.OpenAiChatModel;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class CommonConfiguration {

    @Bean
    public ChatClient chatClient(OpenAiChatModel openAiChatModel, ChatMemory chatMemory) {
        return ChatClient.builder(openAiChatModel)
                .defaultAdvisors(
                        new SimpleLoggerAdvisor(),
                        new MessageChatMemoryAdvisor(chatMemory)
                )
                .build();
    }
}

注意事项

  • 使用 MessageChatMemoryAdvisor 来启用聊天记忆功能
  • 使用 SimpleLoggerAdvisor 来记录聊天交互,方便调试

5.6 实现聊天控制器 (ChatController.java)

实现聊天控制器,处理 AI 对话请求:

java 复制代码
package com.spc.smartpiccommunitybackend.controller;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.spc.smartpiccommunitybackend.repository.RedisChatHistoryRepository;
import com.spc.smartpiccommunitybackend.service.UserService;
import com.spc.smartpiccommunitybackend.utils.ErrorCode;
import com.spc.smartpiccommunitybackend.utils.ThrowUtils;
import com.spc.smartpiccommunitybackend.pojo.User;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.messages.Message;
import org.springframework.ai.chat.messages.SystemMessage;
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.ai.chat.messages.AssistantMessage;
import org.springframework.beans.factory.annotation.Resource;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Flux;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

@RestController
@RequestMapping("/ai")
public class ChatController {

    private final ChatClient chatClient;
    private final RedisChatHistoryRepository chatHistoryRepository;
    @Resource
    private UserService userService;

    public ChatController(ChatClient chatClient, RedisChatHistoryRepository chatHistoryRepository) {
        this.chatClient = chatClient;
        this.chatHistoryRepository = chatHistoryRepository;
    }

    @RequestMapping(value = "/chat", produces = "text/html;charset=UTF-8")
    public Flux<String> chat(@RequestParam(defaultValue = "讲个笑话") String prompt,
                             String chatId,
                             HttpServletRequest request) {
        User loginUser = userService.getLoginUser(request);
        // 校验登录用户是否为空
        ThrowUtils.throwIf(loginUser == null, ErrorCode.NOT_LOGIN_ERROR);
        Long userId = loginUser.getId();
        
        // 保存会话信息
        chatHistoryRepository.save("chat", chatId, userId);

        // 获取历史对话消息作为上下文
        List<Message> messages = new ArrayList<>();
        
        // 添加系统消息
        SystemMessage systemMessage = new SystemMessage(
            "你是一个智能图片社区的AI助手,名为虹小智。请用友好、专业的语气回答用户问题," +
            "提供关于图片社区的相关信息和帮助。"
        );
        messages.add(systemMessage);
        
        // 获取并解析历史消息
        List<String> historyMessages = chatHistoryRepository.getMessages(chatId);
        ObjectMapper objectMapper = new ObjectMapper();
        
        for (String messageStr : historyMessages) {
            try {
                JsonNode node = objectMapper.readTree(messageStr);
                String sender = node.get("sender").asText();
                String content = node.get("content").asText();
                
                if ("user".equals(sender)) {
                    messages.add(new UserMessage(content));
                } else if ("ai".equals(sender)) {
                    messages.add(new AssistantMessage(content));
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        
        // 添加用户当前消息
        messages.add(new UserMessage(prompt));
        
        // 保存用户消息到历史记录
        chatHistoryRepository.saveMessage(chatId, prompt, "user");
        
        // 调用AI模型获取响应
        return chatClient.stream(messages)
                .doOnNext(response -> {
                    // 保存AI响应到历史记录
                    chatHistoryRepository.saveMessage(chatId, response, "ai");
                });
    }
}

注意事项

  • 验证用户登录状态,确保会话隔离
  • 保存用户消息和 AI 响应到历史记录
  • 使用 Flux 实现流式响应,提高用户体验
  • 处理历史消息解析异常,提高系统健壮性

5.7 实现聊天历史控制器 (ChatHistoryController.java)

实现聊天历史控制器,处理聊天历史的获取和删除:

java 复制代码
package com.spc.smartpiccommunitybackend.controller;

import com.spc.smartpiccommunitybackend.repository.RedisChatHistoryRepository;
import com.spc.smartpiccommunitybackend.service.UserService;
import com.spc.smartpiccommunitybackend.utils.ErrorCode;
import com.spc.smartpiccommunitybackend.utils.ThrowUtils;
import com.spc.smartpiccommunitybackend.pojo.User;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import java.util.List;

@RestController
@RequestMapping("/ai/history")
public class ChatHistoryController {

    private final RedisChatHistoryRepository chatHistoryRepository;
    private final UserService userService;

    public ChatHistoryController(RedisChatHistoryRepository chatHistoryRepository, UserService userService) {
        this.chatHistoryRepository = chatHistoryRepository;
        this.userService = userService;
    }

    /**
     * 获取用户的聊天历史ID列表
     */
    @GetMapping("/{type}")
    public List<String> getChatHistory(@PathVariable String type, HttpServletRequest request) {
        User loginUser = userService.getLoginUser(request);
        ThrowUtils.throwIf(loginUser == null, ErrorCode.NOT_LOGIN_ERROR);
        Long userId = loginUser.getId();
        
        return chatHistoryRepository.getChatIds(userId, type);
    }

    /**
     * 删除指定聊天历史
     */
    @DeleteMapping("/{type}/{chatId}")
    public boolean deleteChatHistory(@PathVariable String type, 
                                    @PathVariable String chatId, 
                                    HttpServletRequest request) {
        User loginUser = userService.getLoginUser(request);
        ThrowUtils.throwIf(loginUser == null, ErrorCode.NOT_LOGIN_ERROR);
        Long userId = loginUser.getId();
        
        chatHistoryRepository.deleteChat(userId, type, chatId);
        return true;
    }
}

注意事项

  • 验证用户登录状态,确保只能操作自己的聊天历史
  • 提供获取和删除聊天历史的接口,方便前端管理

6. 测试和验证

6.1 启动服务

  1. 确保 Redis 和 MySQL 服务已启动
  2. 运行 Spring Boot 应用
  3. 访问 http://localhost:9527/api/ai/chat?prompt=你好&chatId=test123 测试 AI 响应

6.2 测试聊天记忆功能

  1. 发送第一条消息:http://localhost:9527/api/ai/chat?prompt=你好,我叫张三&chatId=test123
  2. 发送第二条消息:http://localhost:9527/api/ai/chat?prompt=你知道我叫什么名字吗?&chatId=test123
  3. 验证 AI 能否正确回答你的名字

6.3 测试用户隔离功能

  1. 使用不同用户登录
  2. 验证不同用户的聊天历史是否相互隔离

6.4 测试历史记录持久化

  1. 发送多条消息
  2. 重启服务
  3. 验证聊天历史是否仍然存在

6.5 测试历史记录管理

  1. 获取用户的聊天历史列表:GET http://localhost:9527/api/ai/history/chat
  2. 删除指定聊天历史:DELETE http://localhost:9527/api/ai/history/chat/test123
  3. 验证聊天历史是否已删除
相关推荐
Binary_ey2 小时前
光刻技术第22期 | 贝叶斯压缩感知光源优化的优化技术及对比分析
人工智能·深度学习·机器学习
奔跑草-2 小时前
【AI日报】每日AI最新消息2026-04-07
人工智能·大模型·github·开源软件
rainy雨2 小时前
免费且好用的精益工具在哪里?2026年精益工具清单整理
大数据·人工智能·信息可视化·数据挖掘·数据分析·精益工程
小黄人软件2 小时前
【研究让AI做擅长的事】有哪些强大的研究方法 ,让研究自动发生
人工智能·安全
蚂蚁数据AntData2 小时前
破解AI“机器味“困境:HeartBench评测实践详解
大数据·人工智能·算法·机器学习·语言模型·开源
云水木石2 小时前
实战备忘录:Claude Code + superpowers进行浏览器内核升级
人工智能
Awu12272 小时前
⚡精通 Claude 第 1 课:掌握 Slash Commands
前端·人工智能·ai编程
Mr_Xuhhh2 小时前
从ArrayList到LinkedList:理解链表,掌握Java集合的另一种选择
java·数据结构·链表
Jane - UTS 数据传输系统2 小时前
立足国家“十五五”数智化战略大局,紧扣上海“2+3+6+6”产业布局,UTS数据传输系统筑牢数智化转型数据底座
大数据·人工智能·跨平台·信创·跨数据库·十五五·国产数据库适配