Commit 33a7b6b505d339b4bfdb18a183ec61bacdc25880

Authored by 权海
1 parent d55407d9

feat(ui):本地推送条件后置单独记录

@@ -444,9 +444,30 @@ class AppleHealthRawDataCoreService { @@ -444,9 +444,30 @@ class AppleHealthRawDataCoreService {
444 'latest_sleep': _latestSleepResultPayload(result.sleepResults), 444 'latest_sleep': _latestSleepResultPayload(result.sleepResults),
445 }, 445 },
446 ); 446 );
447 - if (result.hrvStressPoints.isEmpty &&  
448 - result.realtimeStressPoints.isEmpty &&  
449 - result.sleepResults.isEmpty) { 447 + final record = await _readLocalNotificationRecordSafely(result.userId);
  448 + await _saveLocalNotificationDebugEvent(
  449 + <String, Object?>{
  450 + 'event': 'flutterNotificationRecordRead',
  451 + 'user_id': result.userId,
  452 + 'record': _localNotificationRecordPayload(record),
  453 + },
  454 + );
  455 + final candidateData = await _resolveLocalNotificationCandidates(
  456 + result: result,
  457 + record: record,
  458 + );
  459 + await _saveLocalNotificationDebugEvent(
  460 + <String, Object?>{
  461 + 'event': 'flutterNotificationLatestDataTimeChecked',
  462 + 'user_id': result.userId,
  463 + 'record': _localNotificationRecordPayload(record),
  464 + 'latest_data_time': candidateData.latestDataTimePayload,
  465 + },
  466 + );
  467 + final candidateResult = candidateData.result;
  468 + if (candidateResult.hrvStressPoints.isEmpty &&
  469 + candidateResult.realtimeStressPoints.isEmpty &&
  470 + candidateResult.sleepResults.isEmpty) {
450 await _saveLocalNotificationDebugEvent( 471 await _saveLocalNotificationDebugEvent(
451 <String, Object?>{ 472 <String, Object?>{
452 'event': 'flutterNotificationNotBuilt', 473 'event': 'flutterNotificationNotBuilt',
@@ -457,12 +478,13 @@ class AppleHealthRawDataCoreService { @@ -457,12 +478,13 @@ class AppleHealthRawDataCoreService {
457 return; 478 return;
458 } 479 }
459 480
460 - final latestRealtimeStressPoint = result.realtimeStressPoints.isEmpty  
461 - ? null  
462 - : result.realtimeStressPoints.reduce(  
463 - (a, b) => a.rawEndTime >= b.rawEndTime ? a : b,  
464 - );  
465 - var notificationResult = result; 481 + final latestRealtimeStressPoint =
  482 + candidateResult.realtimeStressPoints.isEmpty
  483 + ? null
  484 + : candidateResult.realtimeStressPoints.reduce(
  485 + (a, b) => a.rawEndTime >= b.rawEndTime ? a : b,
  486 + );
  487 + var notificationResult = candidateResult;
466 var realtimeWindow = const <HealthRawRealtimeStressPoint>[]; 488 var realtimeWindow = const <HealthRawRealtimeStressPoint>[];
467 if (latestRealtimeStressPoint != null) { 489 if (latestRealtimeStressPoint != null) {
468 final shouldResetRealtimeStressPushTime = 490 final shouldResetRealtimeStressPushTime =
@@ -479,36 +501,28 @@ class AppleHealthRawDataCoreService { @@ -479,36 +501,28 @@ class AppleHealthRawDataCoreService {
479 }, 501 },
480 ); 502 );
481 await _recordRealtimeStressTimeSafely( 503 await _recordRealtimeStressTimeSafely(
482 - userId: result.userId, 504 + userId: candidateResult.userId,
483 recordTime: latestRealtimeStressPoint.rawEndTime, 505 recordTime: latestRealtimeStressPoint.rawEndTime,
484 ); 506 );
485 - notificationResult = result.copyWith( 507 + notificationResult = candidateResult.copyWith(
486 realtimeStressPoints: const <HealthRawRealtimeStressPoint>[], 508 realtimeStressPoints: const <HealthRawRealtimeStressPoint>[],
487 ); 509 );
488 } else { 510 } else {
489 realtimeWindow = await _queryRealtimeStressNotificationWindowSafely( 511 realtimeWindow = await _queryRealtimeStressNotificationWindowSafely(
490 - userId: result.userId, 512 + userId: candidateResult.userId,
491 latestRawEndTime: latestRealtimeStressPoint.rawEndTime, 513 latestRawEndTime: latestRealtimeStressPoint.rawEndTime,
492 ); 514 );
493 } 515 }
494 } 516 }
495 517
496 - final record = await _readLocalNotificationRecordSafely(result.userId);  
497 - await _saveLocalNotificationDebugEvent(  
498 - <String, Object?>{  
499 - 'event': 'flutterNotificationRecordRead',  
500 - 'user_id': result.userId,  
501 - 'record': _localNotificationRecordPayload(record),  
502 - },  
503 - );  
504 await _saveLocalNotificationDebugEvent( 518 await _saveLocalNotificationDebugEvent(
505 <String, Object?>{ 519 <String, Object?>{
506 'event': 'flutterNotificationBuildDecisions', 520 'event': 'flutterNotificationBuildDecisions',
507 - 'user_id': result.userId, 521 + 'user_id': candidateResult.userId,
508 'decisions': _localNotificationDecisionPayloads( 522 'decisions': _localNotificationDecisionPayloads(
509 result: notificationResult, 523 result: notificationResult,
510 - hasExistingHrv: hasExistingHrv,  
511 - hasExistingSleep: hasExistingSleep, 524 + hasExistingHrv: candidateData.hasExistingHrvDataTime,
  525 + hasExistingSleep: candidateData.hasExistingSleepDataTime,
512 realtimeWindow: realtimeWindow, 526 realtimeWindow: realtimeWindow,
513 record: record, 527 record: record,
514 ), 528 ),
@@ -516,15 +530,15 @@ class AppleHealthRawDataCoreService { @@ -516,15 +530,15 @@ class AppleHealthRawDataCoreService {
516 ); 530 );
517 final notifications = HealthRawLocalNotificationBuilder(l10n).build( 531 final notifications = HealthRawLocalNotificationBuilder(l10n).build(
518 result: notificationResult, 532 result: notificationResult,
519 - hasExistingHrv: hasExistingHrv,  
520 - hasExistingSleep: hasExistingSleep, 533 + hasExistingHrv: candidateData.hasExistingHrvDataTime,
  534 + hasExistingSleep: candidateData.hasExistingSleepDataTime,
521 realtimeWindow: realtimeWindow, 535 realtimeWindow: realtimeWindow,
522 record: record, 536 record: record,
523 ); 537 );
524 await _saveLocalNotificationDebugEvent( 538 await _saveLocalNotificationDebugEvent(
525 <String, Object?>{ 539 <String, Object?>{
526 'event': 'flutterNotificationBuildFinished', 540 'event': 'flutterNotificationBuildFinished',
527 - 'user_id': result.userId, 541 + 'user_id': candidateResult.userId,
528 'notification_count': notifications.length, 542 'notification_count': notifications.length,
529 'notifications': notifications.map(_localNotificationPayload).toList(), 543 'notifications': notifications.map(_localNotificationPayload).toList(),
530 }, 544 },
@@ -534,7 +548,7 @@ class AppleHealthRawDataCoreService { @@ -534,7 +548,7 @@ class AppleHealthRawDataCoreService {
534 <String, Object?>{ 548 <String, Object?>{
535 'event': 'flutterNotificationNotSent', 549 'event': 'flutterNotificationNotSent',
536 'reason': 'no_built_notifications', 550 'reason': 'no_built_notifications',
537 - 'user_id': result.userId, 551 + 'user_id': candidateResult.userId,
538 }, 552 },
539 ); 553 );
540 return; 554 return;
@@ -545,18 +559,18 @@ class AppleHealthRawDataCoreService { @@ -545,18 +559,18 @@ class AppleHealthRawDataCoreService {
545 await _saveLocalNotificationDebugEvent( 559 await _saveLocalNotificationDebugEvent(
546 <String, Object?>{ 560 <String, Object?>{
547 'event': 'flutterNotificationSendStart', 561 'event': 'flutterNotificationSendStart',
548 - 'user_id': result.userId, 562 + 'user_id': candidateResult.userId,
549 'notification': _localNotificationPayload(notification), 563 'notification': _localNotificationPayload(notification),
550 }, 564 },
551 ); 565 );
552 final sent = await _sendLocalNotificationSafely( 566 final sent = await _sendLocalNotificationSafely(
553 - userId: result.userId, 567 + userId: candidateResult.userId,
554 notification: notification, 568 notification: notification,
555 ); 569 );
556 await _saveLocalNotificationDebugEvent( 570 await _saveLocalNotificationDebugEvent(
557 <String, Object?>{ 571 <String, Object?>{
558 'event': 'flutterNotificationSendFinished', 572 'event': 'flutterNotificationSendFinished',
559 - 'user_id': result.userId, 573 + 'user_id': candidateResult.userId,
560 'sent': sent, 574 'sent': sent,
561 'reason': sent ? null : 'native_or_dispatcher_returned_false', 575 'reason': sent ? null : 'native_or_dispatcher_returned_false',
562 'notification': _localNotificationPayload(notification), 576 'notification': _localNotificationPayload(notification),
@@ -569,6 +583,93 @@ class AppleHealthRawDataCoreService { @@ -569,6 +583,93 @@ class AppleHealthRawDataCoreService {
569 _scheduleRealtimeStressServerPush(sentNotifications); 583 _scheduleRealtimeStressServerPush(sentNotifications);
570 } 584 }
571 585
  586 + Future<_HealthRawLocalNotificationCandidateData>
  587 + _resolveLocalNotificationCandidates({
  588 + required HealthRawStressCalculationResult result,
  589 + required HealthRawLocalNotificationRecord record,
  590 + }) async {
  591 + final userId = result.userId;
  592 + final latestHrvRawEndTime = await _localStore.latestHrvRawEndTime(userId);
  593 + final latestRealtimeRawEndTime =
  594 + await _localStore.latestRealtimeRawEndTime(userId);
  595 + final latestSleepDataTime = await _localStore.latestSleepResultTime(userId);
  596 +
  597 + var hrvPoints = const <HealthRawHrvStressPoint>[];
  598 + var realtimePoints = const <HealthRawRealtimeStressPoint>[];
  599 + var sleepResults = const <HealthRawSleepResult>[];
  600 + int? nextHrvDataTime;
  601 + int? nextRealtimeStressDataTime;
  602 + int? nextSleepDataTime;
  603 +
  604 + final hrvDataTime = record.latestHrvDataTime;
  605 + if (latestHrvRawEndTime != null) {
  606 + if (hrvDataTime == null) {
  607 + nextHrvDataTime = latestHrvRawEndTime;
  608 + } else if (hrvDataTime < latestHrvRawEndTime) {
  609 + hrvPoints = await _localStore.queryHrvStressPoints(
  610 + userId: userId,
  611 + startTime: hrvDataTime + 1,
  612 + endTime: latestHrvRawEndTime,
  613 + );
  614 + nextHrvDataTime = latestHrvRawEndTime;
  615 + }
  616 + }
  617 +
  618 + final realtimeDataTime = record.latestRealtimeStressDataTime;
  619 + if (latestRealtimeRawEndTime != null) {
  620 + if (realtimeDataTime == null) {
  621 + nextRealtimeStressDataTime = latestRealtimeRawEndTime;
  622 + } else if (realtimeDataTime < latestRealtimeRawEndTime) {
  623 + realtimePoints = await _localStore.queryRealtimeStressPoints(
  624 + userId: userId,
  625 + startTime: realtimeDataTime + 1,
  626 + endTime: latestRealtimeRawEndTime,
  627 + );
  628 + nextRealtimeStressDataTime = latestRealtimeRawEndTime;
  629 + }
  630 + }
  631 +
  632 + final sleepDataTime = record.latestSleepDataTime;
  633 + if (latestSleepDataTime != null) {
  634 + if (sleepDataTime == null) {
  635 + nextSleepDataTime = latestSleepDataTime;
  636 + } else if (sleepDataTime < latestSleepDataTime) {
  637 + sleepResults = await _localStore.querySleepResults(
  638 + userId: userId,
  639 + startTime: sleepDataTime + 1,
  640 + endTime: latestSleepDataTime,
  641 + );
  642 + nextSleepDataTime = latestSleepDataTime;
  643 + }
  644 + }
  645 +
  646 + await _updateProcessedDataTimesSafely(
  647 + userId: userId,
  648 + latestHrvDataTime: nextHrvDataTime,
  649 + latestRealtimeStressDataTime: nextRealtimeStressDataTime,
  650 + latestSleepDataTime: nextSleepDataTime,
  651 + );
  652 +
  653 + return _HealthRawLocalNotificationCandidateData(
  654 + result: HealthRawStressCalculationResult(
  655 + userId: userId,
  656 + hrvStressPoints: hrvPoints,
  657 + realtimeStressPoints: realtimePoints,
  658 + dailyStressPoints: result.dailyStressPoints,
  659 + sleepResults: sleepResults,
  660 + ),
  661 + hasExistingHrvDataTime: hrvDataTime != null,
  662 + hasExistingRealtimeStressDataTime: realtimeDataTime != null,
  663 + hasExistingSleepDataTime: sleepDataTime != null,
  664 + currentHrvDataTime: latestHrvRawEndTime,
  665 + currentRealtimeStressDataTime: latestRealtimeRawEndTime,
  666 + currentSleepDataTime: latestSleepDataTime,
  667 + nextHrvDataTime: nextHrvDataTime,
  668 + nextRealtimeStressDataTime: nextRealtimeStressDataTime,
  669 + nextSleepDataTime: nextSleepDataTime,
  670 + );
  671 + }
  672 +
572 Future<HealthRawLocalNotificationRecord> _readLocalNotificationRecordSafely( 673 Future<HealthRawLocalNotificationRecord> _readLocalNotificationRecordSafely(
573 int userId, 674 int userId,
574 ) async { 675 ) async {
@@ -580,6 +681,28 @@ class AppleHealthRawDataCoreService { @@ -580,6 +681,28 @@ class AppleHealthRawDataCoreService {
580 } 681 }
581 } 682 }
582 683
  684 + Future<void> _updateProcessedDataTimesSafely({
  685 + required int userId,
  686 + int? latestHrvDataTime,
  687 + int? latestRealtimeStressDataTime,
  688 + int? latestSleepDataTime,
  689 + }) async {
  690 + try {
  691 + await _localNotificationDispatcher.updateProcessedDataTimes(
  692 + userId: userId,
  693 + latestHrvDataTime: latestHrvDataTime,
  694 + latestRealtimeStressDataTime: latestRealtimeStressDataTime,
  695 + latestSleepDataTime: latestSleepDataTime,
  696 + );
  697 + } catch (error, stackTrace) {
  698 + _logError(
  699 + 'update local notification processed data time failed',
  700 + error,
  701 + stackTrace,
  702 + );
  703 + }
  704 + }
  705 +
583 Future<void> _recordRealtimeStressTimeSafely({ 706 Future<void> _recordRealtimeStressTimeSafely({
584 required int userId, 707 required int userId,
585 required int recordTime, 708 required int recordTime,
@@ -864,6 +987,9 @@ class AppleHealthRawDataCoreService { @@ -864,6 +987,9 @@ class AppleHealthRawDataCoreService {
864 'last_sleep_time': record.lastSleepTime, 987 'last_sleep_time': record.lastSleepTime,
865 'last_hrv_time': record.lastHrvTime, 988 'last_hrv_time': record.lastHrvTime,
866 'last_realtime_stress_time': record.lastRealtimeStressTime, 989 'last_realtime_stress_time': record.lastRealtimeStressTime,
  990 + 'latest_hrv_data_time': record.latestHrvDataTime,
  991 + 'latest_realtime_stress_data_time': record.latestRealtimeStressDataTime,
  992 + 'latest_sleep_data_time': record.latestSleepDataTime,
867 }; 993 };
868 } 994 }
869 995
@@ -1675,6 +1801,50 @@ class HealthRawStressDbRows { @@ -1675,6 +1801,50 @@ class HealthRawStressDbRows {
1675 final List<Map<String, Object?>> sleepRows; 1801 final List<Map<String, Object?>> sleepRows;
1676 } 1802 }
1677 1803
  1804 +class _HealthRawLocalNotificationCandidateData {
  1805 + const _HealthRawLocalNotificationCandidateData({
  1806 + required this.result,
  1807 + required this.hasExistingHrvDataTime,
  1808 + required this.hasExistingRealtimeStressDataTime,
  1809 + required this.hasExistingSleepDataTime,
  1810 + required this.currentHrvDataTime,
  1811 + required this.currentRealtimeStressDataTime,
  1812 + required this.currentSleepDataTime,
  1813 + required this.nextHrvDataTime,
  1814 + required this.nextRealtimeStressDataTime,
  1815 + required this.nextSleepDataTime,
  1816 + });
  1817 +
  1818 + final HealthRawStressCalculationResult result;
  1819 + final bool hasExistingHrvDataTime;
  1820 + final bool hasExistingRealtimeStressDataTime;
  1821 + final bool hasExistingSleepDataTime;
  1822 + final int? currentHrvDataTime;
  1823 + final int? currentRealtimeStressDataTime;
  1824 + final int? currentSleepDataTime;
  1825 + final int? nextHrvDataTime;
  1826 + final int? nextRealtimeStressDataTime;
  1827 + final int? nextSleepDataTime;
  1828 +
  1829 + Map<String, Object?> get latestDataTimePayload {
  1830 + return <String, Object?>{
  1831 + 'has_existing_hrv_data_time': hasExistingHrvDataTime,
  1832 + 'has_existing_realtime_stress_data_time':
  1833 + hasExistingRealtimeStressDataTime,
  1834 + 'has_existing_sleep_data_time': hasExistingSleepDataTime,
  1835 + 'current_hrv_data_time': currentHrvDataTime,
  1836 + 'current_realtime_stress_data_time': currentRealtimeStressDataTime,
  1837 + 'current_sleep_data_time': currentSleepDataTime,
  1838 + 'next_hrv_data_time': nextHrvDataTime,
  1839 + 'next_realtime_stress_data_time': nextRealtimeStressDataTime,
  1840 + 'next_sleep_data_time': nextSleepDataTime,
  1841 + 'candidate_hrv_count': result.hrvStressPoints.length,
  1842 + 'candidate_realtime_count': result.realtimeStressPoints.length,
  1843 + 'candidate_sleep_count': result.sleepResults.length,
  1844 + };
  1845 + }
  1846 +}
  1847 +
1678 double _integerDouble(num value) => value.toInt().toDouble(); 1848 double _integerDouble(num value) => value.toInt().toDouble();
1679 1849
1680 class HealthRawStressLocalStore { 1850 class HealthRawStressLocalStore {