feat: 新增喝水提醒功能,支持定期提醒和目标检查
This commit is contained in:
@@ -638,6 +638,210 @@ export class MoodNotificationHelpers {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 喝水相关的通知辅助函数
|
||||
*/
|
||||
export class WaterNotificationHelpers {
|
||||
/**
|
||||
* 检查喝水目标完成情况并发送提醒
|
||||
* @param userName 用户名
|
||||
* @param todayStats 今日喝水统计数据
|
||||
* @param currentHour 当前小时(用于时间限制检查)
|
||||
* @returns 是否发送了通知
|
||||
*/
|
||||
static async checkWaterGoalAndNotify(
|
||||
userName: string,
|
||||
todayStats: { totalAmount: number; dailyGoal: number; completionRate: number },
|
||||
currentHour: number = new Date().getHours()
|
||||
): Promise<boolean> {
|
||||
try {
|
||||
// 检查时间限制:早上9点以前和晚上9点以后不通知
|
||||
if (currentHour < 9 || currentHour >= 21) {
|
||||
console.log(`当前时间${currentHour}点,不在通知时间范围内(9:00-21:00),跳过喝水提醒`);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 检查喝水目标是否已达成
|
||||
if (todayStats.completionRate >= 100) {
|
||||
console.log('喝水目标已达成,无需发送提醒');
|
||||
return false;
|
||||
}
|
||||
|
||||
// 检查是否在过去2小时内已经发送过喝水提醒,避免重复打扰
|
||||
const lastNotificationKey = '@last_water_notification';
|
||||
const AsyncStorage = (await import('@react-native-async-storage/async-storage')).default;
|
||||
const lastNotificationTime = await AsyncStorage.getItem(lastNotificationKey);
|
||||
const now = new Date().getTime();
|
||||
const twoHoursAgo = now - (2 * 60 * 60 * 1000); // 2小时前
|
||||
|
||||
if (lastNotificationTime && parseInt(lastNotificationTime) > twoHoursAgo) {
|
||||
console.log('2小时内已发送过喝水提醒,跳过本次通知');
|
||||
return false;
|
||||
}
|
||||
|
||||
// 计算还需要喝多少水
|
||||
const remainingAmount = todayStats.dailyGoal - todayStats.totalAmount;
|
||||
const completionPercentage = Math.round(todayStats.completionRate);
|
||||
|
||||
// 根据完成度生成不同的提醒消息
|
||||
let title = '💧 该喝水啦!';
|
||||
let body = '';
|
||||
|
||||
if (completionPercentage < 30) {
|
||||
// 完成度低于30%
|
||||
const encouragingMessages = [
|
||||
`${userName},今天才喝了${completionPercentage}%的水哦!还需要${Math.round(remainingAmount)}ml,记得多补水~身体会感谢您的!💙`,
|
||||
`${userName},水分补充进度${completionPercentage}%,再喝${Math.round(remainingAmount)}ml就更健康啦!来一大杯清水吧~🚰`,
|
||||
`${userName},喝水进度才${completionPercentage}%呢~身体需要更多水分,还差${Math.round(remainingAmount)}ml,一起加油!✨`,
|
||||
];
|
||||
body = encouragingMessages[Math.floor(Math.random() * encouragingMessages.length)];
|
||||
} else if (completionPercentage < 60) {
|
||||
// 完成度30-60%
|
||||
const moderateMessages = [
|
||||
`${userName},喝水进度${completionPercentage}%,还需要${Math.round(remainingAmount)}ml哦!保持这个节奏,您做得很棒!👍`,
|
||||
`${userName},水分补充已完成${completionPercentage}%,再来${Math.round(remainingAmount)}ml就达标了!继续保持~💪`,
|
||||
`${userName},今日饮水${completionPercentage}%完成!距离目标还有${Math.round(remainingAmount)}ml,加把劲!🌊`,
|
||||
];
|
||||
body = moderateMessages[Math.floor(Math.random() * moderateMessages.length)];
|
||||
} else {
|
||||
// 完成度60-99%
|
||||
const almostDoneMessages = [
|
||||
`${userName},喝水进度${completionPercentage}%,太棒了!最后${Math.round(remainingAmount)}ml就达成目标啦!🎉`,
|
||||
`${userName},已经完成${completionPercentage}%了!还有${Math.round(remainingAmount)}ml就成功了,您很快就能达成今天的目标!🏆`,
|
||||
`${userName},水分补充进度${completionPercentage}%,就差最后一点点!再喝${Math.round(remainingAmount)}ml就胜利了!🥳`,
|
||||
];
|
||||
body = almostDoneMessages[Math.floor(Math.random() * almostDoneMessages.length)];
|
||||
}
|
||||
|
||||
// 发送通知
|
||||
const notificationId = await notificationService.sendImmediateNotification({
|
||||
title,
|
||||
body,
|
||||
data: {
|
||||
type: 'water_reminder',
|
||||
completionRate: completionPercentage,
|
||||
remainingAmount: Math.round(remainingAmount),
|
||||
dailyGoal: todayStats.dailyGoal,
|
||||
currentAmount: todayStats.totalAmount,
|
||||
url: '/statistics' // 跳转到统计页面查看详情
|
||||
},
|
||||
sound: true,
|
||||
priority: 'normal',
|
||||
});
|
||||
|
||||
// 记录通知发送时间
|
||||
await AsyncStorage.setItem(lastNotificationKey, now.toString());
|
||||
|
||||
console.log(`喝水提醒通知已发送,ID: ${notificationId},完成度: ${completionPercentage}%`);
|
||||
return true;
|
||||
|
||||
} catch (error) {
|
||||
console.error('检查喝水目标并发送通知失败:', error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送立即喝水提醒
|
||||
* @param userName 用户名
|
||||
* @param message 自定义消息(可选)
|
||||
*/
|
||||
static async sendWaterReminder(userName: string, message?: string) {
|
||||
const defaultMessage = `${userName},记得要多喝水哦!保持身体水分充足很重要~💧`;
|
||||
|
||||
return notificationService.sendImmediateNotification({
|
||||
title: '💧 喝水提醒',
|
||||
body: message || defaultMessage,
|
||||
data: {
|
||||
type: 'water_reminder',
|
||||
url: '/statistics'
|
||||
},
|
||||
sound: true,
|
||||
priority: 'normal',
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 安排定期喝水提醒(每2小时一次,在9:00-21:00之间)
|
||||
* @param userName 用户名
|
||||
* @returns 通知ID数组
|
||||
*/
|
||||
static async scheduleRegularWaterReminders(userName: string): Promise<string[]> {
|
||||
try {
|
||||
const notificationIds: string[] = [];
|
||||
|
||||
// 检查是否已经存在定期喝水提醒
|
||||
const existingNotifications = await notificationService.getAllScheduledNotifications();
|
||||
|
||||
const existingWaterReminders = existingNotifications.filter(
|
||||
notification =>
|
||||
notification.content.data?.type === 'regular_water_reminder' &&
|
||||
notification.content.data?.isRegularReminder === true
|
||||
);
|
||||
|
||||
if (existingWaterReminders.length > 0) {
|
||||
console.log('定期喝水提醒已存在,跳过重复注册');
|
||||
return existingWaterReminders.map(n => n.identifier);
|
||||
}
|
||||
|
||||
// 创建多个时间点的喝水提醒(9:00-21:00,每2小时一次)
|
||||
const reminderHours = [9, 11, 13, 15, 17, 19, 21];
|
||||
|
||||
for (const hour of reminderHours) {
|
||||
const notificationId = await notificationService.scheduleCalendarRepeatingNotification(
|
||||
{
|
||||
title: '💧 定时喝水提醒',
|
||||
body: `${userName},该喝水啦!记得补充水分,保持身体健康~`,
|
||||
data: {
|
||||
type: 'regular_water_reminder',
|
||||
isRegularReminder: true,
|
||||
reminderHour: hour,
|
||||
url: '/statistics'
|
||||
},
|
||||
sound: true,
|
||||
priority: 'normal',
|
||||
},
|
||||
{
|
||||
type: 'DAILY' as any,
|
||||
hour: hour,
|
||||
minute: 0,
|
||||
}
|
||||
);
|
||||
|
||||
notificationIds.push(notificationId);
|
||||
console.log(`已安排${hour}:00的定期喝水提醒,通知ID: ${notificationId}`);
|
||||
}
|
||||
|
||||
console.log(`定期喝水提醒设置完成,共${notificationIds.length}个通知`);
|
||||
return notificationIds;
|
||||
|
||||
} catch (error) {
|
||||
console.error('设置定期喝水提醒失败:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消所有喝水提醒
|
||||
*/
|
||||
static async cancelAllWaterReminders(): Promise<void> {
|
||||
try {
|
||||
const notifications = await notificationService.getAllScheduledNotifications();
|
||||
|
||||
for (const notification of notifications) {
|
||||
if (notification.content.data?.type === 'water_reminder' ||
|
||||
notification.content.data?.type === 'regular_water_reminder') {
|
||||
await notificationService.cancelNotification(notification.identifier);
|
||||
console.log('已取消喝水提醒:', notification.identifier);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('取消喝水提醒失败:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 通用通知辅助函数
|
||||
*/
|
||||
@@ -805,4 +1009,38 @@ export const NotificationTemplates = {
|
||||
};
|
||||
},
|
||||
},
|
||||
water: {
|
||||
reminder: (userName: string, completionRate: number, remainingAmount: number) => ({
|
||||
title: '💧 该喝水啦!',
|
||||
body: `${userName},今日喝水进度${completionRate}%,还需要${Math.round(remainingAmount)}ml,记得补充水分~`,
|
||||
data: {
|
||||
type: 'water_reminder',
|
||||
completionRate,
|
||||
remainingAmount: Math.round(remainingAmount),
|
||||
url: '/statistics'
|
||||
},
|
||||
sound: true,
|
||||
priority: 'normal' as const,
|
||||
}),
|
||||
regular: (userName: string) => ({
|
||||
title: '💧 定时喝水提醒',
|
||||
body: `${userName},该喝水啦!记得补充水分,保持身体健康~`,
|
||||
data: {
|
||||
type: 'regular_water_reminder',
|
||||
url: '/statistics'
|
||||
},
|
||||
sound: true,
|
||||
priority: 'normal' as const,
|
||||
}),
|
||||
achievement: (userName: string) => ({
|
||||
title: '🎉 喝水目标达成!',
|
||||
body: `${userName},恭喜您完成了今天的喝水目标!继续保持健康的饮水习惯~`,
|
||||
data: {
|
||||
type: 'water_achievement',
|
||||
url: '/statistics'
|
||||
},
|
||||
sound: true,
|
||||
priority: 'high' as const,
|
||||
}),
|
||||
},
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user