Files
keyboard/keyBoard/Class/AiTalk/VM/AiVM.m

903 lines
31 KiB
Mathematica
Raw Normal View History

2026-01-22 13:47:34 +08:00
//
// AiVM.m
// keyBoard
//
// Created by Mac on 2026/1/22.
//
#import "AiVM.h"
2026-01-22 22:03:56 +08:00
#import "KBAPI.h"
#import "KBNetworkManager.h"
2026-01-27 21:32:52 +08:00
#import "KBCommentModel.h"
2026-01-28 16:35:47 +08:00
#import "KBLikedCompanionModel.h"
#import "KBChattedCompanionModel.h"
2026-01-28 18:58:30 +08:00
#import "KBChatSessionResetModel.h"
2026-01-29 16:03:21 +08:00
#import "KBAICompanionDetailModel.h"
2026-01-22 22:03:56 +08:00
#import <MJExtension/MJExtension.h>
@implementation KBAiSyncData
- (void)setAudioBase64:(NSString *)audioBase64 {
if (![audioBase64 isKindOfClass:[NSString class]]) {
_audioBase64 = nil;
self.audioData = nil;
return;
}
_audioBase64 = [audioBase64 copy];
if (_audioBase64.length == 0) {
self.audioData = nil;
return;
}
NSString *cleanBase64 = _audioBase64;
NSRange commaRange = [cleanBase64 rangeOfString:@","];
if ([cleanBase64 hasPrefix:@"data:"] && commaRange.location != NSNotFound) {
cleanBase64 = [cleanBase64 substringFromIndex:commaRange.location + 1];
}
self.audioData = [[NSData alloc]
initWithBase64EncodedString:cleanBase64
options:NSDataBase64DecodingIgnoreUnknownCharacters];
}
@end
@implementation KBAiSyncResponse
@end
@implementation KBAiMessageData
@end
@implementation KBAiMessageResponse
@end
2026-01-22 13:47:34 +08:00
2026-01-27 16:28:17 +08:00
@implementation KBAiSpeechTranscribeData
@end
@implementation KBAiSpeechTranscribeResponse
@end
2026-01-22 13:47:34 +08:00
@implementation AiVM
2026-01-22 22:03:56 +08:00
- (void)syncChatWithTranscript:(NSString *)transcript
completion:(AiVMSyncCompletion)completion {
if (transcript.length == 0) {
NSError *error = [NSError
errorWithDomain:@"AiVM"
code:-1
userInfo:@{NSLocalizedDescriptionKey : @"transcript is empty"}];
if (completion) {
completion(nil, error);
}
return;
}
NSDictionary *params = @{ @"transcript" : transcript ?: @"" };
CFAbsoluteTime startTime = CFAbsoluteTimeGetCurrent();
NSLog(@"[AiVM] /chat/sync request: %@", params);
[[KBNetworkManager shared]
POST:API_AI_CHAT_SYNC
jsonBody:params
headers:nil
autoShowBusinessError:NO
completion:^(NSDictionary *_Nullable json,
NSURLResponse *_Nullable response,
NSError *_Nullable error) {
CFAbsoluteTime elapsed =
(CFAbsoluteTimeGetCurrent() - startTime) * 1000.0;
if (error) {
NSLog(@"[AiVM] /chat/sync failed: %@",
error.localizedDescription ?: @"");
NSLog(@"[AiVM] /chat/sync duration: %.0f ms", elapsed);
if (completion) {
completion(nil, error);
}
return;
}
NSLog(@"[AiVM] /chat/sync response received");
NSLog(@"[AiVM] /chat/sync duration: %.0f ms", elapsed);
KBAiSyncResponse *model =
[KBAiSyncResponse mj_objectWithKeyValues:json];
if (completion) {
completion(model, nil);
}
}];
}
- (void)requestChatMessageWithContent:(NSString *)content
2026-01-26 18:51:37 +08:00
companionId:(NSInteger)companionId
2026-01-22 22:03:56 +08:00
completion:(AiVMMessageCompletion)completion {
if (content.length == 0) {
NSError *error = [NSError
errorWithDomain:@"AiVM"
code:-1
userInfo:@{NSLocalizedDescriptionKey : @"content is empty"}];
if (completion) {
completion(nil, error);
}
return;
}
2026-02-03 20:22:28 +08:00
NSString *path = API_AI_CHAT_MESSAGE;
2026-01-26 18:51:37 +08:00
NSDictionary *params = @{
@"content" : content ?: @"",
@"companionId" : @(companionId)
};
2026-01-22 22:03:56 +08:00
[[KBNetworkManager shared]
POST:path
jsonBody:params
headers:nil
autoShowBusinessError:NO
completion:^(NSDictionary *_Nullable json,
NSURLResponse *_Nullable response,
NSError *_Nullable error) {
2026-01-27 16:28:17 +08:00
KBAiMessageResponse *model =
[KBAiMessageResponse mj_objectWithKeyValues:json];
2026-01-22 22:03:56 +08:00
if (error) {
if (completion) {
2026-01-27 16:28:17 +08:00
completion(model, error);
2026-01-22 22:03:56 +08:00
}
return;
}
2026-01-27 16:28:17 +08:00
2026-01-23 21:51:37 +08:00
id dataObj = json[@"data"];
if (!model.data && [dataObj isKindOfClass:[NSString class]]) {
KBAiMessageData *data = [[KBAiMessageData alloc] init];
data.content = (NSString *)dataObj;
model.data = data;
}
2026-01-22 22:03:56 +08:00
if (completion) {
completion(model, nil);
}
}];
}
2026-01-23 21:51:37 +08:00
- (void)requestAudioWithAudioId:(NSString *)audioId
completion:(AiVMAudioURLCompletion)completion {
if (audioId.length == 0) {
2026-01-22 22:03:56 +08:00
NSError *error = [NSError
errorWithDomain:@"AiVM"
code:-1
2026-01-23 21:51:37 +08:00
userInfo:@{NSLocalizedDescriptionKey : @"audioId is empty"}];
2026-01-22 22:03:56 +08:00
if (completion) {
completion(nil, error);
}
return;
}
2026-01-23 21:51:37 +08:00
NSString *path = [NSString stringWithFormat:@"/chat/audio/%@", audioId];
[[KBNetworkManager shared]
GET:path
parameters:nil
headers:nil
autoShowBusinessError:NO
completion:^(NSDictionary *_Nullable json,
NSURLResponse *_Nullable response,
NSError *_Nullable error) {
if (error) {
if (completion) {
completion(nil, error);
}
return;
}
// URL
NSString *audioURL = nil;
if ([json isKindOfClass:[NSDictionary class]]) {
// {"code": 0, "data": {"audioUrl": "http://...", "url": "http://..."}}
id dataObj = json[@"data"];
if ([dataObj isKindOfClass:[NSDictionary class]]) {
NSDictionary *dataDict = (NSDictionary *)dataObj;
// 使 audioUrl url
id audioUrlObj = dataDict[@"audioUrl"] ?: dataDict[@"url"];
// NSNull
if (audioUrlObj && ![audioUrlObj isKindOfClass:[NSNull class]] && [audioUrlObj isKindOfClass:[NSString class]]) {
audioURL = (NSString *)audioUrlObj;
}
} else if ([dataObj isKindOfClass:[NSString class]]) {
audioURL = (NSString *)dataObj;
}
// URL
if (!audioURL) {
id audioUrlObj = json[@"audioUrl"] ?: json[@"url"];
if (audioUrlObj && ![audioUrlObj isKindOfClass:[NSNull class]] && [audioUrlObj isKindOfClass:[NSString class]]) {
audioURL = (NSString *)audioUrlObj;
}
}
}
// audioURL nil nil
if (!audioURL || audioURL.length == 0) {
if (completion) {
completion(nil, nil); // nil
}
return;
}
if (completion) {
completion(audioURL, nil);
}
}];
2026-01-22 22:03:56 +08:00
}
2026-01-27 13:57:32 +08:00
- (void)uploadAudioFileAtURL:(NSURL *)fileURL
completion:(AiVMUploadAudioCompletion)completion {
if (!fileURL || !fileURL.isFileURL) {
NSError *error = [NSError errorWithDomain:@"AiVM"
code:-1
userInfo:@{NSLocalizedDescriptionKey : @"invalid fileURL"}];
if (completion) {
completion(nil, error);
}
return;
}
[[KBNetworkManager shared] uploadFile:API_AI_AUDIO_UPLOAD
fileURL:fileURL
name:@"file"
2026-01-28 20:18:18 +08:00
mimeType:@"audio/m4a"
2026-01-27 13:57:32 +08:00
parameters:nil
headers:nil
completion:^(NSDictionary *_Nullable json,
NSURLResponse *_Nullable response,
NSError *_Nullable error) {
if (error) {
if (completion) {
completion(nil, error);
}
return;
}
NSString *fileURLString = nil;
id dataObj = json[@"data"];
if ([dataObj isKindOfClass:[NSString class]]) {
fileURLString = (NSString *)dataObj;
} else if ([dataObj isKindOfClass:[NSDictionary class]]) {
id urlObj = dataObj[@"url"] ?: dataObj[@"audioUrl"];
if ([urlObj isKindOfClass:[NSString class]]) {
fileURLString = (NSString *)urlObj;
}
}
if (completion) {
completion(fileURLString, nil);
}
}];
}
2026-01-27 16:28:17 +08:00
- (void)transcribeAudioFileAtURL:(NSURL *)fileURL
completion:(AiVMSpeechTranscribeCompletion)completion {
if (!fileURL || !fileURL.isFileURL) {
NSError *error = [NSError errorWithDomain:@"AiVM"
code:-1
userInfo:@{NSLocalizedDescriptionKey : @"invalid fileURL"}];
if (completion) {
completion(nil, error);
}
return;
}
[[KBNetworkManager shared] uploadFile:API_AI_SPEECH_TRANSCRIBE
fileURL:fileURL
name:@"file"
mimeType:@"audio/m4a"
parameters:nil
headers:nil
completion:^(NSDictionary *_Nullable json,
NSURLResponse *_Nullable response,
NSError *_Nullable error) {
if (error) {
if (completion) {
completion(nil, error);
}
return;
}
KBAiSpeechTranscribeResponse *model =
[KBAiSpeechTranscribeResponse mj_objectWithKeyValues:json];
if (completion) {
completion(model, nil);
}
}];
}
2026-01-26 16:53:41 +08:00
#pragma mark -
- (void)fetchPersonasWithPageNum:(NSInteger)pageNum
pageSize:(NSInteger)pageSize
completion:(void (^)(KBPersonaPageModel * _Nullable, NSError * _Nullable))completion {
NSDictionary *params = @{
@"pageNum": @(pageNum),
@"pageSize": @(pageSize)
};
NSLog(@"[AiVM] /ai-companion/page request: %@", params);
[[KBNetworkManager shared]
POST:@"/ai-companion/page"
jsonBody:params
headers:nil
autoShowBusinessError:NO
completion:^(NSDictionary *_Nullable json,
NSURLResponse *_Nullable response,
NSError *_Nullable error) {
if (error) {
NSLog(@"[AiVM] /ai-companion/page failed: %@", error.localizedDescription ?: @"");
if (completion) {
completion(nil, error);
}
return;
}
NSLog(@"[AiVM] /ai-companion/page response: %@", json);
//
NSInteger code = [json[@"code"] integerValue];
if (code != 0) {
NSString *message = json[@"message"] ?: @"请求失败";
NSError *bizError = [NSError errorWithDomain:@"AiVM"
code:code
userInfo:@{NSLocalizedDescriptionKey: message}];
if (completion) {
completion(nil, bizError);
}
return;
}
//
id dataObj = json[@"data"];
if ([dataObj isKindOfClass:[NSDictionary class]]) {
KBPersonaPageModel *pageModel = [KBPersonaPageModel mj_objectWithKeyValues:dataObj];
if (completion) {
completion(pageModel, nil);
}
} else {
NSError *parseError = [NSError errorWithDomain:@"AiVM"
code:-1
userInfo:@{NSLocalizedDescriptionKey: @"数据格式错误"}];
if (completion) {
completion(nil, parseError);
}
}
}];
}
2026-01-26 18:17:02 +08:00
#pragma mark -
- (void)fetchChatHistoryWithCompanionId:(NSInteger)companionId
pageNum:(NSInteger)pageNum
pageSize:(NSInteger)pageSize
completion:(void (^)(KBChatHistoryPageModel * _Nullable, NSError * _Nullable))completion {
NSDictionary *params = @{
@"companionId": @(companionId),
@"pageNum": @(pageNum),
@"pageSize": @(pageSize)
};
NSLog(@"[AiVM] /chat/history request: %@", params);
[[KBNetworkManager shared]
POST:@"/chat/history"
jsonBody:params
headers:nil
autoShowBusinessError:NO
completion:^(NSDictionary *_Nullable json,
NSURLResponse *_Nullable response,
NSError *_Nullable error) {
if (error) {
NSLog(@"[AiVM] /chat/history failed: %@", error.localizedDescription ?: @"");
if (completion) {
completion(nil, error);
}
return;
}
NSLog(@"[AiVM] /chat/history response: %@", json);
//
NSInteger code = [json[@"code"] integerValue];
if (code != 0) {
NSString *message = json[@"message"] ?: @"请求失败";
NSError *bizError = [NSError errorWithDomain:@"AiVM"
code:code
userInfo:@{NSLocalizedDescriptionKey: message}];
if (completion) {
completion(nil, bizError);
}
return;
}
//
id dataObj = json[@"data"];
if ([dataObj isKindOfClass:[NSDictionary class]]) {
KBChatHistoryPageModel *pageModel = [KBChatHistoryPageModel mj_objectWithKeyValues:dataObj];
if (completion) {
completion(pageModel, nil);
}
} else {
NSError *parseError = [NSError errorWithDomain:@"AiVM"
code:-1
userInfo:@{NSLocalizedDescriptionKey: @"数据格式错误"}];
if (completion) {
completion(nil, parseError);
}
}
}];
}
2026-01-27 21:32:52 +08:00
#pragma mark -
- (void)addCommentWithCompanionId:(NSInteger)companionId
content:(NSString *)content
parentId:(nullable NSNumber *)parentId
2026-01-29 15:53:26 +08:00
rootId:(nullable NSNumber *)rootId
completion:(void (^)(KBCommentItem * _Nullable, NSInteger, NSError * _Nullable))completion {
2026-01-27 21:32:52 +08:00
if (content.length == 0) {
NSError *error = [NSError errorWithDomain:@"AiVM"
2026-01-29 15:53:26 +08:00
code:-1
userInfo:@{NSLocalizedDescriptionKey: @"评论内容不能为空"}];
2026-01-27 21:32:52 +08:00
if (completion) {
2026-01-29 15:53:26 +08:00
completion(nil, -1, error);
2026-01-27 21:32:52 +08:00
}
return;
}
NSMutableDictionary *params = [NSMutableDictionary dictionary];
params[@"companionId"] = @(companionId);
params[@"content"] = content;
2026-01-29 15:53:26 +08:00
if (rootId) {
params[@"rootId"] = rootId;
}
2026-01-27 21:32:52 +08:00
if (parentId) {
params[@"parentId"] = parentId;
}
NSLog(@"[AiVM] /ai-companion/comment/add request: %@", params);
[[KBNetworkManager shared]
POST:@"/ai-companion/comment/add"
jsonBody:[params copy]
headers:nil
autoShowBusinessError:NO
completion:^(NSDictionary *_Nullable json,
NSURLResponse *_Nullable response,
NSError *_Nullable error) {
if (error) {
NSLog(@"[AiVM] /ai-companion/comment/add failed: %@", error.localizedDescription ?: @"");
if (completion) {
2026-01-29 15:53:26 +08:00
completion(nil, -1, error);
2026-01-27 21:32:52 +08:00
}
return;
}
NSLog(@"[AiVM] /ai-companion/comment/add response: %@", json);
NSInteger code = [json[@"code"] integerValue];
2026-01-29 15:53:26 +08:00
if (code != 0) {
NSString *message = json[@"message"] ?: @"请求失败";
NSError *bizError = [NSError errorWithDomain:@"AiVM"
code:code
userInfo:@{NSLocalizedDescriptionKey: message}];
if (completion) {
completion(nil, code, bizError);
}
return;
2026-01-27 21:32:52 +08:00
}
2026-01-29 15:53:26 +08:00
KBCommentItem *newItem = nil;
id dataObj = json[@"data"];
if ([dataObj isKindOfClass:[NSDictionary class]]) {
newItem = [KBCommentItem mj_objectWithKeyValues:(NSDictionary *)dataObj];
} else if ([dataObj isKindOfClass:[NSNumber class]]) {
KBCommentItem *tmp = [[KBCommentItem alloc] init];
tmp.commentId = [(NSNumber *)dataObj integerValue];
tmp.companionId = companionId;
tmp.content = content;
tmp.parentId = parentId;
tmp.rootId = rootId;
newItem = tmp;
}
if (completion) {
completion(newItem, code, nil);
}
}];
2026-01-27 21:32:52 +08:00
}
- (void)fetchCommentsWithCompanionId:(NSInteger)companionId
pageNum:(NSInteger)pageNum
pageSize:(NSInteger)pageSize
completion:(void (^)(KBCommentPageModel * _Nullable, NSError * _Nullable))completion {
NSDictionary *params = @{
@"companionId": @(companionId),
@"pageNum": @(pageNum > 0 ? pageNum : 1),
@"pageSize": @(pageSize > 0 ? pageSize : 20)
};
NSLog(@"[AiVM] /ai-companion/comment/page request: %@", params);
[[KBNetworkManager shared]
POST:@"/ai-companion/comment/page"
jsonBody:params
headers:nil
autoShowBusinessError:NO
completion:^(NSDictionary *_Nullable json,
NSURLResponse *_Nullable response,
NSError *_Nullable error) {
if (error) {
NSLog(@"[AiVM] /ai-companion/comment/page failed: %@", error.localizedDescription ?: @"");
if (completion) {
completion(nil, error);
}
return;
}
NSLog(@"[AiVM] /ai-companion/comment/page response: %@", json);
NSInteger code = [json[@"code"] integerValue];
if (code != 0) {
NSString *message = json[@"message"] ?: @"请求失败";
NSError *bizError = [NSError errorWithDomain:@"AiVM"
code:code
userInfo:@{NSLocalizedDescriptionKey: message}];
if (completion) {
completion(nil, bizError);
}
return;
}
id dataObj = json[@"data"];
if ([dataObj isKindOfClass:[NSDictionary class]]) {
KBCommentPageModel *pageModel = [KBCommentPageModel mj_objectWithKeyValues:dataObj];
if (completion) {
completion(pageModel, nil);
}
} else {
NSError *parseError = [NSError errorWithDomain:@"AiVM"
code:-1
userInfo:@{NSLocalizedDescriptionKey: @"数据格式错误"}];
if (completion) {
completion(nil, parseError);
}
}
}];
}
- (void)likeCommentWithCommentId:(NSInteger)commentId
completion:(void (^)(KBCommentLikeResponse * _Nullable, NSError * _Nullable))completion {
NSDictionary *params = @{
@"commentId": @(commentId)
};
NSLog(@"[AiVM] /ai-companion/comment/like request: %@", params);
[[KBNetworkManager shared]
POST:@"/ai-companion/comment/like"
jsonBody:params
headers:nil
autoShowBusinessError:NO
completion:^(NSDictionary *_Nullable json,
NSURLResponse *_Nullable response,
NSError *_Nullable error) {
if (error) {
NSLog(@"[AiVM] /ai-companion/comment/like failed: %@", error.localizedDescription ?: @"");
if (completion) {
completion(nil, error);
}
return;
}
NSLog(@"[AiVM] /ai-companion/comment/like response: %@", json);
KBCommentLikeResponse *likeResponse = [KBCommentLikeResponse mj_objectWithKeyValues:json];
if (completion) {
completion(likeResponse, nil);
}
}];
}
- (void)likeCompanionWithCompanionId:(NSInteger)companionId
completion:(void (^)(KBCommentLikeResponse * _Nullable, NSError * _Nullable))completion {
NSDictionary *params = @{
@"companionId": @(companionId)
};
NSLog(@"[AiVM] /ai-companion/like request: %@", params);
[[KBNetworkManager shared]
POST:@"/ai-companion/like"
jsonBody:params
headers:nil
autoShowBusinessError:NO
completion:^(NSDictionary *_Nullable json,
NSURLResponse *_Nullable response,
NSError *_Nullable error) {
if (error) {
NSLog(@"[AiVM] /ai-companion/like failed: %@", error.localizedDescription ?: @"");
if (completion) {
completion(nil, error);
}
return;
}
NSLog(@"[AiVM] /ai-companion/like response: %@", json);
KBCommentLikeResponse *likeResponse = [KBCommentLikeResponse mj_objectWithKeyValues:json];
if (completion) {
completion(likeResponse, nil);
}
}];
}
2026-01-28 16:35:47 +08:00
#pragma mark -
- (void)fetchLikedCompanionsWithCompletion:(void (^)(NSArray<KBLikedCompanionModel *> * _Nullable, NSError * _Nullable))completion {
NSLog(@"[AiVM] /ai-companion/liked request");
[[KBNetworkManager shared]
GET:@"/ai-companion/liked"
parameters:nil
headers:nil
autoShowBusinessError:NO
completion:^(NSDictionary *_Nullable json,
NSURLResponse *_Nullable response,
NSError *_Nullable error) {
if (error) {
NSLog(@"[AiVM] /ai-companion/liked failed: %@", error.localizedDescription ?: @"");
if (completion) {
completion(nil, error);
}
return;
}
NSLog(@"[AiVM] /ai-companion/liked response: %@", json);
NSInteger code = [json[@"code"] integerValue];
if (code != 0) {
NSString *message = json[@"message"] ?: @"请求失败";
NSError *bizError = [NSError errorWithDomain:@"AiVM"
code:code
userInfo:@{NSLocalizedDescriptionKey: message}];
if (completion) {
completion(nil, bizError);
}
return;
}
NSArray *dataArray = json[@"data"];
if (![dataArray isKindOfClass:[NSArray class]]) {
dataArray = @[];
}
NSArray<KBLikedCompanionModel *> *list = [KBLikedCompanionModel mj_objectArrayWithKeyValuesArray:dataArray];
if (completion) {
completion(list, nil);
}
}];
}
- (void)fetchChattedCompanionsWithCompletion:(void (^)(NSArray<KBChattedCompanionModel *> * _Nullable, NSError * _Nullable))completion {
NSLog(@"[AiVM] /ai-companion/chatted request");
[[KBNetworkManager shared]
GET:@"/ai-companion/chatted"
parameters:nil
headers:nil
autoShowBusinessError:NO
completion:^(NSDictionary *_Nullable json,
NSURLResponse *_Nullable response,
NSError *_Nullable error) {
if (error) {
NSLog(@"[AiVM] /ai-companion/chatted failed: %@", error.localizedDescription ?: @"");
if (completion) {
completion(nil, error);
}
return;
}
NSLog(@"[AiVM] /ai-companion/chatted response: %@", json);
NSInteger code = [json[@"code"] integerValue];
if (code != 0) {
NSString *message = json[@"message"] ?: @"请求失败";
NSError *bizError = [NSError errorWithDomain:@"AiVM"
code:code
userInfo:@{NSLocalizedDescriptionKey: message}];
if (completion) {
completion(nil, bizError);
}
return;
}
NSArray *dataArray = json[@"data"];
if (![dataArray isKindOfClass:[NSArray class]]) {
dataArray = @[];
}
NSArray<KBChattedCompanionModel *> *list = [KBChattedCompanionModel mj_objectArrayWithKeyValuesArray:dataArray];
if (completion) {
completion(list, nil);
}
}];
}
2026-01-28 18:58:30 +08:00
#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);
}
}];
}
2026-01-29 16:03:21 +08:00
#pragma mark - AI
- (void)fetchCompanionDetailWithCompanionId:(NSInteger)companionId
completion:(void (^)(KBAICompanionDetailModel * _Nullable, NSError * _Nullable))completion {
NSString *path = [NSString stringWithFormat:@"/ai-companion/%ld", (long)companionId];
NSLog(@"[AiVM] %@ request", path);
[[KBNetworkManager shared]
GET:path
parameters:nil
headers:nil
autoShowBusinessError:NO
completion:^(NSDictionary *_Nullable json,
NSURLResponse *_Nullable response,
NSError *_Nullable error) {
if (error) {
NSLog(@"[AiVM] %@ failed: %@", path, error.localizedDescription ?: @"");
if (completion) {
completion(nil, error);
}
return;
}
NSLog(@"[AiVM] %@ response: %@", path, json);
NSInteger code = [json[@"code"] integerValue];
if (code != 0) {
NSString *message = json[@"message"] ?: @"请求失败";
NSError *bizError = [NSError errorWithDomain:@"AiVM"
code:code
userInfo:@{NSLocalizedDescriptionKey: message}];
if (completion) {
completion(nil, bizError);
}
return;
}
id dataObj = json[@"data"];
if ([dataObj isKindOfClass:[NSDictionary class]]) {
KBAICompanionDetailModel *detail = [KBAICompanionDetailModel mj_objectWithKeyValues:dataObj];
if (completion) {
completion(detail, nil);
}
} else {
NSError *parseError = [NSError errorWithDomain:@"AiVM"
code:-1
userInfo:@{NSLocalizedDescriptionKey: @"数据格式错误"}];
if (completion) {
completion(nil, parseError);
}
}
}];
}
2026-02-02 17:07:46 +08:00
#pragma mark -
- (void)reportCompanionWithCompanionId:(NSInteger)companionId
reportTypes:(NSArray<NSNumber *> *)reportTypes
reportDesc:(nullable NSString *)reportDesc
chatContext:(nullable NSString *)chatContext
evidenceImageUrl:(nullable NSString *)evidenceImageUrl
completion:(void (^)(BOOL, NSError * _Nullable))completion {
if (companionId <= 0) {
NSError *error = [NSError errorWithDomain:@"AiVM"
code:-1
userInfo:@{NSLocalizedDescriptionKey : @"invalid companionId"}];
if (completion) {
completion(NO, error);
}
return;
}
NSMutableArray<NSNumber *> *typeList = [NSMutableArray array];
for (id item in reportTypes) {
if ([item isKindOfClass:[NSNumber class]]) {
[typeList addObject:(NSNumber *)item];
} else if ([item isKindOfClass:[NSString class]]) {
NSInteger value = [(NSString *)item integerValue];
[typeList addObject:@(value)];
}
}
if (typeList.count == 0) {
NSError *error = [NSError errorWithDomain:@"AiVM"
code:-1
userInfo:@{NSLocalizedDescriptionKey : @"reportTypes is empty"}];
if (completion) {
completion(NO, error);
}
return;
}
NSMutableDictionary *params = [NSMutableDictionary dictionary];
params[@"companionId"] = @(companionId);
params[@"reportTypes"] = [typeList copy];
if (reportDesc.length > 0) {
params[@"reportDesc"] = reportDesc;
}
if (chatContext.length > 0) {
params[@"chatContext"] = chatContext;
}
if (evidenceImageUrl.length > 0) {
params[@"evidenceImageUrl"] = evidenceImageUrl;
}
NSLog(@"[AiVM] /ai-companion/report request: %@", params);
[[KBNetworkManager shared]
POST:@"/ai-companion/report"
jsonBody:[params copy]
headers:nil
autoShowBusinessError:NO
completion:^(NSDictionary *_Nullable json,
NSURLResponse *_Nullable response,
NSError *_Nullable error) {
if (error) {
NSLog(@"[AiVM] /ai-companion/report failed: %@", error.localizedDescription ?: @"");
if (completion) {
completion(NO, error);
}
return;
}
NSLog(@"[AiVM] /ai-companion/report response: %@", json);
if (![json isKindOfClass:[NSDictionary class]]) {
NSError *parseError = [NSError errorWithDomain:@"AiVM"
code:-1
userInfo:@{NSLocalizedDescriptionKey : @"数据格式错误"}];
if (completion) {
completion(NO, parseError);
}
return;
}
NSInteger code = [json[@"code"] integerValue];
if (code != 0) {
NSString *message = json[@"message"] ?: @"请求失败";
NSError *bizError = [NSError errorWithDomain:@"AiVM"
code:code
userInfo:@{NSLocalizedDescriptionKey : message}];
if (completion) {
completion(NO, bizError);
}
return;
}
if (completion) {
completion(YES, nil);
}
}];
}
2026-01-22 13:47:34 +08:00
@end