Files
digital-pilates/services/workoutMonitor.ts
richarjiang 971aebd560 feat(workout): 新增锻炼结束监听和个性化通知功能
实现了iOS HealthKit锻炼数据实时监听,当用户完成锻炼时自动发送个性化鼓励通知。包括锻炼类型筛选、时间范围控制、用户偏好设置等完整功能,并提供了测试工具和详细文档。
2025-10-13 10:05:02 +08:00

173 lines
5.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { fetchRecentWorkouts, WorkoutData } from '@/utils/health';
import AsyncStorage from '@/utils/kvStore';
import { NativeEventEmitter, NativeModules } from 'react-native';
import { analyzeWorkoutAndSendNotification } from './workoutNotificationService';
const { HealthKitManager } = NativeModules;
const workoutEmitter = new NativeEventEmitter(HealthKitManager);
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<void> {
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<void> {
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<void> {
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<void> {
try {
await AsyncStorage.setItem('@last_processed_workout_id', workoutId);
this.lastProcessedWorkoutId = workoutId;
} catch (error) {
console.error('保存上次处理的锻炼ID失败:', error);
}
}
private async handleWorkoutUpdate(event: any): Promise<void> {
console.log('收到锻炼更新事件:', event);
// 防抖处理,避免短时间内重复处理
if (this.processingTimeout) {
clearTimeout(this.processingTimeout);
}
this.processingTimeout = setTimeout(async () => {
try {
await this.checkForNewWorkouts();
} catch (error) {
console.error('检查新锻炼失败:', error);
}
}, 5000); // 5秒延迟确保 HealthKit 数据已完全更新
}
private async checkForNewWorkouts(): Promise<void> {
try {
console.log('检查新的锻炼记录...');
// 获取最近1小时的锻炼记录
const oneHourAgo = new Date(Date.now() - 60 * 60 * 1000);
const recentWorkouts = await fetchRecentWorkouts({
startDate: oneHourAgo.toISOString(),
endDate: new Date().toISOString(),
limit: 10
});
console.log(`找到 ${recentWorkouts.length} 条最近的锻炼记录`);
// 检查是否有新的锻炼记录
for (const workout of recentWorkouts) {
if (workout.id !== this.lastProcessedWorkoutId) {
console.log('检测到新锻炼:', {
id: workout.id,
type: workout.workoutActivityTypeString,
duration: workout.duration,
startDate: workout.startDate
});
await this.processNewWorkout(workout);
await this.saveLastProcessedWorkoutId(workout.id);
} else {
console.log('锻炼已处理过,跳过:', workout.id);
}
}
} catch (error) {
console.error('检查新锻炼失败:', error);
}
}
private async processNewWorkout(workout: WorkoutData): Promise<void> {
try {
console.log('开始处理新锻炼:', workout.id);
// 分析锻炼并发送通知
await analyzeWorkoutAndSendNotification(workout);
console.log('新锻炼处理完成:', workout.id);
} catch (error) {
console.error('处理新锻炼失败:', error);
}
}
// 手动触发检查(用于测试)
async manualCheck(): Promise<void> {
console.log('手动触发锻炼检查...');
await this.checkForNewWorkouts();
}
// 获取服务状态
getStatus(): { initialized: boolean; lastProcessedWorkoutId: string | null } {
return {
initialized: this.isInitialized,
lastProcessedWorkoutId: this.lastProcessedWorkoutId
};
}
}
export const workoutMonitorService = WorkoutMonitorService.getInstance();