Files
keyboard/keyBoard/Class/AiTalk/M/聊天记录Model说明.md
2026-01-26 20:36:51 +08:00

10 KiB
Raw Blame History

聊天记录 Model 说明

📦 Model 结构

1. KBChatHistoryModel聊天记录模型

@interface KBChatHistoryModel : NSObject

/// 消息 ID
@property (nonatomic, assign) NSInteger messageId;

/// 发送者0-用户1-AI
@property (nonatomic, assign) KBChatSender sender;

/// 消息内容
@property (nonatomic, copy) NSString *content;

/// 创建时间
@property (nonatomic, copy) NSString *createdAt;

/// 是否是用户消息
@property (nonatomic, assign, readonly) BOOL isUserMessage;

/// 是否是 AI 消息
@property (nonatomic, assign, readonly) BOOL isAssistantMessage;

@end

2. KBChatHistoryPageModel分页数据模型

@interface KBChatHistoryPageModel : NSObject

/// 聊天记录列表
@property (nonatomic, strong) NSArray<KBChatHistoryModel *> *records;

/// 总记录数
@property (nonatomic, assign) NSInteger total;

/// 每页大小
@property (nonatomic, assign) NSInteger size;

/// 当前页码
@property (nonatomic, assign) NSInteger current;

/// 总页数
@property (nonatomic, assign) NSInteger pages;

/// 是否还有更多数据
@property (nonatomic, assign, readonly) BOOL hasMore;

@end

🌐 接口说明

接口地址

POST /chat/history

请求参数

{
  "companionId": 0,  // AI 陪聊角色 ID使用 KBPersonaModel.personaId
  "pageNum": 1,      // 页码
  "pageSize": 20     // 每页大小
}

参数说明

  • companionId:使用 fetchPersonasWithPageNum 接口返回的 KBPersonaModel.personaId
  • pageNum:页码,从 1 开始
  • pageSize:每页大小,建议 20

响应格式

{
  "code": 0,
  "message": "success",
  "data": {
    "records": [
      {
        "id": 1,
        "sender": 1,        // 1-用户右侧2-AI左侧
        "content": "你好",
        "createdAt": "2026-01-26 10:00:00"
      },
      {
        "id": 2,
        "sender": 2,
        "content": "你好!有什么可以帮助你的吗?",
        "createdAt": "2026-01-26 10:00:05"
      }
    ],
    "total": 100,
    "current": 1,
    "pages": 5
  }
}

sender 字段说明

  • sender = 1:用户消息(显示在右侧)
  • sender = 2AI 消息(显示在左侧)

📝 使用示例

1. 在 AiVM 中调用接口

#import "AiVM.h"

AiVM *aiVM = [[AiVM alloc] init];

[aiVM fetchChatHistoryWithCompanionId:123
                               pageNum:1
                              pageSize:20
                            completion:^(KBChatHistoryPageModel *pageModel, NSError *error) {
    if (error) {
        NSLog(@"加载失败:%@", error.localizedDescription);
        return;
    }
    
    NSLog(@"加载成功:%ld 条消息", pageModel.records.count);
    
    for (KBChatHistoryModel *message in pageModel.records) {
        if (message.isUserMessage) {
            NSLog(@"用户:%@", message.content);
        } else {
            NSLog(@"AI%@", message.content);
        }
    }
    
    if (pageModel.hasMore) {
        NSLog(@"还有更多数据");
    }
}];

2. 在 VC 中使用

@interface YourViewController ()
@property (nonatomic, strong) AiVM *aiVM;
@property (nonatomic, strong) NSMutableArray<KBChatHistoryModel *> *messages;
@property (nonatomic, assign) NSInteger currentPage;
@property (nonatomic, assign) BOOL hasMore;
@end

@implementation YourViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.aiVM = [[AiVM alloc] init];
    self.messages = [NSMutableArray array];
    self.currentPage = 1;
    self.hasMore = YES;
    
    [self loadChatHistory];
}

- (void)loadChatHistory {
    NSInteger companionId = 123; // 当前人设 ID
    
    __weak typeof(self) weakSelf = self;
    [self.aiVM fetchChatHistoryWithCompanionId:companionId
                                        pageNum:self.currentPage
                                       pageSize:20
                                     completion:^(KBChatHistoryPageModel *pageModel, NSError *error) {
        if (error) {
            NSLog(@"加载失败:%@", error.localizedDescription);
            return;
        }
        
        // 追加数据
        [weakSelf.messages addObjectsFromArray:pageModel.records];
        weakSelf.hasMore = pageModel.hasMore;
        
        // 刷新 UI
        [weakSelf.tableView reloadData];
    }];
}

- (void)loadMoreHistory {
    if (!self.hasMore) {
        return;
    }
    
    self.currentPage++;
    [self loadChatHistory];
}

@end

3. 转换为 KBAiChatMessage

// 将 KBChatHistoryModel 转换为 KBAiChatMessage用于聊天界面展示
- (KBAiChatMessage *)convertToAiChatMessage:(KBChatHistoryModel *)historyModel {
    if (historyModel.isUserMessage) {
        return [KBAiChatMessage userMessageWithText:historyModel.content];
    } else {
        return [KBAiChatMessage assistantMessageWithText:historyModel.content];
    }
}

// 批量转换
- (NSArray<KBAiChatMessage *> *)convertHistoryToMessages:(NSArray<KBChatHistoryModel *> *)history {
    NSMutableArray *messages = [NSMutableArray array];
    for (KBChatHistoryModel *item in history) {
        [messages addObject:[self convertToAiChatMessage:item]];
    }
    return messages;
}

