// // BuglyManager.m // YuMi // // Created by BuglyManager // Copyright © 2024 YuMi. All rights reserved. // #import "BuglyManager.h" #import #import "TurboModeStateManager.h" @interface BuglyManager () @property (nonatomic, strong) NSString *appId; @property (nonatomic, assign) BOOL isConfigured; // 卡顿计数相关 @property (nonatomic, assign) NSInteger lagCount; @property (nonatomic, assign) BOOL isInRoom; @property (nonatomic, assign) BOOL isTurboModeEnabled; @end @implementation BuglyManager #pragma mark - Singleton + (instancetype)sharedManager { static BuglyManager *instance = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ instance = [[BuglyManager alloc] init]; }); return instance; } - (instancetype)init { self = [super init]; if (self) { _isConfigured = NO; _lagCount = 0; _isInRoom = NO; _isTurboModeEnabled = NO; // 监听 turbo mode 状态变化 [self setupTurboModeNotifications]; // 🔧 修复:初始化时获取当前 turbo mode 状态 [self updateTurboModeState]; } return self; } #pragma mark - BuglyDelegate - (NSString * BLY_NULLABLE)attachmentForException:(NSException * BLY_NULLABLE)exception { NSString *message = [NSString stringWithFormat:@"%@ - %@", exception.name, exception.reason]; [self handleLagDetection:message]; return message; } #pragma mark - Public Methods - (void)configureWithAppId:(NSString *)appId debug:(BOOL)isDebug { if (self.isConfigured) { NSLog(@"[BuglyManager] Bugly 已经配置,跳过重复配置"); return; } if (!appId || appId.length == 0) { NSLog(@"[BuglyManager] 错误:appId 不能为空"); return; } self.appId = appId; // 创建 Bugly 配置 BuglyConfig *config = [[BuglyConfig alloc] init]; config.delegate = self; // 基础配置 config.blockMonitorTimeout = 3.0; // 卡顿监控超时时间:3秒 config.blockMonitorEnable = YES; // 启用卡顿监控 // 调试模式配置 if (isDebug) { config.debugMode = NO; // 生产环境关闭调试模式 config.channel = [self getAppChannel]; config.reportLogLevel = BuglyLogLevelWarn; // 设置日志级别 } else { config.unexpectedTerminatingDetectionEnable = YES; // 非正常退出事件记录 config.debugMode = NO; config.channel = [self getAppChannel]; config.blockMonitorEnable = YES; config.reportLogLevel = BuglyLogLevelWarn; } // 启动 Bugly [Bugly startWithAppId:appId config:config]; self.isConfigured = YES; NSLog(@"[BuglyManager] Bugly 配置完成 - AppID: %@, Debug: %@", appId, isDebug ? @"YES" : @"NO"); } - (void)reportError:(NSString *)domain code:(NSInteger)code userInfo:(NSDictionary *)userInfo { if (!self.isConfigured) { NSLog(@"[BuglyManager] 错误:Bugly 未配置,无法上报错误"); return; } if (!domain || domain.length == 0) { domain = @"UnknownError"; } // 创建错误对象 NSError *error = [NSError errorWithDomain:domain code:code userInfo:userInfo]; // 异步上报错误 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ [Bugly reportError:error]; NSLog(@"[BuglyManager] 错误上报成功 - Domain: %@, Code: %ld", domain, (long)code); }); } - (void)reportBusinessError:(NSString *)message code:(NSInteger)code context:(NSDictionary *)context { NSMutableDictionary *userInfo = [NSMutableDictionary dictionary]; // 添加基础信息 if (message && message.length > 0) { [userInfo setObject:message forKey:@"error_message"]; } [userInfo setObject:@(code) forKey:@"error_code"]; [userInfo setObject:@"BusinessError" forKey:@"error_type"]; // 添加上下文信息 if (context && context.count > 0) { [userInfo addEntriesFromDictionary:context]; } // 添加时间戳 [userInfo setObject:@([[NSDate date] timeIntervalSince1970]) forKey:@"timestamp"]; [self reportError:@"BusinessError" code:code userInfo:userInfo]; } - (void)reportNetworkError:(NSString *)uid api:(NSString *)api code:(NSInteger)code userInfo:(NSDictionary *)userInfo { NSMutableDictionary *errorInfo = [NSMutableDictionary dictionary]; // 添加网络错误特有信息 if (uid && uid.length > 0) { [errorInfo setObject:uid forKey:@"user_id"]; } if (api && api.length > 0) { [errorInfo setObject:api forKey:@"api_path"]; } [errorInfo setObject:@(code) forKey:@"http_code"]; [errorInfo setObject:@"NetworkError" forKey:@"error_type"]; // 添加调用栈信息 [errorInfo setObject:[NSThread callStackSymbols] forKey:@"call_stack_symbols"]; // 合并额外信息 if (userInfo && userInfo.count > 0) { [errorInfo addEntriesFromDictionary:userInfo]; } [self reportError:@"NetworkError" code:code userInfo:errorInfo]; } - (void)reportIAPError:(NSString *)uid transactionId:(NSString *)transactionId orderId:(NSString *)orderId status:(NSInteger)status context:(NSDictionary *)context { NSMutableDictionary *errorInfo = [NSMutableDictionary dictionary]; // 添加内购错误特有信息 if (uid && uid.length > 0) { [errorInfo setObject:uid forKey:@"user_id"]; } if (transactionId && transactionId.length > 0) { [errorInfo setObject:transactionId forKey:@"transaction_id"]; } if (orderId && orderId.length > 0) { [errorInfo setObject:orderId forKey:@"order_id"]; } [errorInfo setObject:@(status) forKey:@"status_code"]; [errorInfo setObject:@"IAPError" forKey:@"error_type"]; // 添加状态描述 NSString *statusMsg = [self getIAPStatusMessage:status]; if (statusMsg) { [errorInfo setObject:statusMsg forKey:@"status_message"]; } // 合并上下文信息 if (context && context.count > 0) { [errorInfo addEntriesFromDictionary:context]; } // 生成错误码 NSInteger errorCode = -20000 + status; [self reportError:@"IAPError" code:errorCode userInfo:errorInfo]; } - (void)startLagDetection { if (!self.isConfigured) { NSLog(@"[BuglyManager] 错误:Bugly 未配置,无法启动卡顿检测"); return; } NSLog(@"[BuglyManager] 手动启动卡顿检测"); // Bugly 会自动进行卡顿检测,这里主要是日志记录 } #pragma mark - 测试方法 - (void)simulateLagDetection { NSLog(@"[BuglyManager] 🧪 模拟卡顿检测(测试按钮触发)"); // 模拟卡顿检测,触发计数逻辑 [self handleLagCountWithDuration:3.0 stackTrace:@"模拟卡顿 - 测试按钮触发"]; } #pragma mark - Private Methods - (void)handleLagDetection:(NSString *)stackTrace { NSLog(@"[BuglyManager] 🚨 检测到卡顿 - StackTrace: %@", stackTrace); // 计算卡顿持续时间(这里假设为3秒,实际应该从 Bugly 配置中获取) NSTimeInterval duration = 3.0; // 通知代理 if (self.delegate && [self.delegate respondsToSelector:@selector(buglyManager:didDetectLag:)]) { dispatch_async(dispatch_get_main_queue(), ^{ [(id)self.delegate buglyManager:self didDetectLag:duration]; }); } // 🔧 新增:卡顿计数逻辑 [self handleLagCountWithDuration:duration stackTrace:stackTrace]; } #pragma mark - 卡顿计数逻辑 - (void)handleLagCountWithDuration:(NSTimeInterval)duration stackTrace:(NSString *)stackTrace { // 规则2:当 turbo mode 开启时,不计数 if (self.isTurboModeEnabled) { NSLog(@"[BuglyManager] 🎮 Turbo Mode 已开启,跳过卡顿计数"); return; } // 规则1:只有在房间内才计数 if (!self.isInRoom) { NSLog(@"[BuglyManager] 🏠 不在房间内,跳过卡顿计数"); return; } // 增加计数 self.lagCount++; NSLog(@"[BuglyManager] 📊 卡顿计数: %ld/3", (long)self.lagCount); // 检查是否达到3次 if (self.lagCount >= 3) { NSLog(@"[BuglyManager] 🚨 累计卡顿3次,触发 Turbo Mode Tips"); // 发送通知给 Tips Manager dispatch_async(dispatch_get_main_queue(), ^{ [[NSNotificationCenter defaultCenter] postNotificationName:@"BuglyManagerDidDetectLag" object:nil userInfo:@{@"duration": @(duration), @"stackTrace": stackTrace ?: @"", @"lagCount": @(self.lagCount), @"shouldShowTips": @YES}]; }); // 重置计数 self.lagCount = 0; } else { // 未达到3次,只发送计数通知 dispatch_async(dispatch_get_main_queue(), ^{ [[NSNotificationCenter defaultCenter] postNotificationName:@"BuglyManagerDidDetectLag" object:nil userInfo:@{@"duration": @(duration), @"stackTrace": stackTrace ?: @"", @"lagCount": @(self.lagCount), @"shouldShowTips": @NO}]; }); } } - (void)setupTurboModeNotifications { // 监听 turbo mode 状态变化 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(turboModeStateChanged:) name:@"TurboModeStateChanged" object:nil]; // 监听房间进入/退出 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(roomDidEnter:) name:@"RoomDidEnter" object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(roomDidExit:) name:@"RoomDidExit" object:nil]; } - (void)turboModeStateChanged:(NSNotification *)notification { NSNumber *enabled = notification.userInfo[@"enabled"]; if (enabled) { self.isTurboModeEnabled = [enabled boolValue]; NSLog(@"[BuglyManager] 🎮 Turbo Mode 状态变化: %@", enabled.boolValue ? @"开启" : @"关闭"); // 规则2:当 turbo mode 开启时,计数复位为0 if (enabled.boolValue) { self.lagCount = 0; NSLog(@"[BuglyManager] 🔄 Turbo Mode 开启,卡顿计数复位为0"); } } } - (void)roomDidEnter:(NSNotification *)notification { self.isInRoom = YES; // 🔧 修复:进入房间时主动获取当前 turbo mode 状态 [self updateTurboModeState]; NSLog(@"[BuglyManager] 🏠 用户进入房间,开始卡顿监控 - Turbo Mode: %@", self.isTurboModeEnabled ? @"开启" : @"关闭"); } - (void)roomDidExit:(NSNotification *)notification { self.isInRoom = NO; // 规则3:当用户退出房间时,计数复位为0 self.lagCount = 0; NSLog(@"[BuglyManager] 🚪 用户退出房间,卡顿计数复位为0"); } #pragma mark - 状态更新方法 - (void)updateTurboModeState { // 从 TurboModeStateManager 获取当前状态 BOOL currentTurboModeState = [[TurboModeStateManager sharedManager] isTurboModeEnabled]; if (self.isTurboModeEnabled != currentTurboModeState) { self.isTurboModeEnabled = currentTurboModeState; NSLog(@"[BuglyManager] 🔄 主动更新 Turbo Mode 状态: %@", currentTurboModeState ? @"开启" : @"关闭"); // 如果 turbo mode 开启,计数复位为0 if (currentTurboModeState) { self.lagCount = 0; NSLog(@"[BuglyManager] 🔄 Turbo Mode 开启,卡顿计数复位为0"); } } } - (NSString *)getAppChannel { // 这里应该调用项目中的工具方法获取渠道信息 // 暂时返回默认值 return @"AppStore"; } #pragma mark - Dealloc - (void)dealloc { [[NSNotificationCenter defaultCenter] removeObserver:self]; } - (NSString *)getIAPStatusMessage:(NSInteger)status { switch (status) { case 0: return @"尝试验单"; case 1: return @"验单-补单成功"; case 2: return @"验单-补单失败"; case 3: return @"验单-补单 id 异常"; case 4: return @"重试次数过多"; case 5: return @"过期交易清理"; default: return @"未知状态"; } } @end