feat: 新增喝水提醒功能,支持定期提醒和目标检查

This commit is contained in:
richarjiang
2025-09-02 18:56:40 +08:00
parent ccbc3417bc
commit 70e3152158
6 changed files with 569 additions and 61 deletions

View File

@@ -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,
}),
},
};