From 66d85f78a07fdabc791957747b311589f9a836b8 Mon Sep 17 00:00:00 2001 From: CodeST <694468528@qq.com> Date: Wed, 28 Jan 2026 18:58:30 +0800 Subject: [PATCH] 1 --- keyBoard.xcodeproj/project.pbxproj | 6 + .../AiTalk/M/KBAIChatMessageCacheManager.h | 49 +++++++ .../AiTalk/M/KBAIChatMessageCacheManager.m | 123 ++++++++++++++++++ .../Class/AiTalk/M/KBChatSessionResetModel.h | 35 +++++ .../Class/AiTalk/M/KBChatSessionResetModel.m | 16 +++ keyBoard/Class/AiTalk/V/KBPersonaChatCell.m | 45 +++++-- keyBoard/Class/AiTalk/VM/AiVM.h | 9 ++ keyBoard/Class/AiTalk/VM/AiVM.m | 35 +++++ 8 files changed, 310 insertions(+), 8 deletions(-) create mode 100644 keyBoard/Class/AiTalk/M/KBAIChatMessageCacheManager.h create mode 100644 keyBoard/Class/AiTalk/M/KBAIChatMessageCacheManager.m create mode 100644 keyBoard/Class/AiTalk/M/KBChatSessionResetModel.h create mode 100644 keyBoard/Class/AiTalk/M/KBChatSessionResetModel.m diff --git a/keyBoard.xcodeproj/project.pbxproj b/keyBoard.xcodeproj/project.pbxproj index b49be76..bea2f51 100644 --- a/keyBoard.xcodeproj/project.pbxproj +++ b/keyBoard.xcodeproj/project.pbxproj @@ -154,6 +154,7 @@ 048FFD372F29F410005D62AE /* KBAIMessageCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 048FFD392F29F410005D62AE /* KBAIMessageCell.m */; }; 048FFD3C2F29F500005D62AE /* KBLikedCompanionModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 048FFD3B2F29F500005D62AE /* KBLikedCompanionModel.m */; }; 048FFD3F2F29F600005D62AE /* KBChattedCompanionModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 048FFD3E2F29F600005D62AE /* KBChattedCompanionModel.m */; }; + 048FFD422F29F700005D62AE /* KBChatSessionResetModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 048FFD412F29F700005D62AE /* KBChatSessionResetModel.m */; }; 0498BD622EDFFC12006CC1D5 /* KBMyVM.m in Sources */ = {isa = PBXBuildFile; fileRef = 0498BD612EDFFC12006CC1D5 /* KBMyVM.m */; }; 0498BD652EE0116D006CC1D5 /* KBEmailLoginVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 0498BD642EE0116D006CC1D5 /* KBEmailLoginVC.m */; }; 0498BD682EE01180006CC1D5 /* KBEmailRegistVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 0498BD672EE01180006CC1D5 /* KBEmailRegistVC.m */; }; @@ -574,6 +575,8 @@ 048FFD3B2F29F500005D62AE /* KBLikedCompanionModel.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBLikedCompanionModel.m; sourceTree = ""; }; 048FFD3D2F29F600005D62AE /* KBChattedCompanionModel.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBChattedCompanionModel.h; sourceTree = ""; }; 048FFD3E2F29F600005D62AE /* KBChattedCompanionModel.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBChattedCompanionModel.m; sourceTree = ""; }; + 048FFD402F29F700005D62AE /* KBChatSessionResetModel.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBChatSessionResetModel.h; sourceTree = ""; }; + 048FFD412F29F700005D62AE /* KBChatSessionResetModel.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBChatSessionResetModel.m; sourceTree = ""; }; 0498BD5E2EDF2157006CC1D5 /* KBBizCode.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBBizCode.h; sourceTree = ""; }; 0498BD602EDFFC12006CC1D5 /* KBMyVM.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBMyVM.h; sourceTree = ""; }; 0498BD612EDFFC12006CC1D5 /* KBMyVM.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBMyVM.m; sourceTree = ""; }; @@ -1039,6 +1042,8 @@ 048FFD3B2F29F500005D62AE /* KBLikedCompanionModel.m */, 048FFD3D2F29F600005D62AE /* KBChattedCompanionModel.h */, 048FFD3E2F29F600005D62AE /* KBChattedCompanionModel.m */, + 048FFD402F29F700005D62AE /* KBChatSessionResetModel.h */, + 048FFD412F29F700005D62AE /* KBChatSessionResetModel.m */, ); path = M; sourceTree = ""; @@ -2340,6 +2345,7 @@ 048FFD372F29F410005D62AE /* KBAIMessageCell.m in Sources */, 048FFD3C2F29F500005D62AE /* KBLikedCompanionModel.m in Sources */, 048FFD3F2F29F600005D62AE /* KBChattedCompanionModel.m in Sources */, + 048FFD422F29F700005D62AE /* KBChatSessionResetModel.m in Sources */, 04791F952ED48028004E8522 /* KBFeedBackVC.m in Sources */, 04890A042EC0BBBB00FABA60 /* KBCategoryTitleImageCell.m in Sources */, 04890A052EC0BBBB00FABA60 /* KBCategoryTitleImageView.m in Sources */, diff --git a/keyBoard/Class/AiTalk/M/KBAIChatMessageCacheManager.h b/keyBoard/Class/AiTalk/M/KBAIChatMessageCacheManager.h new file mode 100644 index 0000000..67730b3 --- /dev/null +++ b/keyBoard/Class/AiTalk/M/KBAIChatMessageCacheManager.h @@ -0,0 +1,49 @@ +// +// KBAIChatMessageCacheManager.h +// keyBoard +// +// Created by Mac on 2026/1/28. +// + +#import +@class KBAiChatMessage; + +NS_ASSUME_NONNULL_BEGIN + +/// 聊天消息缓存管理器 +/// 用于避免重复请求消息数据,提升性能 +@interface KBAIChatMessageCacheManager : NSObject + +/// 单例 ++ (instancetype)shared; + +/// 保存消息到缓存 +/// @param messages 消息数组 +/// @param companionId 人设 ID +- (void)saveMessages:(NSArray *)messages forCompanionId:(NSInteger)companionId; + +/// 从缓存获取消息 +/// @param companionId 人设 ID +/// @return 消息数组,如果缓存中没有则返回 nil +- (NSArray * _Nullable)messagesForCompanionId:(NSInteger)companionId; + +/// 追加消息到缓存(用于上拉加载更多历史消息) +/// @param messages 新消息数组 +/// @param companionId 人设 ID +- (void)appendMessages:(NSArray *)messages forCompanionId:(NSInteger)companionId; + +/// 清空指定人设的消息缓存 +/// @param companionId 人设 ID +- (void)clearMessagesForCompanionId:(NSInteger)companionId; + +/// 清空所有消息缓存 +- (void)clearAllMessages; + +/// 获取缓存状态 +/// @param companionId 人设 ID +/// @return 是否有缓存 +- (BOOL)hasCacheForCompanionId:(NSInteger)companionId; + +@end + +NS_ASSUME_NONNULL_END diff --git a/keyBoard/Class/AiTalk/M/KBAIChatMessageCacheManager.m b/keyBoard/Class/AiTalk/M/KBAIChatMessageCacheManager.m new file mode 100644 index 0000000..5893bbc --- /dev/null +++ b/keyBoard/Class/AiTalk/M/KBAIChatMessageCacheManager.m @@ -0,0 +1,123 @@ +// +// KBAIChatMessageCacheManager.m +// keyBoard +// +// Created by Mac on 2026/1/28. +// + +#import "KBAIChatMessageCacheManager.h" +#import "KBAiChatMessage.h" +#import + +@interface KBAIChatMessageCacheManager () +/// 消息缓存:Key = companionId,Value = messages 数组 +@property (nonatomic, strong) NSCache *> *messageCache; +@end + +@implementation KBAIChatMessageCacheManager + +#pragma mark - Singleton + ++ (instancetype)shared { + static KBAIChatMessageCacheManager *instance = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + instance = [[self alloc] init]; + }); + return instance; +} + +- (instancetype)init { + if (self = [super init]) { + _messageCache = [[NSCache alloc] init]; + + // 设置缓存上限,防止内存过大 + _messageCache.countLimit = 20; // 最多缓存 20 个人设的消息 + _messageCache.totalCostLimit = 10 * 1024 * 1024; // 最大 10MB + + // 监听内存警告,自动清理缓存 + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(handleMemoryWarning) + name:UIApplicationDidReceiveMemoryWarningNotification + object:nil]; + + NSLog(@"[MessageCache] 缓存管理器初始化成功,限制:20个人设,最大10MB"); + } + return self; +} + +#pragma mark - Public Methods + +- (void)saveMessages:(NSArray *)messages forCompanionId:(NSInteger)companionId { + if (!messages || messages.count == 0) { + NSLog(@"[MessageCache] 警告:尝试保存空消息数组,companionId=%ld", (long)companionId); + return; + } + + NSNumber *key = @(companionId); + NSMutableArray *mutableMessages = [messages mutableCopy]; + [self.messageCache setObject:mutableMessages forKey:key]; + + NSLog(@"[MessageCache] 保存成功:companionId=%ld, 消息数=%ld", (long)companionId, (long)messages.count); +} + +- (NSArray *)messagesForCompanionId:(NSInteger)companionId { + NSNumber *key = @(companionId); + NSMutableArray *messages = [self.messageCache objectForKey:key]; + + if (messages) { + NSLog(@"[MessageCache] 缓存命中:companionId=%ld, 消息数=%ld", (long)companionId, (long)messages.count); + } else { + NSLog(@"[MessageCache] 缓存未命中:companionId=%ld", (long)companionId); + } + + return messages; +} + +- (void)appendMessages:(NSArray *)messages forCompanionId:(NSInteger)companionId { + if (!messages || messages.count == 0) { + return; + } + + NSNumber *key = @(companionId); + NSMutableArray *existingMessages = [self.messageCache objectForKey:key]; + + if (existingMessages) { + // 追加到现有消息数组 + [existingMessages addObjectsFromArray:messages]; + NSLog(@"[MessageCache] 追加消息:companionId=%ld, 新增=%ld, 总计=%ld", + (long)companionId, (long)messages.count, (long)existingMessages.count); + } else { + // 如果缓存中没有,直接保存 + [self saveMessages:messages forCompanionId:companionId]; + } +} + +- (void)clearMessagesForCompanionId:(NSInteger)companionId { + NSNumber *key = @(companionId); + [self.messageCache removeObjectForKey:key]; + NSLog(@"[MessageCache] 清空缓存:companionId=%ld", (long)companionId); +} + +- (void)clearAllMessages { + [self.messageCache removeAllObjects]; + NSLog(@"[MessageCache] 清空所有缓存"); +} + +- (BOOL)hasCacheForCompanionId:(NSInteger)companionId { + NSNumber *key = @(companionId); + return [self.messageCache objectForKey:key] != nil; +} + +#pragma mark - Memory Warning + +- (void)handleMemoryWarning { + NSLog(@"[MessageCache] ⚠️ 收到内存警告,清空所有消息缓存"); + [self clearAllMessages]; +} + +- (void)dealloc { + [[NSNotificationCenter defaultCenter] removeObserver:self]; +} + +@end diff --git a/keyBoard/Class/AiTalk/M/KBChatSessionResetModel.h b/keyBoard/Class/AiTalk/M/KBChatSessionResetModel.h new file mode 100644 index 0000000..8b1e6b8 --- /dev/null +++ b/keyBoard/Class/AiTalk/M/KBChatSessionResetModel.h @@ -0,0 +1,35 @@ +// +// KBChatSessionResetModel.h +// keyBoard +// +// Created by Mac on 2026/1/28. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +/// 重置会话响应数据 +@interface KBChatSessionResetData : NSObject + +/// 会话 ID +@property (nonatomic, assign) NSInteger sessionId; +/// AI 角色 ID +@property (nonatomic, assign) NSInteger companionId; +/// 重置版本号 +@property (nonatomic, assign) NSInteger resetVersion; +/// 创建时间 +@property (nonatomic, copy) NSString *createdAt; + +@end + +/// 重置会话响应 +@interface KBChatSessionResetResponse : NSObject + +@property (nonatomic, assign) NSInteger code; +@property (nonatomic, strong, nullable) KBChatSessionResetData *data; +@property (nonatomic, copy, nullable) NSString *message; + +@end + +NS_ASSUME_NONNULL_END diff --git a/keyBoard/Class/AiTalk/M/KBChatSessionResetModel.m b/keyBoard/Class/AiTalk/M/KBChatSessionResetModel.m new file mode 100644 index 0000000..5421428 --- /dev/null +++ b/keyBoard/Class/AiTalk/M/KBChatSessionResetModel.m @@ -0,0 +1,16 @@ +// +// KBChatSessionResetModel.m +// keyBoard +// +// Created by Mac on 2026/1/28. +// + +#import "KBChatSessionResetModel.h" + +@implementation KBChatSessionResetData + +@end + +@implementation KBChatSessionResetResponse + +@end diff --git a/keyBoard/Class/AiTalk/V/KBPersonaChatCell.m b/keyBoard/Class/AiTalk/V/KBPersonaChatCell.m index 7d8ac0b..ea837d8 100644 --- a/keyBoard/Class/AiTalk/V/KBPersonaChatCell.m +++ b/keyBoard/Class/AiTalk/V/KBPersonaChatCell.m @@ -11,6 +11,7 @@ #import "AiVM.h" #import "KBImagePositionButton.h" #import "KBAICommentView.h" +#import "KBAIChatMessageCacheManager.h" #import #import #import @@ -71,16 +72,18 @@ return self; } -/// 关键修复:Cell 复用时重置状态 +/// 关键修复:Cell 复用时不清空数据,避免重复请求 - (void)prepareForReuse { [super prepareForReuse]; // 停止音频播放 [self.chatView stopPlayingAudio]; - // 重置加载状态 + // 重置加载状态标志(但不清空 hasLoadedData) self.isLoading = NO; - self.hasLoadedData = NO; + + // ✅ 移除了 self.hasLoadedData = NO; + // 这样 Cell 复用时不会重复请求数据 } #pragma mark - 1:控件初始化 @@ -157,11 +160,23 @@ _persona = persona; // 重置状态 - self.hasLoadedData = NO; self.isLoading = NO; self.currentPage = 1; self.hasMoreHistory = YES; - self.messages = [NSMutableArray array]; + + // ✅ 尝试从缓存加载 messages + NSArray *cachedMessages = [[KBAIChatMessageCacheManager shared] messagesForCompanionId:persona.personaId]; + if (cachedMessages.count > 0) { + // 缓存命中,直接使用 + self.messages = [cachedMessages mutableCopy]; + self.hasLoadedData = YES; + NSLog(@"[Cell] ✅ 从缓存加载:personaId=%ld, 消息数=%ld", (long)persona.personaId, (long)cachedMessages.count); + } else { + // 缓存未命中,需要请求 + self.messages = [NSMutableArray array]; + self.hasLoadedData = NO; + NSLog(@"[Cell] ⚠️ 缓存未命中:personaId=%ld, 需要请求数据", (long)persona.personaId); + } // 设置 UI [self.backgroundImageView sd_setImageWithURL:[NSURL URLWithString:persona.coverImageUrl] @@ -173,11 +188,19 @@ // 关键修复:清空消息时停止音频播放,避免状态混乱 [self.chatView stopPlayingAudio]; - [self.chatView clearMessages]; + + // 如果有缓存,直接显示 + if (self.messages.count > 0) { + [self.chatView reloadWithMessages:self.messages + hasMoreHistory:self.hasMoreHistory + completion:nil]; + } else { + [self.chatView clearMessages]; + } + [self.commentButton setTitle:persona.commentCount forState:UIControlStateNormal]; [self.likeButton setTitle:persona.likeCount forState:UIControlStateNormal]; self.likeButton.selected = persona.liked; - } #pragma mark - 2:数据加载 @@ -269,10 +292,16 @@ keepOffset:keepOffset scrollToBottom:scrollToBottom]; [weakSelf.chatView endLoadMoreWithHasMoreData:weakSelf.hasMoreHistory]; + + // ✅ 保存到缓存 + [[KBAIChatMessageCacheManager shared] saveMessages:weakSelf.messages + forCompanionId:companionId]; }); + weakSelf.currentPage++; + NSLog(@"[KBPersonaChatCell] 加载成功:第 %ld 页,%ld 条消息,还有更多:%@", - (long)weakSelf.currentPage, + (long)weakSelf.currentPage - 1, (long)newMessages.count, pageModel.hasMore ? @"是" : @"否"); }]; diff --git a/keyBoard/Class/AiTalk/VM/AiVM.h b/keyBoard/Class/AiTalk/VM/AiVM.h index a95868f..18ba73b 100644 --- a/keyBoard/Class/AiTalk/VM/AiVM.h +++ b/keyBoard/Class/AiTalk/VM/AiVM.h @@ -11,6 +11,7 @@ #import "KBCommentModel.h" #import "KBLikedCompanionModel.h" #import "KBChattedCompanionModel.h" +#import "KBChatSessionResetModel.h" NS_ASSUME_NONNULL_BEGIN @@ -154,6 +155,14 @@ typedef void (^AiVMSpeechTranscribeCompletion)(KBAiSpeechTranscribeResponse *_Nu /// @param completion 完成回调(返回聊天角色数组) - (void)fetchChattedCompanionsWithCompletion:(void(^)(NSArray * _Nullable list, NSError * _Nullable error))completion; +#pragma mark - 会话管理接口 + +/// 重置会话(将当前会话设为不活跃并创建新会话) +/// @param companionId AI 角色 ID +/// @param completion 完成回调(返回新会话信息) +- (void)resetChatSessionWithCompanionId:(NSInteger)companionId + completion:(void(^)(KBChatSessionResetResponse * _Nullable response, NSError * _Nullable error))completion; + @end NS_ASSUME_NONNULL_END diff --git a/keyBoard/Class/AiTalk/VM/AiVM.m b/keyBoard/Class/AiTalk/VM/AiVM.m index be7ef9d..e040ab7 100644 --- a/keyBoard/Class/AiTalk/VM/AiVM.m +++ b/keyBoard/Class/AiTalk/VM/AiVM.m @@ -11,6 +11,7 @@ #import "KBCommentModel.h" #import "KBLikedCompanionModel.h" #import "KBChattedCompanionModel.h" +#import "KBChatSessionResetModel.h" #import @implementation KBAiSyncData @@ -691,4 +692,38 @@ autoShowBusinessError:NO }]; } +#pragma mark - 会话管理接口 + +- (void)resetChatSessionWithCompanionId:(NSInteger)companionId + completion:(void (^)(KBChatSessionResetResponse * _Nullable, NSError * _Nullable))completion { + NSDictionary *params = @{ + @"companionId": @(companionId) + }; + + NSLog(@"[AiVM] /chat/session/reset request: %@", params); + [[KBNetworkManager shared] + POST:@"/chat/session/reset" + jsonBody:params + headers:nil +autoShowBusinessError:NO + completion:^(NSDictionary *_Nullable json, + NSURLResponse *_Nullable response, + NSError *_Nullable error) { + if (error) { + NSLog(@"[AiVM] /chat/session/reset failed: %@", error.localizedDescription ?: @""); + if (completion) { + completion(nil, error); + } + return; + } + + NSLog(@"[AiVM] /chat/session/reset response: %@", json); + + KBChatSessionResetResponse *resetResponse = [KBChatSessionResetResponse mj_objectWithKeyValues:json]; + if (completion) { + completion(resetResponse, nil); + } + }]; +} + @end