feat: 优化后台任务管理,添加系统权限和用户偏好的完整检查,增强通知功能
This commit is contained in:
10
app.json
10
app.json
@@ -18,8 +18,7 @@
|
|||||||
"NSPhotoLibraryUsageDescription": "应用需要访问相册以选择您的体态照片用于AI测评。",
|
"NSPhotoLibraryUsageDescription": "应用需要访问相册以选择您的体态照片用于AI测评。",
|
||||||
"NSPhotoLibraryAddUsageDescription": "应用需要写入相册以保存拍摄的体态照片(可选)。",
|
"NSPhotoLibraryAddUsageDescription": "应用需要写入相册以保存拍摄的体态照片(可选)。",
|
||||||
"UIBackgroundModes": [
|
"UIBackgroundModes": [
|
||||||
"background-fetch",
|
"processing"
|
||||||
"background-processing"
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -70,12 +69,7 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
[
|
"expo-background-task"
|
||||||
"expo-background-fetch",
|
|
||||||
{
|
|
||||||
"minimumInterval": 15
|
|
||||||
}
|
|
||||||
]
|
|
||||||
],
|
],
|
||||||
"experiments": {
|
"experiments": {
|
||||||
"typedRoutes": true
|
"typedRoutes": true
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import { useAppDispatch, useAppSelector } from '@/hooks/redux';
|
|||||||
import { useAuthGuard } from '@/hooks/useAuthGuard';
|
import { useAuthGuard } from '@/hooks/useAuthGuard';
|
||||||
import { useBackgroundTasks } from '@/hooks/useBackgroundTasks';
|
import { useBackgroundTasks } from '@/hooks/useBackgroundTasks';
|
||||||
import { notificationService } from '@/services/notifications';
|
import { notificationService } from '@/services/notifications';
|
||||||
|
import { backgroundTaskManager } from '@/services/backgroundTaskManager';
|
||||||
import { selectHealthDataByDate, setHealthData } from '@/store/healthSlice';
|
import { selectHealthDataByDate, setHealthData } from '@/store/healthSlice';
|
||||||
import { fetchDailyMoodCheckins, selectLatestMoodRecordByDate } from '@/store/moodSlice';
|
import { fetchDailyMoodCheckins, selectLatestMoodRecordByDate } from '@/store/moodSlice';
|
||||||
import { fetchDailyNutritionData, selectNutritionSummaryByDate } from '@/store/nutritionSlice';
|
import { fetchDailyNutritionData, selectNutritionSummaryByDate } from '@/store/nutritionSlice';
|
||||||
@@ -37,7 +38,8 @@ import {
|
|||||||
ScrollView,
|
ScrollView,
|
||||||
StyleSheet,
|
StyleSheet,
|
||||||
Text,
|
Text,
|
||||||
View
|
View,
|
||||||
|
TouchableOpacity
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
||||||
|
|
||||||
@@ -618,6 +620,19 @@ export default function ExploreScreen() {
|
|||||||
<View style={styles.headerTextContainer}>
|
<View style={styles.headerTextContainer}>
|
||||||
<Text style={styles.headerTitle}>海豹健康</Text>
|
<Text style={styles.headerTitle}>海豹健康</Text>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
|
{/* 开发环境调试按钮 */}
|
||||||
|
{__DEV__ && (
|
||||||
|
<TouchableOpacity
|
||||||
|
style={styles.debugButton}
|
||||||
|
onPress={async () => {
|
||||||
|
console.log('🔧 手动触发后台任务测试...');
|
||||||
|
await backgroundTaskManager.debugExecuteBackgroundTask();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Text style={styles.debugButtonText}>🔧</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
)}
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
@@ -813,6 +828,25 @@ const styles = StyleSheet.create({
|
|||||||
fontWeight: '500',
|
fontWeight: '500',
|
||||||
color: '#192126',
|
color: '#192126',
|
||||||
},
|
},
|
||||||
|
debugButton: {
|
||||||
|
width: 32,
|
||||||
|
height: 32,
|
||||||
|
borderRadius: 16,
|
||||||
|
backgroundColor: '#FF6B6B',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
shadowColor: '#000',
|
||||||
|
shadowOffset: {
|
||||||
|
width: 0,
|
||||||
|
height: 2,
|
||||||
|
},
|
||||||
|
shadowOpacity: 0.1,
|
||||||
|
shadowRadius: 4,
|
||||||
|
elevation: 3,
|
||||||
|
},
|
||||||
|
debugButtonText: {
|
||||||
|
fontSize: 12,
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
sectionTitle: {
|
sectionTitle: {
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
import { BackgroundTaskType as BackgroundTask, backgroundTaskManager, TaskStatusType as TaskStatus } from '@/services/backgroundTaskManager';
|
import { BackgroundTaskConfig, backgroundTaskManager, TaskStatus } from '@/services/backgroundTaskManager';
|
||||||
import * as BackgroundFetch from 'expo-background-fetch';
|
import * as BackgroundTask from 'expo-background-task';
|
||||||
import { useCallback, useEffect, useState } from 'react';
|
import { useCallback, useEffect, useState } from 'react';
|
||||||
|
|
||||||
export interface UseBackgroundTasksReturn {
|
export interface UseBackgroundTasksReturn {
|
||||||
// 状态
|
// 状态
|
||||||
isInitialized: boolean;
|
isInitialized: boolean;
|
||||||
taskStatuses: TaskStatus[];
|
taskStatuses: TaskStatus[];
|
||||||
registeredTasks: BackgroundTask[];
|
registeredTasks: BackgroundTaskConfig[];
|
||||||
|
|
||||||
// 方法
|
// 方法
|
||||||
registerTask: (task: BackgroundTask) => Promise<void>;
|
registerTask: (task: BackgroundTaskConfig) => Promise<void>;
|
||||||
unregisterTask: (taskId: string) => Promise<void>;
|
unregisterTask: (taskId: string) => Promise<void>;
|
||||||
executeTask: (taskId: string, data?: any) => Promise<void>;
|
executeTask: (taskId: string, data?: any) => Promise<void>;
|
||||||
executeAllTasks: () => Promise<{ [taskId: string]: 'success' | 'failed' }>;
|
executeAllTasks: () => Promise<{ [taskId: string]: 'success' | 'failed' }>;
|
||||||
@@ -17,15 +17,15 @@ export interface UseBackgroundTasksReturn {
|
|||||||
cleanupTaskStatuses: () => Promise<void>;
|
cleanupTaskStatuses: () => Promise<void>;
|
||||||
|
|
||||||
// 后台任务状态
|
// 后台任务状态
|
||||||
backgroundTaskStatus: BackgroundFetch.BackgroundFetchStatus | null;
|
backgroundTaskStatus: BackgroundTask.BackgroundTaskStatus | null;
|
||||||
getBackgroundTaskStatus: () => Promise<void>;
|
getBackgroundTaskStatus: () => Promise<void>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useBackgroundTasks = (): UseBackgroundTasksReturn => {
|
export const useBackgroundTasks = (): UseBackgroundTasksReturn => {
|
||||||
const [isInitialized, setIsInitialized] = useState(false);
|
const [isInitialized, setIsInitialized] = useState(false);
|
||||||
const [taskStatuses, setTaskStatuses] = useState<TaskStatus[]>([]);
|
const [taskStatuses, setTaskStatuses] = useState<TaskStatus[]>([]);
|
||||||
const [registeredTasks, setRegisteredTasks] = useState<BackgroundTask[]>([]);
|
const [registeredTasks, setRegisteredTasks] = useState<BackgroundTaskConfig[]>([]);
|
||||||
const [backgroundTaskStatus, setBackgroundTaskStatus] = useState<BackgroundFetch.BackgroundFetchStatus | null>(null);
|
const [backgroundTaskStatus, setBackgroundTaskStatus] = useState<BackgroundTask.BackgroundTaskStatus | null>(null);
|
||||||
|
|
||||||
// 初始化
|
// 初始化
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -49,7 +49,7 @@ export const useBackgroundTasks = (): UseBackgroundTasksReturn => {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// 注册任务
|
// 注册任务
|
||||||
const registerTask = useCallback(async (task: BackgroundTask) => {
|
const registerTask = useCallback(async (task: BackgroundTaskConfig) => {
|
||||||
await backgroundTaskManager.registerTask(task);
|
await backgroundTaskManager.registerTask(task);
|
||||||
refreshData();
|
refreshData();
|
||||||
}, [refreshData]);
|
}, [refreshData]);
|
||||||
|
|||||||
@@ -1,16 +1,38 @@
|
|||||||
import AsyncStorage from '@react-native-async-storage/async-storage';
|
import AsyncStorage from '@react-native-async-storage/async-storage';
|
||||||
import * as BackgroundFetch from 'expo-background-fetch';
|
import * as BackgroundTask from 'expo-background-task';
|
||||||
import * as TaskManager from 'expo-task-manager';
|
import * as TaskManager from 'expo-task-manager';
|
||||||
|
|
||||||
|
// 后台任务名称常量
|
||||||
|
const BACKGROUND_TASK_NAME = 'health-background-task';
|
||||||
|
|
||||||
|
// 必须在全局作用域定义任务处理器
|
||||||
|
TaskManager.defineTask(BACKGROUND_TASK_NAME, async () => {
|
||||||
|
console.log('======= 后台任务被系统调用 =======');
|
||||||
|
const now = new Date();
|
||||||
|
console.log(`后台任务执行时间: ${now.toISOString()}`);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 获取后台任务管理器实例并执行所有注册的任务
|
||||||
|
const manager = BackgroundTaskManager.getInstance();
|
||||||
|
console.log('开始执行所有注册的任务...');
|
||||||
|
const results = await manager.executeAllTasks();
|
||||||
|
console.log('后台任务执行结果:', results);
|
||||||
|
|
||||||
|
// 返回成功状态
|
||||||
|
return BackgroundTask.BackgroundTaskResult.Success;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('后台任务执行失败:', error);
|
||||||
|
return BackgroundTask.BackgroundTaskResult.Failed;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// 任务类型定义
|
// 任务类型定义
|
||||||
export interface BackgroundTask {
|
export interface BackgroundTaskConfig {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
handler: (data?: any) => Promise<void>;
|
handler: (data?: any) => Promise<void>;
|
||||||
options?: {
|
options?: {
|
||||||
minimumInterval?: number; // 最小间隔时间(分钟)
|
minimumInterval?: number; // 最小间隔时间(分钟)
|
||||||
stopOnTerminate?: boolean; // 应用终止时是否停止
|
|
||||||
startOnBoot?: boolean; // 设备重启时是否启动
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -27,9 +49,10 @@ export interface TaskStatus {
|
|||||||
// 后台任务管理器类
|
// 后台任务管理器类
|
||||||
class BackgroundTaskManager {
|
class BackgroundTaskManager {
|
||||||
private static instance: BackgroundTaskManager;
|
private static instance: BackgroundTaskManager;
|
||||||
private tasks: Map<string, BackgroundTask> = new Map();
|
private tasks: Map<string, BackgroundTaskConfig> = new Map();
|
||||||
private taskStatuses: Map<string, TaskStatus> = new Map();
|
private taskStatuses: Map<string, TaskStatus> = new Map();
|
||||||
private isInitialized = false;
|
private isInitialized = false;
|
||||||
|
private systemTaskRegistered = false;
|
||||||
|
|
||||||
// 单例模式
|
// 单例模式
|
||||||
public static getInstance(): BackgroundTaskManager {
|
public static getInstance(): BackgroundTaskManager {
|
||||||
@@ -49,7 +72,7 @@ class BackgroundTaskManager {
|
|||||||
this.isInitialized = true;
|
this.isInitialized = true;
|
||||||
|
|
||||||
// 注册后台任务
|
// 注册后台任务
|
||||||
await this.registerBackgroundTask();
|
await this.registerSystemBackgroundTask();
|
||||||
|
|
||||||
// 加载已保存的任务状态
|
// 加载已保存的任务状态
|
||||||
await this.loadTaskStatuses();
|
await this.loadTaskStatuses();
|
||||||
@@ -61,53 +84,49 @@ class BackgroundTaskManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 注册系统后台任务
|
||||||
|
private async registerSystemBackgroundTask(): Promise<void> {
|
||||||
|
console.log('开始注册系统后台任务...');
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 检查后台任务状态
|
||||||
|
const status = await BackgroundTask.getStatusAsync();
|
||||||
|
console.log('后台任务服务状态:', BackgroundTask.BackgroundTaskStatus[status]);
|
||||||
|
|
||||||
|
if (status === BackgroundTask.BackgroundTaskStatus.Available) {
|
||||||
|
// 检查任务是否已经注册
|
||||||
|
const isRegistered = await TaskManager.isTaskRegisteredAsync(BACKGROUND_TASK_NAME);
|
||||||
|
console.log('系统任务是否已注册:', isRegistered);
|
||||||
|
|
||||||
|
if (!isRegistered) {
|
||||||
// 注册后台任务
|
// 注册后台任务
|
||||||
private async registerBackgroundTask(): Promise<void> {
|
await BackgroundTask.registerTaskAsync(BACKGROUND_TASK_NAME);
|
||||||
const BACKGROUND_FETCH_TASK = 'background-fetch-task';
|
console.log('✅ 系统后台任务注册成功');
|
||||||
|
this.systemTaskRegistered = true;
|
||||||
console.log('注册后台获取任务');
|
|
||||||
|
|
||||||
// 定义后台获取任务
|
|
||||||
TaskManager.defineTask(BACKGROUND_FETCH_TASK, async () => {
|
|
||||||
console.log('后台获取任务被系统调用');
|
|
||||||
const now = new Date();
|
|
||||||
|
|
||||||
try {
|
|
||||||
console.log(`开始执行后台任务 - ${now.toISOString()}`);
|
|
||||||
|
|
||||||
// 执行所有注册的任务
|
|
||||||
const results = await this.executeAllTasks();
|
|
||||||
|
|
||||||
console.log('后台任务执行完成:', results);
|
|
||||||
|
|
||||||
// 返回成功状态
|
|
||||||
return BackgroundFetch.BackgroundFetchResult.NewData;
|
|
||||||
} catch (error) {
|
|
||||||
console.error('后台任务执行失败:', error);
|
|
||||||
return BackgroundFetch.BackgroundFetchResult.Failed;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// 注册后台获取任务
|
|
||||||
try {
|
|
||||||
const status = await BackgroundFetch.getStatusAsync();
|
|
||||||
if (status === BackgroundFetch.BackgroundFetchStatus.Available) {
|
|
||||||
await BackgroundFetch.registerTaskAsync(BACKGROUND_FETCH_TASK, {
|
|
||||||
minimumInterval: 15 * 60, // 15分钟(以秒为单位)
|
|
||||||
stopOnTerminate: false,
|
|
||||||
startOnBoot: true,
|
|
||||||
});
|
|
||||||
console.log('后台获取任务注册成功');
|
|
||||||
} else {
|
} else {
|
||||||
console.warn('后台获取不可用,状态:', status);
|
console.log('✅ 系统后台任务已经注册,跳过重复注册');
|
||||||
|
this.systemTaskRegistered = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const statusText = Object.keys(BackgroundTask.BackgroundTaskStatus).find(
|
||||||
|
key => BackgroundTask.BackgroundTaskStatus[key as keyof typeof BackgroundTask.BackgroundTaskStatus] === status
|
||||||
|
);
|
||||||
|
console.warn('❌ 后台任务服务不可用,状态:', statusText || status);
|
||||||
|
console.warn('可能的原因:');
|
||||||
|
console.warn('- 设备省电模式开启');
|
||||||
|
console.warn('- 后台应用刷新被禁用');
|
||||||
|
console.warn('- 设备电量过低');
|
||||||
|
this.systemTaskRegistered = false;
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('注册后台获取任务失败:', error);
|
console.error('❌ 注册系统后台任务失败:', error);
|
||||||
|
this.systemTaskRegistered = false;
|
||||||
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 注册自定义任务
|
// 注册自定义任务
|
||||||
public async registerTask(task: BackgroundTask): Promise<void> {
|
public async registerTask(task: BackgroundTaskConfig): Promise<void> {
|
||||||
try {
|
try {
|
||||||
// 检查任务是否已存在
|
// 检查任务是否已存在
|
||||||
if (this.tasks.has(task.id)) {
|
if (this.tasks.has(task.id)) {
|
||||||
@@ -197,7 +216,7 @@ class BackgroundTaskManager {
|
|||||||
public async executeAllTasks(): Promise<{ [taskId: string]: 'success' | 'failed' }> {
|
public async executeAllTasks(): Promise<{ [taskId: string]: 'success' | 'failed' }> {
|
||||||
const results: { [taskId: string]: 'success' | 'failed' } = {};
|
const results: { [taskId: string]: 'success' | 'failed' } = {};
|
||||||
|
|
||||||
for (const [taskId, task] of this.tasks) {
|
for (const [taskId, task] of Array.from(this.tasks.entries())) {
|
||||||
try {
|
try {
|
||||||
await this.executeTask(taskId);
|
await this.executeTask(taskId);
|
||||||
results[taskId] = 'success';
|
results[taskId] = 'success';
|
||||||
@@ -221,13 +240,13 @@ class BackgroundTaskManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 获取已注册的任务列表
|
// 获取已注册的任务列表
|
||||||
public getRegisteredTasks(): BackgroundTask[] {
|
public getRegisteredTasks(): BackgroundTaskConfig[] {
|
||||||
return Array.from(this.tasks.values());
|
return Array.from(this.tasks.values());
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查后台任务状态
|
// 检查后台任务状态
|
||||||
public async getBackgroundTaskStatus(): Promise<BackgroundFetch.BackgroundFetchStatus | null> {
|
public async getBackgroundTaskStatus(): Promise<BackgroundTask.BackgroundTaskStatus> {
|
||||||
return await BackgroundFetch.getStatusAsync();
|
return await BackgroundTask.getStatusAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 保存任务状态到本地存储
|
// 保存任务状态到本地存储
|
||||||
@@ -269,40 +288,82 @@ class BackgroundTaskManager {
|
|||||||
await this.saveTaskStatuses();
|
await this.saveTaskStatuses();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 调试函数:强制触发后台任务执行
|
// 手动触发后台任务(仅开发环境)
|
||||||
|
public async triggerTaskForTesting(): Promise<void> {
|
||||||
|
if (!__DEV__) {
|
||||||
|
console.warn('⚠️ triggerTaskForTesting 仅在开发环境可用');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
console.log('🧪 触发后台任务进行测试...');
|
||||||
|
await BackgroundTask.triggerTaskWorkerForTestingAsync();
|
||||||
|
console.log('✅ 后台任务测试触发成功');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('❌ 触发后台任务测试失败:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 调试函数:显示后台任务状态
|
||||||
public async debugExecuteBackgroundTask(): Promise<void> {
|
public async debugExecuteBackgroundTask(): Promise<void> {
|
||||||
console.log('=== 调试:手动触发后台任务执行 ===');
|
console.log('===============================');
|
||||||
|
console.log('🔧 调试:后台任务状态检查');
|
||||||
|
console.log('===============================');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 获取后台任务状态
|
// 获取后台任务状态
|
||||||
const status = await this.getBackgroundTaskStatus();
|
const status = await this.getBackgroundTaskStatus();
|
||||||
console.log('后台获取状态:', status);
|
const statusText = Object.keys(BackgroundTask.BackgroundTaskStatus).find(
|
||||||
|
key => BackgroundTask.BackgroundTaskStatus[key as keyof typeof BackgroundTask.BackgroundTaskStatus] === status
|
||||||
|
);
|
||||||
|
console.log('📊 后台任务服务状态:', statusText || status);
|
||||||
|
|
||||||
// 执行所有注册的任务
|
// 检查系统任务是否已注册
|
||||||
console.log('当前注册的任务数量:', this.tasks.size);
|
const isSystemTaskRegistered = await TaskManager.isTaskRegisteredAsync(BACKGROUND_TASK_NAME);
|
||||||
|
console.log('🔄 系统后台任务是否已注册:', isSystemTaskRegistered);
|
||||||
|
console.log('🔄 管理器中的系统任务状态:', this.systemTaskRegistered);
|
||||||
|
|
||||||
|
// 显示自定义任务信息
|
||||||
|
console.log('📝 当前注册的自定义任务数量:', this.tasks.size);
|
||||||
this.tasks.forEach((task, id) => {
|
this.tasks.forEach((task, id) => {
|
||||||
console.log(`- 任务ID: ${id}, 名称: ${task.name}`);
|
console.log(` - 任务ID: ${id}, 名称: ${task.name}`);
|
||||||
});
|
});
|
||||||
|
|
||||||
const results = await this.executeAllTasks();
|
if (this.tasks.size === 0) {
|
||||||
console.log('任务执行结果:', results);
|
console.warn('⚠️ 没有注册的自定义任务');
|
||||||
|
|
||||||
// 显示任务状态
|
|
||||||
const taskStatuses = this.getAllTaskStatuses();
|
|
||||||
taskStatuses.forEach(status => {
|
|
||||||
console.log(`任务 ${status.id} 状态:`, {
|
|
||||||
isRegistered: status.isRegistered,
|
|
||||||
executionCount: status.executionCount,
|
|
||||||
lastExecution: status.lastExecution?.toISOString(),
|
|
||||||
lastError: status.lastError
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
console.error('调试执行失败:', error);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('=== 调试执行完成 ===');
|
// 手动执行所有任务
|
||||||
|
console.log('🚀 手动执行所有注册的任务...');
|
||||||
|
const results = await this.executeAllTasks();
|
||||||
|
console.log('✅ 任务执行结果:', results);
|
||||||
|
|
||||||
|
// 显示详细的任务状态
|
||||||
|
console.log('📈 任务执行统计:');
|
||||||
|
const taskStatuses = this.getAllTaskStatuses();
|
||||||
|
taskStatuses.forEach(taskStatus => {
|
||||||
|
console.log(` 📊 任务 ${taskStatus.id}:`, {
|
||||||
|
注册状态: taskStatus.isRegistered ? '✅ 已注册' : '❌ 未注册',
|
||||||
|
执行次数: taskStatus.executionCount,
|
||||||
|
最后执行: taskStatus.lastExecution?.toLocaleString('zh-CN') || '从未执行',
|
||||||
|
最后错误: taskStatus.lastError || '无'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// 开发环境下触发测试
|
||||||
|
if (__DEV__) {
|
||||||
|
console.log('🧪 开发环境:触发后台任务测试...');
|
||||||
|
await this.triggerTaskForTesting();
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('===============================');
|
||||||
|
console.log('✅ 调试检查完成');
|
||||||
|
console.log('===============================');
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('❌ 调试执行失败:', error);
|
||||||
|
console.log('===============================');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -310,4 +371,4 @@ class BackgroundTaskManager {
|
|||||||
export const backgroundTaskManager = BackgroundTaskManager.getInstance();
|
export const backgroundTaskManager = BackgroundTaskManager.getInstance();
|
||||||
|
|
||||||
// 导出类型
|
// 导出类型
|
||||||
export type { BackgroundTask as BackgroundTaskType, TaskStatus as TaskStatusType };
|
export type { BackgroundTaskConfig as BackgroundTaskType, TaskStatus as TaskStatusType };
|
||||||
|
|||||||
@@ -182,7 +182,7 @@ export class NotificationService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 检查用户是否允许推送通知
|
* 检查用户是否允许推送通知(不包括系统权限检查,仅检查用户偏好)
|
||||||
*/
|
*/
|
||||||
private async isNotificationAllowed(): Promise<boolean> {
|
private async isNotificationAllowed(): Promise<boolean> {
|
||||||
try {
|
try {
|
||||||
@@ -193,16 +193,35 @@ export class NotificationService {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('检查推送权限失败:', error);
|
||||||
|
// 如果检查失败,默认允许(避免阻塞重要的健康提醒)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 完整的权限检查(包括系统权限和用户偏好)
|
||||||
|
*/
|
||||||
|
private async hasFullNotificationPermission(): Promise<boolean> {
|
||||||
|
try {
|
||||||
// 检查系统权限
|
// 检查系统权限
|
||||||
const permissionStatus = await this.getPermissionStatus();
|
const permissionStatus = await this.getPermissionStatus();
|
||||||
if (permissionStatus !== 'granted') {
|
if (permissionStatus !== 'granted') {
|
||||||
console.log('系统推送权限未授予');
|
console.log('系统推送权限未授予:', permissionStatus);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查用户偏好
|
||||||
|
const userPreferenceEnabled = await this.isNotificationAllowed();
|
||||||
|
if (!userPreferenceEnabled) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('检查推送权限失败:', error);
|
console.error('完整权限检查失败:', error);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -215,11 +234,11 @@ export class NotificationService {
|
|||||||
trigger?: Notifications.NotificationTriggerInput
|
trigger?: Notifications.NotificationTriggerInput
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
try {
|
try {
|
||||||
// 检查用户是否允许推送通知
|
// 检查完整权限(系统权限 + 用户偏好)
|
||||||
const isAllowed = await this.isNotificationAllowed();
|
const hasPermission = await this.hasFullNotificationPermission();
|
||||||
if (!isAllowed) {
|
if (!hasPermission) {
|
||||||
console.log('推送通知被用户偏好设置或系统权限阻止,跳过发送');
|
console.log('推送通知被系统权限或用户偏好设置阻止,跳过发送');
|
||||||
return 'blocked_by_user_preference';
|
return 'blocked_by_permission_or_preference';
|
||||||
}
|
}
|
||||||
|
|
||||||
const notificationId = await Notifications.scheduleNotificationAsync({
|
const notificationId = await Notifications.scheduleNotificationAsync({
|
||||||
@@ -234,10 +253,10 @@ export class NotificationService {
|
|||||||
trigger: trigger || null, // null表示立即发送
|
trigger: trigger || null, // null表示立即发送
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log('本地通知已安排,ID:', notificationId);
|
console.log('✅ 本地通知已安排,ID:', notificationId);
|
||||||
return notificationId;
|
return notificationId;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('安排本地通知失败:', error);
|
console.error('❌ 安排本地通知失败:', error);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -246,6 +265,7 @@ export class NotificationService {
|
|||||||
* 发送立即通知
|
* 发送立即通知
|
||||||
*/
|
*/
|
||||||
async sendImmediateNotification(notification: NotificationData): Promise<string> {
|
async sendImmediateNotification(notification: NotificationData): Promise<string> {
|
||||||
|
console.log('📱 准备发送立即通知:', notification.title);
|
||||||
return this.scheduleLocalNotification(notification);
|
return this.scheduleLocalNotification(notification);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user