Commit 1016dd218fd3f291b41cc482127758e974f9576c

Authored by 权海
1 parent 9c4410d2

feat(ui):活动消耗向上取整

@@ -431,6 +431,10 @@ class HealthRawDataCoreService { @@ -431,6 +431,10 @@ class HealthRawDataCoreService {
431 ); 431 );
432 } 432 }
433 433
  434 + Future<int?> queryEarliestHrRawEndTime() {
  435 + return _localStore.earliestRealtimeRawEndTime(_userId);
  436 + }
  437 +
434 Future<List<HealthRawDailyStressPoint>> queryDailyStressPoints({ 438 Future<List<HealthRawDailyStressPoint>> queryDailyStressPoints({
435 required int startDate, 439 required int startDate,
436 required int endDate, 440 required int endDate,
@@ -1555,6 +1559,10 @@ class HealthRawStressLocalStore { @@ -1555,6 +1559,10 @@ class HealthRawStressLocalStore {
1555 return _latestRawEndTime(userId, realtimeStressResultsTable); 1559 return _latestRawEndTime(userId, realtimeStressResultsTable);
1556 } 1560 }
1557 1561
  1562 + Future<int?> earliestRealtimeRawEndTime(int userId) {
  1563 + return _earliestRawEndTime(userId, realtimeStressResultsTable);
  1564 + }
  1565 +
1558 Future<int?> latestSleepResultTime(int userId) async { 1566 Future<int?> latestSleepResultTime(int userId) async {
1559 final db = await _database(userId); 1567 final db = await _database(userId);
1560 final rows = await db.query( 1568 final rows = await db.query(
@@ -1878,6 +1886,18 @@ CREATE TABLE IF NOT EXISTS $sleepResultsTable ( @@ -1878,6 +1886,18 @@ CREATE TABLE IF NOT EXISTS $sleepResultsTable (
1878 return rows.first['raw_end_time'] as int; 1886 return rows.first['raw_end_time'] as int;
1879 } 1887 }
1880 1888
  1889 + Future<int?> _earliestRawEndTime(int userId, String table) async {
  1890 + final db = await _database(userId);
  1891 + final rows = await db.query(
  1892 + table,
  1893 + columns: ['raw_end_time'],
  1894 + orderBy: 'raw_end_time ASC',
  1895 + limit: 1,
  1896 + );
  1897 + if (rows.isEmpty) return null;
  1898 + return rows.first['raw_end_time'] as int;
  1899 + }
  1900 +
1881 Future<void> _markUploaded({ 1901 Future<void> _markUploaded({
1882 required int userId, 1902 required int userId,
1883 required String table, 1903 required String table,
@@ -447,7 +447,7 @@ class LocalHealthDataConvert { @@ -447,7 +447,7 @@ class LocalHealthDataConvert {
447 .whereType<num>() 447 .whereType<num>()
448 .toList(), 448 .toList(),
449 )?.floor(), 449 )?.floor(),
450 - move: activitySummary.move.round(), 450 + move: activitySummary.move.ceil(),
451 exercise: activitySummary.exerciseSeconds.round(), 451 exercise: activitySummary.exerciseSeconds.round(),
452 stand: activitySummary.standHours.round(), 452 stand: activitySummary.standHours.round(),
453 steps: activitySummary.steps.round(), 453 steps: activitySummary.steps.round(),
@@ -492,7 +492,7 @@ class LocalHealthDataConvert { @@ -492,7 +492,7 @@ class LocalHealthDataConvert {
492 hrAvg: _averageOrNull( 492 hrAvg: _averageOrNull(
493 sleepHr.map((e) => e.value).whereType<num>().toList(), 493 sleepHr.map((e) => e.value).whereType<num>().toList(),
494 )?.floor(), 494 )?.floor(),
495 - move: activitySummary.move.round(), 495 + move: activitySummary.move.ceil(),
496 exercise: activitySummary.exerciseSeconds.round(), 496 exercise: activitySummary.exerciseSeconds.round(),
497 stand: activitySummary.standHours.round(), 497 stand: activitySummary.standHours.round(),
498 steps: activitySummary.steps.round(), 498 steps: activitySummary.steps.round(),
@@ -146,8 +146,13 @@ class LocalHealthDataSource implements HealthDataSource { @@ -146,8 +146,13 @@ class LocalHealthDataSource implements HealthDataSource {
146 @override 146 @override
147 Future<AppResult<V2HealthEverUploaded>> getHealthDataEverUploaded() async { 147 Future<AppResult<V2HealthEverUploaded>> getHealthDataEverUploaded() async {
148 try { 148 try {
149 - final hasHealthData = await coreService.hasHealthData();  
150 - return AppSuccess(V2HealthEverUploaded(flag: hasHealthData)); 149 + final startTime = await coreService.queryEarliestHrRawEndTime();
  150 + return AppSuccess(
  151 + V2HealthEverUploaded(
  152 + flag: true,
  153 + startTime: startTime,
  154 + ),
  155 + );
151 } catch (error) { 156 } catch (error) {
152 return AppFailure(AppUnknownError(error)); 157 return AppFailure(AppUnknownError(error));
153 } 158 }
  1 +import 'package:doublefeel_flutter/core/result/app_result.dart';
1 import 'package:doublefeel_flutter/core/services/health_raw_data_core_service.dart'; 2 import 'package:doublefeel_flutter/core/services/health_raw_data_core_service.dart';
2 import 'package:doublefeel_flutter/data/datasource/health/health_local_data_convert.dart'; 3 import 'package:doublefeel_flutter/data/datasource/health/health_local_data_convert.dart';
  4 +import 'package:doublefeel_flutter/data/datasource/health/health_local_datasource.dart';
3 import 'package:doublefeel_flutter/data/models/enums/app_enums.dart'; 5 import 'package:doublefeel_flutter/data/models/enums/app_enums.dart';
4 import 'package:doublefeel_flutter/pigeon/health_kit_api.g.dart'; 6 import 'package:doublefeel_flutter/pigeon/health_kit_api.g.dart';
5 import 'package:doublefeel_flutter/pigeon/health_kit_raw_data_api.g.dart'; 7 import 'package:doublefeel_flutter/pigeon/health_kit_raw_data_api.g.dart';
@@ -411,6 +413,38 @@ void main() { @@ -411,6 +413,38 @@ void main() {
411 expect(api.sleepUploadCallCount, 1); 413 expect(api.sleepUploadCallCount, 1);
412 expect(rows.sleepRows.single['uploaded'], 1); 414 expect(rows.sleepRows.single['uploaded'], 1);
413 }); 415 });
  416 +
  417 + test('local data source returns earliest local hr start time', () async {
  418 + const base = 1800000000;
  419 + final api = _FakeHealthKitRawDataHostApi();
  420 + final store = _MemoryHealthRawStressLocalStore();
  421 + final service = HealthRawDataCoreService(
  422 + healthApi: _FakeHealthKitHostApi(),
  423 + rawDataApi: api,
  424 + localStore: store,
  425 + userIdProvider: () => 42,
  426 + uploadResultsAfterCalculation: false,
  427 + );
  428 + await store.upsertResult(
  429 + HealthRawStressCalculationResult(
  430 + userId: 42,
  431 + hrvStressPoints: const <HealthRawHrvStressPoint>[],
  432 + realtimeStressPoints: [
  433 + _realtimeStressPoint(base + 200),
  434 + _realtimeStressPoint(base + 100),
  435 + ],
  436 + dailyStressPoints: const <HealthRawDailyStressPoint>[],
  437 + ),
  438 + );
  439 +
  440 + final result = await LocalHealthDataSource(coreService: service)
  441 + .getHealthDataEverUploaded();
  442 +
  443 + expect(result, isA<AppSuccess>());
  444 + final data = (result as AppSuccess).data;
  445 + expect(data.flag, isTrue);
  446 + expect(data.startTime, base + 100);
  447 + });
414 } 448 }
415 449
416 HealthKitRawDataPoint _point( 450 HealthKitRawDataPoint _point(
@@ -427,6 +461,17 @@ HealthKitRawDataPoint _point( @@ -427,6 +461,17 @@ HealthKitRawDataPoint _point(
427 ); 461 );
428 } 462 }
429 463
  464 +HealthRawRealtimeStressPoint _realtimeStressPoint(int time) {
  465 + return HealthRawRealtimeStressPoint(
  466 + userId: 42,
  467 + rawEndTime: time,
  468 + rawHr: 70,
  469 + result: 30,
  470 + sourceStartTime: time,
  471 + sourceEndTime: time,
  472 + );
  473 +}
  474 +
430 class _FakeHealthKitRawDataHostApi extends HealthKitRawDataHostApi { 475 class _FakeHealthKitRawDataHostApi extends HealthKitRawDataHostApi {
431 final Map<int, List<HealthKitRawDataPoint>> _pointsByType = {}; 476 final Map<int, List<HealthKitRawDataPoint>> _pointsByType = {};
432 final List<HealthKitRawWorkoutDataPoint> _workoutPoints = []; 477 final List<HealthKitRawWorkoutDataPoint> _workoutPoints = [];
@@ -436,6 +481,7 @@ class _FakeHealthKitRawDataHostApi extends HealthKitRawDataHostApi { @@ -436,6 +481,7 @@ class _FakeHealthKitRawDataHostApi extends HealthKitRawDataHostApi {
436 var workoutCallCount = 0; 481 var workoutCallCount = 0;
437 var sleepUploadCallCount = 0; 482 var sleepUploadCallCount = 0;
438 var sleepUploadUntil = 0; 483 var sleepUploadUntil = 0;
  484 + var hasData = true;
439 485
440 void setPoints(int dataType, List<HealthKitRawDataPoint> points) { 486 void setPoints(int dataType, List<HealthKitRawDataPoint> points) {
441 _pointsByType[dataType] = points; 487 _pointsByType[dataType] = points;
@@ -458,6 +504,9 @@ class _FakeHealthKitRawDataHostApi extends HealthKitRawDataHostApi { @@ -458,6 +504,9 @@ class _FakeHealthKitRawDataHostApi extends HealthKitRawDataHostApi {
458 } 504 }
459 505
460 @override 506 @override
  507 + Future<bool> hasHealthData() async => hasData;
  508 +
  509 + @override
461 Future<List<HealthKitRawDataPoint>> getHealthKitRawData( 510 Future<List<HealthKitRawDataPoint>> getHealthKitRawData(
462 int dataType, 511 int dataType,
463 int startTime, 512 int startTime,
@@ -777,6 +826,13 @@ class _MemoryHealthRawStressLocalStore extends HealthRawStressLocalStore { @@ -777,6 +826,13 @@ class _MemoryHealthRawStressLocalStore extends HealthRawStressLocalStore {
777 } 826 }
778 827
779 @override 828 @override
  829 + Future<int?> earliestRealtimeRawEndTime(int userId) async {
  830 + final points = _realtime[userId];
  831 + if (points == null || points.isEmpty) return null;
  832 + return points.first.rawEndTime;
  833 + }
  834 +
  835 + @override
780 Future<int?> latestSleepResultTime(int userId) async { 836 Future<int?> latestSleepResultTime(int userId) async {
781 final results = _sleep[userId]; 837 final results = _sleep[userId];
782 if (results == null || results.isEmpty) return null; 838 if (results == null || results.isEmpty) return null;