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

161 lines
5.7 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 { getWorkoutTypeDisplayName, WorkoutData } from '@/utils/health';
import { getNotificationEnabled } from '@/utils/userPreferences';
import {
getWorkoutNotificationEnabled,
isNotificationTimeAllowed,
isWorkoutTypeEnabled
} from '@/utils/workoutPreferences';
import { notificationService, NotificationTypes } from './notifications';
import { getWorkoutDetailMetrics } from './workoutDetail';
interface WorkoutEncouragementMessage {
title: string;
body: string;
data: Record<string, any>;
}
export async function analyzeWorkoutAndSendNotification(workout: WorkoutData): Promise<void> {
try {
// 检查用户是否启用了通用通知
const notificationsEnabled = await getNotificationEnabled();
if (!notificationsEnabled) {
console.log('用户已禁用通知,跳过锻炼结束通知');
return;
}
// 检查用户是否启用了锻炼通知
const workoutNotificationsEnabled = await getWorkoutNotificationEnabled();
if (!workoutNotificationsEnabled) {
console.log('用户已禁用锻炼通知,跳过锻炼结束通知');
return;
}
// 检查时间限制(避免深夜打扰)
const timeAllowed = await isNotificationTimeAllowed();
if (!timeAllowed) {
console.log('当前时间不适合发送通知,跳过锻炼结束通知');
return;
}
// 检查特定锻炼类型是否启用了通知
const workoutTypeEnabled = await isWorkoutTypeEnabled(workout.workoutActivityTypeString || '');
if (!workoutTypeEnabled) {
console.log('该锻炼类型已禁用通知,跳过锻炼结束通知:', workout.workoutActivityTypeString);
return;
}
// 获取详细的锻炼指标
const workoutMetrics = await getWorkoutDetailMetrics(workout);
// 生成个性化鼓励消息
const message = generateEncouragementMessage(workout, workoutMetrics);
// 发送通知
await notificationService.sendImmediateNotification({
title: message.title,
body: message.body,
data: {
type: NotificationTypes.WORKOUT_COMPLETION,
workoutId: workout.id,
workoutType: workout.workoutActivityTypeString,
duration: workout.duration,
calories: workout.totalEnergyBurned,
...message.data
},
sound: true,
priority: 'high'
});
console.log('锻炼结束通知已发送:', message.title);
} catch (error) {
console.error('发送锻炼结束通知失败:', error);
}
}
function generateEncouragementMessage(
workout: WorkoutData,
metrics: any
): WorkoutEncouragementMessage {
const workoutType = getWorkoutTypeDisplayName(workout.workoutActivityTypeString);
const durationMinutes = Math.round(workout.duration / 60);
const calories = workout.totalEnergyBurned ? Math.round(workout.totalEnergyBurned) : 0;
// 基于锻炼类型和指标生成个性化消息
let title = '锻炼完成!';
let body = '';
let data: Record<string, any> = {};
switch (workout.workoutActivityTypeString?.toLowerCase()) {
case 'running':
title = '🏃‍♂️ 跑步完成!';
body = `太棒了!您刚刚完成了${durationMinutes}分钟的跑步,消耗了约${calories}千卡热量。`;
if (metrics.averageHeartRate) {
body += `平均心率${metrics.averageHeartRate}次/分。`;
}
body += '坚持运动让身体更健康!💪';
break;
case 'cycling':
title = '🚴‍♂️ 骑行完成!';
body = `骑行${durationMinutes}分钟完成!消耗了约${calories}千卡热量。`;
if (workout.totalDistance) {
const distanceKm = (workout.totalDistance / 1000).toFixed(2);
body += `骑行距离${distanceKm}公里。`;
}
body += '享受骑行的自由吧!🌟';
break;
case 'swimming':
title = '🏊‍♂️ 游泳完成!';
body = `游泳${durationMinutes}分钟完成!消耗了约${calories}千卡热量。`;
body += '全身运动效果极佳,继续保持!💦';
break;
case 'yoga':
title = '🧘‍♀️ 瑜伽完成!';
body = `${durationMinutes}分钟的瑜伽练习完成!提升了柔韧性和内心平静。`;
body += '继续保持这份宁静!🌸';
break;
case 'functionalstrengthtraining':
case 'traditionalstrengthtraining':
title = '💪 力量训练完成!';
body = `力量训练${durationMinutes}分钟完成!消耗了约${calories}千卡热量。`;
if (metrics.mets && metrics.mets > 6) {
body += '高强度训练,效果显著!🔥';
}
body += '肌肉正在变得更强壮!';
break;
case 'highintensityintervaltraining':
title = '🔥 HIIT训练完成';
body = `高强度间歇训练${durationMinutes}分钟完成!消耗了约${calories}千卡热量。`;
body += '心肺功能得到有效提升,您的努力值得称赞!⚡';
break;
default:
title = '🎯 锻炼完成!';
body = `${workoutType}${durationMinutes}分钟完成!消耗了约${calories}千卡热量。`;
body += '坚持运动,健康生活!🌟';
break;
}
// 添加心率区间分析(如果有心率数据)
if (metrics.heartRateZones && metrics.heartRateZones.length > 0) {
const dominantZone = metrics.heartRateZones.reduce((prev: any, current: any) =>
current.durationMinutes > prev.durationMinutes ? current : prev
);
if (dominantZone.durationMinutes > 5) {
data.heartRateZone = dominantZone.key;
data.heartRateZoneLabel = dominantZone.label;
}
}
// 添加锻炼强度评估
if (metrics.mets) {
data.intensity = metrics.mets < 3 ? 'low' : metrics.mets < 6 ? 'moderate' : 'high';
}
return { title, body, data };
}