diff --git a/CLAUDE.md b/CLAUDE.md index 5cf00f3..67cd392 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -3,42 +3,121 @@ This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## Commands -- **Run on iOS**: `npm run ios` + +### Development +- **`npm run ios`** - Build and run on iOS Simulator (iOS-only deployment) + +### Testing +- Automated testing is minimal; complex logic should include Jest + React Native Testing Library specs under `__tests__/` directories or alongside modules ## Architecture -- **Framework**: React Native (Expo Prebuild/Ejected) with TypeScript using Expo Router for file-based navigation -- **State Management**: Redux Toolkit with domain-specific slices (`store/`) and typed hooks (`hooks/redux.ts`) -- **Authentication**: Custom auth guard system with `useAuthGuard` hook for protected navigation -- **Navigation**: - - File-based routing in `app/` directory with nested layouts - - Tab-based navigation with custom styling and haptic feedback - - Route constants defined in `constants/Routes.ts`, every page should use Routes define and jump -- **UI System**: - - Themed components (`ThemedText`, `ThemedView`) with color scheme support - - Custom icon system with `IconSymbol` component for iOS symbols - - Reusable UI components in `components/ui/` - - UI Colors in `constants/Colors.ts` -- **Data Layer**: - - API services in `services/` directory with centralized API client - - AsyncStorage for local persistence - - Background task management for sync operations -- **Native Integration**: - - Health data integration with HealthKit - - Apple Authentication - - Camera and photo library access for posture assessment - - Push notifications with background task support - - Haptic feedback integration + +### Core Stack +- **Framework**: React Native (Expo Prebuild/Ejected) with TypeScript +- **Navigation**: Expo Router with file-based routing in `app/` directory +- **State Management**: Redux Toolkit with domain-specific slices in `store/` +- **Styling**: Custom theme system with light/dark mode support + +### Directory Structure +- **`app/`** - Expo Router screens; tab flows in `app/(tabs)/`, feature-specific pages in nested directories +- **`store/`** - Redux slices organized by feature (user, nutrition, workout, etc.) +- **`services/`** - API services, backend integration, and data layer logic +- **`components/`** - Reusable UI components and domain-specific components +- **`hooks/`** - Custom React hooks including typed Redux hooks (`hooks/redux.ts`) +- **`utils/`** - Utility functions (health data, notifications, fasting, etc.) +- **`contexts/`** - React Context providers (ToastContext, MembershipModalContext) +- **`constants/`** - Route definitions (`Routes.ts`), colors, and app-wide constants +- **`types/`** - TypeScript type definitions +- **`assets/`** - Images, fonts, and media files +- **`ios/`** - iOS native code and configuration + +### Navigation +- **File-based routing**: Pages defined by file structure in `app/` +- **Tab navigation**: Main tabs in `app/(tabs)/` (Explore, Coach, Statistics, Challenges, Personal, Fasting) +- **Route constants**: All route paths defined in `constants/Routes.ts` +- **Nested layouts**: Feature-specific layouts in nested directories (e.g., `app/nutrition/_layout.tsx`) + +### State Management +- **Redux slices**: Feature-based state organization (17+ slices including user, nutrition, workout, mood, etc.) +- **Auto-sync middleware**: Listener middleware automatically syncs checkin data changes to backend +- **Typed hooks**: Use `useAppSelector` and `useAppDispatch` from `hooks/redux.ts` for type safety +- **Persistence**: AsyncStorage for local data persistence + +### UI System +- **Themed components**: `ThemedText`, `ThemedView` with dynamic color scheme support +- **Custom icons**: `IconSymbol` component for iOS SF Symbols +- **UI library**: Reusable components in `components/ui/` +- **Colors**: Centralized in `constants/Colors.ts` +- **Safe areas**: `useSafeAreaTop` and `useSafeAreaWithPadding` hooks for device-safe layouts + +### Data Layer +- **API client**: Centralized in `services/api.ts` with interceptors and error handling +- **Service modules**: Domain-specific services in `services/` (nutrition, workout, notifications, etc.) +- **Background tasks**: Managed by `backgroundTaskManager.ts` for sync operations +- **Local storage**: AsyncStorage for offline-first data persistence + +### Native Integration +- **HealthKit**: Health data integration in `utils/health.ts` and `utils/healthKit.ts` +- **Apple Authentication**: Configured in Expo settings +- **Camera & Photos**: Food recognition and posture assessment features +- **Push Notifications**: `services/notifications.ts` with background task support +- **Haptic Feedback**: `utils/haptics.ts` for user interactions +- **Quick Actions**: Expo quick actions integration + +### Context Providers +- **ToastContext** - Global toast notification system +- **MembershipModalContext** - VIP membership feature access control ## Key Architecture Patterns -- **Redux Auto-sync**: Listener middleware automatically syncs checkin data changes to backend -- **Type-safe Navigation**: Uses Expo Router with TypeScript for route type safety + +- **Redux Auto-sync**: Listener middleware in `store/index.ts` automatically syncs checkin data changes to backend +- **Type-safe Navigation**: Expo Router with TypeScript for compile-time route safety - **Authentication Flow**: `pushIfAuthedElseLogin` function handles auth-protected navigation -- **Theme System**: Dynamic theming with light/dark mode support and color tokens +- **Theme System**: Dynamic theming with light/dark mode and color tokens - **Service Layer**: Centralized API client with interceptors and error handling +- **Background Sync**: Automatic data synchronization via background task manager ## Development Conventions -- Use absolute imports with `@/` prefix for all internal imports -- Follow existing Redux slice patterns for state management -- Implement auth guards using `useAuthGuard` hook for protected features -- Use themed components for consistent styling -- Follow established navigation patterns with typed routes \ No newline at end of file + +### Import Patterns +- **Absolute imports**: Use `@/` prefix for all internal imports (e.g., `@/store`, `@/services/api`) +- **Alias configuration**: Defined in `tsconfig.json` paths + +### Redux Patterns +- **Feature slices**: Each feature has its own slice (userSlice.ts, nutritionSlice.ts, etc.) +- **Typed hooks**: Always use `useAppSelector` and `useAppDispatch` from `hooks/redux.ts` +- **Async actions**: Use Redux Toolkit thunks for async operations +- **Auto-sync**: Listener middleware handles automatic data synchronization + +### Naming Conventions +- **Components**: PascalCase (e.g., `ThemedText.tsx`, `FitnessRingsCard.tsx`) +- **Hooks**: camelCase with "use" prefix (e.g., `useAppSelector`, `useSafeAreaTop`) +- **Utilities**: camelCase (e.g., `health.ts`, `notificationHelpers.ts`) +- **Screen files**: kebab-case (e.g., `ai-posture-assessment.tsx`, `nutrition-label-analysis.tsx`) +- **Constants**: UPPER_SNAKE_CASE for values, PascalCase for types + +### Code Style +- **ESLint**: Configured with `eslint-config-expo` in `eslint.config.js` +- **Formatting**: 2 spaces, trailing commas, single quotes (Prettier defaults) +- **TypeScript**: Strict mode enabled, use proper type annotations + +### Navigation & Routing +- **Route constants**: Always use constants from `constants/Routes.ts` for navigation +- **Auth guards**: Implement using `useAuthGuard` hook for protected features +- **Typed routes**: Leverage Expo Router's TypeScript integration + +### Testing Guidelines +- **Minimal automated tests**: Add Jest + React Native Testing Library for complex logic +- **HealthKit testing**: Requires real device; verify on iOS Simulator when possible +- **Integration tests**: Include reproduction steps and logs in PR descriptions + +### iOS Development +- **Native changes**: Update `ios/` directory and re-run `npm run ios` after modifying Swift or entitlements +- **HealthKit**: Requires entitlements configuration; coordinate with release engineering +- **App signing**: Keep bundle IDs consistent with `app.json` and iOS project configuration +- **App Groups**: Required for widget and quick actions integration + +### Git Workflow +- **Conventional Commits**: Use `feat`, `fix`, `chore` prefixes with optional scope +- **PR descriptions**: Include problem, solution, test evidence (screenshots, commands), iOS setup notes +- **Change grouping**: Group related changes; avoid bundling unrelated features \ No newline at end of file diff --git a/app.json b/app.json index 15ce99d..44754af 100644 --- a/app.json +++ b/app.json @@ -21,6 +21,10 @@ "NSMicrophoneUsageDescription": "应用需要使用麦克风进行语音识别,将您的语音转换为文字记录饮食信息。", "NSSpeechRecognitionUsageDescription": "应用需要使用语音识别功能来转换您的语音为文字,帮助您快速记录饮食信息。", "NSUserNotificationsUsageDescription": "应用需要发送通知以提醒您喝水和站立活动。", + "BGTaskSchedulerPermittedIdentifiers": [ + "com.expo.modules.backgroundtask.processing", + "com.anonymous.digitalpilates.task" + ], "UIBackgroundModes": [ "processing", "fetch", diff --git a/app/_layout.tsx b/app/_layout.tsx index b9a4fdc..9135a21 100644 --- a/app/_layout.tsx +++ b/app/_layout.tsx @@ -20,7 +20,7 @@ import { fetchMyProfile, setPrivacyAgreed } from '@/store/userSlice'; import { createWaterRecordAction } from '@/store/waterSlice'; import { loadActiveFastingSchedule } from '@/utils/fasting'; import { ensureHealthPermissions, initializeHealthPermissions } from '@/utils/health'; -import { MoodNotificationHelpers, NutritionNotificationHelpers } from '@/utils/notificationHelpers'; +import { MoodNotificationHelpers, NutritionNotificationHelpers, WaterNotificationHelpers } from '@/utils/notificationHelpers'; import { clearPendingWaterRecords, syncPendingWidgetChanges } from '@/utils/widgetDataSync'; import React, { useEffect } from 'react'; @@ -32,8 +32,20 @@ import { STORAGE_KEYS } from '@/services/api'; import { BackgroundTaskManager } from '@/services/backgroundTaskManager'; import { fetchChallenges } from '@/store/challengesSlice'; import AsyncStorage from '@/utils/kvStore'; +import { logger } from '@/utils/logger'; import { Provider } from 'react-redux'; +// 在开发环境中导入调试工具 +let BackgroundTaskDebugger: any = null; +if (__DEV__) { + try { + const debuggerModule = require('@/services/backgroundTaskDebugger'); + BackgroundTaskDebugger = debuggerModule.BackgroundTaskDebugger; + } catch (error) { + logger.warn('无法导入后台任务调试工具:', error); + } +} + function Bootstrapper({ children }: { children: React.ReactNode }) { const dispatch = useAppDispatch(); @@ -63,7 +75,7 @@ function Bootstrapper({ children }: { children: React.ReactNode }) { if (store.getState().fasting.activeSchedule) return; dispatch(hydrateActiveSchedule(stored)); } catch (error) { - console.warn('恢复断食计划失败:', error); + logger.warn('恢复断食计划失败:', error); } }; @@ -89,60 +101,75 @@ function Bootstrapper({ children }: { children: React.ReactNode }) { const initHealthPermissions = async () => { // 初始化 HealthKit 权限管理系统 try { - console.log('初始化 HealthKit 权限管理系统...'); + logger.info('初始化 HealthKit 权限管理系统...'); initializeHealthPermissions(); // 延迟请求权限,避免应用启动时弹窗 setTimeout(async () => { try { await ensureHealthPermissions(); - console.log('HealthKit 权限请求完成'); + logger.info('HealthKit 权限请求完成'); } catch (error) { - console.warn('HealthKit 权限请求失败,可能在模拟器上运行:', error); + logger.warn('HealthKit 权限请求失败,可能在模拟器上运行:', error); } }, 2000); - console.log('HealthKit 权限管理初始化完成'); + logger.info('HealthKit 权限管理初始化完成'); } catch (error) { - console.warn('HealthKit 权限管理初始化失败:', error); + logger.warn('HealthKit 权限管理初始化失败:', error); } } const initializeNotifications = async () => { try { - await BackgroundTaskManager.getInstance().initialize(); + try { + await BackgroundTaskManager.getInstance().initialize(); + + // 在开发环境中初始化调试工具 + if (__DEV__) { + BackgroundTaskDebugger.getInstance().initialize(); + logger.info('后台任务调试工具已初始化(开发环境)'); + } + } catch (backgroundError) { + logger.error('后台任务管理器初始化失败,将跳过后台任务:', backgroundError); + } + // 初始化通知服务 await notificationService.initialize(); - console.log('通知服务初始化成功'); + logger.info('通知服务初始化成功'); // 注册午餐提醒(12:00) await NutritionNotificationHelpers.scheduleDailyLunchReminder(profile.name || ''); - console.log('午餐提醒已注册'); + logger.info('午餐提醒已注册'); // 注册晚餐提醒(18:00) await NutritionNotificationHelpers.scheduleDailyDinnerReminder(profile.name || ''); - console.log('晚餐提醒已注册'); + logger.info('晚餐提醒已注册'); // 注册心情提醒(21:00) await MoodNotificationHelpers.scheduleDailyMoodReminder(profile.name || ''); - console.log('心情提醒已注册'); + logger.info('心情提醒已注册'); + + // 注册默认喝水提醒(9:00-21:00,每2小时一次) + await WaterNotificationHelpers.scheduleRegularWaterReminders(profile.name || '用户'); + logger.info('默认喝水提醒已注册'); // 初始化快捷动作 await setupQuickActions(); - console.log('快捷动作初始化成功'); + logger.info('快捷动作初始化成功'); // 初始化喝水记录 bridge initializeWaterRecordBridge(); - console.log('喝水记录 Bridge 初始化成功'); + logger.info('喝水记录 Bridge 初始化成功'); // 初始化锻炼监听服务 const initializeWorkoutMonitoring = async () => { try { await workoutMonitorService.initialize(); - console.log('锻炼监听服务初始化成功'); + logger.info('锻炼监听服务初始化成功'); } catch (error) { - console.warn('锻炼监听服务初始化失败:', error); + logger.warn('锻炼监听服务初始化失败:', error); } }; @@ -151,7 +178,7 @@ function Bootstrapper({ children }: { children: React.ReactNode }) { // 检查并同步Widget数据更改 const widgetSync = await syncPendingWidgetChanges(); if (widgetSync.hasPendingChanges && widgetSync.pendingRecords) { - console.log(`检测到 ${widgetSync.pendingRecords.length} 条待同步的水记录`); + logger.info(`检测到 ${widgetSync.pendingRecords.length} 条待同步的水记录`); // 将待同步的记录添加到 Redux store for (const record of widgetSync.pendingRecords) { @@ -162,18 +189,18 @@ function Bootstrapper({ children }: { children: React.ReactNode }) { source: WaterRecordSource.Auto, // 标记为自动添加(来自Widget) })).unwrap(); - console.log(`成功同步水记录: ${record.amount}ml at ${record.recordedAt}`); + logger.info(`成功同步水记录: ${record.amount}ml at ${record.recordedAt}`); } catch (error) { - console.error('同步水记录失败:', error); + logger.error('同步水记录失败:', error); } } // 清除已同步的记录 await clearPendingWaterRecords(); - console.log('所有待同步的水记录已处理完成'); + logger.info('所有待同步的水记录已处理完成'); } } catch (error) { - console.error('通知服务、后台任务管理器或快捷动作初始化失败:', error); + logger.error('通知服务、后台任务管理器或快捷动作初始化失败:', error); } }; diff --git a/ios/OutLive/Info.plist b/ios/OutLive/Info.plist index 315ee6e..93b69e0 100644 --- a/ios/OutLive/Info.plist +++ b/ios/OutLive/Info.plist @@ -5,6 +5,7 @@ BGTaskSchedulerPermittedIdentifiers com.expo.modules.backgroundtask.processing + com.anonymous.digitalpilates.task CADisableMinimumFrameDurationOnPhone @@ -25,7 +26,7 @@ CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString - 1.0.21 + 1.0.22 CFBundleSignature ???? CFBundleURLTypes @@ -81,6 +82,7 @@ fetch remote-notification + processing UILaunchStoryboardName SplashScreen diff --git a/services/backgroundTaskDebugger.ts b/services/backgroundTaskDebugger.ts new file mode 100644 index 0000000..2155639 --- /dev/null +++ b/services/backgroundTaskDebugger.ts @@ -0,0 +1,177 @@ +import AsyncStorage from '@/utils/kvStore'; +import { BackgroundTaskManager } from './backgroundTaskManager'; + +/** + * 后台任务调试工具 + * 提供简单的调试和测试功能 + */ +export class BackgroundTaskDebugger { + private static instance: BackgroundTaskDebugger; + + static getInstance(): BackgroundTaskDebugger { + if (!BackgroundTaskDebugger.instance) { + BackgroundTaskDebugger.instance = new BackgroundTaskDebugger(); + } + return BackgroundTaskDebugger.instance; + } + + /** + * 获取后台任务诊断信息 + */ + async getDiagnosticInfo(): Promise<{ + taskManager: any; + storage: any; + }> { + const taskManager = BackgroundTaskManager.getInstance(); + + return { + taskManager: { + isInitialized: await this.getTaskManagerInitStatus(), + status: await taskManager.getStatus(), + statusText: await taskManager.checkStatus(), + }, + storage: await this.getRelevantStorageValues(), + }; + } + + /** + * 手动测试后台任务 + */ + async testBackgroundTask(): Promise<{ + success: boolean; + error?: string; + executionTime: number; + }> { + const startTime = Date.now(); + + try { + const taskManager = BackgroundTaskManager.getInstance(); + await taskManager.testBackgroundTask(); + + const executionTime = Date.now() - startTime; + + return { + success: true, + executionTime + }; + } catch (error) { + const executionTime = Date.now() - startTime; + + return { + success: false, + error: error instanceof Error ? error.message : String(error), + executionTime + }; + } + } + + /** + * 清理后台任务相关数据 + */ + async clearBackgroundTaskData(): Promise { + const keys = [ + '@last_background_water_check', + '@last_background_test_notification', + ]; + + for (const key of keys) { + try { + await AsyncStorage.removeItem(key); + console.log(`已清理存储键: ${key}`); + } catch (error) { + console.error(`清理存储键失败 ${key}:`, error); + } + } + } + + /** + * 重置后台任务管理器 + */ + async resetBackgroundTaskManager(): Promise { + try { + const taskManager = BackgroundTaskManager.getInstance(); + await taskManager.stop(); + await this.clearBackgroundTaskData(); + + // 重新初始化 + await taskManager.initialize(); + + console.log('后台任务管理器已重置'); + } catch (error) { + console.error('重置后台任务管理器失败:', error); + throw error; + } + } + + /** + * 启用测试通知 + */ + async enableTestNotification(): Promise { + try { + await AsyncStorage.setItem('@enable_test_notification', 'true'); + console.log('已启用测试通知'); + } catch (error) { + console.error('启用测试通知失败:', error); + } + } + + /** + * 禁用测试通知 + */ + async disableTestNotification(): Promise { + try { + await AsyncStorage.removeItem('@enable_test_notification'); + console.log('已禁用测试通知'); + } catch (error) { + console.error('禁用测试通知失败:', error); + } + } + + /** + * 检查是否启用了测试通知 + */ + async isTestNotificationEnabled(): Promise { + try { + const enabled = await AsyncStorage.getItem('@enable_test_notification'); + return enabled === 'true'; + } catch (error) { + console.error('检查测试通知状态失败:', error); + return false; + } + } + + private async getTaskManagerInitStatus(): Promise { + try { + const taskManager = BackgroundTaskManager.getInstance(); + const status = await taskManager.getStatus(); + const statusText = await taskManager.checkStatus(); + return statusText !== '受限制'; + } catch (error) { + console.error('获取任务管理器状态失败:', error); + return false; + } + } + + private async getRelevantStorageValues(): Promise> { + const keys = [ + '@last_background_water_check', + '@last_background_test_notification', + '@enable_test_notification', + ]; + + const values: Record = {}; + + for (const key of keys) { + try { + const value = await AsyncStorage.getItem(key); + values[key] = value; + } catch (error) { + values[key] = `Error: ${error}`; + } + } + + return values; + } +} + +export const backgroundTaskDebugger = BackgroundTaskDebugger.getInstance(); \ No newline at end of file diff --git a/services/backgroundTaskManager.ts b/services/backgroundTaskManager.ts index 29dfc46..adb38fc 100644 --- a/services/backgroundTaskManager.ts +++ b/services/backgroundTaskManager.ts @@ -31,10 +31,25 @@ async function executeWaterReminderTask(): Promise { try { console.log('执行喝水提醒后台任务...'); - // 获取当前状态 - const state = store.getState(); - const waterStats = state.water.todayStats; - const userProfile = state.user.profile; + // 获取当前状态,添加错误处理 + let state; + try { + state = store.getState(); + } catch (error) { + console.log('无法获取 Redux state,使用本地存储:', error); + // 使用本地存储作为后备方案 + const dailyGoal = await getWaterGoalFromStorage(); + if (!dailyGoal || dailyGoal <= 0) { + console.log('没有设置喝水目标,跳过喝水提醒'); + return; + } + // 简化的提醒逻辑 + await sendSimpleWaterReminder(); + return; + } + + const waterStats = state.water?.todayStats; + const userProfile = state.user?.profile; // 优先使用 Redux 中的目标,若无则读取本地存储 let dailyGoal = waterStats?.dailyGoal ?? 0; @@ -111,9 +126,14 @@ async function executeChallengeReminderTask(): Promise { try { console.log('执行挑战鼓励提醒后台任务...'); - const state = store.getState(); - const normalizedUserName = state.user.profile?.name?.trim(); - const userName = normalizedUserName && normalizedUserName.length > 0 ? normalizedUserName : '朋友'; + let userName = '朋友'; + try { + const state = store.getState(); + const normalizedUserName = state.user?.profile?.name?.trim(); + userName = normalizedUserName && normalizedUserName.length > 0 ? normalizedUserName : '朋友'; + } catch (error) { + console.log('无法获取用户名,使用默认值:', error); + } const challenges = await listChallenges(); const joinedChallenges = challenges.filter((challenge) => challenge.isJoined && challenge.progress); @@ -201,6 +221,33 @@ async function sendTestNotification(): Promise { } } +/** + * 发送简单的喝水提醒(后备方案) + */ +async function sendSimpleWaterReminder(): Promise { + try { + const userName = '朋友'; // 默认用户名 + const Notifications = await import('expo-notifications'); + + const notificationId = await Notifications.scheduleNotificationAsync({ + content: { + title: '💧 该喝水啦!', + body: `${userName},记得补充水分,保持身体健康~`, + data: { + type: 'water_reminder', + url: '/statistics' + }, + sound: 'default', + }, + trigger: null, // 立即发送 + }); + + console.log('简单喝水提醒已发送,ID:', notificationId); + } catch (error) { + console.error('发送简单喝水提醒失败:', error); + } +} + // 后台任务执行函数 async function executeBackgroundTasks(): Promise { console.log('开始执行后台任务...'); @@ -213,14 +260,30 @@ async function executeBackgroundTasks(): Promise { return; } - // await sendTestNotification() + // 确保 Redux store 已初始化 + try { + const state = store.getState(); + if (!state) { + console.log('Redux store 未初始化,跳过后台任务'); + return; + } + } catch (error) { + console.log('无法访问 Redux store,跳过后台任务:', error); + return; + } - // 执行喝水提醒检查任务 - 已禁用,改为由用户手动在设置页面管理 + // await sendTestNotification() // 可选:启用测试通知 + + // 检查是否启用测试通知 + const testNotificationsEnabled = await AsyncStorage.getItem('@background_test_notifications_enabled') === 'true'; + if (testNotificationsEnabled) { + await sendTestNotification(); + } + + // 执行喝水提醒检查任务 await executeWaterReminderTask(); - // 执行站立提醒检查任务 - // await executeStandReminderTask(); - + // 执行挑战鼓励提醒任务 await executeChallengeReminderTask(); console.log('后台任务执行完成'); @@ -269,13 +332,15 @@ export class BackgroundTaskManager { if (await TaskManager.isTaskRegisteredAsync(BACKGROUND_TASK_IDENTIFIER)) { log.info('[BackgroundTask] 任务已注册'); - return + } else { + log.info('[BackgroundTask] 任务未注册,开始注册...'); + await BackgroundTask.registerTaskAsync(BACKGROUND_TASK_IDENTIFIER); + log.info('[BackgroundTask] 任务注册完成'); } - log.info('[BackgroundTask] 任务未注册, 开始注册...'); - // 注册后台任务 - await BackgroundTask.registerTaskAsync(BACKGROUND_TASK_IDENTIFIER); - + // 验证任务状态 + const status = await BackgroundTask.getStatusAsync(); + log.info(`[BackgroundTask] 任务状态: ${status}`); this.isInitialized = true; log.info('后台任务管理器初始化完成');