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

## 1. 需求概述

修改 ChatController 中的 `chat` 和 `chatStreaming` 接口，将对话数据保存到 `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<String> 转 JSON |
| query | query | 直接映射 |
| response_mode | response_mode | 直接映射 |
| conversation_id | conversation_id | 直接映射 |
| user | user_id | 直接映射 |
| appId | app_id | 直接映射 |

**从 Dify 响应获取（blocking模式）：**
```json
{
  "task_id": "...",
  "message_id": "...",
  "answer": "...",
  "conversation_id": "..."
}
```

**从 Dify 响应获取（streaming模式）：**
流式数据最后一帧包含完整信息，需要解析 `data:` 行中的 JSON。

## 4. 实现细节

### 4.1 实体类 IaMessageEntity

```java
@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() 方法

```java
@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` 方法或使用拦截器模式：

```java
// 方案：修改 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 建表语句

```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对话消息记录表';
```