🔄 与 KBPersonaChatCell 集成

在 KBPersonaChatCell 中加载聊天记录

// KBPersonaChatCell.m

- (void)setPersona:(KBPersonaModel *)persona {
    _persona = persona;
    
    // 重置状态
    self.hasLoadedData = NO;
    self.isLoading = NO;
    self.currentPage = 1;
    self.hasMoreHistory = YES;
    self.messages = [NSMutableArray array];
    self.aiVM = [[AiVM alloc] init];
    
    // 设置 UI
    [self updateUI];
}

- (void)preloadDataIfNeeded {
    if (self.hasLoadedData || self.isLoading) {
        return;
    }
    
    [self loadChatHistory];
}

- (void)loadChatHistory {
    if (self.isLoading || !self.hasMoreHistory) {
        return;
    }
    
    self.isLoading = YES;
    
    // 使用 persona.personaId 作为 companionId
    NSInteger companionId = self.persona.personaId;
    
    __weak typeof(self) weakSelf = self;
    [self.aiVM fetchChatHistoryWithCompanionId:companionId
                                        pageNum:self.currentPage
                                       pageSize:20
                                     completion:^(KBChatHistoryPageModel *pageModel, NSError *error) {
        weakSelf.isLoading = NO;
        
        if (error) {
            NSLog(@"加载聊天记录失败:%@", error.localizedDescription);
            return;
        }
        
        weakSelf.hasLoadedData = YES;
        weakSelf.hasMoreHistory = pageModel.hasMore;
        
        // 转换为 KBAiChatMessage
        NSMutableArray *newMessages = [NSMutableArray array];
        for (KBChatHistoryModel *item in pageModel.records) {
            KBAiChatMessage *message;
            if (item.isUserMessage) {
                message = [KBAiChatMessage userMessageWithText:item.content];
            } else {
                message = [KBAiChatMessage assistantMessageWithText:item.content];
            }
            message.isComplete = YES;
            [newMessages addObject:message];
        }
        
        // 插入到顶部(历史消息)
        if (weakSelf.currentPage == 1) {
            weakSelf.messages = newMessages;
        } else {
            NSIndexSet *indexSet = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, newMessages.count)];
            [weakSelf.messages insertObjects:newMessages atIndexes:indexSet];
        }
        
        // 刷新 UI
        dispatch_async(dispatch_get_main_queue(), ^{
            [weakSelf.tableView reloadData];
        });
    }];
}

- (void)loadMoreHistory {
    if (!self.hasMoreHistory || self.isLoading) {
        return;
    }
    
    self.currentPage++;
    [self loadChatHistory];
}

// 下拉加载更多
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
    CGFloat offsetY = scrollView.contentOffset.y;
    
    if (offsetY <= -50 && !self.isLoading) {
        [self loadMoreHistory];
    }
}

📊 数据流程

1. 用户滑动到某个人设
   ↓
2. KBPersonaChatCell 触发预加载
   ↓
3. 调用 AiVM.fetchChatHistoryWithCompanionId
   ↓
4. 请求 POST /chat/history
   ↓
5. 返回 KBChatHistoryPageModel
   ↓
6. 转换为 KBAiChatMessage
   ↓
7. 在 TableView 中展示

🎯 字段映射

MJExtension 自动映射

JSON 字段 Model 属性 说明
id messageId 消息 ID
sender sender 发送者0-用户1-AI
content content 消息内容
createdAt createdAt 创建时间

枚举值说明

typedef NS_ENUM(NSInteger, KBChatSender) {
    KBChatSenderUser = 1,      // 用户(右侧显示)
    KBChatSenderAssistant = 2  // AI 助手(左侧显示)
};

与 KBAiChatMessage 的映射关系

// KBChatHistoryModel → KBAiChatMessage
if (historyModel.sender == KBChatSenderUser) {
    // sender = 1 → KBAiChatMessageTypeUser右侧
    message = [KBAiChatMessage userMessageWithText:historyModel.content];
} else if (historyModel.sender == KBChatSenderAssistant) {
    // sender = 2 → KBAiChatMessageTypeAssistant左侧
    message = [KBAiChatMessage assistantMessageWithText:historyModel.content];
}

已完成功能

  1. KBChatHistoryModel聊天记录模型
  2. KBChatHistoryPageModel分页数据模型
  3. AiVM 新增接口fetchChatHistoryWithCompanionId
  4. MJExtension 配置JSON 自动转 Model
  5. 扩展属性isUserMessage、isAssistantMessage、hasMore
  6. 容错处理:字段为 null 时设置默认值

🚧 待实现功能

  1. 在 KBPersonaChatCell 中集成聊天记录加载
  2. 实现下拉加载更多历史消息
  3. 实现消息时间戳显示
  4. 实现消息发送功能
  5. 实现消息缓存(避免重复请求)

📌 注意事项

  1. companionId:直接使用 KBPersonaModel.personaId(人设列表接口返回的 ID
  2. 时间格式createdAt 是字符串,需要根据实际格式解析
  3. 分页方向:默认从最新消息开始加载,下拉加载历史消息
  4. 消息顺序:需要根据实际需求决定是正序还是倒序
  5. 容错处理:已做 null 值容错,确保不会崩溃

数据关联关系

KBPersonaModel (人设列表)
  └─ personaId (人设 ID)
       ↓
  用作 companionId 参数
       ↓
KBChatHistoryPageModel (聊天记录)
  └─ records (消息列表)