import { fetchRecentWorkouts, WorkoutData } from '@/utils/health'; import AsyncStorage from '@/utils/kvStore'; import { logger } from '@/utils/logger'; import { NativeEventEmitter, NativeModules } from 'react-native'; import { analyzeWorkoutAndSendNotification } from './workoutNotificationService'; const { HealthKitManager } = NativeModules; const workoutEmitter = new NativeEventEmitter(HealthKitManager); const INITIAL_LOOKBACK_WINDOW_MS = 24 * 60 * 60 * 1000; // 24小时 const DEFAULT_LOOKBACK_WINDOW_MS = 12 * 60 * 60 * 1000; // 12小时 class WorkoutMonitorService { private static instance: WorkoutMonitorService; private isInitialized = false; private lastProcessedWorkoutId: string | null = null; private processingTimeout: any = null; private eventListenerSubscription: any = null; static getInstance(): WorkoutMonitorService { if (!WorkoutMonitorService.instance) { WorkoutMonitorService.instance = new WorkoutMonitorService(); } return WorkoutMonitorService.instance; } async initialize(): Promise { if (this.isInitialized) { console.log('锻炼监听服务已初始化'); return; } try { // 获取上次处理的锻炼ID await this.loadLastProcessedWorkoutId(); // 启动 iOS 原生锻炼监听器 await HealthKitManager.startWorkoutObserver(); // 监听锻炼更新事件 this.eventListenerSubscription = workoutEmitter.addListener( 'workoutUpdate', this.handleWorkoutUpdate.bind(this) ); this.isInitialized = true; console.log('锻炼监听服务初始化成功'); } catch (error) { console.error('锻炼监听服务初始化失败:', error); throw error; } } async stop(): Promise { try { // 停止原生监听器 await HealthKitManager.stopWorkoutObserver(); // 移除事件监听器 if (this.eventListenerSubscription) { this.eventListenerSubscription.remove(); this.eventListenerSubscription = null; } // 清理定时器 if (this.processingTimeout) { clearTimeout(this.processingTimeout); this.processingTimeout = null; } this.isInitialized = false; console.log('锻炼监听服务已停止'); } catch (error) { console.error('停止锻炼监听服务失败:', error); } } private async loadLastProcessedWorkoutId(): Promise { try { const storedId = await AsyncStorage.getItem('@last_processed_workout_id'); this.lastProcessedWorkoutId = storedId; console.log('上次处理的锻炼ID:', this.lastProcessedWorkoutId); } catch (error) { console.error('加载上次处理的锻炼ID失败:', error); } } private async saveLastProcessedWorkoutId(workoutId: string): Promise { try { await AsyncStorage.setItem('@last_processed_workout_id', workoutId); this.lastProcessedWorkoutId = workoutId; } catch (error) { console.error('保存上次处理的锻炼ID失败:', error); } } private async handleWorkoutUpdate(event: any): Promise { logger.info('收到锻炼更新事件:', event); // 防抖处理,避免短时间内重复处理 if (this.processingTimeout) { clearTimeout(this.processingTimeout); } this.processingTimeout = setTimeout(async () => { try { await this.checkForNewWorkouts(); } catch (error) { logger.error('检查新锻炼失败:', error); } }, 5000); // 5秒延迟,确保 HealthKit 数据已完全更新 } private async checkForNewWorkouts(): Promise { try { logger.info('检查新的锻炼记录...'); const lookbackWindowMs = this.lastProcessedWorkoutId ? DEFAULT_LOOKBACK_WINDOW_MS : INITIAL_LOOKBACK_WINDOW_MS; const startDate = new Date(Date.now() - lookbackWindowMs); const endDate = new Date(); logger.info( `锻炼查询窗口: ${Math.round(lookbackWindowMs / (1000 * 60 * 60))} 小时 (${startDate.toISOString()} - ${endDate.toISOString()})` ); const recentWorkouts = await fetchRecentWorkouts({ startDate: startDate.toISOString(), endDate: endDate.toISOString(), limit: 10 }); logger.info(`找到 ${recentWorkouts.length} 条最近的锻炼记录`); if (this.lastProcessedWorkoutId && !recentWorkouts.some(workout => workout.id === this.lastProcessedWorkoutId)) { console.warn('上次处理的锻炼记录不在当前查询窗口内,可能存在漏报风险'); } const newWorkouts: WorkoutData[] = []; for (const workout of recentWorkouts) { if (workout.id === this.lastProcessedWorkoutId) { break; } newWorkouts.push(workout); } if (newWorkouts.length === 0) { logger.info('没有检测到新的锻炼记录'); return; } logger.info(`检测到 ${newWorkouts.length} 条新的锻炼记录,将按时间顺序处理`); // 先处理最旧的锻炼,确保通知顺序正确 for (const workout of newWorkouts.reverse()) { logger.info('处理新锻炼:', { id: workout.id, type: workout.workoutActivityTypeString, duration: workout.duration, startDate: workout.startDate }); await this.processNewWorkout(workout); } await this.saveLastProcessedWorkoutId(newWorkouts[0].id); logger.info('锻炼处理完成,最新处理的锻炼ID:', newWorkouts[0].id); } catch (error) { logger.error('检查新锻炼失败:', error); } } private async processNewWorkout(workout: WorkoutData): Promise { try { logger.info('开始处理新锻炼:', workout.id); // 分析锻炼并发送通知 await analyzeWorkoutAndSendNotification(workout); logger.info('新锻炼处理完成:', workout.id); } catch (error) { logger.error('处理新锻炼失败:', error); } } // 手动触发检查(用于测试) async manualCheck(): Promise { console.log('手动触发锻炼检查...'); await this.checkForNewWorkouts(); } // 获取服务状态 getStatus(): { initialized: boolean; lastProcessedWorkoutId: string | null } { return { initialized: this.isInitialized, lastProcessedWorkoutId: this.lastProcessedWorkoutId }; } } export const workoutMonitorService = WorkoutMonitorService.getInstance();