import { getWorkoutTypeDisplayName, WorkoutData } from '@/utils/health'; import { logger } from '@/utils/logger'; 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; } export async function analyzeWorkoutAndSendNotification(workout: WorkoutData): Promise { try { // 检查用户是否启用了通用通知 const notificationsEnabled = await getNotificationEnabled(); if (!notificationsEnabled) { logger.info('用户已禁用通知,跳过锻炼结束通知'); return; } // 检查用户是否启用了锻炼通知 const workoutNotificationsEnabled = await getWorkoutNotificationEnabled(); if (!workoutNotificationsEnabled) { logger.info('用户已禁用锻炼通知,跳过锻炼结束通知'); return; } // 检查时间限制(避免深夜打扰) const timeAllowed = await isNotificationTimeAllowed(); if (!timeAllowed) { logger.info('当前时间不适合发送通知,跳过锻炼结束通知'); return; } // 检查特定锻炼类型是否启用了通知 const workoutTypeEnabled = await isWorkoutTypeEnabled(workout.workoutActivityTypeString || ''); if (!workoutTypeEnabled) { logger.info('该锻炼类型已禁用通知,跳过锻炼结束通知:', 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' }); logger.info('锻炼结束通知已发送:', message.title); } catch (error) { console.error('发送锻炼结束通知失败:', error); } } interface WorkoutMessageConfig { emoji: string; titleTemplate: string; bodyTemplate: (params: { workoutType: string; durationMinutes: number; calories: number; distanceKm?: string; averageHeartRate?: number; mets?: number; }) => string; encouragement: string; dataExtractor?: (workout: WorkoutData, metrics: any) => Record; } const WORKOUT_MESSAGES: Record = { running: { emoji: '🏃‍♂️', titleTemplate: '跑步完成!', bodyTemplate: ({ durationMinutes, calories, averageHeartRate }) => { let body = `您完成了${durationMinutes}分钟的跑步`; if (calories > 0) { body += `,消耗${calories}千卡`; } if (averageHeartRate) { body += `(平均心率${averageHeartRate}次/分)`; } return body + '!'; }, encouragement: '坚持就是胜利!💪', dataExtractor: (workout, metrics) => ({ heartRateContext: metrics.averageHeartRate ? 'provided' : 'none' }) }, cycling: { emoji: '🚴‍♂️', titleTemplate: '骑行完成!', bodyTemplate: ({ durationMinutes, calories, distanceKm }) => { let body = `${durationMinutes}分钟骑行`; if (distanceKm) { body += `,行程${distanceKm}公里`; } if (calories > 0) { body += `,消耗${calories}千卡`; } return body + '!'; }, encouragement: '追寻风的自由!🌟' }, swimming: { emoji: '🏊‍♂️', titleTemplate: '游泳完成!', bodyTemplate: ({ durationMinutes }) => { return `水中${durationMinutes}分钟的锻炼完成!`; }, encouragement: '全身肌肉都得到了锻炼!💦' }, yoga: { emoji: '🧘‍♀️', titleTemplate: '瑜伽完成!', bodyTemplate: ({ durationMinutes }) => { return `${durationMinutes}分钟的瑜伽练习`; }, encouragement: '身心合一,平静致远!🌸' }, functionalstrengthtraining: { emoji: '💪', titleTemplate: '力量训练完成!', bodyTemplate: ({ durationMinutes, calories, mets }) => { let body = `${durationMinutes}分钟力量训练`; if (calories > 0) { body += `,消耗${calories}千卡`; } if (mets && mets > 6) { body += '(高强度)'; } return body + '!'; }, encouragement: '肌肉正在变得更强壮!🔥', dataExtractor: (workout, metrics) => ({ strengthLevel: metrics?.mets && metrics.mets > 6 ? 'high' : 'moderate' }) }, traditionalstrengthtraining: { emoji: '💪', titleTemplate: '力量训练完成!', bodyTemplate: ({ durationMinutes, calories, mets }) => { let body = `${durationMinutes}分钟力量训练`; if (calories > 0) { body += `,消耗${calories}千卡`; } if (mets && mets > 6) { body += '(高强度)'; } return body + '!'; }, encouragement: '肌肉正在变得更强壮!🔥', dataExtractor: (workout, metrics) => ({ strengthLevel: metrics?.mets && metrics.mets > 6 ? 'high' : 'moderate' }) }, highintensityintervaltraining: { emoji: '🔥', titleTemplate: 'HIIT训练完成!', bodyTemplate: ({ durationMinutes, calories }) => { let body = `${durationMinutes}分钟高强度间歇训练`; if (calories > 0) { body += `,消耗${calories}千卡`; } return body + '!'; }, encouragement: '心肺功能显著提升!⚡', dataExtractor: (workout, metrics) => ({ hiitCompleted: true }) } }; function getWorkoutMessage(workoutTypeString?: string): WorkoutMessageConfig | null { if (!workoutTypeString) return null; const normalizedType = workoutTypeString.toLowerCase(); if (normalizedType.includes('strength')) { return WORKOUT_MESSAGES.traditionalstrengthtraining; } return WORKOUT_MESSAGES[normalizedType] || null; } function generateEncouragementMessage( workout: WorkoutData, metrics: any ): WorkoutEncouragementMessage { if (!workout) { return { title: '锻炼完成!', body: '恭喜您完成锻炼!', data: {} }; } const workoutType = getWorkoutTypeDisplayName(workout.workoutActivityTypeString) || '锻炼'; const durationMinutes = workout.duration ? Math.round(workout.duration / 60) : 0; const calories = workout.totalEnergyBurned ? Math.round(workout.totalEnergyBurned) : 0; const distanceKm = workout.totalDistance && workout.totalDistance > 0 ? (workout.totalDistance / 1000).toFixed(2) : undefined; const messageConfig = getWorkoutMessage(workout.workoutActivityTypeString); let title = '锻炼完成!'; let body = ''; const data: Record = {}; if (messageConfig) { title = `${messageConfig.emoji} ${messageConfig.titleTemplate}`; body = messageConfig.bodyTemplate({ workoutType, durationMinutes, calories, distanceKm, averageHeartRate: metrics?.averageHeartRate, mets: metrics?.mets }); body += messageConfig.encouragement; if (messageConfig.dataExtractor) { Object.assign(data, messageConfig.dataExtractor(workout, metrics)); } } else { body = `${workoutType} ${durationMinutes}分钟完成!`; if (calories > 0) { body += `消耗${calories}千卡热量。`; } body += '坚持运动,收获健康!🌟'; } if (metrics?.heartRateZones && Array.isArray(metrics.heartRateZones) && metrics.heartRateZones.length > 0) { try { 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; } } catch (error) { console.warn('心率区间分析失败:', error); } } if (metrics?.mets) { data.intensity = metrics.mets < 3 ? 'low' : metrics.mets < 6 ? 'moderate' : 'high'; } return { title, body, data }; }