| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507 |
- //
- // ChatMessageModel.m
- // AIIM
- //
- // Created by qitewei on 2025/5/14.
- //
- #import "ChatMessageModel.h"
- #import "CryptoAES.h"
- #import "ChatsStore.h"
- #import "AVFoundation/AVFoundation.h"
- #import "FileNetApi.h"
- #import <SDWebImage/SDImageCache.h>
- #import "OSSManager.h"
- @implementation ChatMessageModel
- + (instancetype)modelWithDictionary:(NSDictionary *)dict {
- ChatMessageModel *model = [[ChatMessageModel alloc] init];
- [model setupWithDictionary:dict];
- return model;
- }
- - (void)setupWithDictionary:(NSDictionary *)dict {
-
- NSMutableDictionary * mutableDict = [NSMutableDictionary dictionaryWithDictionary:dict];
- [mutableDict jk_each:^(id k, id v) {
- if ([v isKindOfClass:[NSNull class]]) {
- [mutableDict setObject:@"" forKey:k];
- }
- }];
-
- //原消息
- self.formerMessage = dict;
- self.chatId = mutableDict[@"chatId"];
- // 消息类型
- NSInteger type = [mutableDict[@"messageType"] integerValue];
- self.messageType = (ChatMessageType)type;
- self.type = [mutableDict[@"type"] integerValue];
- self.nickName = mutableDict[@"fromName"]?:@"";
- self.avatar = mutableDict[@"fromAvatar"]?:@"";
-
- // 通用内容
- // self.content = mutableDict[@"content"] ?: @"";//本地明文存储
- self.content =[CryptoAES.shareInstance decryptDataL:mutableDict[@"content"] ?: @""];//本地加密存储
-
-
- self.msgId = mutableDict[@"id"];
- self.fromId = mutableDict[@"fromId"];
- // 发送方标识
- NSDictionary *userinfo = [UDManager.shareInstance getDDManager:dkuserinfo];
- NSString *userId = userinfo[@"id"];
- if([self.fromId isEqualToString:userId]){
- self.isSender=true;
- }
- else{
- self.isSender=false;
- }
-
- // 处理媒体尺寸
- CGFloat width = [mutableDict[@"width"] floatValue];
- CGFloat height = [mutableDict[@"height"] floatValue];
- if (width > 0 && height > 0) {
- self.mediaSize = CGSizeMake(width, height);
- } else {
- // 默认尺寸
- self.mediaSize = CGSizeMake(200, 150);
- }
- self.placeholderImage = kImageMake(@"pictrue_placeholder");
-
- // 文件消息
- NSDictionary * extend = [mutableDict[@"extend"] isKindOfClass:NSDictionary.class]?mutableDict[@"extend"]:@{};
- self.fileName = extend[@"fileName"] ?: @"未知文件";
- self.fileSize = extend[@"size"] ?: @"0KB";
-
-
- self.url = extend[@"url"] ?: @"";
- // NSCharacterSet *allowedCharacters = [NSCharacterSet URLQueryAllowedCharacterSet];
- // self.url = [self.url stringByAddingPercentEncodingWithAllowedCharacters:allowedCharacters];
- self.localurl = extend[@"localurl"] ?: @"";
-
- self.quoteMessage = ![extend jk_hasKey:@"quoteMessage"] ?NULL: [ChatMessageModel modelWithDictionary:extend[@"quoteMessage"]];
-
- // 语音消息
- NSInteger duration = [extend[@"time"] integerValue];
- if (duration<=60) {
- self.voiceDuration = duration;
- }else{
- self.voiceDuration = duration/1000;
- }
- self.voiceWidth = [self voiceBubbleWidth];
-
- //文件传输成功或失败
- self.fileError = [extend[@"fileError"] integerValue];
-
- //合并转发
- self.forwardMsgArray = extend[@"messageList"] ?:@[];
-
- // 通话记录
- self.callDuration = [mutableDict[@"duration"] integerValue];
- self.isCallSuccess = [mutableDict[@"result"] boolValue];
- self.isVideo = [mutableDict[@"video"] boolValue];
-
- //@相关
- self.atAll = extend[@"atAll"]?[extend[@"atAll"] boolValue]:NO;
- self.atUserIds = extend[@"atUserIds"]?:@[];
-
- // 时间戳(如果需要)
- self.timestamp = [mutableDict[@"timestamp"] longLongValue];
- self.localtime = [mutableDict[@"localtime"] longLongValue];
- self.formatterTime = [self timeF:self.timestamp];
-
- // 其他自定义字段...
- self.readStatus = [self getReadStatus];
-
- NSString *localPath = [self checkHasLocalPath];
- if (localPath.length) {
- self.localurl = localPath;
- [self updateFileSizeString];
- }
- }
- - (void)exchangeModelWithModel:(ChatMessageModel *)model{
- self.msgId = model.msgId;
- self.formerMessage = model.formerMessage;
-
- if(self.messageType!=model.messageType){
- self.cellHeight=0;
- }
- self.messageType = model.messageType;
-
- self.type = model.type;
- self.isSender = model.isSender;
- self.content = model.content;
- self.msgId = model.msgId;
- self.mediaSize = model.mediaSize;
- self.placeholderImage = model.placeholderImage;
- self.fileName = model.fileName;
- self.fileSize = model.fileSize;
- self.url = model.url;
- self.localurl = model.localurl;
- //文件传输成功或失败
- self.fileError = model.fileError;
-
- // 通话记录
- self.callDuration = model.callDuration;
- self.isCallSuccess = model.isCallSuccess;
- self.isVideo = model.isVideo;
- self.avatar = model.avatar;
- self.nickName = model.nickName;
-
- //@相关
- self.atAll = model.atAll;
- self.atUserIds = model.atUserIds;
-
- // 时间戳(如果需要)
- self.timestamp = model.timestamp;
- self.localtime = model.localtime;
- self.formatterTime = model.formatterTime;
-
- // // 其他自定义字段...
- // 其他自定义字段...
- self.readStatus = [self getReadStatus];
- }
- -(NSString *)timeF:(NSInteger)time{
- //NSLog(@"time:%ld",(long)time);
- // 创建日期格式器
- NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
- [formatter setDateFormat:@"dd"]; // 设置你想要的日期格式
-
-
- long long timestamp =(long)time/1000; // 例如:2021-10-01 00:00:00 UTC
- // 转换为NSDate
- NSDate *date = [NSDate dateWithTimeIntervalSince1970:timestamp];
- NSString *dateString = [formatter stringFromDate:date];
-
-
- NSDate *now = [NSDate date];
- NSTimeInterval trt = [now timeIntervalSince1970];
- date = [NSDate dateWithTimeIntervalSince1970:trt];
- NSString *tdateString = [formatter stringFromDate:date];
-
-
- NSInteger shijiancha = trt-timestamp;
-
- if(shijiancha<60){
- return NSLocalizedString(@"time-oneminint", @"");
- }
- if(shijiancha>24*3600||dateString.intValue!=tdateString.intValue){
- // 假设这是你的时间戳(以秒为单位)
- long long timestamp =(long)time/1000; // 例如:2021-10-01 00:00:00 UTC
- // 转换为NSDate
- NSDate *date = [NSDate dateWithTimeIntervalSince1970:timestamp];
- // 创建日期格式器
- NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
- [formatter setDateFormat:@"yyyy-MM-dd HH:mm"]; // 设置你想要的日期格式
- // 转换为字符串
- NSString *dateString = [formatter stringFromDate:date];
- return dateString;
- }
- else{
- NSInteger xiaoshi = shijiancha/3600;
- NSInteger fenzhong = shijiancha/60;
- if(xiaoshi>0){
- // 假设这是你的时间戳(以秒为单位)
- long long timestamp =(long)time/1000; // 例如:2021-10-01 00:00:00 UTC
- // 转换为NSDate
- NSDate *date = [NSDate dateWithTimeIntervalSince1970:timestamp];
- // 创建日期格式器
- NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
- [formatter setDateFormat:@"HH:mm"]; // 设置你想要的日期格式
- // 转换为字符串
- NSString *dateString = [formatter stringFromDate:date];
- return dateString;
- }
- else{
- return [NSString stringWithFormat:@"%ld%@",(long)fenzhong,NSLocalizedString(@"time-outmin", @"")];
- }
- }
-
- return @"";
- }
- - (NSString *)getReadStatus{
- if(self.isSent){
- //判断已读未读
- if(self.timestamp>ChatsStore.shareInstance.lastreadTime){
- return @"未读";
- }
- else{
- return @"已读";
- }
- }
- return @"";
- }
- - (void)generateThumbnailWithCompletion:(void(^)(UIImage *thumbnail))completion {
- // 如果内存中已有缩略图,直接返回
- if (self.videoThumbnailImage) {
- if (completion) completion(self.videoThumbnailImage);
- return;
- }
-
- // 生成缓存 key,优先使用 msgId,如果没有则使用 URL 的 hash
- NSString *cacheKey = nil;
- if (self.msgId.length > 0) {
- cacheKey = [NSString stringWithFormat:@"video_thumbnail_%@", self.msgId];
- } else if (self.url.length > 0) {
- cacheKey = [NSString stringWithFormat:@"video_thumbnail_%lu", (unsigned long)[self.url hash]];
- }
-
- // 从 SDWebImage 缓存中查找
- if (cacheKey) {
- SDImageCache *imageCache = [SDImageCache sharedImageCache];
- UIImage *cachedImage = [imageCache imageFromCacheForKey:cacheKey];
- if (cachedImage) {
- NSLog(@"从缓存加载缩略图: %@", cacheKey);
- self.videoThumbnailImage = cachedImage;
- if (completion) completion(cachedImage);
- return;
- }
- }
-
- // 没有缓存,需要生成缩略图
- NSURL *videoURL = nil;
- if (self.localurl.length != 0 && [[NSFileManager defaultManager] fileExistsAtPath:self.localurl]) {
- // 从本地文件生成缩略图
- NSLog(@"generateThumbnailWithCompletion localurl:%@",self.localurl);
- videoURL = [NSURL fileURLWithPath:self.localurl];
- NSString *finalCacheKey = cacheKey;
- dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
- AVURLAsset *asset = [[AVURLAsset alloc] initWithURL:videoURL options:nil];
-
- AVAssetImageGenerator *generator = [[AVAssetImageGenerator alloc] initWithAsset:asset];
- generator.appliesPreferredTrackTransform = YES;
- CMTime acttime;
- NSError *error = nil;
- CGImageRef imageRef = [generator copyCGImageAtTime:CMTimeMake(0, 600) actualTime:&acttime error:&error];
- NSLog(@"error:%@",error);
- if (!error && imageRef) {
- UIImage *thumbnail = [[UIImage alloc] initWithCGImage:imageRef];
- CGImageRelease(imageRef);
-
- // 缓存到内存
- self.videoThumbnailImage = thumbnail;
-
- // 使用 SDWebImage 缓存到磁盘和内存
- if (finalCacheKey) {
- SDImageCache *imageCache = [SDImageCache sharedImageCache];
- [imageCache storeImage:thumbnail forKey:finalCacheKey completion:^{
- NSLog(@"缩略图已缓存: %@", finalCacheKey);
- }];
- }
-
- if (completion){
- completion(thumbnail);
- }
- } else {
- if (completion) completion(nil);
- }
- });
- } else {
- // 从远程 URL 生成缩略图
- NSLog(@"generateThumbnailWithCompletion url");
- if (self.url.length == 0){
- if (completion) completion(nil);
- return;
- }
-
- NSDictionary *options = @{AVURLAssetAllowsCellularAccessKey: @YES,
- AVURLAssetPreferPreciseDurationAndTimingKey: @YES};
- AVURLAsset *asset = [AVURLAsset URLAssetWithURL:getURL(self.url) options:options];
-
- // 创建图像生成器
- AVAssetImageGenerator *generator = [AVAssetImageGenerator assetImageGeneratorWithAsset:asset];
- generator.appliesPreferredTrackTransform = YES; // 应用视频的方向
- generator.maximumSize = CGSizeMake(320, 0); // 设置最大尺寸,保持宽高比
-
- NSString *finalCacheKey = cacheKey;
- // 异步生成缩略图
- [generator generateCGImagesAsynchronouslyForTimes:@[[NSValue valueWithCMTime:kCMTimeZero]]
- completionHandler:^(CMTime requestedTime, CGImageRef image, CMTime actualTime, AVAssetImageGeneratorResult result, NSError *error) {
- __block UIImage *thumbnail = nil;
- // 处理生成结果
- if (result == AVAssetImageGeneratorSucceeded && image) {
- // 创建UIImage
- thumbnail = [UIImage imageWithCGImage:image];
- self.videoThumbnailImage = thumbnail;
-
- // 使用 SDWebImage 缓存到磁盘和内存
- if (finalCacheKey) {
- SDImageCache *imageCache = [SDImageCache sharedImageCache];
- [imageCache storeImage:thumbnail forKey:finalCacheKey completion:^{
- NSLog(@"缩略图已缓存: %@", finalCacheKey);
- }];
- }
- }
- dispatch_async(dispatch_get_main_queue(), ^{
- if (completion) completion(thumbnail);
- });
- }];
- }
- }
- //下载视频文件
- -(void)downloadFileIfNeed:(void(^)(NSInteger persent))loading{
- if ([[NSFileManager defaultManager] fileExistsAtPath:self.localurl]){
- if (loading) {
- loading(100);
- }
- NSLog(@"downloadFileIfNeed 0: %@", self.localurl);
- return;
- }
-
- if (![self.url hasPrefix:@"http"]) {
- NSLog(@"文件URL异常:%@", self.formerMessage);
- return;
- }
-
- [FileNetApi downLoadWToken:getURL(self.url) thrid:self.msgId succ:^(int code, NSDictionary * res) {
- if(res!=nil){
- NSMutableDictionary *mutableDict = [self.formerMessage mutableCopy];
- NSMutableDictionary *extend = [self.formerMessage[@"extend"] mutableCopy];
- [extend setObject:res[@"filePath"] forKey:@"localurl"];
- [mutableDict setObject:extend forKey:@"extend"];
- self.localurl = res[@"filePath"];
- NSDictionary *NewMsg = [mutableDict copy];
- [ChatsStore.shareInstance reciveMsg:NewMsg];
- [self updateFileSizeString];
- if (loading) {
- loading(100);
- }
- }
- else{
- if (code>=0&&code<200) {
- if (loading) {
- loading(code);
- }
- }
- }
- } fail:^(NSError * _Nonnull error) {
- NSLog(@"downLoadWToken error:%@",error);
- }];
- }
- // 根据语音时长计算气泡宽度
- - (CGFloat)voiceBubbleWidth {
- // 基础宽度
- CGFloat minWidth = 60.0f;
- CGFloat maxWidth = 200.0f;
-
- // 最大限制时长(如60秒)
- NSInteger maxDuration = 60;
- NSInteger duration = MIN(self.voiceDuration, maxDuration);
-
- // 线性增长计算
- CGFloat width = minWidth + (maxWidth - minWidth) * (duration / (CGFloat)maxDuration);
-
- return width;
- }
- #pragma mark - 消息状态管理
- - (void)updateFileSizeString {
- NSString *filePath = self.localurl;
- NSFileManager *fileManager = [NSFileManager defaultManager];
- // 获取文件属性
- NSError *error = nil;
- NSDictionary *attributes = [fileManager attributesOfItemAtPath:filePath error:&error];
- NSString *string = @"0KB";
- if (error) {
- NSLog(@"获取文件属性失败: %@", error);
- } else {
- // 获取文件大小(单位:字节)
- unsigned long long fileSize = [attributes fileSize];
- NSLog(@"文件大小: %llu 字节", fileSize);
- // 可选:转换为 KB、MB
- double fileSizeKB = fileSize / 1024.0;
- double fileSizeMB = fileSizeKB / 1024.0;
- NSLog(@"文件大小: %.2f KB (%.2f MB)", fileSizeKB, fileSizeMB);
- string = [NSString stringWithFormat:@"%.2fMB", fileSizeMB];
- }
- _customFileSize = string;
- }
- - (NSString *)checkHasLocalPath {
- if (!self.isUploadFile) {
- return nil;
- }
-
- NSURL *documentsDirectoryURL = [[NSFileManager defaultManager] URLForDirectory:NSDocumentDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:NO error:nil];
-
- NSString *fileName;
- NSURL *localURL = [NSURL URLWithString:self.localurl];
- if (localURL) {
- fileName = localURL.lastPathComponent;
- } else {
- fileName = [NSURL URLWithString:self.url].lastPathComponent;
- }
- if (fileName.length == 0) {
- return nil;
- }
-
- NSString *path = [documentsDirectoryURL URLByAppendingPathComponent:fileName].path;
- if ([[NSFileManager defaultManager] fileExistsAtPath:path]){
- NSLog(@"checkHasLocalPath: %@", path);
- return path;
- }
- return nil;
- }
- - (BOOL)isUploadFile {
- return self.messageType == ChatMessageTypeFile ||
- self.messageType == ChatMessageTypeVoice ||
- self.messageType == ChatMessageTypeImage ||
- self.messageType == ChatMessageTypeVideo;
- }
- - (BOOL)isSent {
- return ![self.msgId isEqualToString:[NSString stringWithFormat:@"%ld", self.localtime]];
- }
- - (BOOL)checkIsSendFail {
- // NSDate *now = [NSDate date];
- // NSTimeInterval trt = [now timeIntervalSince1970];
- // NSInteger time = trt*1000;
- // NSInteger outTime =time-self.localtime;
- // if(outTime>33000){
- // return YES;
- // }
- return NO;
- }
- - (FileUploadStatus)fileUploadStatus {
- NSString *key = [self getKey];
- NSInteger value = [[[NSUserDefaults standardUserDefaults] valueForKey:key] integerValue];
- NSLog(@"checkIsNeedResend key: %@ value: %ld", key, value);
- if (value >= 100) {
- return FileUploadCompleted;
- }
-
- if (value == -1) {
- return FileUploadFail;
- }
- return FileUploading;
- }
- - (void)updateFileStatusToUploading {
- [[NSUserDefaults standardUserDefaults] setValue:@(0) forKey:[self getKey]];
- }
- -(NSString *)getKey {
- NSString *localtime = [NSString stringWithFormat:@"%ld", self.localtime];
- return [[OSSManager sharedManager] fileMessageKeyWith:localtime];
- }
- @end
|