Commit ab7dd1907089e92f01ae41f2cb81cc7a3e600406

Authored by 权海
1 parent 6529d91b

feat(ui):修复首页睡眠显示问题

... ... @@ -165,11 +165,11 @@ class LocalHealthDataConvert {
required List<HealthKitRawDataPoint> heartRate,
}) {
final daily = [
for (final day in days) _sleepResultForDay(day, sleepResults),
for (final day in days) sleepResultForDay(day, sleepResults),
];
final validSleep = daily.whereType<HealthRawSleepResult>().toList();
final previousDaily = [
for (final day in previousDays) _sleepResultForDay(day, sleepResults),
for (final day in previousDays) sleepResultForDay(day, sleepResults),
];
final previousValidSleep =
previousDaily.whereType<HealthRawSleepResult>().toList();
... ... @@ -493,6 +493,7 @@ class LocalHealthDataConvert {
required List<HealthKitRawDataPoint> sleepingHeartRate,
required List<HealthKitRawDataPoint> restingHeartRate,
required HealthRawSleepResult? sleepResult,
SleepTrendList? sleepTrend,
}) {
final activitySummary = _activityDaySummary(day, activity);
final dayStart = unixSeconds(day);
... ... @@ -524,13 +525,26 @@ class LocalHealthDataConvert {
exercise: activitySummary.exerciseSeconds.round(),
stand: activitySummary.standHours.round(),
steps: 0,
sleepDuration: sleepResult == null ? 0 : sleepResult.sleepMinutes * 60,
sleepScore: sleepResult?.sleepScore.toDouble(),
sleepState: sleepResult?.sleepStateValue,
sleepDuration: sleepTrend?.totalTime?.round() ??
sleepDurationSecondsFromResult(sleepResult),
sleepScore:
sleepTrend?.score?.toDouble() ?? sleepResult?.sleepScore.toDouble(),
sleepState:
sleepTrend?.sleepEvaluate?.round() ?? sleepResult?.sleepStateValue,
sleepTimeList: sleepTimeList,
);
}
static int sleepDurationSecondsFromResult(HealthRawSleepResult? sleepResult) {
if (sleepResult == null ||
sleepResult.sleepScore <= 0 ||
sleepResult.sleepStateValue <= 0 ||
sleepResult.sleepMinutes <= 0) {
return 0;
}
return sleepResult.sleepMinutes * 60;
}
static V2HrvTrendData v2HrvTrendData(
List<HealthRawHrvStressPoint> hrvPoints,
) {
... ... @@ -624,23 +638,6 @@ class LocalHealthDataConvert {
);
}
static HealthRawHrvStressPoint? _previousHrvPoint({
required HealthRawHrvStressPoint? latest,
required List<HealthRawHrvStressPoint> hrvPoints,
required List<HealthRawHrvStressPoint> previousHrvPoints,
}) {
if (latest == null) return null;
return [...previousHrvPoints, ...hrvPoints]
.where((item) => item.rawEndTime < latest.rawEndTime)
.fold<HealthRawHrvStressPoint?>(
null,
(previous, item) =>
previous == null || item.rawEndTime > previous.rawEndTime
? item
: previous,
);
}
static String? _latestHrvTip({required HealthRawHrvStressPoint? latest}) {
if (latest == null) return null;
... ... @@ -1079,7 +1076,7 @@ class LocalHealthDataConvert {
.toList();
}
static HealthRawSleepResult? _sleepResultForDay(
static HealthRawSleepResult? sleepResultForDay(
DateTime day,
List<HealthRawSleepResult> sleepResults,
) {
... ...
... ... @@ -215,10 +215,23 @@ class LocalHealthDataSource implements HealthDataSource {
startTime: dayStart,
endTime: dayEnd,
);
final sleepResult = sleepResults.isEmpty ? null : sleepResults.last;
final sleepResult = LocalHealthDataConvert.sleepResultForDay(
day,
sleepResults,
);
final sleepingHeartRate = await _querySleepHeartRate(
sleepResult == null ? const [] : [sleepResult],
);
final sleepStatistics = LocalHealthDataConvert.sleepStatisticsFromResults(
dateRangeType: 3,
days: [day],
previousDays: const [],
sleepResults: sleepResults,
heartRate: sleepingHeartRate,
);
final sleepTrend = sleepStatistics.sleepTrendList?.isEmpty ?? true
? null
: sleepStatistics.sleepTrendList!.first;
final restingHeartRate = await coreService.queryRawDataPoints(
dataType: HealthDataUploadType.restingHeartRate.type,
startTime: dayStart,
... ... @@ -232,6 +245,7 @@ class LocalHealthDataSource implements HealthDataSource {
sleepingHeartRate: sleepingHeartRate,
restingHeartRate: restingHeartRate,
sleepResult: sleepResult,
sleepTrend: sleepTrend,
),
);
} catch (error) {
... ...
import 'package:doublefeel_flutter/core/services/health_raw_data_core_service.dart';
import 'package:doublefeel_flutter/data/datasource/health/health_local_data_convert.dart';
import 'package:doublefeel_flutter/data/models/health/sleep/sleep_statistics_data.dart';
import 'package:doublefeel_flutter/pigeon/health_kit_raw_data_api.g.dart';
import 'package:flutter_test/flutter_test.dart';
... ... @@ -119,6 +120,83 @@ void main() {
expect(statistics.avgSleepEvaluate, 2);
});
test('v2 health sleep duration matches sleep statistics result duration',
() {
final day = DateTime(2026, 7, 16);
final sleepStart = DateTime(2026, 7, 15, 23);
final sleepEnd = DateTime(2026, 7, 16, 7);
final result = HealthRawSleepResult(
userId: 1,
date: LocalHealthDataConvert.unixSeconds(sleepEnd),
startDate: LocalHealthDataConvert.unixSeconds(sleepStart),
sleepScore: 85,
sleepState: 1,
inBedMinutes: 480,
awakMinutes: 80,
sleepMinutes: 400,
);
final statistics = LocalHealthDataConvert.sleepStatisticsFromResults(
dateRangeType: 3,
days: [day],
previousDays: const [],
sleepResults: [result],
heartRate: const [],
);
final selected = LocalHealthDataConvert.sleepResultForDay(day, [result]);
final healthData = LocalHealthDataConvert.v2HealthDataFromSleepResult(
day: day,
hrvPoints: const [],
activity: const [],
sleepingHeartRate: const [],
restingHeartRate: const [],
sleepResult: selected,
);
expect(statistics.sleepTrendList?.single.totalTime, 400 * 60);
expect(healthData.sleepDuration,
statistics.sleepTrendList?.single.totalTime);
expect(
healthData.sleepDuration,
isNot(result.date - result.startDate),
);
});
test('v2 health sleep values prefer sleep statistics trend result', () {
final day = DateTime(2026, 7, 16);
final result = HealthRawSleepResult(
userId: 1,
date: LocalHealthDataConvert.unixSeconds(
day.add(const Duration(hours: 7)),
),
startDate: LocalHealthDataConvert.unixSeconds(
day.subtract(const Duration(hours: 1)),
),
sleepScore: 90,
sleepState: 1,
inBedMinutes: 520,
awakMinutes: 40,
sleepMinutes: 480,
);
final healthData = LocalHealthDataConvert.v2HealthDataFromSleepResult(
day: day,
hrvPoints: const [],
activity: const [],
sleepingHeartRate: const [],
restingHeartRate: const [],
sleepResult: result,
sleepTrend: SleepTrendList(
timeKey: 20260716,
totalTime: 400 * 60,
score: 72,
sleepEvaluate: 2,
),
);
expect(healthData.sleepDuration, 400 * 60);
expect(healthData.sleepScore, 72);
expect(healthData.sleepState, 2);
});
test('compares result bedtime extremes by sleep-end-day start offset', () {
final july27 = DateTime(2026, 7, 27);
final july28 = DateTime(2026, 7, 28);
... ...