feat(workout): 新增锻炼结束监听和个性化通知功能

实现了iOS HealthKit锻炼数据实时监听,当用户完成锻炼时自动发送个性化鼓励通知。包括锻炼类型筛选、时间范围控制、用户偏好设置等完整功能,并提供了测试工具和详细文档。
This commit is contained in:
richarjiang
2025-10-13 10:05:02 +08:00
parent 12883c5410
commit 971aebd560
18 changed files with 2210 additions and 1264 deletions

152
utils/workoutPreferences.ts Normal file
View File

@@ -0,0 +1,152 @@
import AsyncStorage from '@/utils/kvStore';
const WORKOUT_NOTIFICATION_ENABLED_KEY = '@workout_notification_enabled';
const WORKOUT_NOTIFICATION_TIME_RANGE_KEY = '@workout_notification_time_range';
const WORKOUT_NOTIFICATION_TYPES_KEY = '@workout_notification_types';
export interface WorkoutNotificationPreferences {
enabled: boolean;
startTimeHour: number; // 开始时间小时0-23
endTimeHour: number; // 结束时间小时0-23
enabledWorkoutTypes: string[]; // 启用通知的锻炼类型
}
const DEFAULT_PREFERENCES: WorkoutNotificationPreferences = {
enabled: true,
startTimeHour: 8, // 早上8点
endTimeHour: 22, // 晚上10点
enabledWorkoutTypes: [] // 空数组表示所有类型都启用
};
/**
* 获取锻炼通知偏好设置
*/
export async function getWorkoutNotificationPreferences(): Promise<WorkoutNotificationPreferences> {
try {
const enabled = await AsyncStorage.getItem(WORKOUT_NOTIFICATION_ENABLED_KEY);
const timeRange = await AsyncStorage.getItem(WORKOUT_NOTIFICATION_TIME_RANGE_KEY);
const workoutTypes = await AsyncStorage.getItem(WORKOUT_NOTIFICATION_TYPES_KEY);
return {
enabled: enabled !== 'false', // 默认启用
startTimeHour: timeRange ? JSON.parse(timeRange).start : DEFAULT_PREFERENCES.startTimeHour,
endTimeHour: timeRange ? JSON.parse(timeRange).end : DEFAULT_PREFERENCES.endTimeHour,
enabledWorkoutTypes: workoutTypes ? JSON.parse(workoutTypes) : DEFAULT_PREFERENCES.enabledWorkoutTypes
};
} catch (error) {
console.error('获取锻炼通知偏好设置失败:', error);
return DEFAULT_PREFERENCES;
}
}
/**
* 保存锻炼通知偏好设置
*/
export async function saveWorkoutNotificationPreferences(preferences: Partial<WorkoutNotificationPreferences>): Promise<void> {
try {
const currentPrefs = await getWorkoutNotificationPreferences();
const newPrefs = { ...currentPrefs, ...preferences };
if (preferences.enabled !== undefined) {
await AsyncStorage.setItem(WORKOUT_NOTIFICATION_ENABLED_KEY, preferences.enabled.toString());
}
if (preferences.startTimeHour !== undefined || preferences.endTimeHour !== undefined) {
const timeRange = {
start: preferences.startTimeHour ?? currentPrefs.startTimeHour,
end: preferences.endTimeHour ?? currentPrefs.endTimeHour
};
await AsyncStorage.setItem(WORKOUT_NOTIFICATION_TIME_RANGE_KEY, JSON.stringify(timeRange));
}
if (preferences.enabledWorkoutTypes !== undefined) {
await AsyncStorage.setItem(WORKOUT_NOTIFICATION_TYPES_KEY, JSON.stringify(preferences.enabledWorkoutTypes));
}
console.log('锻炼通知偏好设置已保存:', newPrefs);
} catch (error) {
console.error('保存锻炼通知偏好设置失败:', error);
throw error;
}
}
/**
* 检查当前时间是否在允许的通知时间范围内
*/
export async function isNotificationTimeAllowed(): Promise<boolean> {
try {
const preferences = await getWorkoutNotificationPreferences();
const currentHour = new Date().getHours();
// 处理跨天的情况(例如 22:00 - 8:00
if (preferences.startTimeHour <= preferences.endTimeHour) {
// 正常情况,如 8:00 - 22:00
return currentHour >= preferences.startTimeHour && currentHour <= preferences.endTimeHour;
} else {
// 跨天情况,如 22:00 - 8:00
return currentHour >= preferences.startTimeHour || currentHour <= preferences.endTimeHour;
}
} catch (error) {
console.error('检查通知时间失败:', error);
return true; // 默认允许
}
}
/**
* 检查特定锻炼类型是否启用了通知
*/
export async function isWorkoutTypeEnabled(workoutType: string): Promise<boolean> {
try {
const preferences = await getWorkoutNotificationPreferences();
// 如果启用的类型列表为空,表示所有类型都启用
if (preferences.enabledWorkoutTypes.length === 0) {
return true;
}
return preferences.enabledWorkoutTypes.includes(workoutType);
} catch (error) {
console.error('检查锻炼类型通知设置失败:', error);
return true; // 默认允许
}
}
/**
* 获取锻炼通知的简化状态(仅检查是否启用)
*/
export async function getWorkoutNotificationEnabled(): Promise<boolean> {
try {
const preferences = await getWorkoutNotificationPreferences();
return preferences.enabled;
} catch (error) {
console.error('获取锻炼通知启用状态失败:', error);
return true; // 默认启用
}
}
/**
* 设置锻炼通知启用状态
*/
export async function setWorkoutNotificationEnabled(enabled: boolean): Promise<void> {
try {
await saveWorkoutNotificationPreferences({ enabled });
} catch (error) {
console.error('设置锻炼通知启用状态失败:', error);
throw error;
}
}
/**
* 重置锻炼通知偏好设置为默认值
*/
export async function resetWorkoutNotificationPreferences(): Promise<void> {
try {
await AsyncStorage.removeItem(WORKOUT_NOTIFICATION_ENABLED_KEY);
await AsyncStorage.removeItem(WORKOUT_NOTIFICATION_TIME_RANGE_KEY);
await AsyncStorage.removeItem(WORKOUT_NOTIFICATION_TYPES_KEY);
console.log('锻炼通知偏好设置已重置为默认值');
} catch (error) {
console.error('重置锻炼通知偏好设置失败:', error);
throw error;
}
}