feat(chat): 新增AI聊天记录持久化功能

新增KeyboardAiChatMessage实体及对应Mapper、Service,在ChatServiceImpl中同步对话时保存用户与AI消息到数据库,实现聊天记录持久化
This commit is contained in:
2026-01-26 17:11:18 +08:00
parent 6bb905bb30
commit b887e52f55
6 changed files with 175 additions and 0 deletions

View File

@@ -0,0 +1,12 @@
package com.yolo.keyborad.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.yolo.keyborad.model.entity.KeyboardAiChatMessage;
/*
* @author: ziin
* @date: 2026/1/26 17:00
*/
public interface KeyboardAiChatMessageMapper extends BaseMapper<KeyboardAiChatMessage> {
}

View File

@@ -0,0 +1,78 @@
package com.yolo.keyborad.model.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.v3.oas.annotations.media.Schema;
import java.util.Date;
import lombok.Data;
/*
* @author: ziin
* @date: 2026/1/26 17:00
*/
/**
* 用户与AI情感陪伴角色的聊天记录表
*/
@Schema(description="用户与AI情感陪伴角色的聊天记录表")
@Data
@TableName(value = "keyboard_ai_chat_message")
public class KeyboardAiChatMessage {
/**
* 聊天消息唯一ID
*/
@TableId(value = "id", type = IdType.AUTO)
@Schema(description="聊天消息唯一ID")
private Long id;
/**
* 用户ID
*/
@TableField(value = "user_id")
@Schema(description="用户ID")
private Long userId;
/**
* 陪伴角色ID
*/
@TableField(value = "companion_id")
@Schema(description="陪伴角色ID")
private Long companionId;
/**
* 消息发送方1=用户2=AI
*/
@TableField(value = "sender")
@Schema(description="消息发送方1=用户2=AI")
private Short sender;
/**
* 聊天消息内容
*/
@TableField(value = "content")
@Schema(description="聊天消息内容")
private String content;
/**
* AI识别到的用户情绪
*/
@TableField(value = "emotion_detected")
@Schema(description="AI识别到的用户情绪")
private String emotionDetected;
/**
* AI提供的支持类型倾听/共情/安抚等)
*/
@TableField(value = "support_type")
@Schema(description="AI提供的支持类型倾听/共情/安抚等)")
private String supportType;
/**
* 消息创建时间
*/
@TableField(value = "created_at")
@Schema(description="消息创建时间")
private Date createdAt;
}

View File

@@ -0,0 +1,13 @@
package com.yolo.keyborad.service;
import com.yolo.keyborad.model.entity.KeyboardAiChatMessage;
import com.baomidou.mybatisplus.extension.service.IService;
/*
* @author: ziin
* @date: 2026/1/26 17:00
*/
public interface KeyboardAiChatMessageService extends IService<KeyboardAiChatMessage>{
}

View File

@@ -10,6 +10,7 @@ import com.yolo.keyborad.config.NacosAppConfigCenter;
import com.yolo.keyborad.exception.BusinessException;
import com.yolo.keyborad.model.dto.chat.ChatReq;
import com.yolo.keyborad.model.dto.chat.ChatStreamMessage;
import com.yolo.keyborad.model.entity.KeyboardAiChatMessage;
import com.yolo.keyborad.model.entity.KeyboardCharacter;
import com.yolo.keyborad.model.entity.KeyboardUser;
import com.yolo.keyborad.model.entity.KeyboardUserCallLog;
@@ -77,6 +78,9 @@ public class ChatServiceImpl implements ChatService {
@Resource
private KeyboardAiCompanionService aiCompanionService;
@Resource
private KeyboardAiChatMessageService aiChatMessageService;
@Resource
private ElevenLabsService elevenLabsService;
@@ -372,6 +376,9 @@ public class ChatServiceImpl implements ChatService {
long llmDuration = System.currentTimeMillis() - startTime;
log.info("LLM 完成, userId: {}, 耗时: {}ms, 响应长度: {}", userId, llmDuration, response.length());
// 保存用户消息和AI响应到聊天记录
saveChatMessages(Long.parseLong(userId), companionId, content, response);
// 生成音频任务 ID
String audioId = UUID.randomUUID().toString().replace("-", "");
@@ -388,6 +395,33 @@ public class ChatServiceImpl implements ChatService {
.build();
}
/**
* 保存用户消息和AI响应到聊天记录
*/
private void saveChatMessages(Long userId, Long companionId, String userContent, String aiResponse) {
Date now = new Date();
// 保存用户消息 (sender=1)
KeyboardAiChatMessage userMessage = new KeyboardAiChatMessage();
userMessage.setUserId(userId);
userMessage.setCompanionId(companionId);
userMessage.setSender((short) 1);
userMessage.setContent(userContent);
userMessage.setCreatedAt(now);
// 保存AI响应 (sender=2)
KeyboardAiChatMessage aiMessage = new KeyboardAiChatMessage();
aiMessage.setUserId(userId);
aiMessage.setCompanionId(companionId);
aiMessage.setSender((short) 2);
aiMessage.setContent(aiResponse);
aiMessage.setCreatedAt(now);
// 批量保存
aiChatMessageService.saveBatch(List.of(userMessage, aiMessage));
log.info("聊天记录保存成功, userId: {}, companionId: {}", userId, companionId);
}
/**
* 调用 LLM 生成响应
*/

View File

@@ -0,0 +1,18 @@
package com.yolo.keyborad.service.impl;
import org.springframework.stereotype.Service;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.List;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yolo.keyborad.mapper.KeyboardAiChatMessageMapper;
import com.yolo.keyborad.model.entity.KeyboardAiChatMessage;
import com.yolo.keyborad.service.KeyboardAiChatMessageService;
/*
* @author: ziin
* @date: 2026/1/26 17:00
*/
@Service
public class KeyboardAiChatMessageServiceImpl extends ServiceImpl<KeyboardAiChatMessageMapper, KeyboardAiChatMessage> implements KeyboardAiChatMessageService{
}

View File

@@ -0,0 +1,20 @@
<?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.yolo.keyborad.mapper.KeyboardAiChatMessageMapper">
<resultMap id="BaseResultMap" type="com.yolo.keyborad.model.entity.KeyboardAiChatMessage">
<!--@mbg.generated-->
<!--@Table keyboard_ai_chat_message-->
<id column="id" jdbcType="BIGINT" property="id" />
<result column="user_id" jdbcType="BIGINT" property="userId" />
<result column="companion_id" jdbcType="BIGINT" property="companionId" />
<result column="sender" jdbcType="SMALLINT" property="sender" />
<result column="content" jdbcType="VARCHAR" property="content" />
<result column="emotion_detected" jdbcType="VARCHAR" property="emotionDetected" />
<result column="support_type" jdbcType="VARCHAR" property="supportType" />
<result column="created_at" jdbcType="TIMESTAMP" property="createdAt" />
</resultMap>
<sql id="Base_Column_List">
<!--@mbg.generated-->
id, user_id, companion_id, sender, content, emotion_detected, support_type, created_at
</sql>
</mapper>