Commit dc191e87c313b897b25bdbc16d272f45d276e993

Authored by 刘宏哲
1 parent 3cbf45f8

feat(app): bug fixed

@@ -178,6 +178,13 @@ class ApiHrvReportDataSource implements HrvReportDataSource { @@ -178,6 +178,13 @@ class ApiHrvReportDataSource implements HrvReportDataSource {
178 if (_stressLevelFromApiId(item.hrvLevel) case final level?) 178 if (_stressLevelFromApiId(item.hrvLevel) case final level?)
179 date: level, 179 date: level,
180 }; 180 };
  181 + final dailyStressScoresByDate = <DateTime, double>{
  182 + for (final item
  183 + in data.dailyDistributionList ?? const <DailyDistributionList>[])
  184 + if (_parseApiDate(item.date) case final date?)
  185 + if (item.comprehensiveStressScore case final score?)
  186 + date: score.toDouble(),
  187 + };
181 final distributionCounts = _distributionCounts(data.hrvDistributionList); 188 final distributionCounts = _distributionCounts(data.hrvDistributionList);
182 189
183 return YearlyHrvReport( 190 return YearlyHrvReport(
@@ -189,6 +196,7 @@ class ApiHrvReportDataSource implements HrvReportDataSource { @@ -189,6 +196,7 @@ class ApiHrvReportDataSource implements HrvReportDataSource {
189 trendsByDate, 196 trendsByDate,
190 levelsByDate, 197 levelsByDate,
191 preferDailyLevel: true, 198 preferDailyLevel: true,
  199 + dailyStressScoresByDate: dailyStressScoresByDate,
192 ), 200 ),
193 ], 201 ],
194 distributionCounts: 202 distributionCounts:
@@ -203,6 +211,7 @@ class ApiHrvReportDataSource implements HrvReportDataSource { @@ -203,6 +211,7 @@ class ApiHrvReportDataSource implements HrvReportDataSource {
203 Map<DateTime, HrvTrendList> trendsByDate, 211 Map<DateTime, HrvTrendList> trendsByDate,
204 Map<DateTime, HrvStressLevel> levelsByDate, { 212 Map<DateTime, HrvStressLevel> levelsByDate, {
205 bool preferDailyLevel = false, 213 bool preferDailyLevel = false,
  214 + Map<DateTime, double> dailyStressScoresByDate = const {},
206 }) { 215 }) {
207 final trend = trendsByDate[date]; 216 final trend = trendsByDate[date];
208 final trendLevel = _stressLevelFromApiId(trend?.state); 217 final trendLevel = _stressLevelFromApiId(trend?.state);
@@ -215,6 +224,7 @@ class ApiHrvReportDataSource implements HrvReportDataSource { @@ -215,6 +224,7 @@ class ApiHrvReportDataSource implements HrvReportDataSource {
215 averageHeartRate: trend?.hrAverage?.round(), 224 averageHeartRate: trend?.hrAverage?.round(),
216 lastRestingHrValue: trend?.lastRestingHrValue, 225 lastRestingHrValue: trend?.lastRestingHrValue,
217 comprehensiveStressScore: trend?.comprehensiveStressScore?.toDouble(), 226 comprehensiveStressScore: trend?.comprehensiveStressScore?.toDouble(),
  227 + dailyComprehensiveStressScore: dailyStressScoresByDate[date],
218 trendLevel: trendLevel, 228 trendLevel: trendLevel,
219 level: level, 229 level: level,
220 ); 230 );
@@ -418,6 +428,16 @@ class MockHrvReportDataSource implements HrvReportDataSource { @@ -418,6 +428,16 @@ class MockHrvReportDataSource implements HrvReportDataSource {
418 if (year == 2025) return YearlyHrvReport.empty(year); 428 if (year == 2025) return YearlyHrvReport.empty(year);
419 429
420 const monthlyValues = [58.0, 82.0, 64.0, 74.0, 31.0, 45.0, 31.0]; 430 const monthlyValues = [58.0, 82.0, 64.0, 74.0, 31.0, 45.0, 31.0];
  431 + const monthlyStressScores = [42.0, 29.0, 48.0, 33.0, 72.0, 86.0, 55.0];
  432 + const monthlyTrendLevels = [
  433 + HrvStressLevel.normal,
  434 + HrvStressLevel.normal,
  435 + HrvStressLevel.normal,
  436 + HrvStressLevel.normal,
  437 + HrvStressLevel.attention,
  438 + HrvStressLevel.overload,
  439 + HrvStressLevel.normal,
  440 + ];
421 final days = <HrvDayReport>[]; 441 final days = <HrvDayReport>[];
422 var dataIndex = 0; 442 var dataIndex = 0;
423 final levels = <HrvStressLevel>[ 443 final levels = <HrvStressLevel>[
@@ -437,10 +457,26 @@ class MockHrvReportDataSource implements HrvReportDataSource { @@ -437,10 +457,26 @@ class MockHrvReportDataSource implements HrvReportDataSource {
437 averageHrv: hasData ? monthlyValues[month - 1] : null, 457 averageHrv: hasData ? monthlyValues[month - 1] : null,
438 averageHeartRate: hasData ? 67 : null, 458 averageHeartRate: hasData ? 67 : null,
439 level: level, 459 level: level,
  460 + comprehensiveStressScore:
  461 + day == 1 && month <= monthlyStressScores.length
  462 + ? monthlyStressScores[month - 1]
  463 + : null,
  464 + dailyComprehensiveStressScore:
  465 + hasData ? _mockDailyStressScore(level!) : null,
  466 + trendLevel: day == 1 && month <= monthlyTrendLevels.length
  467 + ? monthlyTrendLevels[month - 1]
  468 + : null,
440 ), 469 ),
441 ); 470 );
442 } 471 }
443 } 472 }
444 return YearlyHrvReport(year: year, days: days, validDayCount: 133); 473 return YearlyHrvReport(year: year, days: days, validDayCount: 133);
445 } 474 }
  475 +
  476 + double _mockDailyStressScore(HrvStressLevel level) => switch (level) {
  477 + HrvStressLevel.excellent => 10,
  478 + HrvStressLevel.normal => 40,
  479 + HrvStressLevel.attention => 70,
  480 + HrvStressLevel.overload => 90,
  481 + };
