doc.md 5.3 KB

保存聊天记录到 ia_messages 表设计文档

1. 需求概述

修改 ChatController 中的 chatchatStreaming 接口,将对话数据保存到 ia_messages 表中。

2. 表结构

字段名 类型 说明
id BIGINT 主键,自增
task_id VARCHAR(64) 任务id (Dify返回)
message_id VARCHAR(64) 信息id (Dify返回)
conversation_id VARCHAR(64) 会话id
app_id VARCHAR(64) 应用id
inputs TEXT 输入 (JSON格式)
query TEXT 问题
response_mode VARCHAR(20) 回复模式 (blocking/streaming)
user_id VARCHAR(64) 用户id
answer LONGTEXT AI回复
created_at DATETIME 创建时间

3. 技术方案

3.1 架构设计

采用标准三层架构:

  • Entity: IaMessageEntity - 映射 ia_messages 表
  • Mapper: IaMessageMapper - 数据访问层(继承 MyBaseMapper)
  • Service: IaMessageService / IaMessageServiceImpl - 业务逻辑层
  • Controller: 修改 ChatController - 在返回前调用 Service 保存数据

3.2 数据流

ChatController.chat/chatStreaming
    ↓
调用 Dify API
    ↓
获取响应结果
    ↓
提取关键字段 → 构建 IaMessageEntity
    ↓
调用 IaMessageService.save()
    ↓
返回原结果给客户端

3.3 数据来源映射

从 ChatDTO 获取: | ChatDTO 字段 | ia_messages 字段 | 说明 | |--------------|------------------|------| | input | inputs | List 转 JSON | | query | query | 直接映射 | | response_mode | response_mode | 直接映射 | | conversation_id | conversation_id | 直接映射 | | user | user_id | 直接映射 | | appId | app_id | 直接映射 |

从 Dify 响应获取(blocking模式):

{
  "task_id": "...",
  "message_id": "...",
  "answer": "...",
  "conversation_id": "..."
}

从 Dify 响应获取(streaming模式): 流式数据最后一帧包含完整信息,需要解析 data: 行中的 JSON。

4. 实现细节

4.1 实体类 IaMessageEntity

@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("ia_messages")
public class IaMessageEntity {
    @TableId(type = IdType.AUTO)
    private Long id;
    private String taskId;
    private String messageId;
    private String conversationId;
    private String appId;
    private String inputs;
    private String query;
    private String responseMode;
    private String userId;
    private String answer;
    private LocalDateTime createdAt;
}

4.2 修改 chat() 方法

@PostMapping("chat")
public Result<Object> chat(@RequestBody ChatDTO chatDTO) throws JsonProcessingException {
    String url = difyProperties.getBaseUrl() + "chat-messages";
    String body = JacksonHolder.OBJECT_MAPPER.writeValueAsString(chatDTO);
    Result<Object> result = httpService.post(url, null, "Bearer "+difyProperties.getAppKeys().get(chatDTO.getAppId()), body, new TypeReference<>() {});
    
    // 保存到数据库(异步处理,不影响主流程)
    if (result.isOk() && result.getData() != null) {
        saveChatMessage(chatDTO, result.getData());
    }
    
    return result;
}

4.3 修改 chatStreaming() 方法

流式响应需要先收集完整 answer,再保存。修改 postStream 方法或使用拦截器模式:

// 方案:修改 postStream 方法,增加回调处理最后一帧数据
httpService.postStream(url, authorization, body, emitter, (lastData) -> {
    // 解析 lastData 保存到数据库
    saveStreamingMessage(chatDTO, lastData);
});

5. 边界条件处理

  1. 数据库保存失败:使用 try-catch 包裹,不影响 API 返回
  2. Dify 响应解析失败:记录日志,不影响主流程
  3. 流式响应中断:在 emitter 的 onError 中处理
  4. 字段为空处理:允许为空,按实际返回数据存储

6. 影响文件

文件类型 路径 操作
Entity storlead-knowledge-core/.../entity/IaMessageEntity.java 新增
Mapper storlead-knowledge-biz/.../mapper/IaMessageMapper.java 新增
Service storlead-knowledge-biz/.../service/IaMessageService.java 新增
ServiceImpl storlead-knowledge-biz/.../service/impl/IaMessageServiceImpl.java 新增
Controller storlead-knowledge-api/.../api/ChatController.java 修改
HttpService storlead-knowledge-core/.../utils/HttpService.java 修改(增加回调)

7. SQL 建表语句

CREATE TABLE IF NOT EXISTS `ia_messages` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
  `task_id` varchar(64) DEFAULT NULL COMMENT '任务id',
  `message_id` varchar(64) DEFAULT NULL COMMENT '信息id',
  `conversation_id` varchar(64) DEFAULT NULL COMMENT '会话id',
  `app_id` varchar(64) DEFAULT NULL COMMENT '应用id',
  `inputs` text COMMENT '输入(JSON)',
  `query` text COMMENT '问题',
  `response_mode` varchar(20) DEFAULT NULL COMMENT '回复模式',
  `user_id` varchar(64) DEFAULT NULL COMMENT '用户id',
  `answer` longtext COMMENT 'AI回复',
  `created_at` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  PRIMARY KEY (`id`),
  KEY `idx_conversation_id` (`conversation_id`),
  KEY `idx_user_id` (`user_id`),
  KEY `idx_app_id` (`app_id`),
  KEY `idx_created_at` (`created_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='AI对话消息记录表';