feat: 优化睡眠数据

This commit is contained in:
richarjiang
2025-09-09 10:01:11 +08:00
parent 9ccd15319e
commit cacfde064f
16 changed files with 1212 additions and 291 deletions

View File

@@ -162,6 +162,8 @@ async function executeBackgroundTasks(): Promise<void> {
return;
}
await sendTestNotification()
// 执行喝水提醒检查任务
await executeWaterReminderTask();

View File

@@ -163,18 +163,19 @@ function calculateSleepStageStats(samples: SleepSample[]): SleepStageStats[] {
stageMap.set(sample.value, currentDuration + duration);
});
// 计算睡眠时间(排除在床时间)
const totalSleepTime = Array.from(stageMap.entries())
// 计算实际睡眠时间(包括所有睡眠阶段,排除在床时间)
const actualSleepTime = Array.from(stageMap.entries())
.filter(([stage]) => stage !== SleepStage.InBed)
.reduce((total, [, duration]) => total + duration, 0);
// 生成统计数据
// 生成统计数据,包含所有睡眠阶段(包括清醒时间)
const stats: SleepStageStats[] = [];
stageMap.forEach((duration, stage) => {
if (stage === SleepStage.InBed || stage === SleepStage.Awake) return;
// 只排除在床时间,保留清醒时间
if (stage === SleepStage.InBed) return;
const percentage = totalSleepTime > 0 ? (duration / totalSleepTime) * 100 : 0;
const percentage = actualSleepTime > 0 ? (duration / actualSleepTime) * 100 : 0;
let quality: SleepQuality;
// 根据睡眠阶段和比例判断质量
@@ -194,6 +195,12 @@ function calculateSleepStageStats(samples: SleepSample[]): SleepStageStats[] {
percentage >= 35 ? SleepQuality.Good :
percentage >= 25 ? SleepQuality.Fair : SleepQuality.Poor;
break;
case SleepStage.Awake:
// 清醒时间越少越好
quality = percentage <= 5 ? SleepQuality.Excellent :
percentage <= 10 ? SleepQuality.Good :
percentage <= 15 ? SleepQuality.Fair : SleepQuality.Poor;
break;
default:
quality = SleepQuality.Fair;
}
@@ -285,12 +292,12 @@ export function getSleepStageDisplayName(stage: SleepStage): string {
export function getSleepStageColor(stage: SleepStage): string {
switch (stage) {
case SleepStage.Deep:
return '#1E40AF'; // 深蓝色
case SleepStage.Core:
return '#3B82F6'; // 蓝色
case SleepStage.Core:
return '#8B5CF6'; // 紫色
case SleepStage.REM:
case SleepStage.Asleep:
return '#06B6D4'; //
return '#EC4899'; //
case SleepStage.Awake:
return '#F59E0B'; // 橙色
case SleepStage.InBed:
@@ -313,21 +320,71 @@ export async function fetchSleepDetailForDate(date: Date): Promise<SleepDetailDa
return null;
}
// 找到上床时间和起床时间
const inBedSamples = sleepSamples.filter(sample => sample.value === SleepStage.InBed);
const bedtime = inBedSamples.length > 0 ? inBedSamples[0].startDate : sleepSamples[0].startDate;
const wakeupTime = inBedSamples.length > 0 ?
inBedSamples[inBedSamples.length - 1].endDate :
sleepSamples[sleepSamples.length - 1].endDate;
// 找到入睡时间和起床时间
// 过滤出实际睡眠阶段(排除在床时间和清醒时间)
const actualSleepSamples = sleepSamples.filter(sample =>
sample.value !== SleepStage.InBed && sample.value !== SleepStage.Awake
);
// 入睡时间:第一个实际睡眠阶段的开始时间
// 起床时间:最后一个实际睡眠阶段的结束时间
let bedtime: string;
let wakeupTime: string;
if (actualSleepSamples.length > 0) {
// 按开始时间排序
const sortedSleepSamples = actualSleepSamples.sort((a, b) =>
new Date(a.startDate).getTime() - new Date(b.startDate).getTime()
);
bedtime = sortedSleepSamples[0].startDate;
wakeupTime = sortedSleepSamples[sortedSleepSamples.length - 1].endDate;
console.log('计算入睡和起床时间:');
console.log('- 入睡时间:', dayjs(bedtime).format('YYYY-MM-DD HH:mm:ss'));
console.log('- 起床时间:', dayjs(wakeupTime).format('YYYY-MM-DD HH:mm:ss'));
} else {
// 如果没有实际睡眠数据,回退到使用所有样本数据
console.warn('没有找到实际睡眠阶段数据,使用所有样本数据');
const sortedAllSamples = sleepSamples.sort((a, b) =>
new Date(a.startDate).getTime() - new Date(b.startDate).getTime()
);
bedtime = sortedAllSamples[0].startDate;
wakeupTime = sortedAllSamples[sortedAllSamples.length - 1].endDate;
}
// 计算在床时间
const timeInBed = dayjs(wakeupTime).diff(dayjs(bedtime), 'minute');
// 计算在床时间 - 使用 INBED 样本数据
let timeInBed: number;
const inBedSamples = sleepSamples.filter(sample => sample.value === SleepStage.InBed);
if (inBedSamples.length > 0) {
// 使用 INBED 样本计算在床时间
const sortedInBedSamples = inBedSamples.sort((a, b) =>
new Date(a.startDate).getTime() - new Date(b.startDate).getTime()
);
const inBedStart = sortedInBedSamples[0].startDate;
const inBedEnd = sortedInBedSamples[sortedInBedSamples.length - 1].endDate;
timeInBed = dayjs(inBedEnd).diff(dayjs(inBedStart), 'minute');
console.log('在床时间计算:');
console.log('- 上床时间:', dayjs(inBedStart).format('YYYY-MM-DD HH:mm:ss'));
console.log('- 离床时间:', dayjs(inBedEnd).format('YYYY-MM-DD HH:mm:ss'));
console.log('- 在床时长:', timeInBed, '分钟');
} else {
// 如果没有 INBED 数据,使用睡眠时间作为在床时间
timeInBed = dayjs(wakeupTime).diff(dayjs(bedtime), 'minute');
console.log('没有INBED数据使用睡眠时间作为在床时间:', timeInBed, '分钟');
}
// 计算睡眠阶段统计
const sleepStages = calculateSleepStageStats(sleepSamples);
// 计算总睡眠时间
const totalSleepTime = sleepStages.reduce((total, stage) => total + stage.duration, 0);
// 计算总睡眠时间(排除清醒时间)
const totalSleepTime = sleepStages
.filter(stage => stage.stage !== SleepStage.Awake)
.reduce((total, stage) => total + stage.duration, 0);
// 计算睡眠效率
const sleepEfficiency = timeInBed > 0 ? Math.round((totalSleepTime / timeInBed) * 100) : 0;