- 修复iOS后台任务注册时机问题,确保任务能正常触发 - 添加后台任务调试辅助工具和完整测试指南 - 优化断食通知系统,增加防抖机制避免频繁重调度 - 改进断食自动续订逻辑,使用固定时间而非相对时间计算 - 优化统计页面布局,添加身体指标section标题 - 增强饮水详情页面视觉效果,改进卡片样式和配色 - 添加用户反馈入口到个人设置页面 - 完善锻炼摘要卡片条件渲染逻辑 - 增强日志记录和错误处理机制 这些改进显著提升了应用的稳定性、性能和用户体验,特别是在iOS后台任务执行和断食功能方面。
308 lines
8.5 KiB
TypeScript
308 lines
8.5 KiB
TypeScript
/**
|
||
* 后台任务调试辅助工具
|
||
*
|
||
* 用于在开发和测试阶段验证后台任务配置和执行情况
|
||
*/
|
||
|
||
import { logger } from '@/utils/logger';
|
||
import { NativeModules, Platform } from 'react-native';
|
||
import { BackgroundTaskManager } from './backgroundTaskManagerV2';
|
||
|
||
const NativeBackgroundModule = NativeModules.BackgroundTaskBridge;
|
||
|
||
export class BackgroundTaskDebugHelper {
|
||
private static instance: BackgroundTaskDebugHelper;
|
||
|
||
static getInstance(): BackgroundTaskDebugHelper {
|
||
if (!BackgroundTaskDebugHelper.instance) {
|
||
BackgroundTaskDebugHelper.instance = new BackgroundTaskDebugHelper();
|
||
}
|
||
return BackgroundTaskDebugHelper.instance;
|
||
}
|
||
|
||
/**
|
||
* 执行完整的后台任务诊断
|
||
*/
|
||
async runFullDiagnostics(): Promise<DiagnosticsReport> {
|
||
logger.info('[BackgroundTaskDebug] ====== 开始后台任务诊断 ======');
|
||
|
||
const report: DiagnosticsReport = {
|
||
platform: Platform.OS,
|
||
timestamp: new Date().toISOString(),
|
||
checks: {},
|
||
};
|
||
|
||
// 1. 检查平台支持
|
||
report.checks.platformSupport = this.checkPlatformSupport();
|
||
|
||
// 2. 检查原生模块
|
||
report.checks.nativeModule = await this.checkNativeModule();
|
||
|
||
// 3. 检查后台刷新权限
|
||
report.checks.backgroundRefresh = await this.checkBackgroundRefreshStatus();
|
||
|
||
// 4. 检查待处理任务
|
||
report.checks.pendingTasks = await this.checkPendingTasks();
|
||
|
||
// 5. 检查配置
|
||
report.checks.configuration = this.checkConfiguration();
|
||
|
||
// 6. 检查最后执行时间
|
||
report.checks.lastExecution = await this.checkLastExecution();
|
||
|
||
logger.info('[BackgroundTaskDebug] ====== 诊断完成 ======');
|
||
logger.info('[BackgroundTaskDebug] 报告:', JSON.stringify(report, null, 2));
|
||
|
||
return report;
|
||
}
|
||
|
||
/**
|
||
* 检查平台支持
|
||
*/
|
||
private checkPlatformSupport(): CheckResult {
|
||
const isIOS = Platform.OS === 'ios';
|
||
return {
|
||
status: isIOS ? 'success' : 'error',
|
||
message: isIOS
|
||
? 'iOS 平台支持后台任务'
|
||
: `当前平台 (${Platform.OS}) 不支持后台任务`,
|
||
details: {
|
||
platform: Platform.OS,
|
||
version: Platform.Version,
|
||
},
|
||
};
|
||
}
|
||
|
||
/**
|
||
* 检查原生模块
|
||
*/
|
||
private async checkNativeModule(): Promise<CheckResult> {
|
||
if (!NativeBackgroundModule) {
|
||
return {
|
||
status: 'error',
|
||
message: '原生模块 BackgroundTaskBridge 不可用',
|
||
details: {
|
||
available: false,
|
||
},
|
||
};
|
||
}
|
||
|
||
try {
|
||
// 尝试调用一个简单的方法来验证模块是否正常工作
|
||
const status = await NativeBackgroundModule.backgroundRefreshStatus();
|
||
return {
|
||
status: 'success',
|
||
message: '原生模块可用且正常工作',
|
||
details: {
|
||
available: true,
|
||
backgroundRefreshStatus: status,
|
||
},
|
||
};
|
||
} catch (error) {
|
||
return {
|
||
status: 'error',
|
||
message: '原生模块存在但调用失败',
|
||
details: {
|
||
available: true,
|
||
error: (error as Error).message,
|
||
},
|
||
};
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 检查后台刷新权限状态
|
||
*/
|
||
private async checkBackgroundRefreshStatus(): Promise<CheckResult> {
|
||
try {
|
||
const manager = BackgroundTaskManager.getInstance();
|
||
const status = await manager.getStatus();
|
||
const statusText = await manager.checkStatus();
|
||
|
||
let resultStatus: 'success' | 'warning' | 'error';
|
||
let message: string;
|
||
|
||
switch (status) {
|
||
case 'available':
|
||
resultStatus = 'success';
|
||
message = '后台刷新权限已启用';
|
||
break;
|
||
case 'denied':
|
||
resultStatus = 'error';
|
||
message = '后台刷新被拒绝,请在设置中启用';
|
||
break;
|
||
case 'restricted':
|
||
resultStatus = 'error';
|
||
message = '后台刷新被限制(可能是家长控制)';
|
||
break;
|
||
default:
|
||
resultStatus = 'warning';
|
||
message = `后台刷新状态未知: ${status}`;
|
||
}
|
||
|
||
return {
|
||
status: resultStatus,
|
||
message,
|
||
details: {
|
||
status,
|
||
statusText,
|
||
},
|
||
};
|
||
} catch (error) {
|
||
return {
|
||
status: 'error',
|
||
message: '检查后台刷新状态失败',
|
||
details: {
|
||
error: (error as Error).message,
|
||
},
|
||
};
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 检查待处理的任务
|
||
*/
|
||
private async checkPendingTasks(): Promise<CheckResult> {
|
||
try {
|
||
const manager = BackgroundTaskManager.getInstance();
|
||
const pendingRequests = await manager.getPendingRequests();
|
||
|
||
return {
|
||
status: pendingRequests.length > 0 ? 'success' : 'warning',
|
||
message: pendingRequests.length > 0
|
||
? `有 ${pendingRequests.length} 个待处理任务`
|
||
: '没有待处理的任务(可能需要调度)',
|
||
details: {
|
||
count: pendingRequests.length,
|
||
tasks: pendingRequests,
|
||
},
|
||
};
|
||
} catch (error) {
|
||
return {
|
||
status: 'error',
|
||
message: '检查待处理任务失败',
|
||
details: {
|
||
error: (error as Error).message,
|
||
},
|
||
};
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 检查配置
|
||
*/
|
||
private checkConfiguration(): CheckResult {
|
||
const config = {
|
||
identifier: 'com.anonymous.digitalpilates.task',
|
||
defaultDelay: 15 * 60, // 15分钟
|
||
};
|
||
|
||
return {
|
||
status: 'info',
|
||
message: '后台任务配置',
|
||
details: config,
|
||
};
|
||
}
|
||
|
||
/**
|
||
* 检查最后执行时间
|
||
*/
|
||
private async checkLastExecution(): Promise<CheckResult> {
|
||
try {
|
||
const manager = BackgroundTaskManager.getInstance();
|
||
const lastCheckTime = await manager.getLastBackgroundCheckTime();
|
||
|
||
if (!lastCheckTime) {
|
||
return {
|
||
status: 'warning',
|
||
message: '后台任务从未执行过',
|
||
details: {
|
||
lastExecution: null,
|
||
},
|
||
};
|
||
}
|
||
|
||
const timeSinceLastCheck = Date.now() - lastCheckTime;
|
||
const hoursSinceLastCheck = timeSinceLastCheck / (1000 * 60 * 60);
|
||
|
||
return {
|
||
status: hoursSinceLastCheck > 24 ? 'warning' : 'success',
|
||
message: hoursSinceLastCheck > 24
|
||
? `距离上次执行已超过24小时 (${hoursSinceLastCheck.toFixed(1)}小时)`
|
||
: `上次执行时间: ${new Date(lastCheckTime).toLocaleString()}`,
|
||
details: {
|
||
lastExecution: lastCheckTime,
|
||
timeSinceLastCheck: `${hoursSinceLastCheck.toFixed(1)} 小时`,
|
||
},
|
||
};
|
||
} catch (error) {
|
||
return {
|
||
status: 'error',
|
||
message: '检查最后执行时间失败',
|
||
details: {
|
||
error: (error as Error).message,
|
||
},
|
||
};
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 生成可读的诊断报告
|
||
*/
|
||
generateReadableReport(report: DiagnosticsReport): string {
|
||
let output = '\n========== 后台任务诊断报告 ==========\n';
|
||
output += `时间: ${new Date(report.timestamp).toLocaleString()}\n`;
|
||
output += `平台: ${report.platform}\n`;
|
||
output += '\n';
|
||
|
||
Object.entries(report.checks).forEach(([key, check]) => {
|
||
const icon = check.status === 'success' ? '✅' : check.status === 'error' ? '❌' : '⚠️';
|
||
output += `${icon} ${key}: ${check.message}\n`;
|
||
if (check.details && Object.keys(check.details).length > 0) {
|
||
output += ` 详情: ${JSON.stringify(check.details, null, 2)}\n`;
|
||
}
|
||
output += '\n';
|
||
});
|
||
|
||
output += '=====================================\n';
|
||
return output;
|
||
}
|
||
|
||
/**
|
||
* 模拟触发后台任务(仅用于测试)
|
||
*/
|
||
async triggerTestTask(): Promise<void> {
|
||
logger.info('[BackgroundTaskDebug] 触发测试任务...');
|
||
|
||
try {
|
||
const manager = BackgroundTaskManager.getInstance();
|
||
await manager.triggerTaskForTesting();
|
||
logger.info('[BackgroundTaskDebug] ✅ 测试任务执行完成');
|
||
} catch (error: any) {
|
||
const errorCode = error?.code || '';
|
||
|
||
if (errorCode === 'SIMULATOR_NOT_SUPPORTED') {
|
||
logger.info('[BackgroundTaskDebug] ℹ️ 在模拟器上执行了后台任务逻辑');
|
||
logger.info('[BackgroundTaskDebug] 模拟器不支持完整的后台任务调度');
|
||
logger.info('[BackgroundTaskDebug] 这是正常的,请在真机上测试完整功能');
|
||
} else {
|
||
logger.error('[BackgroundTaskDebug] ❌ 测试任务执行失败', error);
|
||
throw error;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
export interface DiagnosticsReport {
|
||
platform: string;
|
||
timestamp: string;
|
||
checks: {
|
||
[key: string]: CheckResult;
|
||
};
|
||
}
|
||
|
||
export interface CheckResult {
|
||
status: 'success' | 'warning' | 'error' | 'info';
|
||
message: string;
|
||
details?: Record<string, any>;
|
||
} |