- 新增晚餐提醒(18:00)和心情提醒(21:00)的定时通知 - 实现基于HRV数据的压力检测和智能鼓励通知 - 添加后台任务处理支持,修改iOS后台模式为processing - 优化营养记录页面使用Redux状态管理,支持实时数据更新 - 重构卡路里计算公式,移除目标卡路里概念,改为基代+运动-饮食 - 新增营养目标动态计算功能,基于用户身体数据智能推荐 - 完善通知点击跳转逻辑,支持多种提醒类型的路由处理
811 lines
24 KiB
TypeScript
811 lines
24 KiB
TypeScript
import * as Notifications from 'expo-notifications';
|
||
import { NotificationData, notificationService } from '../services/notifications';
|
||
|
||
/**
|
||
* 构建 coach 页面的深度链接
|
||
*/
|
||
export function buildCoachDeepLink(params: {
|
||
action?: 'diet' | 'weight' | 'mood' | 'workout';
|
||
subAction?: 'record' | 'photo' | 'text' | 'card';
|
||
meal?: 'breakfast' | 'lunch' | 'dinner' | 'snack';
|
||
message?: string;
|
||
}): string {
|
||
const baseUrl = '/coach';
|
||
const searchParams = new URLSearchParams();
|
||
|
||
if (params.action) searchParams.set('action', params.action);
|
||
if (params.subAction) searchParams.set('subAction', params.subAction);
|
||
if (params.meal) searchParams.set('meal', params.meal);
|
||
if (params.message) searchParams.set('message', encodeURIComponent(params.message));
|
||
|
||
const queryString = searchParams.toString();
|
||
return queryString ? `${baseUrl}?${queryString}` : baseUrl;
|
||
}
|
||
|
||
/**
|
||
* 运动相关的通知辅助函数
|
||
*/
|
||
export class WorkoutNotificationHelpers {
|
||
/**
|
||
* 发送运动开始提醒
|
||
*/
|
||
static async sendWorkoutStartReminder(userName: string) {
|
||
return notificationService.sendImmediateNotification({
|
||
title: '运动时间到',
|
||
body: `${userName},该开始今天的普拉提训练了!`,
|
||
data: { type: 'workout_start' },
|
||
sound: true,
|
||
priority: 'high',
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 发送运动完成通知
|
||
*/
|
||
static async sendWorkoutCompleteNotification(userName: string, duration: number) {
|
||
return notificationService.sendImmediateNotification({
|
||
title: '运动完成',
|
||
body: `${userName},恭喜您完成了${duration}分钟的训练!`,
|
||
data: { type: 'workout_complete' },
|
||
sound: true,
|
||
priority: 'normal',
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 安排每日运动提醒
|
||
*/
|
||
static async scheduleDailyWorkoutReminder(userName: string, hour: number = 9, minute: number = 0) {
|
||
const reminderTime = new Date();
|
||
reminderTime.setHours(hour, minute, 0, 0);
|
||
|
||
// 如果今天的时间已经过了,设置为明天
|
||
if (reminderTime.getTime() <= Date.now()) {
|
||
reminderTime.setDate(reminderTime.getDate() + 1);
|
||
}
|
||
|
||
return notificationService.scheduleRepeatingNotification(
|
||
{
|
||
title: '每日运动提醒',
|
||
body: `${userName},该开始今天的普拉提训练了!`,
|
||
data: { type: 'daily_workout_reminder' },
|
||
sound: true,
|
||
priority: 'high',
|
||
},
|
||
{ days: 1 }
|
||
);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 目标相关的通知辅助函数
|
||
*/
|
||
export class GoalNotificationHelpers {
|
||
/**
|
||
* 发送目标达成通知
|
||
*/
|
||
static async sendGoalAchievementNotification(userName: string, goalName: string) {
|
||
return notificationService.sendImmediateNotification({
|
||
title: '目标达成',
|
||
body: `${userName},恭喜您达成了目标:${goalName}!`,
|
||
data: { type: 'goal_achievement', goalName },
|
||
sound: true,
|
||
priority: 'high',
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 发送目标进度更新通知
|
||
*/
|
||
static async sendGoalProgressNotification(userName: string, goalName: string, progress: number) {
|
||
return notificationService.sendImmediateNotification({
|
||
title: '目标进度',
|
||
body: `${userName},您的目标"${goalName}"已完成${progress}%!`,
|
||
data: { type: 'goal_progress', goalName, progress },
|
||
sound: true,
|
||
priority: 'normal',
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 安排目标提醒
|
||
*/
|
||
static async scheduleGoalReminder(userName: string, goalName: string, deadline: Date) {
|
||
// 在截止日期前一天发送提醒
|
||
const reminderDate = new Date(deadline);
|
||
reminderDate.setDate(reminderDate.getDate() - 1);
|
||
|
||
return notificationService.scheduleNotificationAtDate(
|
||
{
|
||
title: '目标截止提醒',
|
||
body: `${userName},您的目标"${goalName}"明天就要截止了,加油!`,
|
||
data: { type: 'goal_deadline_reminder', goalName },
|
||
sound: true,
|
||
priority: 'high',
|
||
},
|
||
reminderDate
|
||
);
|
||
}
|
||
|
||
/**
|
||
* 根据目标设置创建定时推送
|
||
* @param goalData 目标数据
|
||
* @param userName 用户名
|
||
* @returns 通知ID数组
|
||
*/
|
||
static async scheduleGoalNotifications(
|
||
goalData: {
|
||
title: string;
|
||
repeatType: 'daily' | 'weekly' | 'monthly';
|
||
frequency: number;
|
||
hasReminder: boolean;
|
||
reminderTime?: string;
|
||
customRepeatRule?: {
|
||
weekdays?: number[];
|
||
dayOfMonth?: number[];
|
||
};
|
||
startTime?: number;
|
||
},
|
||
userName: string
|
||
): Promise<string[]> {
|
||
const notificationIds: string[] = [];
|
||
|
||
// 如果没有开启提醒,直接返回
|
||
if (!goalData.hasReminder || !goalData.reminderTime) {
|
||
console.log('目标未开启提醒或未设置提醒时间');
|
||
return notificationIds;
|
||
}
|
||
|
||
try {
|
||
// 解析提醒时间
|
||
const [hours, minutes] = goalData.reminderTime.split(':').map(Number);
|
||
|
||
// 创建通知内容
|
||
const notification: NotificationData = {
|
||
title: '目标提醒',
|
||
body: `${userName},该完成您的目标"${goalData.title}"了!`,
|
||
data: {
|
||
type: 'goal_reminder',
|
||
goalTitle: goalData.title,
|
||
repeatType: goalData.repeatType,
|
||
frequency: goalData.frequency
|
||
},
|
||
sound: true,
|
||
priority: 'high',
|
||
};
|
||
|
||
// 根据重复类型创建不同的通知
|
||
switch (goalData.repeatType) {
|
||
case 'daily':
|
||
// 每日重复 - 使用日历重复通知
|
||
const dailyId = await notificationService.scheduleCalendarRepeatingNotification(
|
||
notification,
|
||
{
|
||
type: Notifications.SchedulableTriggerInputTypes.DAILY,
|
||
hour: hours,
|
||
minute: minutes,
|
||
}
|
||
);
|
||
notificationIds.push(dailyId);
|
||
console.log(`已安排每日目标提醒,通知ID:${dailyId}`);
|
||
break;
|
||
|
||
case 'weekly':
|
||
// 每周重复 - 为每个选中的星期几创建单独的通知
|
||
if (goalData.customRepeatRule?.weekdays && goalData.customRepeatRule.weekdays.length > 0) {
|
||
for (const weekday of goalData.customRepeatRule.weekdays) {
|
||
const weeklyId = await notificationService.scheduleCalendarRepeatingNotification(
|
||
notification,
|
||
{
|
||
type: Notifications.SchedulableTriggerInputTypes.WEEKLY,
|
||
hour: hours,
|
||
minute: minutes,
|
||
weekdays: [weekday],
|
||
}
|
||
);
|
||
notificationIds.push(weeklyId);
|
||
console.log(`已安排每周目标提醒,星期${weekday},通知ID:${weeklyId}`);
|
||
}
|
||
} else {
|
||
// 默认每周重复
|
||
const weeklyId = await notificationService.scheduleCalendarRepeatingNotification(
|
||
notification,
|
||
{
|
||
type: Notifications.SchedulableTriggerInputTypes.WEEKLY,
|
||
hour: hours,
|
||
minute: minutes,
|
||
}
|
||
);
|
||
notificationIds.push(weeklyId);
|
||
console.log(`已安排每周目标提醒,通知ID:${weeklyId}`);
|
||
}
|
||
break;
|
||
|
||
case 'monthly':
|
||
// 每月重复 - 为每个选中的日期创建单独的通知
|
||
if (goalData.customRepeatRule?.dayOfMonth && goalData.customRepeatRule.dayOfMonth.length > 0) {
|
||
for (const dayOfMonth of goalData.customRepeatRule.dayOfMonth) {
|
||
const monthlyId = await notificationService.scheduleCalendarRepeatingNotification(
|
||
notification,
|
||
{
|
||
type: Notifications.SchedulableTriggerInputTypes.MONTHLY,
|
||
hour: hours,
|
||
minute: minutes,
|
||
dayOfMonth: dayOfMonth,
|
||
}
|
||
);
|
||
notificationIds.push(monthlyId);
|
||
console.log(`已安排每月目标提醒,${dayOfMonth}号,通知ID:${monthlyId}`);
|
||
}
|
||
} else {
|
||
// 默认每月重复
|
||
const monthlyId = await notificationService.scheduleCalendarRepeatingNotification(
|
||
notification,
|
||
{
|
||
type: Notifications.SchedulableTriggerInputTypes.MONTHLY,
|
||
hour: hours,
|
||
minute: minutes,
|
||
dayOfMonth: 1,
|
||
}
|
||
);
|
||
notificationIds.push(monthlyId);
|
||
console.log(`已安排每月目标提醒,通知ID:${monthlyId}`);
|
||
}
|
||
break;
|
||
}
|
||
|
||
console.log(`目标"${goalData.title}"的定时推送已创建完成,共${notificationIds.length}个通知`);
|
||
return notificationIds;
|
||
|
||
} catch (error) {
|
||
console.error('创建目标定时推送失败:', error);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
|
||
|
||
/**
|
||
* 取消特定目标的所有通知
|
||
*/
|
||
static async cancelGoalNotifications(goalTitle: string): Promise<void> {
|
||
try {
|
||
const notifications = await notificationService.getAllScheduledNotifications();
|
||
|
||
for (const notification of notifications) {
|
||
if (notification.content.data?.type === 'goal_reminder' &&
|
||
notification.content.data?.goalTitle === goalTitle) {
|
||
await notificationService.cancelNotification(notification.identifier);
|
||
console.log(`已取消目标"${goalTitle}"的通知:${notification.identifier}`);
|
||
}
|
||
}
|
||
} catch (error) {
|
||
console.error('取消目标通知失败:', error);
|
||
throw error;
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 营养相关的通知辅助函数
|
||
*/
|
||
export class NutritionNotificationHelpers {
|
||
/**
|
||
* 发送营养记录提醒
|
||
*/
|
||
static async sendNutritionRecordReminder(userName: string) {
|
||
return notificationService.sendImmediateNotification({
|
||
title: '营养记录',
|
||
body: `${userName},记得记录今天的饮食情况`,
|
||
data: { type: 'nutrition_record_reminder' },
|
||
sound: true,
|
||
priority: 'normal',
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 安排每日午餐提醒
|
||
* @param userName 用户名
|
||
* @param hour 小时 (默认12点)
|
||
* @param minute 分钟 (默认0分)
|
||
* @returns 通知ID
|
||
*/
|
||
static async scheduleDailyLunchReminder(
|
||
userName: string,
|
||
hour: number = 12,
|
||
minute: number = 0
|
||
): Promise<string | null> {
|
||
try {
|
||
// 检查是否已经存在午餐提醒
|
||
const existingNotifications = await notificationService.getAllScheduledNotifications();
|
||
|
||
|
||
console.log('existingNotifications', existingNotifications);
|
||
const existingLunchReminder = existingNotifications.find(
|
||
notification =>
|
||
notification.content.data?.type === 'lunch_reminder' &&
|
||
notification.content.data?.isDailyReminder === true
|
||
);
|
||
|
||
if (existingLunchReminder) {
|
||
console.log('午餐提醒已存在,跳过重复注册:', existingLunchReminder.identifier);
|
||
return existingLunchReminder.identifier;
|
||
}
|
||
|
||
// 构建跳转到 coach 页面的深度链接
|
||
const coachUrl = buildCoachDeepLink({
|
||
action: 'diet',
|
||
subAction: 'card',
|
||
meal: 'lunch'
|
||
});
|
||
|
||
// 创建午餐提醒通知
|
||
const notificationId = await notificationService.scheduleCalendarRepeatingNotification(
|
||
{
|
||
title: '午餐记录提醒',
|
||
body: `${userName},记得记录今天的午餐情况哦!`,
|
||
data: {
|
||
type: 'lunch_reminder',
|
||
isDailyReminder: true,
|
||
meal: '午餐',
|
||
url: coachUrl // 添加深度链接
|
||
},
|
||
sound: true,
|
||
priority: 'normal',
|
||
},
|
||
{
|
||
type: Notifications.SchedulableTriggerInputTypes.DAILY,
|
||
hour: hour,
|
||
minute: minute,
|
||
}
|
||
);
|
||
|
||
console.log('每日午餐提醒已安排,ID:', notificationId);
|
||
return notificationId;
|
||
} catch (error) {
|
||
console.error('安排每日午餐提醒失败:', error);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 发送午餐记录提醒
|
||
*/
|
||
static async sendLunchReminder(userName: string) {
|
||
const coachUrl = buildCoachDeepLink({
|
||
action: 'diet',
|
||
subAction: 'card',
|
||
meal: 'lunch'
|
||
});
|
||
|
||
return notificationService.sendImmediateNotification({
|
||
title: '午餐记录提醒',
|
||
body: `${userName},记得记录今天的午餐情况哦!`,
|
||
data: {
|
||
type: 'lunch_reminder',
|
||
meal: '午餐',
|
||
url: coachUrl
|
||
},
|
||
sound: true,
|
||
priority: 'normal',
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 取消午餐提醒
|
||
*/
|
||
static async cancelLunchReminder(): Promise<void> {
|
||
try {
|
||
const notifications = await notificationService.getAllScheduledNotifications();
|
||
|
||
for (const notification of notifications) {
|
||
if (notification.content.data?.type === 'lunch_reminder' &&
|
||
notification.content.data?.isDailyReminder === true) {
|
||
await notificationService.cancelNotification(notification.identifier);
|
||
console.log('已取消午餐提醒:', notification.identifier);
|
||
}
|
||
}
|
||
} catch (error) {
|
||
console.error('取消午餐提醒失败:', error);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 安排每日晚餐提醒
|
||
* @param userName 用户名
|
||
* @param hour 小时 (默认18点)
|
||
* @param minute 分钟 (默认0分)
|
||
* @returns 通知ID
|
||
*/
|
||
static async scheduleDailyDinnerReminder(
|
||
userName: string,
|
||
hour: number = 18,
|
||
minute: number = 0
|
||
): Promise<string | null> {
|
||
try {
|
||
// 检查是否已经存在晚餐提醒
|
||
const existingNotifications = await notificationService.getAllScheduledNotifications();
|
||
|
||
const existingDinnerReminder = existingNotifications.find(
|
||
notification =>
|
||
notification.content.data?.type === 'dinner_reminder' &&
|
||
notification.content.data?.isDailyReminder === true
|
||
);
|
||
|
||
if (existingDinnerReminder) {
|
||
console.log('晚餐提醒已存在,跳过重复注册:', existingDinnerReminder.identifier);
|
||
return existingDinnerReminder.identifier;
|
||
}
|
||
|
||
// 创建晚餐提醒通知
|
||
const notificationId = await notificationService.scheduleCalendarRepeatingNotification(
|
||
{
|
||
title: '🍽️ 晚餐时光到啦!',
|
||
body: `${userName},美好的晚餐时光开始了~记得记录今天的晚餐哦!营养均衡很重要呢 💪`,
|
||
data: {
|
||
type: 'dinner_reminder',
|
||
isDailyReminder: true,
|
||
meal: '晚餐',
|
||
url: '/nutrition/records' // 直接跳转到营养记录页面
|
||
},
|
||
sound: true,
|
||
priority: 'normal',
|
||
},
|
||
{
|
||
type: Notifications.SchedulableTriggerInputTypes.DAILY,
|
||
hour: hour,
|
||
minute: minute,
|
||
}
|
||
);
|
||
|
||
console.log('每日晚餐提醒已安排,ID:', notificationId);
|
||
return notificationId;
|
||
} catch (error) {
|
||
console.error('安排每日晚餐提醒失败:', error);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 取消晚餐提醒
|
||
*/
|
||
static async cancelDinnerReminder(): Promise<void> {
|
||
try {
|
||
const notifications = await notificationService.getAllScheduledNotifications();
|
||
|
||
for (const notification of notifications) {
|
||
if (notification.content.data?.type === 'dinner_reminder' &&
|
||
notification.content.data?.isDailyReminder === true) {
|
||
await notificationService.cancelNotification(notification.identifier);
|
||
console.log('已取消晚餐提醒:', notification.identifier);
|
||
}
|
||
}
|
||
} catch (error) {
|
||
console.error('取消晚餐提醒失败:', error);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 安排营养记录提醒
|
||
*/
|
||
static async scheduleNutritionReminders(userName: string) {
|
||
// 安排三餐提醒
|
||
const mealTimes = [
|
||
{ hour: 8, minute: 0, meal: '早餐' },
|
||
{ hour: 12, minute: 0, meal: '午餐' },
|
||
{ hour: 18, minute: 0, meal: '晚餐' },
|
||
];
|
||
|
||
const notifications = [];
|
||
|
||
for (const mealTime of mealTimes) {
|
||
const reminderTime = new Date();
|
||
reminderTime.setHours(mealTime.hour, mealTime.minute, 0, 0);
|
||
|
||
// 如果今天的时间已经过了,设置为明天
|
||
if (reminderTime.getTime() <= Date.now()) {
|
||
reminderTime.setDate(reminderTime.getDate() + 1);
|
||
}
|
||
|
||
// 构建深度链接
|
||
const mealTypeMap: Record<string, string> = {
|
||
'早餐': 'breakfast',
|
||
'午餐': 'lunch',
|
||
'晚餐': 'dinner'
|
||
};
|
||
|
||
const coachUrl = buildCoachDeepLink({
|
||
action: 'diet',
|
||
subAction: 'card',
|
||
meal: mealTypeMap[mealTime.meal] as 'breakfast' | 'lunch' | 'dinner'
|
||
});
|
||
|
||
const notificationId = await notificationService.scheduleRepeatingNotification(
|
||
{
|
||
title: `${mealTime.meal}提醒`,
|
||
body: `${userName},记得记录您的${mealTime.meal}情况`,
|
||
data: {
|
||
type: 'meal_reminder',
|
||
meal: mealTime.meal,
|
||
url: coachUrl
|
||
},
|
||
sound: true,
|
||
priority: 'normal',
|
||
},
|
||
{ days: 1 }
|
||
);
|
||
|
||
notifications.push(notificationId);
|
||
}
|
||
|
||
return notifications;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 心情相关的通知辅助函数
|
||
*/
|
||
export class MoodNotificationHelpers {
|
||
/**
|
||
* 安排每日心情提醒
|
||
* @param userName 用户名
|
||
* @param hour 小时 (默认21点)
|
||
* @param minute 分钟 (默认0分)
|
||
* @returns 通知ID
|
||
*/
|
||
static async scheduleDailyMoodReminder(
|
||
userName: string,
|
||
hour: number = 21,
|
||
minute: number = 0
|
||
): Promise<string | null> {
|
||
try {
|
||
// 检查是否已经存在心情提醒
|
||
const existingNotifications = await notificationService.getAllScheduledNotifications();
|
||
|
||
const existingMoodReminder = existingNotifications.find(
|
||
notification =>
|
||
notification.content.data?.type === 'mood_reminder' &&
|
||
notification.content.data?.isDailyReminder === true
|
||
);
|
||
|
||
if (existingMoodReminder) {
|
||
console.log('心情提醒已存在,跳过重复注册:', existingMoodReminder.identifier);
|
||
return existingMoodReminder.identifier;
|
||
}
|
||
|
||
// 创建心情提醒通知
|
||
const notificationId = await notificationService.scheduleCalendarRepeatingNotification(
|
||
{
|
||
title: '🌙 今天过得怎么样呀?',
|
||
body: `${userName},夜深了~来记录一下今天的心情吧!每一份情感都值得被珍藏 ✨💕`,
|
||
data: {
|
||
type: 'mood_reminder',
|
||
isDailyReminder: true,
|
||
url: '/mood-statistics' // 跳转到心情统计页面
|
||
},
|
||
sound: true,
|
||
priority: 'normal',
|
||
},
|
||
{
|
||
type: Notifications.SchedulableTriggerInputTypes.DAILY,
|
||
hour: hour,
|
||
minute: minute,
|
||
}
|
||
);
|
||
|
||
console.log('每日心情提醒已安排,ID:', notificationId);
|
||
return notificationId;
|
||
} catch (error) {
|
||
console.error('安排每日心情提醒失败:', error);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 发送心情记录提醒
|
||
*/
|
||
static async sendMoodReminder(userName: string) {
|
||
return notificationService.sendImmediateNotification({
|
||
title: '🌙 今天过得怎么样呀?',
|
||
body: `${userName},夜深了~来记录一下今天的心情吧!每一份情感都值得被珍藏 ✨💕`,
|
||
data: {
|
||
type: 'mood_reminder',
|
||
url: '/mood-statistics'
|
||
},
|
||
sound: true,
|
||
priority: 'normal',
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 取消心情提醒
|
||
*/
|
||
static async cancelMoodReminder(): Promise<void> {
|
||
try {
|
||
const notifications = await notificationService.getAllScheduledNotifications();
|
||
|
||
for (const notification of notifications) {
|
||
if (notification.content.data?.type === 'mood_reminder' &&
|
||
notification.content.data?.isDailyReminder === true) {
|
||
await notificationService.cancelNotification(notification.identifier);
|
||
console.log('已取消心情提醒:', notification.identifier);
|
||
}
|
||
}
|
||
} catch (error) {
|
||
console.error('取消心情提醒失败:', error);
|
||
throw error;
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 通用通知辅助函数
|
||
*/
|
||
export class GeneralNotificationHelpers {
|
||
/**
|
||
* 发送欢迎通知
|
||
*/
|
||
static async sendWelcomeNotification(userName: string) {
|
||
return notificationService.sendImmediateNotification({
|
||
title: '欢迎使用',
|
||
body: `${userName},欢迎来到普拉提世界!开始您的健康之旅吧`,
|
||
data: { type: 'welcome' },
|
||
sound: true,
|
||
priority: 'normal',
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 发送成就通知
|
||
*/
|
||
static async sendAchievementNotification(userName: string, achievement: string) {
|
||
return notificationService.sendImmediateNotification({
|
||
title: '新成就',
|
||
body: `${userName},恭喜您获得了新成就:${achievement}!`,
|
||
data: { type: 'achievement', achievement },
|
||
sound: true,
|
||
priority: 'high',
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 发送系统维护通知
|
||
*/
|
||
static async sendMaintenanceNotification(message: string) {
|
||
return notificationService.sendImmediateNotification({
|
||
title: '系统通知',
|
||
body: message,
|
||
data: { type: 'maintenance' },
|
||
sound: true,
|
||
priority: 'high',
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 取消特定类型的通知
|
||
*/
|
||
static async cancelNotificationsByType(type: string) {
|
||
const notifications = await notificationService.getAllScheduledNotifications();
|
||
|
||
for (const notification of notifications) {
|
||
if (notification.content.data?.type === type) {
|
||
await notificationService.cancelNotification(notification.identifier);
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 批量发送通知
|
||
*/
|
||
static async sendBatchNotifications(notifications: NotificationData[]) {
|
||
const results = [];
|
||
|
||
for (const notification of notifications) {
|
||
try {
|
||
const id = await notificationService.sendImmediateNotification(notification);
|
||
results.push({ success: true, id, notification });
|
||
} catch (error) {
|
||
results.push({ success: false, error, notification });
|
||
}
|
||
}
|
||
|
||
return results;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 通知模板
|
||
*/
|
||
export const NotificationTemplates = {
|
||
workout: {
|
||
start: (userName: string) => ({
|
||
title: '运动时间到',
|
||
body: `${userName},该开始今天的普拉提训练了!`,
|
||
data: { type: 'workout_start' },
|
||
sound: true,
|
||
priority: 'high' as const,
|
||
}),
|
||
complete: (userName: string, duration: number) => ({
|
||
title: '运动完成',
|
||
body: `${userName},恭喜您完成了${duration}分钟的训练!`,
|
||
data: { type: 'workout_complete' },
|
||
sound: true,
|
||
priority: 'normal' as const,
|
||
}),
|
||
},
|
||
goal: {
|
||
achievement: (userName: string, goalName: string) => ({
|
||
title: '目标达成',
|
||
body: `${userName},恭喜您达成了目标:${goalName}!`,
|
||
data: { type: 'goal_achievement', goalName },
|
||
sound: true,
|
||
priority: 'high' as const,
|
||
}),
|
||
progress: (userName: string, goalName: string, progress: number) => ({
|
||
title: '目标进度',
|
||
body: `${userName},您的目标"${goalName}"已完成${progress}%!`,
|
||
data: { type: 'goal_progress', goalName, progress },
|
||
sound: true,
|
||
priority: 'normal' as const,
|
||
}),
|
||
},
|
||
mood: {
|
||
reminder: (userName: string) => ({
|
||
title: '心情打卡',
|
||
body: `${userName},记得记录今天的心情状态哦`,
|
||
data: { type: 'mood_checkin_reminder' },
|
||
sound: true,
|
||
priority: 'normal' as const,
|
||
}),
|
||
},
|
||
nutrition: {
|
||
reminder: (userName: string, meal: string) => {
|
||
const mealTypeMap: Record<string, string> = {
|
||
'早餐': 'breakfast',
|
||
'午餐': 'lunch',
|
||
'晚餐': 'dinner',
|
||
'加餐': 'snack'
|
||
};
|
||
|
||
const coachUrl = buildCoachDeepLink({
|
||
action: 'diet',
|
||
subAction: 'card',
|
||
meal: mealTypeMap[meal] as 'breakfast' | 'lunch' | 'dinner' | 'snack'
|
||
});
|
||
|
||
return {
|
||
title: `${meal}提醒`,
|
||
body: `${userName},记得记录您的${meal}情况`,
|
||
data: {
|
||
type: 'meal_reminder',
|
||
meal,
|
||
url: coachUrl
|
||
},
|
||
sound: true,
|
||
priority: 'normal' as const,
|
||
};
|
||
},
|
||
lunch: (userName: string) => {
|
||
const coachUrl = buildCoachDeepLink({
|
||
action: 'diet',
|
||
subAction: 'card',
|
||
meal: 'lunch'
|
||
});
|
||
|
||
return {
|
||
title: '午餐记录提醒',
|
||
body: `${userName},记得记录今天的午餐情况哦!`,
|
||
data: {
|
||
type: 'lunch_reminder',
|
||
meal: '午餐',
|
||
url: coachUrl
|
||
},
|
||
sound: true,
|
||
priority: 'normal' as const,
|
||
};
|
||
},
|
||
},
|
||
};
|