Commit 85d12a8b6cf69944179e5ebde7eba9078b938372

Authored by 权海
1 parent 2a94da76

feat(ui):修复长时间不回调,触发计算时推送多条通知

... ... @@ -64,7 +64,7 @@ class HealthRawLocalNotificationBuilder {
realtimeSleepIntervals.isEmpty && latestRealtimeStressPoint != null
? [_defaultSleepInterval(latestRealtimeStressPoint.rawEndTime)]
: realtimeSleepIntervals;
return [
return _latestNotificationsByRecordType([
if (_sleepNotification(result.sleepResults, hasExistingSleep)
case final notification?)
notification,
... ... @@ -79,7 +79,22 @@ class HealthRawLocalNotificationBuilder {
)
case final notification?)
notification,
];
]);
}
List<HealthRawLocalNotification> _latestNotificationsByRecordType(
List<HealthRawLocalNotification> notifications,
) {
if (notifications.length < 2) return notifications;
final latestByType =
<HealthRawLocalNotificationRecordType, HealthRawLocalNotification>{};
for (final notification in notifications) {
final existing = latestByType[notification.recordType];
if (existing == null || notification.recordTime > existing.recordTime) {
latestByType[notification.recordType] = notification;
}
}
return latestByType.values.toList(growable: false);
}
HealthRawLocalNotification? _sleepNotification(
... ...
... ... @@ -694,7 +694,7 @@ void main() {
});
test(
'startCoreCaculate sends hrv and sleep notifications for new non-first results',
'startCoreCaculate sends only latest hrv notification for new non-first results',
() async {
final now = DateTime.now();
final day = DateTime(now.year, now.month, now.day);
... ... @@ -770,11 +770,11 @@ void main() {
readChunkDays: 1,
);
expect(
notificationDispatcher.sentNotifications.where(
(e) => e.recordType == HealthRawLocalNotificationRecordType.hrv),
hasLength(3),
);
final hrvNotifications = notificationDispatcher.sentNotifications
.where((e) => e.recordType == HealthRawLocalNotificationRecordType.hrv)
.toList();
expect(hrvNotifications, hasLength(1));
expect(hrvNotifications.single.recordTime, base + 720);
expect(
notificationDispatcher.sentNotifications.where(
(e) => e.recordType == HealthRawLocalNotificationRecordType.sleep,
... ... @@ -792,12 +792,17 @@ void main() {
);
expect(
rows.hrvRows
.where((row) => [base + 120, base + 420, base + 720]
.contains(row['raw_end_time']))
.every((row) => row['push_send_time'] != null),
.where(
(row) => [base + 120, base + 420].contains(row['raw_end_time']))
.every((row) => row['push_send_time'] == null),
isTrue,
);
expect(
rows.hrvRows.singleWhere(
(row) => row['raw_end_time'] == base + 720)['push_send_time'],
isNotNull,
);
expect(
rows.sleepRows.singleWhere(
(row) => row['date'] == LocalHealthDataConvert.unixSeconds(sleepEnd),
)['push_send_time'],
... ...
... ... @@ -153,8 +153,7 @@ void main() {
expect(notifications.single.recordTime, latestTime);
});
test('builds hrv notifications for every candidate including sleep likely',
() {
test('builds only the latest hrv notification for multiple candidates', () {
final firstTime = _seconds(DateTime(2026, 1, 1, 9));
final secondTime = _seconds(DateTime(2026, 1, 1, 10));
final notifications = builder.build(
... ... @@ -173,8 +172,55 @@ void main() {
record: const HealthRawLocalNotificationRecord(),
);
expect(notifications, hasLength(1));
expect(notifications.single.recordType,
HealthRawLocalNotificationRecordType.hrv);
expect(notifications.single.recordTime, secondTime);
});
test('keeps the latest notification per type without dropping other types',
() {
final hrvFirstTime = _seconds(DateTime(2026, 1, 1, 9));
final hrvSecondTime = _seconds(DateTime(2026, 1, 1, 10));
final realtimeBase = _seconds(DateTime(2026, 1, 1, 11));
final notifications = builder.build(
result: HealthRawStressCalculationResult(
userId: 1,
hrvStressPoints: [
_hrvPoint(hrvFirstTime),
_hrvPoint(hrvSecondTime),
],
realtimeStressPoints: [
_realtimePoint(realtimeBase + 9 * 300, 70),
],
dailyStressPoints: const [],
),
hasExistingHrv: true,
hasExistingSleep: false,
realtimeWindow: [
for (var i = 0; i < 10; i++) _realtimePoint(realtimeBase + i * 300, 70),
],
record: const HealthRawLocalNotificationRecord(),
);
expect(notifications, hasLength(2));
expect(notifications.map((e) => e.recordTime), [firstTime, secondTime]);
expect(
notifications
.where(
(e) => e.recordType == HealthRawLocalNotificationRecordType.hrv)
.single
.recordTime,
hrvSecondTime,
);
expect(
notifications
.where((e) =>
e.recordType ==
HealthRawLocalNotificationRecordType.realtimeStress)
.single
.recordTime,
realtimeBase + 9 * 300,
);
});
test('builds realtime stress notification from latest 60 minute window', () {
... ...