From 47c8bfc5bc3dc81cd3d90c87aac607d40ce76118 Mon Sep 17 00:00:00 2001 From: richarjiang Date: Tue, 30 Sep 2025 15:10:48 +0800 Subject: [PATCH] =?UTF-8?q?feat(water):=20=E5=90=8E=E5=8F=B0=E4=BB=BB?= =?UTF-8?q?=E5=8A=A1=E5=90=8C=E6=AD=A5HealthKit=E9=A5=AE=E6=B0=B4=E8=AE=B0?= =?UTF-8?q?=E5=BD=95=E5=B9=B6=E4=BC=98=E5=8C=96=E7=9B=AE=E6=A0=87=E8=AF=BB?= =?UTF-8?q?=E5=8F=96=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/challenges/[id]/index.tsx | 6 ++--- services/backgroundTaskManager.ts | 45 +++++++++++++++++++++++++++---- 2 files changed, 43 insertions(+), 8 deletions(-) diff --git a/app/challenges/[id]/index.tsx b/app/challenges/[id]/index.tsx index ae2b627..686261c 100644 --- a/app/challenges/[id]/index.tsx +++ b/app/challenges/[id]/index.tsx @@ -7,19 +7,19 @@ import { useAuthGuard } from '@/hooks/useAuthGuard'; import { useColorScheme } from '@/hooks/useColorScheme'; import { fetchChallengeDetail, + fetchChallengeRankings, joinChallenge, leaveChallenge, reportChallengeProgress, - fetchChallengeRankings, selectChallengeById, selectChallengeDetailError, selectChallengeDetailStatus, + selectChallengeRankingList, selectJoinError, selectJoinStatus, selectLeaveError, selectLeaveStatus, - selectProgressStatus, - selectChallengeRankingList + selectProgressStatus } from '@/store/challengesSlice'; import { Toast } from '@/utils/toast.utils'; import { Ionicons } from '@expo/vector-icons'; diff --git a/services/backgroundTaskManager.ts b/services/backgroundTaskManager.ts index fa0a3a5..5bcbfdc 100644 --- a/services/backgroundTaskManager.ts +++ b/services/backgroundTaskManager.ts @@ -3,9 +3,12 @@ import AsyncStorage from '@/utils/kvStore'; import { log } from '@/utils/logger'; import { listChallenges } from '@/services/challengesApi'; import { ChallengeNotificationHelpers, StandReminderHelpers, WaterNotificationHelpers } from '@/utils/notificationHelpers'; +import { getWaterIntakeFromHealthKit } from '@/utils/health'; +import { getWaterGoalFromStorage } from '@/utils/userPreferences'; import * as BackgroundTask from 'expo-background-task'; import * as TaskManager from 'expo-task-manager'; import { TaskManagerTaskBody } from 'expo-task-manager'; +import dayjs from 'dayjs'; export const BACKGROUND_TASK_IDENTIFIER = 'com.anonymous.digitalpilates.task'; @@ -33,8 +36,13 @@ async function executeWaterReminderTask(): Promise { const waterStats = state.water.todayStats; const userProfile = state.user.profile; - // 检查是否有喝水目标设置 - if (!waterStats || !waterStats.dailyGoal || waterStats.dailyGoal <= 0) { + // 优先使用 Redux 中的目标,若无则读取本地存储 + let dailyGoal = waterStats?.dailyGoal ?? 0; + if (!dailyGoal || dailyGoal <= 0) { + dailyGoal = await getWaterGoalFromStorage(); + } + + if (!dailyGoal || dailyGoal <= 0) { console.log('没有设置喝水目标,跳过喝水提醒'); return; } @@ -45,11 +53,38 @@ async function executeWaterReminderTask(): Promise { // 获取用户名 const userName = userProfile?.name || '朋友'; + const todayRange = { + startDate: dayjs().startOf('day').toDate().toISOString(), + endDate: dayjs().endOf('day').toDate().toISOString() + }; + + let totalAmount = waterStats?.totalAmount ?? 0; + let completionRate = waterStats?.completionRate ?? (dailyGoal > 0 ? (totalAmount / dailyGoal) * 100 : 0); + + try { + const healthKitRecords = await getWaterIntakeFromHealthKit(todayRange); + if (Array.isArray(healthKitRecords) && healthKitRecords.length > 0) { + totalAmount = healthKitRecords.reduce((sum: number, record: unknown) => { + if (record && typeof record === 'object' && 'value' in record) { + const { value } = record as { value?: number | string }; + const numericValue = Number(value ?? 0); + return Number.isFinite(numericValue) ? sum + numericValue : sum; + } + return sum; + }, 0); + completionRate = Math.min((totalAmount / dailyGoal) * 100, 100); + } else { + console.log('HealthKit 未返回今日饮水记录,使用应用内缓存数据'); + } + } catch (healthKitError) { + console.error('从HealthKit获取饮水记录失败,使用应用内缓存数据:', healthKitError); + } + // 构造今日统计数据 const todayWaterStats = { - totalAmount: waterStats.totalAmount || 0, - dailyGoal: waterStats.dailyGoal, - completionRate: waterStats.completionRate || 0 + totalAmount, + dailyGoal, + completionRate: Number.isFinite(completionRate) ? completionRate : 0 }; // 调用喝水通知检查函数