diff --git a/keyBoard/Class/AiTalk/M/KBAICommentModel.m b/keyBoard/Class/AiTalk/M/KBAICommentModel.m index ff61e20..efcb59d 100644 --- a/keyBoard/Class/AiTalk/M/KBAICommentModel.m +++ b/keyBoard/Class/AiTalk/M/KBAICommentModel.m @@ -14,8 +14,11 @@ + (NSDictionary *)mj_replacedKeyFromPropertyName { return @{ @"commentId" : @"id", - @"userName" : @[ @"userName", @"nickname", @"name" ], - @"avatarUrl" : @[ @"avatarUrl", @"avatar" ], + @"userId" : @"userId", + @"userName" : @"userName", + @"avatarUrl" : @"userAvatar", + @"createTime" : @"createdAt", + @"totalReplyCount" : @"replyCount", }; } @@ -23,6 +26,24 @@ return @{@"replies" : [KBAIReplyModel class]}; } +- (void)setLiked:(NSInteger)liked { + // 后端返回的是 NSInteger (0/1),转换为 BOOL + _isLiked = (liked == 1); +} + +- (void)setCreatedAt:(NSString *)createdAt { + // 后端返回的是字符串时间,转换为时间戳 + if (createdAt && createdAt.length > 0) { + NSDateFormatter *formatter = [[NSDateFormatter alloc] init]; + formatter.dateFormat = @"yyyy-MM-dd HH:mm:ss"; + formatter.timeZone = [NSTimeZone timeZoneWithName:@"Asia/Shanghai"]; + NSDate *date = [formatter dateFromString:createdAt]; + if (date) { + _createTime = [date timeIntervalSince1970]; + } + } +} + - (instancetype)init { self = [super init]; if (self) { diff --git a/keyBoard/Class/AiTalk/M/KBAIReplyModel.m b/keyBoard/Class/AiTalk/M/KBAIReplyModel.m index 2238fad..43ea95b 100644 --- a/keyBoard/Class/AiTalk/M/KBAIReplyModel.m +++ b/keyBoard/Class/AiTalk/M/KBAIReplyModel.m @@ -7,17 +7,39 @@ #import "KBAIReplyModel.h" #import +#import "KBAIReplyModel.h" + @implementation KBAIReplyModel + (NSDictionary *)mj_replacedKeyFromPropertyName { return @{ @"replyId" : @"id", - @"userName" : @[ @"userName", @"nickname", @"name" ], - @"avatarUrl" : @[ @"avatarUrl", @"avatar" ], + @"userId" : @"userId", + @"userName" : @"userName", + @"avatarUrl" : @"userAvatar", + @"createTime" : @"createdAt", }; } +- (void)setLiked:(NSInteger)liked { + // 后端返回的是 NSInteger (0/1),转换为 BOOL + _isLiked = (liked == 1); +} + +- (void)setCreatedAt:(NSString *)createdAt { + // 后端返回的是字符串时间,转换为时间戳 + if (createdAt && createdAt.length > 0) { + NSDateFormatter *formatter = [[NSDateFormatter alloc] init]; + formatter.dateFormat = @"yyyy-MM-dd HH:mm:ss"; + formatter.timeZone = [NSTimeZone timeZoneWithName:@"Asia/Shanghai"]; + NSDate *date = [formatter dateFromString:createdAt]; + if (date) { + _createTime = [date timeIntervalSince1970]; + } + } +} + - (NSString *)formattedTime { NSDate *date = [NSDate dateWithTimeIntervalSince1970:self.createTime]; NSTimeInterval interval = [[NSDate date] timeIntervalSinceDate:date]; diff --git a/keyBoard/Class/AiTalk/V/KBAICommentView.h b/keyBoard/Class/AiTalk/V/KBAICommentView.h index 3e6e2e4..6522ba1 100644 --- a/keyBoard/Class/AiTalk/V/KBAICommentView.h +++ b/keyBoard/Class/AiTalk/V/KBAICommentView.h @@ -6,17 +6,23 @@ // #import +#import NS_ASSUME_NONNULL_BEGIN /// 抖音风格评论视图 @interface KBAICommentView : UIView -/// 加载评论数据(从本地 JSON 文件) +/// AI 陪聊角色 ID +@property(nonatomic, assign) NSInteger companionId; + +/// 加载评论数据(从网络) - (void)loadComments; /// 评论总数 @property(nonatomic, readonly) NSInteger totalCommentCount; +@property(nonatomic, weak) LSTPopView *popView +; @end diff --git a/keyBoard/Class/AiTalk/V/KBAICommentView.m b/keyBoard/Class/AiTalk/V/KBAICommentView.m index e3b3326..baa196a 100644 --- a/keyBoard/Class/AiTalk/V/KBAICommentView.m +++ b/keyBoard/Class/AiTalk/V/KBAICommentView.m @@ -12,6 +12,8 @@ #import "KBAICommentModel.h" #import "KBAIReplyCell.h" #import "KBAIReplyModel.h" +#import "KBCommentModel.h" +#import "AiVM.h" #import #import @@ -40,6 +42,9 @@ static NSString *const kCommentFooterIdentifier = @"CommentFooter"; /// 当前回复的目标(二级评论) @property(nonatomic, weak) KBAIReplyModel *replyToReply; +/// AiVM 实例 +@property(nonatomic, strong) AiVM *aiVM; + @end @implementation KBAICommentView @@ -52,7 +57,6 @@ static NSString *const kCommentFooterIdentifier = @"CommentFooter"; self.comments = [NSMutableArray array]; [self setupUI]; [self setupKeyboardObservers]; - [self loadComments]; } return self; } @@ -162,52 +166,66 @@ static NSString *const kCommentFooterIdentifier = @"CommentFooter"; #pragma mark - Data Loading - (void)loadComments { - NSString *filePath = [[NSBundle mainBundle] pathForResource:@"comments_mock" - ofType:@"json"]; - if (!filePath) { - NSLog(@"[KBAICommentView] comments_mock.json not found"); + if (self.companionId <= 0) { + NSLog(@"[KBAICommentView] companionId 未设置,无法加载评论"); return; } + + __weak typeof(self) weakSelf = self; + [self.aiVM fetchCommentsWithCompanionId:self.companionId + pageNum:1 + pageSize:20 + completion:^(KBCommentPageModel *pageModel, NSError *error) { + __strong typeof(weakSelf) strongSelf = weakSelf; + if (!strongSelf) { + return; + } + + if (error) { + NSLog(@"[KBAICommentView] 加载评论失败:%@", error.localizedDescription); + return; + } + + dispatch_async(dispatch_get_main_queue(), ^{ + [strongSelf updateCommentsWithPageModel:pageModel]; + }); + }]; +} - NSData *data = [NSData dataWithContentsOfFile:filePath]; - if (!data) { - NSLog(@"[KBAICommentView] Failed to read comments_mock.json"); +/// 更新评论数据(从后端返回的 KBCommentPageModel 转换为 UI 层的 KBAICommentModel) +- (void)updateCommentsWithPageModel:(KBCommentPageModel *)pageModel { + if (!pageModel) { + NSLog(@"[KBAICommentView] pageModel 为空"); return; } - - NSError *error; - NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data - options:0 - error:&error]; - if (error) { - NSLog(@"[KBAICommentView] JSON parse error: %@", error); - return; - } - - self.totalCommentCount = [json[@"totalCount"] integerValue]; - NSArray *commentsArray = json[@"comments"]; - + + self.totalCommentCount = pageModel.total; + [self.comments removeAllObjects]; - + // 获取 tableView 宽度用于计算高度 CGFloat tableWidth = self.tableView.bounds.size.width; if (tableWidth <= 0) { tableWidth = [UIScreen mainScreen].bounds.size.width; } - - for (NSDictionary *dict in commentsArray) { - KBAICommentModel *comment = [KBAICommentModel mj_objectWithKeyValues:dict]; + + NSLog(@"[KBAICommentView] 加载到 %ld 条评论,共 %ld 条", (long)pageModel.records.count, (long)pageModel.total); + + for (KBCommentItem *item in pageModel.records) { + // 转换为 KBAICommentModel(使用 MJExtension) + KBAICommentModel *comment = [KBAICommentModel mj_objectWithKeyValues:[item mj_keyValues]]; + // 预先计算并缓存 Header 高度 - comment.cachedHeaderHeight = - [comment calculateHeaderHeightWithMaxWidth:tableWidth]; + comment.cachedHeaderHeight = [comment calculateHeaderHeightWithMaxWidth:tableWidth]; + // 预先计算并缓存所有 Reply 高度 for (KBAIReplyModel *reply in comment.replies) { - reply.cachedCellHeight = - [reply calculateCellHeightWithMaxWidth:tableWidth]; + reply.cachedCellHeight = [reply calculateCellHeightWithMaxWidth:tableWidth]; } + [self.comments addObject:comment]; } - + [self updateTitle]; [self.tableView reloadData]; } @@ -422,10 +440,11 @@ static NSInteger const kRepliesLoadCount = 5; #pragma mark - Actions - (void)closeButtonTapped { + [self.popView dismiss]; // 关闭评论视图(由外部处理) - [[NSNotificationCenter defaultCenter] - postNotificationName:@"KBAICommentViewCloseNotification" - object:nil]; +// [[NSNotificationCenter defaultCenter] +// postNotificationName:@"KBAICommentViewCloseNotification" +// object:nil]; } #pragma mark - Lazy Loading @@ -555,100 +574,102 @@ static NSInteger const kRepliesLoadCount = 5; } - (void)sendNewCommentWithText:(NSString *)text tableWidth:(CGFloat)tableWidth { - // 创建新一级评论 - KBAICommentModel *newComment = [[KBAICommentModel alloc] init]; - newComment.commentId = [NSUUID UUID].UUIDString; - newComment.userId = @"current_user"; - newComment.userName = @"我"; - newComment.avatarUrl = @""; - newComment.content = text; - newComment.likeCount = 0; - newComment.isLiked = NO; - newComment.createTime = [[NSDate date] timeIntervalSince1970]; - newComment.replies = @[]; - - // 计算高度缓存 - newComment.cachedHeaderHeight = - [newComment calculateHeaderHeightWithMaxWidth:tableWidth]; - - // 插入到数组第一个 - [self.comments insertObject:newComment atIndex:0]; - self.totalCommentCount++; - [self updateTitle]; - - // 插入新 section - [self.tableView insertSections:[NSIndexSet indexSetWithIndex:0] - withRowAnimation:UITableViewRowAnimationAutomatic]; - - // 滚动到顶部 - [self.tableView setContentOffset:CGPointZero animated:YES]; + // TODO: 调用网络接口发送一级评论 + NSLog(@"[KBAICommentView] 发送一级评论:%@", text); + + // 示例代码: + // [self.aiVM sendCommentWithCompanionId:self.companionId + // content:text + // completion:^(KBCommentItem *newItem, NSError *error) { + // if (error) { + // NSLog(@"[KBAICommentView] 发送评论失败:%@", error.localizedDescription); + // return; + // } + // + // // 转换为 KBAICommentModel + // KBAICommentModel *comment = [KBAICommentModel mj_objectWithKeyValues:[newItem mj_keyValues]]; + // comment.cachedHeaderHeight = [comment calculateHeaderHeightWithMaxWidth:tableWidth]; + // + // // 插入到数组第一个 + // [self.comments insertObject:comment atIndex:0]; + // self.totalCommentCount++; + // [self updateTitle]; + // + // // 插入新 section + // [self.tableView insertSections:[NSIndexSet indexSetWithIndex:0] + // withRowAnimation:UITableViewRowAnimationAutomatic]; + // + // // 滚动到顶部 + // [self.tableView setContentOffset:CGPointZero animated:YES]; + // }]; } - (void)sendReplyWithText:(NSString *)text tableWidth:(CGFloat)tableWidth { KBAICommentModel *comment = self.replyToComment; if (!comment) return; + + // TODO: 调用网络接口发送二级评论(回复) + NSLog(@"[KBAICommentView] 回复评论 %@:%@", comment.commentId, text); + + // 示例代码: + // NSInteger parentId = [comment.commentId integerValue]; + // [self.aiVM replyCommentWithParentId:parentId + // content:text + // completion:^(KBCommentItem *newItem, NSError *error) { + // if (error) { + // NSLog(@"[KBAICommentView] 回复评论失败:%@", error.localizedDescription); + // return; + // } + // + // // 转换为 KBAIReplyModel + // KBAIReplyModel *newReply = [KBAIReplyModel mj_objectWithKeyValues:[newItem mj_keyValues]]; + // newReply.cachedCellHeight = [newReply calculateCellHeightWithMaxWidth:tableWidth]; + // + // // 添加到 replies 数组 + // NSMutableArray *newReplies = [NSMutableArray arrayWithArray:comment.replies]; + // [newReplies addObject:newReply]; + // comment.replies = newReplies; + // comment.totalReplyCount = newReplies.count; + // + // // 找到该评论的 section + // NSInteger section = [self.comments indexOfObject:comment]; + // if (section == NSNotFound) return; + // + // // 如果已展开,添加到 displayedReplies 并插入行 + // if (comment.isRepliesExpanded) { + // NSInteger newRowIndex = comment.displayedReplies.count; + // [comment.displayedReplies addObject:newReply]; + // + // NSIndexPath *indexPath = [NSIndexPath indexPathForRow:newRowIndex inSection:section]; + // [self.tableView insertRowsAtIndexPaths:@[indexPath] + // withRowAnimation:UITableViewRowAnimationAutomatic]; + // + // // 刷新 Footer + // KBAICommentFooterView *footerView = (KBAICommentFooterView *)[self.tableView footerViewForSection:section]; + // if (footerView) { + // [footerView configureWithComment:comment]; + // } + // + // // 滚动到新回复 + // [self.tableView scrollToRowAtIndexPath:indexPath + // atScrollPosition:UITableViewScrollPositionBottom + // animated:YES]; + // } else { + // // 未展开,刷新 Footer 显示新的回复数 + // KBAICommentFooterView *footerView = (KBAICommentFooterView *)[self.tableView footerViewForSection:section]; + // if (footerView) { + // [footerView configureWithComment:comment]; + // } + // } + // }]; +} - // 创建新二级评论 - KBAIReplyModel *newReply = [[KBAIReplyModel alloc] init]; - newReply.replyId = [NSUUID UUID].UUIDString; - newReply.userId = @"current_user"; - newReply.userName = @"我"; - newReply.avatarUrl = @""; - newReply.content = text; - newReply.likeCount = 0; - newReply.isLiked = NO; - newReply.createTime = [[NSDate date] timeIntervalSince1970]; - - // 如果是回复二级评论,设置被回复用户 - if (self.replyToReply) { - newReply.replyToUserName = self.replyToReply.userName; - } - - // 计算高度缓存 - newReply.cachedCellHeight = - [newReply calculateCellHeightWithMaxWidth:tableWidth]; - - // 添加到 replies 数组 - NSMutableArray *newReplies = [NSMutableArray arrayWithArray:comment.replies]; - [newReplies addObject:newReply]; - comment.replies = newReplies; - comment.totalReplyCount = newReplies.count; - - // 找到该评论的 section - NSInteger section = [self.comments indexOfObject:comment]; - if (section == NSNotFound) - return; - - // 如果已展开,添加到 displayedReplies 并插入行 - if (comment.isRepliesExpanded) { - NSInteger newRowIndex = comment.displayedReplies.count; - [comment.displayedReplies addObject:newReply]; - - NSIndexPath *indexPath = [NSIndexPath indexPathForRow:newRowIndex - inSection:section]; - [self.tableView insertRowsAtIndexPaths:@[ indexPath ] - withRowAnimation:UITableViewRowAnimationAutomatic]; - - // 刷新 Footer - KBAICommentFooterView *footerView = - (KBAICommentFooterView *)[self.tableView footerViewForSection:section]; - if (footerView) { - [footerView configureWithComment:comment]; - } - - // 滚动到新回复 - [self.tableView scrollToRowAtIndexPath:indexPath - atScrollPosition:UITableViewScrollPositionBottom - animated:YES]; - } else { - // 未展开,刷新 Footer 显示新的回复数 - KBAICommentFooterView *footerView = - (KBAICommentFooterView *)[self.tableView footerViewForSection:section]; - if (footerView) { - [footerView configureWithComment:comment]; - } +- (AiVM *)aiVM { + if (!_aiVM) { + _aiVM = [[AiVM alloc] init]; } + return _aiVM; } @end diff --git a/keyBoard/Class/AiTalk/V/KBPersonaChatCell.m b/keyBoard/Class/AiTalk/V/KBPersonaChatCell.m index 6e8ac35..e671a18 100644 --- a/keyBoard/Class/AiTalk/V/KBPersonaChatCell.m +++ b/keyBoard/Class/AiTalk/V/KBPersonaChatCell.m @@ -531,12 +531,16 @@ initWithFrame:CGRectMake(0, 0, KB_SCREEN_WIDTH, customViewHeight)]; // 设置评论视图的人设 ID - // customView.companionId = self.persona.personaId; + customView.companionId = self.persona.personaId; + + // 加载评论数据 + [customView loadComments]; LSTPopView *popView = [LSTPopView initWithCustomView:customView parentView:nil popStyle:LSTPopStyleSmoothFromBottom dismissStyle:LSTDismissStyleSmoothToBottom]; + customView.popView = popView; self.popView = popView; popView.priority = 1000; popView.isAvoidKeyboard = NO;