446 } 482 }
@@ -38,6 +38,7 @@ class HrvDayReport { @@ -38,6 +38,7 @@ class HrvDayReport {
38 this.averageHeartRate, 38 this.averageHeartRate,
39 this.lastRestingHrValue, 39 this.lastRestingHrValue,
40 this.comprehensiveStressScore, 40 this.comprehensiveStressScore,
  41 + this.dailyComprehensiveStressScore,
41 this.trendLevel, 42 this.trendLevel,
42 this.level, 43 this.level,
43 }); 44 });
@@ -47,6 +48,7 @@ class HrvDayReport { @@ -47,6 +48,7 @@ class HrvDayReport {
47 final int? averageHeartRate; 48 final int? averageHeartRate;
48 final int? lastRestingHrValue; 49 final int? lastRestingHrValue;
49 final double? comprehensiveStressScore; 50 final double? comprehensiveStressScore;
  51 + final double? dailyComprehensiveStressScore;
50 final HrvStressLevel? trendLevel; 52 final HrvStressLevel? trendLevel;
51 final HrvStressLevel? level; 53 final HrvStressLevel? level;
52 54
@@ -592,51 +592,40 @@ class _DailyDistributionCard extends StatelessWidget { @@ -592,51 +592,40 @@ class _DailyDistributionCard extends StatelessWidget {
592 592
593 ({List<HrvDayReport> mostStressed, List<HrvDayReport> mostRelaxed}) 593 ({List<HrvDayReport> mostStressed, List<HrvDayReport> mostRelaxed})
594 _dailyStressExtremeDays(YearlyHrvReport report) { 594 _dailyStressExtremeDays(YearlyHrvReport report) {
595 - final days = report.days.where((day) => day.level != null).toList()  
596 - ..sort(_compareByStressDescending);  
597 -  
598 - final mostStressed = <HrvDayReport>[];  
599 - final mostRelaxed = <HrvDayReport>[];  
600 - if (days.isEmpty) {  
601 - return (mostStressed: mostStressed, mostRelaxed: mostRelaxed);  
602 - }  
603 -  
604 - if (days.length == 1) {  
605 - final day = days.single;  
606 - if (_isStressedLevel(day.level!)) {  
607 - mostStressed.add(day);  
608 - } else {  
609 - mostRelaxed.add(day);  
610 - }  
611 - return (mostStressed: mostStressed, mostRelaxed: mostRelaxed);  
612 - }  
613 -  
614 - mostStressed.add(days.first);  
615 - mostRelaxed.add(days.last);  
616 -  
617 - if (days.length == 3) {  
618 - final middle = days[1];  
619 - if (_isStressedLevel(middle.level!)) {  
620 - mostStressed.add(middle);  
621 - } else {  
622 - mostRelaxed.add(middle);  
623 - }  
624 - } else if (days.length >= 4) {  
625 - mostStressed.add(days[1]);  
626 - mostRelaxed.add(days[days.length - 2]);  
627 - } 595 + final daysWithScore = report.days.where((day) {
  596 + return day.level != null && day.dailyComprehensiveStressScore != null;
  597 + });
  598 + final mostStressed = daysWithScore
  599 + .where((day) => _isStressedLevel(day.level!))
  600 + .toList()
  601 + ..sort(_compareByDailyStressDescending);
  602 + final mostRelaxed = daysWithScore
  603 + .where((day) => !_isStressedLevel(day.level!))
  604 + .toList()
  605 + ..sort(_compareByDailyStressAscending);
  606 +
  607 + return (
  608 + mostStressed: _takeTwoAndSortByDate(mostStressed),
  609 + mostRelaxed: _takeTwoAndSortByDate(mostRelaxed),
  610 + );
  611 + }
628 612
629 - mostStressed.sort(_compareByDate);  
630 - mostRelaxed.sort(_compareByDate);  
631 - return (mostStressed: mostStressed, mostRelaxed: mostRelaxed); 613 + int _compareByDailyStressDescending(HrvDayReport a, HrvDayReport b) {
  614 + final valueComparison = b.dailyComprehensiveStressScore!
  615 + .compareTo(a.dailyComprehensiveStressScore!);
  616 + return valueComparison == 0 ? a.date.compareTo(b.date) : valueComparison;
632 } 617 }
633 618
634 - int _compareByStressDescending(HrvDayReport a, HrvDayReport b) {  
635 - final valueComparison =  
636 - _stressRank(b.level!).compareTo(_stressRank(a.level!)); 619 + int _compareByDailyStressAscending(HrvDayReport a, HrvDayReport b) {
  620 + final valueComparison = a.dailyComprehensiveStressScore!
  621 + .compareTo(b.dailyComprehensiveStressScore!);
637 return valueComparison == 0 ? a.date.compareTo(b.date) : valueComparison; 622 return valueComparison == 0 ? a.date.compareTo(b.date) : valueComparison;
638 } 623 }
639 624
  625 + List<HrvDayReport> _takeTwoAndSortByDate(List<HrvDayReport> days) {
  626 + return days.take(2).toList()..sort(_compareByDate);
  627 + }
  628 +
640 int _compareByDate(HrvDayReport a, HrvDayReport b) { 629 int _compareByDate(HrvDayReport a, HrvDayReport b) {
641 return a.date.compareTo(b.date); 630 return a.date.compareTo(b.date);
642 } 631 }
@@ -646,14 +635,6 @@ class _DailyDistributionCard extends StatelessWidget { @@ -646,14 +635,6 @@ class _DailyDistributionCard extends StatelessWidget {
646 level == HrvStressLevel.overload; 635 level == HrvStressLevel.overload;
647 } 636 }
648 637
649 - int _stressRank(HrvStressLevel level) {  
650 - return switch (level) {  
651 - HrvStressLevel.excellent => 0,  
652 - HrvStressLevel.normal => 1,  
653 - HrvStressLevel.attention => 2,  
654 - HrvStressLevel.overload => 3,  
655 - };  
656 - }  
657 } 638 }
658 639
659 class _MonthDots extends StatelessWidget { 640 class _MonthDots extends StatelessWidget {
@@ -395,6 +395,7 @@ class LocalHealthDataConvert { @@ -395,6 +395,7 @@ class LocalHealthDataConvert {
395 DailyDistributionList( 395 DailyDistributionList(
396 date: dateKey(item.day), 396 date: dateKey(item.day),
397 hrvLevel: item.state, 397 hrvLevel: item.state,
  398 + comprehensiveStressScore: item.comprehensiveStressScore,
398 ), 399 ),
399 ] 400 ]
400 : null, 401 : null,
@@ -78,20 +78,26 @@ class DailyDistributionList { @@ -78,20 +78,26 @@ class DailyDistributionList {
78 DailyDistributionList({ 78 DailyDistributionList({
79 this.date, 79 this.date,
80 this.hrvLevel, 80 this.hrvLevel,
  81 + this.comprehensiveStressScore,
81 }); 82 });
82 83
83 DailyDistributionList.fromJson(dynamic json) { 84 DailyDistributionList.fromJson(dynamic json) {
84 date = json['date'] ?? json['time_key']; 85 date = json['date'] ?? json['time_key'];
85 hrvLevel = json['hrv_level'] ?? json['stress_state'] ?? json['stress_id']; 86 hrvLevel = json['hrv_level'] ?? json['stress_state'] ?? json['stress_id'];
  87 + comprehensiveStressScore = json['comprehensive_stress_score'];
86 } 88 }
87 89
88 Object? date; 90 Object? date;
89 Object? hrvLevel; 91 Object? hrvLevel;
  92 + num? comprehensiveStressScore;
90 93
91 Map<String, dynamic> toJson() { 94 Map<String, dynamic> toJson() {
92 final map = <String, dynamic>{}; 95 final map = <String, dynamic>{};
93 map['date'] = date; 96 map['date'] = date;
94 map['hrv_level'] = hrvLevel; 97 map['hrv_level'] = hrvLevel;
  98 + if (comprehensiveStressScore != null) {
  99 + map['comprehensive_stress_score'] = comprehensiveStressScore;
  100 + }
95 return map; 101 return map;
96 } 102 }
97 } 103 }
@@ -724,6 +724,10 @@ void main() { @@ -724,6 +724,10 @@ void main() {
724 expect(statistics.dailyDistributionList, hasLength(1)); 724 expect(statistics.dailyDistributionList, hasLength(1));
725 expect(statistics.dailyDistributionList?.single.date, 20260512); 725 expect(statistics.dailyDistributionList?.single.date, 20260512);
726 expect( 726 expect(
  727 + statistics.dailyDistributionList?.single.comprehensiveStressScore,
  728 + 77,
  729 + );
  730 + expect(
727 statistics.dailyDistributionList?.map((e) => e.date), 731 statistics.dailyDistributionList?.map((e) => e.date),
728 isNot(contains(20260101)), 732 isNot(contains(20260101)),
729 ); 733 );