Commit 0df5140e03bbb3adc83e42410be70ed96168890a

Authored by 权海
1 parent 1429e580

feat(ui):实时压力推送剔除is_workout

@@ -410,10 +410,7 @@ class HealthRawDataCoreService { @@ -410,10 +410,7 @@ class HealthRawDataCoreService {
410 : result.realtimeStressPoints.reduce( 410 : result.realtimeStressPoints.reduce(
411 (a, b) => a.rawEndTime >= b.rawEndTime ? a : b, 411 (a, b) => a.rawEndTime >= b.rawEndTime ? a : b,
412 ); 412 );
413 - final shouldSkipRealtimeStressNotification =  
414 - latestRealtimeStressPoint?.isWorkout ?? false;  
415 - final realtimeWindow = latestRealtimeStressPoint == null ||  
416 - shouldSkipRealtimeStressNotification 413 + final realtimeWindow = latestRealtimeStressPoint == null
417 ? const <HealthRawRealtimeStressPoint>[] 414 ? const <HealthRawRealtimeStressPoint>[]
418 : await _localStore.queryRealtimeStressPoints( 415 : await _localStore.queryRealtimeStressPoints(
419 userId: result.userId, 416 userId: result.userId,
@@ -108,10 +108,16 @@ class HealthRawLocalNotificationBuilder { @@ -108,10 +108,16 @@ class HealthRawLocalNotificationBuilder {
108 List<HealthRawRealtimeStressPoint> realtimeWindow, 108 List<HealthRawRealtimeStressPoint> realtimeWindow,
109 HealthRawLocalNotificationRecord? record, 109 HealthRawLocalNotificationRecord? record,
110 ) { 110 ) {
111 - final valid = realtimeWindow  
112 - .where((e) => e.result >= 1 && e.result <= 100)  
113 - .toList() 111 + final window = [...realtimeWindow]
114 ..sort((a, b) => a.rawEndTime.compareTo(b.rawEndTime)); 112 ..sort((a, b) => a.rawEndTime.compareTo(b.rawEndTime));
  113 + final lastWorkoutIndex = window.lastIndexWhere(
  114 + (point) => point.isWorkout || point.isWorkoutRecovery,
  115 + );
  116 + final stressWindow = lastWorkoutIndex < 0
  117 + ? window
  118 + : window.skip(lastWorkoutIndex + 1).toList();
  119 + final valid =
  120 + stressWindow.where((e) => e.result >= 1 && e.result <= 100).toList();
115 if (valid.length < _realtimeMinPointCount) return null; 121 if (valid.length < _realtimeMinPointCount) return null;
116 122
117 final latest = valid.last; 123 final latest = valid.last;
@@ -80,7 +80,6 @@ void main() { @@ -80,7 +80,6 @@ void main() {
80 ); 80 );
81 81
82 api.calls.clear(); 82 api.calls.clear();
83 - store.realtimeNotificationWindowQueryCount = 0;  
84 final second = await service.startCoreCaculate( 83 final second = await service.startCoreCaculate(
85 endTime: base + 800, 84 endTime: base + 800,
86 readChunkDays: 1, 85 readChunkDays: 1,
@@ -91,7 +90,6 @@ void main() { @@ -91,7 +90,6 @@ void main() {
91 second.realtimeStressPoints.map((e) => e.rawEndTime), 90 second.realtimeStressPoints.map((e) => e.rawEndTime),
92 [base + 720], 91 [base + 720],
93 ); 92 );
94 - expect(store.realtimeNotificationWindowQueryCount, 0);  
95 expect( 93 expect(
96 api.firstCallStart(HealthDataUploadType.hrv.type), 94 api.firstCallStart(HealthDataUploadType.hrv.type),
97 lessThanOrEqualTo(base + 120), 95 lessThanOrEqualTo(base + 120),
@@ -871,7 +869,6 @@ class _MemoryHealthRawStressLocalStore extends HealthRawStressLocalStore { @@ -871,7 +869,6 @@ class _MemoryHealthRawStressLocalStore extends HealthRawStressLocalStore {
871 final Map<int, List<HealthRawDailyStressPoint>> _daily = {}; 869 final Map<int, List<HealthRawDailyStressPoint>> _daily = {};
872 final Map<int, List<HealthRawSleepResult>> _sleep = {}; 870 final Map<int, List<HealthRawSleepResult>> _sleep = {};
873 final List<String> operationLog; 871 final List<String> operationLog;
874 - var realtimeNotificationWindowQueryCount = 0;  
875 872
876 void insertDailyStress(HealthRawDailyStressPoint point) { 873 void insertDailyStress(HealthRawDailyStressPoint point) {
877 final byDate = <int, HealthRawDailyStressPoint>{ 874 final byDate = <int, HealthRawDailyStressPoint>{
@@ -1042,9 +1039,6 @@ class _MemoryHealthRawStressLocalStore extends HealthRawStressLocalStore { @@ -1042,9 +1039,6 @@ class _MemoryHealthRawStressLocalStore extends HealthRawStressLocalStore {
1042 required int endTime, 1039 required int endTime,
1043 }) async { 1040 }) async {
1044 operationLog.add('queryRealtimeStressPoints'); 1041 operationLog.add('queryRealtimeStressPoints');
1045 - if (endTime - startTime <= Duration.secondsPerHour) {  
1046 - realtimeNotificationWindowQueryCount += 1;  
1047 - }  
1048 return (_realtime[userId] ?? <HealthRawRealtimeStressPoint>[]) 1042 return (_realtime[userId] ?? <HealthRawRealtimeStressPoint>[])
1049 .where((e) => e.rawEndTime >= startTime && e.rawEndTime <= endTime) 1043 .where((e) => e.rawEndTime >= startTime && e.rawEndTime <= endTime)
1050 .toList(); 1044 .toList();
@@ -123,6 +123,50 @@ void main() { @@ -123,6 +123,50 @@ void main() {
123 123
124 expect(notifications, isEmpty); 124 expect(notifications, isEmpty);
125 }); 125 });
  126 +
  127 + test('resets realtime stress window after workout point', () {
  128 + final base = _seconds(DateTime(2026, 1, 1, 9));
  129 + final notifications = builder.build(
  130 + result: HealthRawStressCalculationResult(
  131 + userId: 1,
  132 + hrvStressPoints: const [],
  133 + realtimeStressPoints: [_realtimePoint(base + 12 * 300, 70)],
  134 + dailyStressPoints: const [],
  135 + ),
  136 + hasExistingHrv: false,
  137 + realtimeWindow: [
  138 + for (var i = 0; i < 3; i++) _realtimePoint(base + i * 300, 70),
  139 + _realtimePoint(base + 3 * 300, 70, isWorkout: true),
  140 + for (var i = 4; i < 13; i++) _realtimePoint(base + i * 300, 70),
  141 + ],
  142 + record: const HealthRawLocalNotificationRecord(),
  143 + );
  144 +
  145 + expect(notifications, isEmpty);
  146 + });
  147 +
  148 + test('builds realtime stress notification after workout reset window refills',
  149 + () {
  150 + final base = _seconds(DateTime(2026, 1, 1, 9));
  151 + final notifications = builder.build(
  152 + result: HealthRawStressCalculationResult(
  153 + userId: 1,
  154 + hrvStressPoints: const [],
  155 + realtimeStressPoints: [_realtimePoint(base + 13 * 300, 70)],
  156 + dailyStressPoints: const [],
  157 + ),
  158 + hasExistingHrv: false,
  159 + realtimeWindow: [
  160 + for (var i = 0; i < 3; i++) _realtimePoint(base + i * 300, 30),
  161 + _realtimePoint(base + 3 * 300, 30, isWorkoutRecovery: true),
  162 + for (var i = 4; i < 14; i++) _realtimePoint(base + i * 300, 70),
  163 + ],
  164 + record: const HealthRawLocalNotificationRecord(),
  165 + );
  166 +
  167 + expect(notifications, hasLength(1));
  168 + expect(notifications.single.title, '注意压力 · 09:05-10:05');
  169 + });
126 } 170 }
127 171
128 int _seconds(DateTime time) => time.millisecondsSinceEpoch ~/ 1000; 172 int _seconds(DateTime time) => time.millisecondsSinceEpoch ~/ 1000;
@@ -147,6 +191,8 @@ HealthRawRealtimeStressPoint _realtimePoint( @@ -147,6 +191,8 @@ HealthRawRealtimeStressPoint _realtimePoint(
147 int rawEndTime, 191 int rawEndTime,
148 double result, { 192 double result, {
149 bool isSleepLikely = false, 193 bool isSleepLikely = false,
  194 + bool isWorkout = false,
  195 + bool isWorkoutRecovery = false,
150 }) { 196 }) {
151 return HealthRawRealtimeStressPoint( 197 return HealthRawRealtimeStressPoint(
152 userId: 1, 198 userId: 1,
@@ -157,8 +203,8 @@ HealthRawRealtimeStressPoint _realtimePoint( @@ -157,8 +203,8 @@ HealthRawRealtimeStressPoint _realtimePoint(
157 sourceEndTime: rawEndTime, 203 sourceEndTime: rawEndTime,
158 flags: HealthRawPointFlags( 204 flags: HealthRawPointFlags(
159 isSleepLikely: isSleepLikely, 205 isSleepLikely: isSleepLikely,
160 - isWorkout: false,  
161 - isWorkoutRecovery: false, 206 + isWorkout: isWorkout,
  207 + isWorkoutRecovery: isWorkoutRecovery,
162 isSuspectedActivity: false, 208 isSuspectedActivity: false,
163 ), 209 ),
164 ); 210 );