feat(background-task): 实现iOS原生后台任务V2系统并重构锻炼通知消息模板

- 新增iOS原生BackgroundTaskBridge桥接模块,支持后台任务注册、调度和完成
- 重构BackgroundTaskManager为V2版本,集成原生iOS后台任务能力
- 在AppDelegate中注册后台任务处理器,确保应用启动时正确初始化
- 重构锻炼通知消息生成逻辑,使用配置化模板提升可维护性
- 扩展健康数据类型映射,支持更多运动项目的中文显示
- 替换原有backgroundTaskManager引用为backgroundTaskManagerV2
This commit is contained in:
richarjiang
2025-11-04 09:41:10 +08:00
parent fbffa07f74
commit f80a1bae78
10 changed files with 1319 additions and 78 deletions

View File

@@ -72,88 +72,198 @@ export async function analyzeWorkoutAndSendNotification(workout: WorkoutData): P
}
}
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<string, any>;
}
const WORKOUT_MESSAGES: Record<string, WorkoutMessageConfig> = {
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 {
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 (!workout) {
return {
title: '锻炼完成!',
body: '恭喜您完成锻炼!',
data: {}
};
}
// 添加心率区间分析(如果有心率数据)
if (metrics.heartRateZones && metrics.heartRateZones.length > 0) {
const dominantZone = metrics.heartRateZones.reduce((prev: any, current: any) =>
current.durationMinutes > prev.durationMinutes ? current : prev
);
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;
if (dominantZone.durationMinutes > 5) {
data.heartRateZone = dominantZone.key;
data.heartRateZoneLabel = dominantZone.label;
const messageConfig = getWorkoutMessage(workout.workoutActivityTypeString);
let title = '🎯 锻炼完成!';
let body = '';
const data: Record<string, any> = {};
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) {
if (metrics?.mets) {
data.intensity = metrics.mets < 3 ? 'low' : metrics.mets < 6 ? 'moderate' : 'high';
}