feat: 添加后台任务管理器,支持喝水和站立提醒功能
This commit is contained in:
@@ -460,13 +460,13 @@ export async function fetchTodayHRV(): Promise<number | null> {
|
||||
// 获取最近几小时内的实时HRV数据
|
||||
export async function fetchRecentHRV(hoursBack: number = 2): Promise<number | null> {
|
||||
console.log(`开始获取最近${hoursBack}小时内的HRV数据...`);
|
||||
|
||||
|
||||
const now = new Date();
|
||||
const options = {
|
||||
startDate: dayjs(now).subtract(hoursBack, 'hour').toDate().toISOString(),
|
||||
endDate: now.toISOString()
|
||||
};
|
||||
|
||||
|
||||
return fetchHeartRateVariability(options);
|
||||
}
|
||||
|
||||
@@ -543,18 +543,18 @@ export async function saveWaterIntakeToHealthKit(amount: number, recordedAt?: st
|
||||
endDate: recordedAt ? new Date(recordedAt).toISOString() : new Date().toISOString(),
|
||||
};
|
||||
|
||||
AppleHealthKit.saveWater(waterOptions, (error: Object, result: boolean) => {
|
||||
AppleHealthKit.saveWater(waterOptions, (error: Object, result) => {
|
||||
if (error) {
|
||||
console.error('添加饮水记录到 HealthKit 失败:', error);
|
||||
resolve(false);
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('成功添加饮水记录到 HealthKit:', {
|
||||
originalAmount: amount,
|
||||
convertedAmount: amount / 1000,
|
||||
recordedAt,
|
||||
result
|
||||
|
||||
console.log('成功添加饮水记录到 HealthKit:', {
|
||||
originalAmount: amount,
|
||||
convertedAmount: amount / 1000,
|
||||
recordedAt,
|
||||
result
|
||||
});
|
||||
resolve(true);
|
||||
});
|
||||
@@ -570,7 +570,7 @@ export async function getWaterIntakeFromHealthKit(options: HealthDataOptions): P
|
||||
resolve([]);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
console.log('从 HealthKit 获取饮水记录:', results);
|
||||
resolve(results || []);
|
||||
});
|
||||
@@ -584,7 +584,31 @@ export async function deleteWaterIntakeFromHealthKit(recordId: string, recordedA
|
||||
// 这是一个占位函数,实际实现可能需要更复杂的逻辑
|
||||
console.log('注意: HealthKit 通常不支持直接删除单条饮水记录');
|
||||
console.log('记录信息:', { recordId, recordedAt });
|
||||
|
||||
|
||||
// 返回 true 表示"成功"(但实际上可能没有真正删除)
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
|
||||
// 获取当前小时的站立状态
|
||||
export async function getCurrentHourStandStatus(): Promise<{ hasStood: boolean; standHours: number; standHoursGoal: number }> {
|
||||
try {
|
||||
const currentHour = new Date().getHours();
|
||||
console.log(`检查当前小时 ${currentHour} 的站立状态...`);
|
||||
|
||||
// 获取今日健康数据
|
||||
const todayHealthData = await fetchTodayHealthData();
|
||||
|
||||
return {
|
||||
hasStood: todayHealthData.standHours > currentHour - 1, // 如果站立小时数大于当前小时-1,说明当前小时已站立
|
||||
standHours: todayHealthData.standHours,
|
||||
standHoursGoal: todayHealthData.standHoursGoal
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('获取当前小时站立状态失败:', error);
|
||||
return {
|
||||
hasStood: true, // 默认认为已站立,避免过度提醒
|
||||
standHours: 0,
|
||||
standHoursGoal: 12
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import * as Notifications from 'expo-notifications';
|
||||
import { NotificationData, notificationService } from '../services/notifications';
|
||||
import { getNotificationEnabled } from './userPreferences';
|
||||
|
||||
/**
|
||||
* 构建 coach 页面的深度链接
|
||||
@@ -917,6 +918,102 @@ export class GeneralNotificationHelpers {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 站立提醒通知助手
|
||||
*/
|
||||
export class StandReminderHelpers {
|
||||
/**
|
||||
* 检查站立状态并发送提醒通知
|
||||
*/
|
||||
static async checkStandStatusAndNotify(userName: string): Promise<boolean> {
|
||||
try {
|
||||
console.log('检查站立状态并发送提醒通知...');
|
||||
|
||||
// 动态导入健康工具,避免循环依赖
|
||||
const { getCurrentHourStandStatus } = await import('@/utils/health');
|
||||
|
||||
// 获取当前小时站立状态
|
||||
const standStatus = await getCurrentHourStandStatus();
|
||||
|
||||
console.log('当前站立状态:', standStatus);
|
||||
|
||||
// 如果已经站立过,不需要提醒
|
||||
if (standStatus.hasStood) {
|
||||
console.log('用户当前小时已经站立,无需提醒');
|
||||
return false;
|
||||
}
|
||||
|
||||
// 检查时间范围(工作时间内提醒,避免深夜或清晨打扰)
|
||||
const currentHour = new Date().getHours();
|
||||
if (currentHour < 9 || currentHour >= 21) {
|
||||
console.log(`当前时间${currentHour}点,不在站立提醒时间范围内`);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 检查是否启用了通知
|
||||
if (!(await getNotificationEnabled())) {
|
||||
console.log('用户未启用通知功能,跳过站立提醒');
|
||||
return false;
|
||||
}
|
||||
|
||||
// 生成提醒消息
|
||||
const reminderMessage = this.generateStandReminderMessage(userName, standStatus.standHours, standStatus.standHoursGoal);
|
||||
|
||||
// 发送站立提醒通知
|
||||
await notificationService.sendImmediateNotification({
|
||||
title: '站立提醒',
|
||||
body: reminderMessage,
|
||||
data: {
|
||||
type: 'stand_reminder',
|
||||
currentStandHours: standStatus.standHours,
|
||||
standHoursGoal: standStatus.standHoursGoal,
|
||||
timestamp: Date.now()
|
||||
},
|
||||
sound: true,
|
||||
priority: 'normal',
|
||||
});
|
||||
|
||||
console.log('站立提醒通知发送成功');
|
||||
return true;
|
||||
|
||||
} catch (error) {
|
||||
console.error('检查站立状态并发送提醒失败:', error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成站立提醒消息
|
||||
*/
|
||||
private static generateStandReminderMessage(userName: string, currentStandHours: number, goalHours: number): string {
|
||||
const currentHour = new Date().getHours();
|
||||
const progress = Math.round((currentStandHours / goalHours) * 100);
|
||||
|
||||
const messages = [
|
||||
`${userName},该站起来活动一下了!当前已完成${progress}%的站立目标`,
|
||||
`${userName},久坐伤身,起来走走吧~已站立${currentStandHours}/${goalHours}小时`,
|
||||
`${userName},站立一会儿对健康有益,目前进度${currentStandHours}/${goalHours}小时`,
|
||||
`${userName},记得起身活动哦!今日站立进度${progress}%`
|
||||
];
|
||||
|
||||
// 根据时间选择不同的消息
|
||||
const messageIndex = currentHour % messages.length;
|
||||
return messages[messageIndex];
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消所有站立提醒通知
|
||||
*/
|
||||
static async cancelStandReminders(): Promise<void> {
|
||||
try {
|
||||
await GeneralNotificationHelpers.cancelNotificationsByType('stand_reminder');
|
||||
console.log('已取消所有站立提醒通知');
|
||||
} catch (error) {
|
||||
console.error('取消站立提醒通知失败:', error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 通知模板
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user