Showing
9 changed files
with
1766 additions
and
522 deletions
| @@ -15,6 +15,7 @@ import '../../../../core/services/raw_data_service/health_raw_models.dart'; | @@ -15,6 +15,7 @@ import '../../../../core/services/raw_data_service/health_raw_models.dart'; | ||
| 15 | import '../../../../core/services/raw_data_service/platform_ios/apple_health_raw_local_notification.dart'; | 15 | import '../../../../core/services/raw_data_service/platform_ios/apple_health_raw_local_notification.dart'; |
| 16 | import '../../../../core/services/raw_data_service/platform_ohos/ohos_harmony_health_raw_data_sync_service_factory.dart'; | 16 | import '../../../../core/services/raw_data_service/platform_ohos/ohos_harmony_health_raw_data_sync_service_factory.dart'; |
| 17 | import '../../../../core/services/raw_data_service/platform_ohos/ohos_health_raw_data_sync_service.dart'; | 17 | import '../../../../core/services/raw_data_service/platform_ohos/ohos_health_raw_data_sync_service.dart'; |
| 18 | +import '../../../../core/services/raw_data_service/platform_ohos/ohos_sqlite_write_benchmark.dart'; | ||
| 18 | import '../../../../data/local/user_preferences_storage.dart'; | 19 | import '../../../../data/local/user_preferences_storage.dart'; |
| 19 | import '../../../../l10n/l10n_extensions.dart'; | 20 | import '../../../../l10n/l10n_extensions.dart'; |
| 20 | import '../../../../core/platform/pigeon_api_facade.dart'; | 21 | import '../../../../core/platform/pigeon_api_facade.dart'; |
| @@ -38,6 +39,7 @@ enum DeveloperOptionsAction { | @@ -38,6 +39,7 @@ enum DeveloperOptionsAction { | ||
| 38 | shareHealthPipelineRecord, | 39 | shareHealthPipelineRecord, |
| 39 | sendTestHrvLocalNotification, | 40 | sendTestHrvLocalNotification, |
| 40 | pullOhosRawData, | 41 | pullOhosRawData, |
| 42 | + runOhosSqliteWriteBenchmark, | ||
| 41 | clearHealthRawDataDatabaseAndUploadLog, | 43 | clearHealthRawDataDatabaseAndUploadLog, |
| 42 | globalEnv, | 44 | globalEnv, |
| 43 | } | 45 | } |
| @@ -95,6 +97,10 @@ class DeveloperOptionsController extends GetxController { | @@ -95,6 +97,10 @@ class DeveloperOptionsController extends GetxController { | ||
| 95 | action: DeveloperOptionsAction.pullOhosRawData, | 97 | action: DeveloperOptionsAction.pullOhosRawData, |
| 96 | ), | 98 | ), |
| 97 | DeveloperOptionsItem( | 99 | DeveloperOptionsItem( |
| 100 | + title: '测试 OHOS SQLite 写入性能', | ||
| 101 | + action: DeveloperOptionsAction.runOhosSqliteWriteBenchmark, | ||
| 102 | + ), | ||
| 103 | + DeveloperOptionsItem( | ||
| 98 | title: '清除本地数据库、上传日志', | 104 | title: '清除本地数据库、上传日志', |
| 99 | action: DeveloperOptionsAction.clearHealthRawDataDatabaseAndUploadLog, | 105 | action: DeveloperOptionsAction.clearHealthRawDataDatabaseAndUploadLog, |
| 100 | ), | 106 | ), |
| @@ -122,6 +128,10 @@ class DeveloperOptionsController extends GetxController { | @@ -122,6 +128,10 @@ class DeveloperOptionsController extends GetxController { | ||
| 122 | action: DeveloperOptionsAction.pullOhosRawData, | 128 | action: DeveloperOptionsAction.pullOhosRawData, |
| 123 | ), | 129 | ), |
| 124 | DeveloperOptionsItem( | 130 | DeveloperOptionsItem( |
| 131 | + title: '测试 OHOS SQLite 写入性能', | ||
| 132 | + action: DeveloperOptionsAction.runOhosSqliteWriteBenchmark, | ||
| 133 | + ), | ||
| 134 | + DeveloperOptionsItem( | ||
| 125 | title: '清除本地数据库、上传日志', | 135 | title: '清除本地数据库、上传日志', |
| 126 | action: DeveloperOptionsAction.clearHealthRawDataDatabaseAndUploadLog, | 136 | action: DeveloperOptionsAction.clearHealthRawDataDatabaseAndUploadLog, |
| 127 | ), | 137 | ), |
| @@ -152,6 +162,9 @@ class DeveloperOptionsController extends GetxController { | @@ -152,6 +162,9 @@ class DeveloperOptionsController extends GetxController { | ||
| 152 | case DeveloperOptionsAction.pullOhosRawData: | 162 | case DeveloperOptionsAction.pullOhosRawData: |
| 153 | await pullOhosRawData(); | 163 | await pullOhosRawData(); |
| 154 | break; | 164 | break; |
| 165 | + case DeveloperOptionsAction.runOhosSqliteWriteBenchmark: | ||
| 166 | + await runOhosSqliteWriteBenchmark(); | ||
| 167 | + break; | ||
| 155 | case DeveloperOptionsAction.clearHealthRawDataDatabaseAndUploadLog: | 168 | case DeveloperOptionsAction.clearHealthRawDataDatabaseAndUploadLog: |
| 156 | await clearHealthRawDataDatabaseAndUploadLog(); | 169 | await clearHealthRawDataDatabaseAndUploadLog(); |
| 157 | break; | 170 | break; |
| @@ -357,6 +370,20 @@ class DeveloperOptionsController extends GetxController { | @@ -357,6 +370,20 @@ class DeveloperOptionsController extends GetxController { | ||
| 357 | } | 370 | } |
| 358 | } | 371 | } |
| 359 | 372 | ||
| 373 | + Future<void> runOhosSqliteWriteBenchmark() async { | ||
| 374 | + try { | ||
| 375 | + late final String report; | ||
| 376 | + await LoadingService.instance.run(() async { | ||
| 377 | + report = await const OhosSqliteWriteBenchmark().run(); | ||
| 378 | + }); | ||
| 379 | + await _platformHostApi.shareText(report); | ||
| 380 | + Get.snackbar('OHOS SQLite 性能测试完成', report); | ||
| 381 | + } catch (error, stackTrace) { | ||
| 382 | + AppLogger.e('Run OHOS SQLite write benchmark failed', error, stackTrace); | ||
| 383 | + Get.snackbar('OHOS SQLite 性能测试失败', error.toString()); | ||
| 384 | + } | ||
| 385 | + } | ||
| 386 | + | ||
| 360 | Future<void> clearHealthRawDataDatabaseAndUploadLog() async { | 387 | Future<void> clearHealthRawDataDatabaseAndUploadLog() async { |
| 361 | try { | 388 | try { |
| 362 | await _healthRawDataCoreService.clearLocalDatabaseAndUploadLog(); | 389 | await _healthRawDataCoreService.clearLocalDatabaseAndUploadLog(); |
| @@ -22,15 +22,17 @@ import '../health_sleep_calculator.dart'; | @@ -22,15 +22,17 @@ import '../health_sleep_calculator.dart'; | ||
| 22 | import '../platform_ios/apple_health_raw_data_core_service.dart'; | 22 | import '../platform_ios/apple_health_raw_data_core_service.dart'; |
| 23 | import '../platform_ios/apple_health_raw_local_notification.dart'; | 23 | import '../platform_ios/apple_health_raw_local_notification.dart'; |
| 24 | import 'huawei_health_raw_stress_calculator.dart'; | 24 | import 'huawei_health_raw_stress_calculator.dart'; |
| 25 | +import 'ohos_health_raw_data_events.dart'; | ||
| 25 | import 'ohos_health_raw_data_sync_service.dart'; | 26 | import 'ohos_health_raw_data_sync_service.dart'; |
| 26 | import 'ohos_health_raw_result_upload_service.dart'; | 27 | import 'ohos_health_raw_result_upload_service.dart'; |
| 27 | 28 | ||
| 28 | class OHOSHealthRawDataCoreService { | 29 | class OHOSHealthRawDataCoreService { |
| 29 | - static const int defaultLookbackDays = 183; | 30 | + static const int defaultLookbackMonths = 2; |
| 30 | static const int defaultReadChunkDays = 7; | 31 | static const int defaultReadChunkDays = 7; |
| 31 | static const String _calculateLogMarker = '[OHOS_HEALTH_CALCULATE]'; | 32 | static const String _calculateLogMarker = '[OHOS_HEALTH_CALCULATE]'; |
| 32 | static const String _dailyStressLogMarker = '[OHOS_DAILY_STRESS_CALC]'; | 33 | static const String _dailyStressLogMarker = '[OHOS_DAILY_STRESS_CALC]'; |
| 33 | static const String _sleepCalcLogMarker = '[OHOS_SLEEP_CALC]'; | 34 | static const String _sleepCalcLogMarker = '[OHOS_SLEEP_CALC]'; |
| 35 | + static const String _profileLogMarker = '[OHOS_HEALTH_RAW_PROFILE]'; | ||
| 34 | 36 | ||
| 35 | OHOSHealthRawDataCoreService({ | 37 | OHOSHealthRawDataCoreService({ |
| 36 | HealthRawDataSource? rawDataSource, | 38 | HealthRawDataSource? rawDataSource, |
| @@ -225,24 +227,37 @@ class OHOSHealthRawDataCoreService { | @@ -225,24 +227,37 @@ class OHOSHealthRawDataCoreService { | ||
| 225 | required int readChunkDays, | 227 | required int readChunkDays, |
| 226 | required int? forceStartTime, | 228 | required int? forceStartTime, |
| 227 | }) async { | 229 | }) async { |
| 230 | + final totalStopwatch = Stopwatch()..start(); | ||
| 231 | + _profileLog( | ||
| 232 | + 'core_start forceStartTime=${forceStartTime ?? ''} ' | ||
| 233 | + 'endTime=${endTime ?? ''} readChunkDays=$readChunkDays', | ||
| 234 | + ); | ||
| 228 | if (readChunkDays <= 0) { | 235 | if (readChunkDays <= 0) { |
| 229 | throw ArgumentError.value(readChunkDays, 'readChunkDays'); | 236 | throw ArgumentError.value(readChunkDays, 'readChunkDays'); |
| 230 | } | 237 | } |
| 231 | 238 | ||
| 232 | final userId = _userId; | 239 | final userId = _userId; |
| 240 | + final ensureStopwatch = Stopwatch()..start(); | ||
| 233 | await _localStore.ensureReadable(userId); | 241 | await _localStore.ensureReadable(userId); |
| 242 | + _profileLog( | ||
| 243 | + 'core_ensureReadable_finish userId=$userId ' | ||
| 244 | + 'elapsedMs=${ensureStopwatch.elapsedMilliseconds}', | ||
| 245 | + ); | ||
| 234 | final effectiveEndTime = | 246 | final effectiveEndTime = |
| 235 | endTime ?? DateTime.now().millisecondsSinceEpoch ~/ 1000; | 247 | endTime ?? DateTime.now().millisecondsSinceEpoch ~/ 1000; |
| 236 | - final earliestStartTime = DateTime.now() | ||
| 237 | - .subtract(const Duration(days: defaultLookbackDays)) | ||
| 238 | - .millisecondsSinceEpoch ~/ | ||
| 239 | - 1000; | 248 | + final earliestStartTime = _twoMonthLookbackStart(effectiveEndTime); |
| 240 | final requestedStartTime = | 249 | final requestedStartTime = |
| 241 | math.max(forceStartTime ?? earliestStartTime, earliestStartTime); | 250 | math.max(forceStartTime ?? earliestStartTime, earliestStartTime); |
| 242 | if (effectiveEndTime < requestedStartTime) { | 251 | if (effectiveEndTime < requestedStartTime) { |
| 243 | throw ArgumentError.value(endTime, 'endTime'); | 252 | throw ArgumentError.value(endTime, 'endTime'); |
| 244 | } | 253 | } |
| 254 | + final authStopwatch = Stopwatch()..start(); | ||
| 245 | final hasAuthorization = await _hasHealthReadAuthorizationSafely(); | 255 | final hasAuthorization = await _hasHealthReadAuthorizationSafely(); |
| 256 | + _profileLog( | ||
| 257 | + 'core_authorization_finish userId=$userId ' | ||
| 258 | + 'hasAuthorization=$hasAuthorization ' | ||
| 259 | + 'elapsedMs=${authStopwatch.elapsedMilliseconds}', | ||
| 260 | + ); | ||
| 246 | final willSyncRawData = | 261 | final willSyncRawData = |
| 247 | hasAuthorization && _rawDataSource is OhosHealthRawDataSource; | 262 | hasAuthorization && _rawDataSource is OhosHealthRawDataSource; |
| 248 | final syncStartTime = willSyncRawData ? DateTime.now() : null; | 263 | final syncStartTime = willSyncRawData ? DateTime.now() : null; |
| @@ -260,23 +275,70 @@ class OHOSHealthRawDataCoreService { | @@ -260,23 +275,70 @@ class OHOSHealthRawDataCoreService { | ||
| 260 | 'endTime=$effectiveEndTime syncedRawStartTime=$syncedRawStartTime ' | 275 | 'endTime=$effectiveEndTime syncedRawStartTime=$syncedRawStartTime ' |
| 261 | 'syncResults=$syncResults', | 276 | 'syncResults=$syncResults', |
| 262 | ); | 277 | ); |
| 278 | + _profileLog( | ||
| 279 | + 'core_sync_finish userId=$userId willSyncRawData=$willSyncRawData ' | ||
| 280 | + 'resultCount=${syncResults.length} storedCount=' | ||
| 281 | + '${syncResults.fold<int>(0, (sum, result) => sum + result.storedCount)} ' | ||
| 282 | + 'elapsedMs=${syncElapsed.inMilliseconds}', | ||
| 283 | + ); | ||
| 263 | 284 | ||
| 264 | final calculationStartTime = DateTime.now(); | 285 | final calculationStartTime = DateTime.now(); |
| 265 | - final storedResult = await _calculateAndStoreSafely( | 286 | + _publishCalculationEvent( |
| 287 | + type: OhosHealthRawDataPipelineEventType.calculationStarted, | ||
| 288 | + flow: 'calculateAndStore', | ||
| 266 | userId: userId, | 289 | userId: userId, |
| 267 | - requestedStartTime: requestedStartTime, | ||
| 268 | - effectiveEndTime: effectiveEndTime, | ||
| 269 | - earliestStartTime: earliestStartTime, | ||
| 270 | - syncedRawStartTime: syncedRawStartTime, | ||
| 271 | - readChunkDays: readChunkDays, | 290 | + startTime: requestedStartTime, |
| 291 | + endTime: effectiveEndTime, | ||
| 272 | ); | 292 | ); |
| 293 | + late final _OhosStoredCalculationResult storedResult; | ||
| 294 | + try { | ||
| 295 | + storedResult = await _calculateAndStoreSafely( | ||
| 296 | + userId: userId, | ||
| 297 | + requestedStartTime: requestedStartTime, | ||
| 298 | + effectiveEndTime: effectiveEndTime, | ||
| 299 | + earliestStartTime: earliestStartTime, | ||
| 300 | + syncedRawStartTime: syncedRawStartTime, | ||
| 301 | + readChunkDays: readChunkDays, | ||
| 302 | + ); | ||
| 303 | + } catch (error) { | ||
| 304 | + final calculationElapsed = | ||
| 305 | + DateTime.now().difference(calculationStartTime); | ||
| 306 | + _publishCalculationEvent( | ||
| 307 | + type: OhosHealthRawDataPipelineEventType.calculationFailed, | ||
| 308 | + flow: 'calculateAndStore', | ||
| 309 | + userId: userId, | ||
| 310 | + startTime: requestedStartTime, | ||
| 311 | + endTime: effectiveEndTime, | ||
| 312 | + elapsedMs: calculationElapsed.inMilliseconds, | ||
| 313 | + error: error.toString(), | ||
| 314 | + ); | ||
| 315 | + rethrow; | ||
| 316 | + } | ||
| 273 | final calculationElapsed = DateTime.now().difference(calculationStartTime); | 317 | final calculationElapsed = DateTime.now().difference(calculationStartTime); |
| 318 | + _profileLog( | ||
| 319 | + 'core_calculateAndStore_finish userId=$userId ' | ||
| 320 | + 'hrv=${storedResult.result.hrvStressPoints.length} ' | ||
| 321 | + 'realtime=${storedResult.result.realtimeStressPoints.length} ' | ||
| 322 | + 'daily=${storedResult.result.dailyStressPoints.length} ' | ||
| 323 | + 'sleep=${storedResult.result.sleepResults.length} ' | ||
| 324 | + 'elapsedMs=${calculationElapsed.inMilliseconds}', | ||
| 325 | + ); | ||
| 326 | + final uploadScheduleStopwatch = Stopwatch()..start(); | ||
| 274 | _scheduleResultUpload(); | 327 | _scheduleResultUpload(); |
| 328 | + _profileLog( | ||
| 329 | + 'core_uploadSchedule_finish userId=$userId ' | ||
| 330 | + 'elapsedMs=${uploadScheduleStopwatch.elapsedMilliseconds}', | ||
| 331 | + ); | ||
| 332 | + final notificationStopwatch = Stopwatch()..start(); | ||
| 275 | await _sendLocalNotificationsAfterCalculationSafely( | 333 | await _sendLocalNotificationsAfterCalculationSafely( |
| 276 | result: storedResult.result, | 334 | result: storedResult.result, |
| 277 | hasExistingHrv: storedResult.hasExistingHrv, | 335 | hasExistingHrv: storedResult.hasExistingHrv, |
| 278 | hasExistingSleep: storedResult.hasExistingSleep, | 336 | hasExistingSleep: storedResult.hasExistingSleep, |
| 279 | ); | 337 | ); |
| 338 | + _profileLog( | ||
| 339 | + 'core_localNotification_finish userId=$userId ' | ||
| 340 | + 'elapsedMs=${notificationStopwatch.elapsedMilliseconds}', | ||
| 341 | + ); | ||
| 280 | if (_hasStoredRawData(syncResults) || | 342 | if (_hasStoredRawData(syncResults) || |
| 281 | _hasCalculatedResult(storedResult.result)) { | 343 | _hasCalculatedResult(storedResult.result)) { |
| 282 | _showDebugTimingToast( | 344 | _showDebugTimingToast( |
| @@ -284,6 +346,23 @@ class OHOSHealthRawDataCoreService { | @@ -284,6 +346,23 @@ class OHOSHealthRawDataCoreService { | ||
| 284 | calculationElapsed: calculationElapsed, | 346 | calculationElapsed: calculationElapsed, |
| 285 | ); | 347 | ); |
| 286 | } | 348 | } |
| 349 | + _profileLog( | ||
| 350 | + 'core_finish userId=$userId syncElapsedMs=${syncElapsed.inMilliseconds} ' | ||
| 351 | + 'calculationElapsedMs=${calculationElapsed.inMilliseconds} ' | ||
| 352 | + 'totalElapsedMs=${totalStopwatch.elapsedMilliseconds}', | ||
| 353 | + ); | ||
| 354 | + _publishCalculationEvent( | ||
| 355 | + type: OhosHealthRawDataPipelineEventType.calculationSucceeded, | ||
| 356 | + flow: 'calculateAndStore', | ||
| 357 | + userId: userId, | ||
| 358 | + startTime: requestedStartTime, | ||
| 359 | + endTime: effectiveEndTime, | ||
| 360 | + elapsedMs: calculationElapsed.inMilliseconds, | ||
| 361 | + hrvCount: storedResult.result.hrvStressPoints.length, | ||
| 362 | + realtimeCount: storedResult.result.realtimeStressPoints.length, | ||
| 363 | + dailyCount: storedResult.result.dailyStressPoints.length, | ||
| 364 | + sleepCount: storedResult.result.sleepResults.length, | ||
| 365 | + ); | ||
| 287 | return storedResult.result; | 366 | return storedResult.result; |
| 288 | } | 367 | } |
| 289 | 368 | ||
| @@ -295,9 +374,11 @@ class OHOSHealthRawDataCoreService { | @@ -295,9 +374,11 @@ class OHOSHealthRawDataCoreService { | ||
| 295 | required int? syncedRawStartTime, | 374 | required int? syncedRawStartTime, |
| 296 | required int readChunkDays, | 375 | required int readChunkDays, |
| 297 | }) async { | 376 | }) async { |
| 377 | + final totalStopwatch = Stopwatch()..start(); | ||
| 298 | var hasExistingHrv = false; | 378 | var hasExistingHrv = false; |
| 299 | var hasExistingSleep = false; | 379 | var hasExistingSleep = false; |
| 300 | try { | 380 | try { |
| 381 | + final contextStopwatch = Stopwatch()..start(); | ||
| 301 | final hrvContextStart = | 382 | final hrvContextStart = |
| 302 | await _localStore.latestHrvSourceStartTime(userId); | 383 | await _localStore.latestHrvSourceStartTime(userId); |
| 303 | final realtimeContextStart = | 384 | final realtimeContextStart = |
| @@ -317,6 +398,10 @@ class OHOSHealthRawDataCoreService { | @@ -317,6 +398,10 @@ class OHOSHealthRawDataCoreService { | ||
| 317 | final latestRawSleepDataTime = await _latestOhosRawDataTime( | 398 | final latestRawSleepDataTime = await _latestOhosRawDataTime( |
| 318 | OhosHealthRawDataType.sleepAnalysis, | 399 | OhosHealthRawDataType.sleepAnalysis, |
| 319 | ); | 400 | ); |
| 401 | + _profileLog( | ||
| 402 | + 'calculate_contextQuery_finish userId=$userId ' | ||
| 403 | + 'elapsedMs=${contextStopwatch.elapsedMilliseconds}', | ||
| 404 | + ); | ||
| 320 | hasExistingHrv = latestHrvRawEndTime != null; | 405 | hasExistingHrv = latestHrvRawEndTime != null; |
| 321 | hasExistingSleep = latestSleepResultTime != null; | 406 | hasExistingSleep = latestSleepResultTime != null; |
| 322 | 407 | ||
| @@ -400,34 +485,73 @@ class OHOSHealthRawDataCoreService { | @@ -400,34 +485,73 @@ class OHOSHealthRawDataCoreService { | ||
| 400 | 'realtimeRecomputeStartTime=$realtimeRecomputeStartTime', | 485 | 'realtimeRecomputeStartTime=$realtimeRecomputeStartTime', |
| 401 | ); | 486 | ); |
| 402 | 487 | ||
| 488 | + final hrvFetchStopwatch = Stopwatch()..start(); | ||
| 403 | final hrvPoints = await _fetchRawDataInChunks( | 489 | final hrvPoints = await _fetchRawDataInChunks( |
| 404 | HealthDataUploadType.hrv.type, | 490 | HealthDataUploadType.hrv.type, |
| 405 | hrvStartTime, | 491 | hrvStartTime, |
| 406 | effectiveEndTime, | 492 | effectiveEndTime, |
| 407 | readChunkDays: readChunkDays, | 493 | readChunkDays: readChunkDays, |
| 408 | ); | 494 | ); |
| 495 | + _profileLog( | ||
| 496 | + 'calculate_fetchRaw_finish userId=$userId ' | ||
| 497 | + 'name=hrv dataType=${HealthDataUploadType.hrv.type} ' | ||
| 498 | + 'startTime=$hrvStartTime endTime=$effectiveEndTime ' | ||
| 499 | + 'count=${hrvPoints.length} ' | ||
| 500 | + 'elapsedMs=${hrvFetchStopwatch.elapsedMilliseconds}', | ||
| 501 | + ); | ||
| 502 | + final heartRateFetchStopwatch = Stopwatch()..start(); | ||
| 409 | final heartRatePoints = await _fetchRawDataInChunks( | 503 | final heartRatePoints = await _fetchRawDataInChunks( |
| 410 | HealthDataUploadType.heartRate.type, | 504 | HealthDataUploadType.heartRate.type, |
| 411 | heartRateStartTime, | 505 | heartRateStartTime, |
| 412 | effectiveEndTime, | 506 | effectiveEndTime, |
| 413 | readChunkDays: readChunkDays, | 507 | readChunkDays: readChunkDays, |
| 414 | ); | 508 | ); |
| 509 | + _profileLog( | ||
| 510 | + 'calculate_fetchRaw_finish userId=$userId ' | ||
| 511 | + 'name=heartRate dataType=${HealthDataUploadType.heartRate.type} ' | ||
| 512 | + 'startTime=$heartRateStartTime endTime=$effectiveEndTime ' | ||
| 513 | + 'count=${heartRatePoints.length} ' | ||
| 514 | + 'elapsedMs=${heartRateFetchStopwatch.elapsedMilliseconds}', | ||
| 515 | + ); | ||
| 516 | + final restingHeartRateFetchStopwatch = Stopwatch()..start(); | ||
| 415 | final restingHeartRatePoints = await _fetchRawDataInChunks( | 517 | final restingHeartRatePoints = await _fetchRawDataInChunks( |
| 416 | HealthDataUploadType.restingHeartRate.type, | 518 | HealthDataUploadType.restingHeartRate.type, |
| 417 | heartRateStartTime, | 519 | heartRateStartTime, |
| 418 | effectiveEndTime, | 520 | effectiveEndTime, |
| 419 | readChunkDays: readChunkDays, | 521 | readChunkDays: readChunkDays, |
| 420 | ); | 522 | ); |
| 523 | + _profileLog( | ||
| 524 | + 'calculate_fetchRaw_finish userId=$userId ' | ||
| 525 | + 'name=restingHeartRate ' | ||
| 526 | + 'dataType=${HealthDataUploadType.restingHeartRate.type} ' | ||
| 527 | + 'startTime=$heartRateStartTime endTime=$effectiveEndTime ' | ||
| 528 | + 'count=${restingHeartRatePoints.length} ' | ||
| 529 | + 'elapsedMs=${restingHeartRateFetchStopwatch.elapsedMilliseconds}', | ||
| 530 | + ); | ||
| 531 | + final sleepFetchStopwatch = Stopwatch()..start(); | ||
| 421 | final sleepIntervals = await _fetchSleepIntervalsInChunks( | 532 | final sleepIntervals = await _fetchSleepIntervalsInChunks( |
| 422 | sleepStartTime, | 533 | sleepStartTime, |
| 423 | effectiveEndTime, | 534 | effectiveEndTime, |
| 424 | readChunkDays: readChunkDays, | 535 | readChunkDays: readChunkDays, |
| 425 | ); | 536 | ); |
| 537 | + _profileLog( | ||
| 538 | + 'calculate_fetchRaw_finish userId=$userId name=sleep ' | ||
| 539 | + 'startTime=$sleepStartTime endTime=$effectiveEndTime ' | ||
| 540 | + 'count=${sleepIntervals.length} ' | ||
| 541 | + 'elapsedMs=${sleepFetchStopwatch.elapsedMilliseconds}', | ||
| 542 | + ); | ||
| 543 | + final workoutFetchStopwatch = Stopwatch()..start(); | ||
| 426 | final workoutIntervals = await _fetchWorkoutIntervalsInChunks( | 544 | final workoutIntervals = await _fetchWorkoutIntervalsInChunks( |
| 427 | heartRateStartTime, | 545 | heartRateStartTime, |
| 428 | effectiveEndTime, | 546 | effectiveEndTime, |
| 429 | readChunkDays: readChunkDays, | 547 | readChunkDays: readChunkDays, |
| 430 | ); | 548 | ); |
| 549 | + _profileLog( | ||
| 550 | + 'calculate_fetchRaw_finish userId=$userId name=workout ' | ||
| 551 | + 'startTime=$heartRateStartTime endTime=$effectiveEndTime ' | ||
| 552 | + 'count=${workoutIntervals.length} ' | ||
| 553 | + 'elapsedMs=${workoutFetchStopwatch.elapsedMilliseconds}', | ||
| 554 | + ); | ||
| 431 | final latestRawHrvTime = _latestRawDataTime(hrvPoints); | 555 | final latestRawHrvTime = _latestRawDataTime(hrvPoints); |
| 432 | final latestRawHrTime = _latestRawDataTime(heartRatePoints); | 556 | final latestRawHrTime = _latestRawDataTime(heartRatePoints); |
| 433 | final latestRawSleepTime = _latestRawDataTime(sleepIntervals); | 557 | final latestRawSleepTime = _latestRawDataTime(sleepIntervals); |
| @@ -445,6 +569,7 @@ class OHOSHealthRawDataCoreService { | @@ -445,6 +569,7 @@ class OHOSHealthRawDataCoreService { | ||
| 445 | 'needSleep=${_needsNewResult(latestRawSleepTime, latestSleepResultTime)}', | 569 | 'needSleep=${_needsNewResult(latestRawSleepTime, latestSleepResultTime)}', |
| 446 | ); | 570 | ); |
| 447 | 571 | ||
| 572 | + final isolateStopwatch = Stopwatch()..start(); | ||
| 448 | final result = await Isolate.run( | 573 | final result = await Isolate.run( |
| 449 | () => HuaweiHealthRawStressCalculator(userId: userId).calculate( | 574 | () => HuaweiHealthRawStressCalculator(userId: userId).calculate( |
| 450 | hrvPoints: hrvPoints, | 575 | hrvPoints: hrvPoints, |
| @@ -457,12 +582,23 @@ class OHOSHealthRawDataCoreService { | @@ -457,12 +582,23 @@ class OHOSHealthRawDataCoreService { | ||
| 457 | ), | 582 | ), |
| 458 | debugName: 'OHOSHealthRawStressCalculator', | 583 | debugName: 'OHOSHealthRawStressCalculator', |
| 459 | ); | 584 | ); |
| 585 | + _profileLog( | ||
| 586 | + 'calculate_isolate_finish userId=$userId ' | ||
| 587 | + 'hrvInput=${hrvPoints.length} heartRateInput=${heartRatePoints.length} ' | ||
| 588 | + 'restingHeartRateInput=${restingHeartRatePoints.length} ' | ||
| 589 | + 'sleepInput=${sleepIntervals.length} workoutInput=${workoutIntervals.length} ' | ||
| 590 | + 'hrv=${result.hrvStressPoints.length} ' | ||
| 591 | + 'realtime=${result.realtimeStressPoints.length} ' | ||
| 592 | + 'daily=${result.dailyStressPoints.length} ' | ||
| 593 | + 'elapsedMs=${isolateStopwatch.elapsedMilliseconds}', | ||
| 594 | + ); | ||
| 460 | _logInfo( | 595 | _logInfo( |
| 461 | '$_calculateLogMarker result_counts userId=$userId ' | 596 | '$_calculateLogMarker result_counts userId=$userId ' |
| 462 | 'hrv=${result.hrvStressPoints.length} ' | 597 | 'hrv=${result.hrvStressPoints.length} ' |
| 463 | 'realtime=${result.realtimeStressPoints.length} ' | 598 | 'realtime=${result.realtimeStressPoints.length} ' |
| 464 | 'daily=${result.dailyStressPoints.length}', | 599 | 'daily=${result.dailyStressPoints.length}', |
| 465 | ); | 600 | ); |
| 601 | + final filterStopwatch = Stopwatch()..start(); | ||
| 466 | final newResult = result.copyWith( | 602 | final newResult = result.copyWith( |
| 467 | hrvStressPoints: _filterNewHrvStressPoints( | 603 | hrvStressPoints: _filterNewHrvStressPoints( |
| 468 | result.hrvStressPoints, | 604 | result.hrvStressPoints, |
| @@ -475,6 +611,14 @@ class OHOSHealthRawDataCoreService { | @@ -475,6 +611,14 @@ class OHOSHealthRawDataCoreService { | ||
| 475 | recomputeStartTime: realtimeRecomputeStartTime, | 611 | recomputeStartTime: realtimeRecomputeStartTime, |
| 476 | ), | 612 | ), |
| 477 | ); | 613 | ); |
| 614 | + _profileLog( | ||
| 615 | + 'calculate_filterNew_finish userId=$userId ' | ||
| 616 | + 'inputHrv=${result.hrvStressPoints.length} ' | ||
| 617 | + 'inputRealtime=${result.realtimeStressPoints.length} ' | ||
| 618 | + 'newHrv=${newResult.hrvStressPoints.length} ' | ||
| 619 | + 'newRealtime=${newResult.realtimeStressPoints.length} ' | ||
| 620 | + 'elapsedMs=${filterStopwatch.elapsedMilliseconds}', | ||
| 621 | + ); | ||
| 478 | _logInfo( | 622 | _logInfo( |
| 479 | '$_calculateLogMarker new_result_counts userId=$userId ' | 623 | '$_calculateLogMarker new_result_counts userId=$userId ' |
| 480 | 'latestHrvRawEndTime=$latestHrvRawEndTime ' | 624 | 'latestHrvRawEndTime=$latestHrvRawEndTime ' |
| @@ -482,27 +626,52 @@ class OHOSHealthRawDataCoreService { | @@ -482,27 +626,52 @@ class OHOSHealthRawDataCoreService { | ||
| 482 | 'hrv=${newResult.hrvStressPoints.length} ' | 626 | 'hrv=${newResult.hrvStressPoints.length} ' |
| 483 | 'realtime=${newResult.realtimeStressPoints.length}', | 627 | 'realtime=${newResult.realtimeStressPoints.length}', |
| 484 | ); | 628 | ); |
| 629 | + final resultStoreStopwatch = Stopwatch()..start(); | ||
| 485 | await _localStore.upsertResult(newResult); | 630 | await _localStore.upsertResult(newResult); |
| 631 | + _profileLog( | ||
| 632 | + 'calculate_storeResult_finish userId=$userId ' | ||
| 633 | + 'hrv=${newResult.hrvStressPoints.length} ' | ||
| 634 | + 'realtime=${newResult.realtimeStressPoints.length} ' | ||
| 635 | + 'elapsedMs=${resultStoreStopwatch.elapsedMilliseconds}', | ||
| 636 | + ); | ||
| 486 | _logInfo( | 637 | _logInfo( |
| 487 | '$_calculateLogMarker result_stored userId=$userId ' | 638 | '$_calculateLogMarker result_stored userId=$userId ' |
| 488 | 'hrv=${newResult.hrvStressPoints.length} ' | 639 | 'hrv=${newResult.hrvStressPoints.length} ' |
| 489 | 'realtime=${newResult.realtimeStressPoints.length}', | 640 | 'realtime=${newResult.realtimeStressPoints.length}', |
| 490 | ); | 641 | ); |
| 491 | 642 | ||
| 643 | + final dailyStopwatch = Stopwatch()..start(); | ||
| 492 | final dailyStressPoints = await _calculateAndStoreDailyStressPoints( | 644 | final dailyStressPoints = await _calculateAndStoreDailyStressPoints( |
| 493 | userId: userId, | 645 | userId: userId, |
| 494 | realtimePoints: newResult.realtimeStressPoints, | 646 | realtimePoints: newResult.realtimeStressPoints, |
| 495 | nowSeconds: effectiveEndTime, | 647 | nowSeconds: effectiveEndTime, |
| 496 | ); | 648 | ); |
| 649 | + _profileLog( | ||
| 650 | + 'calculate_daily_finish userId=$userId ' | ||
| 651 | + 'count=${dailyStressPoints.length} ' | ||
| 652 | + 'elapsedMs=${dailyStopwatch.elapsedMilliseconds}', | ||
| 653 | + ); | ||
| 654 | + final sleepStopwatch = Stopwatch()..start(); | ||
| 497 | final sleepResults = await _calculateAndStoreSleepResults( | 655 | final sleepResults = await _calculateAndStoreSleepResults( |
| 498 | userId: userId, | 656 | userId: userId, |
| 499 | sleepIntervals: sleepIntervals, | 657 | sleepIntervals: sleepIntervals, |
| 500 | latestSleepResultTime: latestSleepResultTime, | 658 | latestSleepResultTime: latestSleepResultTime, |
| 501 | ); | 659 | ); |
| 660 | + _profileLog( | ||
| 661 | + 'calculate_sleep_finish userId=$userId count=${sleepResults.length} ' | ||
| 662 | + 'elapsedMs=${sleepStopwatch.elapsedMilliseconds}', | ||
| 663 | + ); | ||
| 502 | _logInfo( | 664 | _logInfo( |
| 503 | 'calculate_daily_sleep_stored userId=$userId ' | 665 | 'calculate_daily_sleep_stored userId=$userId ' |
| 504 | 'daily=${dailyStressPoints.length} sleep=${sleepResults.length}', | 666 | 'daily=${dailyStressPoints.length} sleep=${sleepResults.length}', |
| 505 | ); | 667 | ); |
| 668 | + _profileLog( | ||
| 669 | + 'calculate_finish userId=$userId ' | ||
| 670 | + 'hrv=${newResult.hrvStressPoints.length} ' | ||
| 671 | + 'realtime=${newResult.realtimeStressPoints.length} ' | ||
| 672 | + 'daily=${dailyStressPoints.length} sleep=${sleepResults.length} ' | ||
| 673 | + 'elapsedMs=${totalStopwatch.elapsedMilliseconds}', | ||
| 674 | + ); | ||
| 506 | return _OhosStoredCalculationResult( | 675 | return _OhosStoredCalculationResult( |
| 507 | result: newResult.copyWith( | 676 | result: newResult.copyWith( |
| 508 | dailyStressPoints: dailyStressPoints, | 677 | dailyStressPoints: dailyStressPoints, |
| @@ -512,6 +681,10 @@ class OHOSHealthRawDataCoreService { | @@ -512,6 +681,10 @@ class OHOSHealthRawDataCoreService { | ||
| 512 | hasExistingSleep: hasExistingSleep, | 681 | hasExistingSleep: hasExistingSleep, |
| 513 | ); | 682 | ); |
| 514 | } catch (error, stackTrace) { | 683 | } catch (error, stackTrace) { |
| 684 | + _profileLog( | ||
| 685 | + 'calculate_failed userId=$userId ' | ||
| 686 | + 'elapsedMs=${totalStopwatch.elapsedMilliseconds} error=$error', | ||
| 687 | + ); | ||
| 515 | _logError( | 688 | _logError( |
| 516 | '$_calculateLogMarker calculate_failed userId=$userId', | 689 | '$_calculateLogMarker calculate_failed userId=$userId', |
| 517 | error, | 690 | error, |
| @@ -531,10 +704,26 @@ class OHOSHealthRawDataCoreService { | @@ -531,10 +704,26 @@ class OHOSHealthRawDataCoreService { | ||
| 531 | var cursor = startTime; | 704 | var cursor = startTime; |
| 532 | while (cursor <= endTime) { | 705 | while (cursor <= endTime) { |
| 533 | final chunkEnd = math.min(cursor + chunkSeconds - 1, endTime); | 706 | final chunkEnd = math.min(cursor + chunkSeconds - 1, endTime); |
| 534 | - final points = await _rawDataSource.getRawData( | ||
| 535 | - dataType, | ||
| 536 | - cursor, | ||
| 537 | - chunkEnd, | 707 | + final chunkStopwatch = Stopwatch()..start(); |
| 708 | + final List<HealthKitRawDataPoint> points; | ||
| 709 | + try { | ||
| 710 | + points = await _rawDataSource.getRawData( | ||
| 711 | + dataType, | ||
| 712 | + cursor, | ||
| 713 | + chunkEnd, | ||
| 714 | + ); | ||
| 715 | + } catch (error) { | ||
| 716 | + _profileLog( | ||
| 717 | + 'rawChunk_failed dataType=$dataType startTime=$cursor ' | ||
| 718 | + 'endTime=$chunkEnd elapsedMs=${chunkStopwatch.elapsedMilliseconds} ' | ||
| 719 | + 'error=$error', | ||
| 720 | + ); | ||
| 721 | + rethrow; | ||
| 722 | + } | ||
| 723 | + _profileLog( | ||
| 724 | + 'rawChunk_finish dataType=$dataType startTime=$cursor ' | ||
| 725 | + 'endTime=$chunkEnd count=${points.length} ' | ||
| 726 | + 'elapsedMs=${chunkStopwatch.elapsedMilliseconds}', | ||
| 538 | ); | 727 | ); |
| 539 | for (final point in points) { | 728 | for (final point in points) { |
| 540 | yield point; | 729 | yield point; |
| @@ -552,12 +741,27 @@ class OHOSHealthRawDataCoreService { | @@ -552,12 +741,27 @@ class OHOSHealthRawDataCoreService { | ||
| 552 | var cursor = startTime; | 741 | var cursor = startTime; |
| 553 | while (cursor <= endTime) { | 742 | while (cursor <= endTime) { |
| 554 | final chunkEnd = math.min(cursor + chunkSeconds - 1, endTime); | 743 | final chunkEnd = math.min(cursor + chunkSeconds - 1, endTime); |
| 555 | - final groups = await _rawDataSource.getRawSleepData(cursor, chunkEnd); | 744 | + final chunkStopwatch = Stopwatch()..start(); |
| 745 | + final List<HealthKitRawSleepDataPoint> groups; | ||
| 746 | + try { | ||
| 747 | + groups = await _rawDataSource.getRawSleepData(cursor, chunkEnd); | ||
| 748 | + } catch (error) { | ||
| 749 | + _profileLog( | ||
| 750 | + 'sleepChunk_failed startTime=$cursor endTime=$chunkEnd ' | ||
| 751 | + 'elapsedMs=${chunkStopwatch.elapsedMilliseconds} error=$error', | ||
| 752 | + ); | ||
| 753 | + rethrow; | ||
| 754 | + } | ||
| 556 | final points = groups | 755 | final points = groups |
| 557 | .expand((group) => group.sleepDataPoints) | 756 | .expand((group) => group.sleepDataPoints) |
| 558 | .where( | 757 | .where( |
| 559 | (point) => point.endTime >= cursor && point.startTime <= chunkEnd) | 758 | (point) => point.endTime >= cursor && point.startTime <= chunkEnd) |
| 560 | .toList(); | 759 | .toList(); |
| 760 | + _profileLog( | ||
| 761 | + 'sleepChunk_finish startTime=$cursor endTime=$chunkEnd ' | ||
| 762 | + 'groupCount=${groups.length} count=${points.length} ' | ||
| 763 | + 'elapsedMs=${chunkStopwatch.elapsedMilliseconds}', | ||
| 764 | + ); | ||
| 561 | for (final point in points) { | 765 | for (final point in points) { |
| 562 | yield point; | 766 | yield point; |
| 563 | } | 767 | } |
| @@ -574,9 +778,27 @@ class OHOSHealthRawDataCoreService { | @@ -574,9 +778,27 @@ class OHOSHealthRawDataCoreService { | ||
| 574 | var cursor = startTime; | 778 | var cursor = startTime; |
| 575 | while (cursor <= endTime) { | 779 | while (cursor <= endTime) { |
| 576 | final chunkEnd = math.min(cursor + chunkSeconds - 1, endTime); | 780 | final chunkEnd = math.min(cursor + chunkSeconds - 1, endTime); |
| 577 | - final points = await _rawDataSource.getRawWorkoutData(cursor, chunkEnd); | ||
| 578 | - for (final point in points.where( | ||
| 579 | - (point) => point.endTime >= cursor && point.startTime <= chunkEnd)) { | 781 | + final chunkStopwatch = Stopwatch()..start(); |
| 782 | + final List<HealthKitRawWorkoutDataPoint> points; | ||
| 783 | + try { | ||
| 784 | + points = await _rawDataSource.getRawWorkoutData(cursor, chunkEnd); | ||
| 785 | + } catch (error) { | ||
| 786 | + _profileLog( | ||
| 787 | + 'workoutChunk_failed startTime=$cursor endTime=$chunkEnd ' | ||
| 788 | + 'elapsedMs=${chunkStopwatch.elapsedMilliseconds} error=$error', | ||
| 789 | + ); | ||
| 790 | + rethrow; | ||
| 791 | + } | ||
| 792 | + final filteredPoints = points | ||
| 793 | + .where( | ||
| 794 | + (point) => point.endTime >= cursor && point.startTime <= chunkEnd) | ||
| 795 | + .toList(growable: false); | ||
| 796 | + _profileLog( | ||
| 797 | + 'workoutChunk_finish startTime=$cursor endTime=$chunkEnd ' | ||
| 798 | + 'count=${filteredPoints.length} rawCount=${points.length} ' | ||
| 799 | + 'elapsedMs=${chunkStopwatch.elapsedMilliseconds}', | ||
| 800 | + ); | ||
| 801 | + for (final point in filteredPoints) { | ||
| 580 | yield point; | 802 | yield point; |
| 581 | } | 803 | } |
| 582 | cursor = chunkEnd + 1; | 804 | cursor = chunkEnd + 1; |
| @@ -1022,13 +1244,7 @@ class OHOSHealthRawDataCoreService { | @@ -1022,13 +1244,7 @@ class OHOSHealthRawDataCoreService { | ||
| 1022 | 1244 | ||
| 1023 | int? _rawBackfillStartTime(int? latestDataTime) { | 1245 | int? _rawBackfillStartTime(int? latestDataTime) { |
| 1024 | if (latestDataTime == null) return null; | 1246 | if (latestDataTime == null) return null; |
| 1025 | - final backfillTime = math.max( | ||
| 1026 | - 0, | ||
| 1027 | - latestDataTime - | ||
| 1028 | - OhosHealthRawDataSyncService.incrementalBackfillDays * | ||
| 1029 | - Duration.secondsPerDay, | ||
| 1030 | - ); | ||
| 1031 | - return _localDay(backfillTime).millisecondsSinceEpoch ~/ 1000; | 1247 | + return _localDay(latestDataTime).millisecondsSinceEpoch ~/ 1000; |
| 1032 | } | 1248 | } |
| 1033 | 1249 | ||
| 1034 | int? _boundedNullableStartTime(int? startTime, int earliestStartTime) { | 1250 | int? _boundedNullableStartTime(int? startTime, int earliestStartTime) { |
| @@ -1108,6 +1324,7 @@ class OHOSHealthRawDataCoreService { | @@ -1108,6 +1324,7 @@ class OHOSHealthRawDataCoreService { | ||
| 1108 | final dailyStressPoints = <HealthRawDailyStressPoint>[]; | 1324 | final dailyStressPoints = <HealthRawDailyStressPoint>[]; |
| 1109 | final emptyDates = <int>[]; | 1325 | final emptyDates = <int>[]; |
| 1110 | for (final date in affectedDates) { | 1326 | for (final date in affectedDates) { |
| 1327 | + final dayStopwatch = Stopwatch()..start(); | ||
| 1111 | if (date != todayDate && existingDates.contains(date)) { | 1328 | if (date != todayDate && existingDates.contains(date)) { |
| 1112 | _logInfo( | 1329 | _logInfo( |
| 1113 | '$_dailyStressLogMarker refresh_existing userId=$userId date=$date', | 1330 | '$_dailyStressLogMarker refresh_existing userId=$userId date=$date', |
| @@ -1119,6 +1336,7 @@ class OHOSHealthRawDataCoreService { | @@ -1119,6 +1336,7 @@ class OHOSHealthRawDataCoreService { | ||
| 1119 | startTime: startTime, | 1336 | startTime: startTime, |
| 1120 | endTime: endTime, | 1337 | endTime: endTime, |
| 1121 | ); | 1338 | ); |
| 1339 | + final queryElapsedMs = dayStopwatch.elapsedMilliseconds; | ||
| 1122 | _logInfo( | 1340 | _logInfo( |
| 1123 | '$_dailyStressLogMarker day_query userId=$userId date=$date ' | 1341 | '$_dailyStressLogMarker day_query userId=$userId date=$date ' |
| 1124 | 'startTime=$startTime endTime=$endTime ' | 1342 | 'startTime=$startTime endTime=$endTime ' |
| @@ -1133,18 +1351,31 @@ class OHOSHealthRawDataCoreService { | @@ -1133,18 +1351,31 @@ class OHOSHealthRawDataCoreService { | ||
| 1133 | ).firstOrNull; | 1351 | ).firstOrNull; |
| 1134 | if (point == null) { | 1352 | if (point == null) { |
| 1135 | emptyDates.add(date); | 1353 | emptyDates.add(date); |
| 1354 | + _profileLog( | ||
| 1355 | + 'dailyStress_day_finish userId=$userId date=$date ' | ||
| 1356 | + 'dayRealtime=${dayRealtimePoints.length} hasResult=false ' | ||
| 1357 | + 'queryElapsedMs=$queryElapsedMs ' | ||
| 1358 | + 'elapsedMs=${dayStopwatch.elapsedMilliseconds}', | ||
| 1359 | + ); | ||
| 1136 | _logInfo( | 1360 | _logInfo( |
| 1137 | '$_dailyStressLogMarker no_result userId=$userId date=$date', | 1361 | '$_dailyStressLogMarker no_result userId=$userId date=$date', |
| 1138 | ); | 1362 | ); |
| 1139 | continue; | 1363 | continue; |
| 1140 | } | 1364 | } |
| 1141 | dailyStressPoints.add(point); | 1365 | dailyStressPoints.add(point); |
| 1366 | + _profileLog( | ||
| 1367 | + 'dailyStress_day_finish userId=$userId date=$date ' | ||
| 1368 | + 'dayRealtime=${dayRealtimePoints.length} hasResult=true ' | ||
| 1369 | + 'queryElapsedMs=$queryElapsedMs ' | ||
| 1370 | + 'elapsedMs=${dayStopwatch.elapsedMilliseconds}', | ||
| 1371 | + ); | ||
| 1142 | _logInfo( | 1372 | _logInfo( |
| 1143 | '$_dailyStressLogMarker result userId=$userId date=$date ' | 1373 | '$_dailyStressLogMarker result userId=$userId date=$date ' |
| 1144 | 'stressValue=${point.stressValue} stressScore=${point.stressScore} ' | 1374 | 'stressValue=${point.stressValue} stressScore=${point.stressScore} ' |
| 1145 | 'state=${point.state.value}', | 1375 | 'state=${point.state.value}', |
| 1146 | ); | 1376 | ); |
| 1147 | } | 1377 | } |
| 1378 | + final dailyStoreStopwatch = Stopwatch()..start(); | ||
| 1148 | await _localStore.upsertDailyStressPoints( | 1379 | await _localStore.upsertDailyStressPoints( |
| 1149 | userId: userId, | 1380 | userId: userId, |
| 1150 | points: dailyStressPoints, | 1381 | points: dailyStressPoints, |
| @@ -1153,6 +1384,11 @@ class OHOSHealthRawDataCoreService { | @@ -1153,6 +1384,11 @@ class OHOSHealthRawDataCoreService { | ||
| 1153 | userId: userId, | 1384 | userId: userId, |
| 1154 | dates: emptyDates, | 1385 | dates: emptyDates, |
| 1155 | ); | 1386 | ); |
| 1387 | + _profileLog( | ||
| 1388 | + 'dailyStress_store_finish userId=$userId ' | ||
| 1389 | + 'stored=${dailyStressPoints.length} deletedEmpty=${emptyDates.length} ' | ||
| 1390 | + 'elapsedMs=${dailyStoreStopwatch.elapsedMilliseconds}', | ||
| 1391 | + ); | ||
| 1156 | _logInfo( | 1392 | _logInfo( |
| 1157 | '$_dailyStressLogMarker stored userId=$userId ' | 1393 | '$_dailyStressLogMarker stored userId=$userId ' |
| 1158 | 'stored=${dailyStressPoints.length} deletedEmptyDates=$emptyDates', | 1394 | 'stored=${dailyStressPoints.length} deletedEmptyDates=$emptyDates', |
| @@ -1184,6 +1420,7 @@ class OHOSHealthRawDataCoreService { | @@ -1184,6 +1420,7 @@ class OHOSHealthRawDataCoreService { | ||
| 1184 | ); | 1420 | ); |
| 1185 | final results = <HealthRawSleepResult>[]; | 1421 | final results = <HealthRawSleepResult>[]; |
| 1186 | for (final day in days) { | 1422 | for (final day in days) { |
| 1423 | + final dayStopwatch = Stopwatch()..start(); | ||
| 1187 | final calculation = HealthSleepCalculator.calculateDay( | 1424 | final calculation = HealthSleepCalculator.calculateDay( |
| 1188 | day: day, | 1425 | day: day, |
| 1189 | sleepIntervals: sleepIntervals, | 1426 | sleepIntervals: sleepIntervals, |
| @@ -1192,6 +1429,12 @@ class OHOSHealthRawDataCoreService { | @@ -1192,6 +1429,12 @@ class OHOSHealthRawDataCoreService { | ||
| 1192 | final score = calculation.score; | 1429 | final score = calculation.score; |
| 1193 | final state = calculation.state; | 1430 | final state = calculation.state; |
| 1194 | if (merged == null || score == null || state == null) { | 1431 | if (merged == null || score == null || state == null) { |
| 1432 | + _profileLog( | ||
| 1433 | + 'sleep_day_finish userId=$userId ' | ||
| 1434 | + 'date=${_dateKeyFromDateTime(day)} hasResult=false ' | ||
| 1435 | + 'reason=missing_score_or_state ' | ||
| 1436 | + 'elapsedMs=${dayStopwatch.elapsedMilliseconds}', | ||
| 1437 | + ); | ||
| 1195 | _logInfo( | 1438 | _logInfo( |
| 1196 | '$_sleepCalcLogMarker skip_invalid userId=$userId ' | 1439 | '$_sleepCalcLogMarker skip_invalid userId=$userId ' |
| 1197 | 'date=${_dateKeyFromDateTime(day)} ' | 1440 | 'date=${_dateKeyFromDateTime(day)} ' |
| @@ -1201,6 +1444,12 @@ class OHOSHealthRawDataCoreService { | @@ -1201,6 +1444,12 @@ class OHOSHealthRawDataCoreService { | ||
| 1201 | continue; | 1444 | continue; |
| 1202 | } | 1445 | } |
| 1203 | if (!calculation.hasValidSleep) { | 1446 | if (!calculation.hasValidSleep) { |
| 1447 | + _profileLog( | ||
| 1448 | + 'sleep_day_finish userId=$userId ' | ||
| 1449 | + 'date=${_dateKeyFromDateTime(day)} hasResult=false ' | ||
| 1450 | + 'reason=no_valid_sleep ' | ||
| 1451 | + 'elapsedMs=${dayStopwatch.elapsedMilliseconds}', | ||
| 1452 | + ); | ||
| 1204 | _logInfo( | 1453 | _logInfo( |
| 1205 | '$_sleepCalcLogMarker skip_invalid userId=$userId ' | 1454 | '$_sleepCalcLogMarker skip_invalid userId=$userId ' |
| 1206 | 'date=${_dateKeyFromDateTime(day)} reason=no_valid_sleep ' | 1455 | 'date=${_dateKeyFromDateTime(day)} reason=no_valid_sleep ' |
| @@ -1210,6 +1459,12 @@ class OHOSHealthRawDataCoreService { | @@ -1210,6 +1459,12 @@ class OHOSHealthRawDataCoreService { | ||
| 1210 | } | 1459 | } |
| 1211 | if (latestSleepResultTime != null && | 1460 | if (latestSleepResultTime != null && |
| 1212 | merged.endTime <= latestSleepResultTime) { | 1461 | merged.endTime <= latestSleepResultTime) { |
| 1462 | + _profileLog( | ||
| 1463 | + 'sleep_day_finish userId=$userId ' | ||
| 1464 | + 'date=${_dateKeyFromDateTime(day)} hasResult=false ' | ||
| 1465 | + 'reason=existing mergedEnd=${merged.endTime} ' | ||
| 1466 | + 'elapsedMs=${dayStopwatch.elapsedMilliseconds}', | ||
| 1467 | + ); | ||
| 1213 | _logInfo( | 1468 | _logInfo( |
| 1214 | '$_sleepCalcLogMarker skip_existing userId=$userId ' | 1469 | '$_sleepCalcLogMarker skip_existing userId=$userId ' |
| 1215 | 'date=${_dateKeyFromDateTime(day)} mergedEnd=${merged.endTime} ' | 1470 | 'date=${_dateKeyFromDateTime(day)} mergedEnd=${merged.endTime} ' |
| @@ -1230,6 +1485,12 @@ class OHOSHealthRawDataCoreService { | @@ -1230,6 +1485,12 @@ class OHOSHealthRawDataCoreService { | ||
| 1230 | uploaded: false, | 1485 | uploaded: false, |
| 1231 | ), | 1486 | ), |
| 1232 | ); | 1487 | ); |
| 1488 | + _profileLog( | ||
| 1489 | + 'sleep_day_finish userId=$userId ' | ||
| 1490 | + 'date=${_dateKeyFromDateTime(day)} hasResult=true ' | ||
| 1491 | + 'sleepMinutes=${calculation.summary.sleepMinutes} ' | ||
| 1492 | + 'elapsedMs=${dayStopwatch.elapsedMilliseconds}', | ||
| 1493 | + ); | ||
| 1233 | _logInfo( | 1494 | _logInfo( |
| 1234 | '$_sleepCalcLogMarker result userId=$userId ' | 1495 | '$_sleepCalcLogMarker result userId=$userId ' |
| 1235 | 'date=${_dateKeyFromDateTime(day)} start=${merged.startTime} ' | 1496 | 'date=${_dateKeyFromDateTime(day)} start=${merged.startTime} ' |
| @@ -1237,7 +1498,12 @@ class OHOSHealthRawDataCoreService { | @@ -1237,7 +1498,12 @@ class OHOSHealthRawDataCoreService { | ||
| 1237 | 'sleepMinutes=${calculation.summary.sleepMinutes}', | 1498 | 'sleepMinutes=${calculation.summary.sleepMinutes}', |
| 1238 | ); | 1499 | ); |
| 1239 | } | 1500 | } |
| 1501 | + final sleepStoreStopwatch = Stopwatch()..start(); | ||
| 1240 | await _localStore.upsertSleepResults(userId: userId, results: results); | 1502 | await _localStore.upsertSleepResults(userId: userId, results: results); |
| 1503 | + _profileLog( | ||
| 1504 | + 'sleep_store_finish userId=$userId stored=${results.length} ' | ||
| 1505 | + 'elapsedMs=${sleepStoreStopwatch.elapsedMilliseconds}', | ||
| 1506 | + ); | ||
| 1241 | _logInfo( | 1507 | _logInfo( |
| 1242 | '$_sleepCalcLogMarker stored userId=$userId stored=${results.length}', | 1508 | '$_sleepCalcLogMarker stored userId=$userId stored=${results.length}', |
| 1243 | ); | 1509 | ); |
| @@ -1392,6 +1658,16 @@ class OHOSHealthRawDataCoreService { | @@ -1392,6 +1658,16 @@ class OHOSHealthRawDataCoreService { | ||
| 1392 | return DateTime(date.year, date.month, date.day); | 1658 | return DateTime(date.year, date.month, date.day); |
| 1393 | } | 1659 | } |
| 1394 | 1660 | ||
| 1661 | + static int _twoMonthLookbackStart(int endTime) { | ||
| 1662 | + final endDate = DateTime.fromMillisecondsSinceEpoch(endTime * 1000); | ||
| 1663 | + return DateTime( | ||
| 1664 | + endDate.year, | ||
| 1665 | + endDate.month - defaultLookbackMonths, | ||
| 1666 | + endDate.day, | ||
| 1667 | + ).millisecondsSinceEpoch ~/ | ||
| 1668 | + 1000; | ||
| 1669 | + } | ||
| 1670 | + | ||
| 1395 | static int _dateKeyFromDateTime(DateTime date) { | 1671 | static int _dateKeyFromDateTime(DateTime date) { |
| 1396 | return date.year * 10000 + date.month * 100 + date.day; | 1672 | return date.year * 10000 + date.month * 100 + date.day; |
| 1397 | } | 1673 | } |
| @@ -1437,6 +1713,42 @@ class OHOSHealthRawDataCoreService { | @@ -1437,6 +1713,42 @@ class OHOSHealthRawDataCoreService { | ||
| 1437 | // Logger may be unavailable in isolated unit tests. | 1713 | // Logger may be unavailable in isolated unit tests. |
| 1438 | } | 1714 | } |
| 1439 | } | 1715 | } |
| 1716 | + | ||
| 1717 | + static void _profileLog(String message) { | ||
| 1718 | + if (!kDebugMode) return; | ||
| 1719 | + _logInfo('$_profileLogMarker $message'); | ||
| 1720 | + } | ||
| 1721 | + | ||
| 1722 | + static void _publishCalculationEvent({ | ||
| 1723 | + required OhosHealthRawDataPipelineEventType type, | ||
| 1724 | + required String flow, | ||
| 1725 | + required int userId, | ||
| 1726 | + required int startTime, | ||
| 1727 | + required int endTime, | ||
| 1728 | + int? elapsedMs, | ||
| 1729 | + int? hrvCount, | ||
| 1730 | + int? realtimeCount, | ||
| 1731 | + int? dailyCount, | ||
| 1732 | + int? sleepCount, | ||
| 1733 | + String? error, | ||
| 1734 | + }) { | ||
| 1735 | + OhosHealthRawDataPipelineEvents.publish( | ||
| 1736 | + OhosHealthRawDataPipelineEvent( | ||
| 1737 | + type: type, | ||
| 1738 | + flow: flow, | ||
| 1739 | + occurredAt: DateTime.now(), | ||
| 1740 | + userId: userId, | ||
| 1741 | + startTime: startTime, | ||
| 1742 | + endTime: endTime, | ||
| 1743 | + elapsedMs: elapsedMs, | ||
| 1744 | + hrvCount: hrvCount, | ||
| 1745 | + realtimeCount: realtimeCount, | ||
| 1746 | + dailyCount: dailyCount, | ||
| 1747 | + sleepCount: sleepCount, | ||
| 1748 | + error: error, | ||
| 1749 | + ), | ||
| 1750 | + ); | ||
| 1751 | + } | ||
| 1440 | } | 1752 | } |
| 1441 | 1753 | ||
| 1442 | class _OhosStoredCalculationResult { | 1754 | class _OhosStoredCalculationResult { |
| 1 | +import 'dart:async'; | ||
| 2 | + | ||
| 3 | +enum OhosHealthRawDataPipelineEventType { | ||
| 4 | + syncStarted, | ||
| 5 | + syncSucceeded, | ||
| 6 | + syncFailed, | ||
| 7 | + calculationStarted, | ||
| 8 | + calculationSucceeded, | ||
| 9 | + calculationFailed, | ||
| 10 | +} | ||
| 11 | + | ||
| 12 | +class OhosHealthRawDataPipelineEvent { | ||
| 13 | + const OhosHealthRawDataPipelineEvent({ | ||
| 14 | + required this.type, | ||
| 15 | + required this.flow, | ||
| 16 | + required this.occurredAt, | ||
| 17 | + this.userId, | ||
| 18 | + this.dataType, | ||
| 19 | + this.dataTypes, | ||
| 20 | + this.startTime, | ||
| 21 | + this.endTime, | ||
| 22 | + this.elapsedMs, | ||
| 23 | + this.pageCount, | ||
| 24 | + this.storedCount, | ||
| 25 | + this.hrvCount, | ||
| 26 | + this.realtimeCount, | ||
| 27 | + this.dailyCount, | ||
| 28 | + this.sleepCount, | ||
| 29 | + this.error, | ||
| 30 | + }); | ||
| 31 | + | ||
| 32 | + final OhosHealthRawDataPipelineEventType type; | ||
| 33 | + final String flow; | ||
| 34 | + final DateTime occurredAt; | ||
| 35 | + final int? userId; | ||
| 36 | + final int? dataType; | ||
| 37 | + final List<int>? dataTypes; | ||
| 38 | + final int? startTime; | ||
| 39 | + final int? endTime; | ||
| 40 | + final int? elapsedMs; | ||
| 41 | + final int? pageCount; | ||
| 42 | + final int? storedCount; | ||
| 43 | + final int? hrvCount; | ||
| 44 | + final int? realtimeCount; | ||
| 45 | + final int? dailyCount; | ||
| 46 | + final int? sleepCount; | ||
| 47 | + final String? error; | ||
| 48 | + | ||
| 49 | + bool get isStarted => | ||
| 50 | + type == OhosHealthRawDataPipelineEventType.syncStarted || | ||
| 51 | + type == OhosHealthRawDataPipelineEventType.calculationStarted; | ||
| 52 | + | ||
| 53 | + bool get isSucceeded => | ||
| 54 | + type == OhosHealthRawDataPipelineEventType.syncSucceeded || | ||
| 55 | + type == OhosHealthRawDataPipelineEventType.calculationSucceeded; | ||
| 56 | + | ||
| 57 | + bool get isFailed => | ||
| 58 | + type == OhosHealthRawDataPipelineEventType.syncFailed || | ||
| 59 | + type == OhosHealthRawDataPipelineEventType.calculationFailed; | ||
| 60 | +} | ||
| 61 | + | ||
| 62 | +class OhosHealthRawDataPipelineEvents { | ||
| 63 | + OhosHealthRawDataPipelineEvents._(); | ||
| 64 | + | ||
| 65 | + static final StreamController<OhosHealthRawDataPipelineEvent> _controller = | ||
| 66 | + StreamController<OhosHealthRawDataPipelineEvent>.broadcast(); | ||
| 67 | + | ||
| 68 | + static Stream<OhosHealthRawDataPipelineEvent> get stream => | ||
| 69 | + _controller.stream; | ||
| 70 | + | ||
| 71 | + static void publish(OhosHealthRawDataPipelineEvent event) { | ||
| 72 | + if (_controller.isClosed) return; | ||
| 73 | + _controller.add(event); | ||
| 74 | + } | ||
| 75 | +} |
| @@ -108,39 +108,53 @@ class OhosHealthRawDataSqliteStore implements OhosHealthRawDataLocalStore { | @@ -108,39 +108,53 @@ class OhosHealthRawDataSqliteStore implements OhosHealthRawDataLocalStore { | ||
| 108 | ); | 108 | ); |
| 109 | return 0; | 109 | return 0; |
| 110 | } | 110 | } |
| 111 | + final storage = await _prepareRawDataStorage(db, dataType); | ||
| 112 | + final itemsToStore = await _filterExistingDuplicates( | ||
| 113 | + db: db, | ||
| 114 | + dataType: dataType, | ||
| 115 | + storage: storage, | ||
| 116 | + items: filteredItems, | ||
| 117 | + latestTime: latestTime, | ||
| 118 | + ); | ||
| 119 | + if (itemsToStore.isEmpty) { | ||
| 120 | + _log( | ||
| 121 | + 'skip_store_existing_duplicates dataType=$dataType ' | ||
| 122 | + 'latestTime=$latestTime incoming=${items.length} ' | ||
| 123 | + 'candidates=${filteredItems.length}', | ||
| 124 | + ); | ||
| 125 | + return 0; | ||
| 126 | + } | ||
| 111 | var storedCount = 0; | 127 | var storedCount = 0; |
| 112 | await db.transaction((txn) async { | 128 | await db.transaction((txn) async { |
| 113 | if (dataType == OhosHealthRawDataType.sleepAnalysis) { | 129 | if (dataType == OhosHealthRawDataType.sleepAnalysis) { |
| 114 | - for (final item in filteredItems) { | ||
| 115 | - final rowId = await txn.insert( | 130 | + final batch = txn.batch(); |
| 131 | + for (final item in itemsToStore) { | ||
| 132 | + batch.insert( | ||
| 116 | sleepDataTable, | 133 | sleepDataTable, |
| 117 | _sleepRow(item, createTime), | 134 | _sleepRow(item, createTime), |
| 118 | conflictAlgorithm: ConflictAlgorithm.ignore, | 135 | conflictAlgorithm: ConflictAlgorithm.ignore, |
| 119 | ); | 136 | ); |
| 120 | - if (rowId > 0) storedCount += 1; | ||
| 121 | } | 137 | } |
| 138 | + storedCount += await _commitInsertBatch(batch); | ||
| 122 | return; | 139 | return; |
| 123 | } | 140 | } |
| 124 | if (dataType == OhosHealthRawDataType.workout) { | 141 | if (dataType == OhosHealthRawDataType.workout) { |
| 125 | - final table = _rawDataTable(dataType); | ||
| 126 | - await _createRawIntervalDataTypeTable(txn, table); | ||
| 127 | - for (final item in filteredItems) { | ||
| 128 | - final rowId = await txn.insert( | ||
| 129 | - table, | 142 | + final batch = txn.batch(); |
| 143 | + for (final item in itemsToStore) { | ||
| 144 | + batch.insert( | ||
| 145 | + storage.table, | ||
| 130 | _intervalRow(item, createTime), | 146 | _intervalRow(item, createTime), |
| 131 | conflictAlgorithm: ConflictAlgorithm.ignore, | 147 | conflictAlgorithm: ConflictAlgorithm.ignore, |
| 132 | ); | 148 | ); |
| 133 | - if (rowId > 0) storedCount += 1; | ||
| 134 | } | 149 | } |
| 150 | + storedCount += await _commitInsertBatch(batch); | ||
| 135 | return; | 151 | return; |
| 136 | } | 152 | } |
| 137 | 153 | ||
| 138 | - final storedDataType = _storedHealthDataType(dataType); | ||
| 139 | - final table = _rawDataTable(storedDataType); | ||
| 140 | - await _createRawDataTypeTable(txn, table); | ||
| 141 | - for (final item in filteredItems) { | ||
| 142 | - final rowId = await txn.insert( | ||
| 143 | - table, | 154 | + final batch = txn.batch(); |
| 155 | + for (final item in itemsToStore) { | ||
| 156 | + batch.insert( | ||
| 157 | + storage.table, | ||
| 144 | _rawRow(item, createTime), | 158 | _rawRow(item, createTime), |
| 145 | conflictAlgorithm: _dailyItemNeedsRefresh( | 159 | conflictAlgorithm: _dailyItemNeedsRefresh( |
| 146 | dataType: dataType, | 160 | dataType: dataType, |
| @@ -150,10 +164,10 @@ class OhosHealthRawDataSqliteStore implements OhosHealthRawDataLocalStore { | @@ -150,10 +164,10 @@ class OhosHealthRawDataSqliteStore implements OhosHealthRawDataLocalStore { | ||
| 150 | ? ConflictAlgorithm.replace | 164 | ? ConflictAlgorithm.replace |
| 151 | : ConflictAlgorithm.ignore, | 165 | : ConflictAlgorithm.ignore, |
| 152 | ); | 166 | ); |
| 153 | - if (rowId > 0) storedCount += 1; | ||
| 154 | } | 167 | } |
| 168 | + storedCount += await _commitInsertBatch(batch); | ||
| 155 | }); | 169 | }); |
| 156 | - final skippedCount = filteredItems.length - storedCount; | 170 | + final skippedCount = filteredItems.length - itemsToStore.length; |
| 157 | if (skippedCount > 0) { | 171 | if (skippedCount > 0) { |
| 158 | _log( | 172 | _log( |
| 159 | 'skip_store_duplicates dataType=$dataType latestTime=$latestTime ' | 173 | 'skip_store_duplicates dataType=$dataType latestTime=$latestTime ' |
| @@ -164,6 +178,104 @@ class OhosHealthRawDataSqliteStore implements OhosHealthRawDataLocalStore { | @@ -164,6 +178,104 @@ class OhosHealthRawDataSqliteStore implements OhosHealthRawDataLocalStore { | ||
| 164 | return storedCount; | 178 | return storedCount; |
| 165 | } | 179 | } |
| 166 | 180 | ||
| 181 | + Future<_RawDataStorage> _prepareRawDataStorage( | ||
| 182 | + Database db, | ||
| 183 | + int dataType, | ||
| 184 | + ) async { | ||
| 185 | + if (dataType == OhosHealthRawDataType.sleepAnalysis) { | ||
| 186 | + return const _RawDataStorage( | ||
| 187 | + table: sleepDataTable, | ||
| 188 | + keyColumn: 'from_time', | ||
| 189 | + ); | ||
| 190 | + } | ||
| 191 | + if (dataType == OhosHealthRawDataType.workout) { | ||
| 192 | + final table = _rawDataTable(dataType); | ||
| 193 | + await _createRawIntervalDataTypeTable(db, table); | ||
| 194 | + return _RawDataStorage(table: table, keyColumn: 'from_time'); | ||
| 195 | + } | ||
| 196 | + | ||
| 197 | + final storedDataType = _storedHealthDataType(dataType); | ||
| 198 | + final table = _rawDataTable(storedDataType); | ||
| 199 | + await _createRawDataTypeTable(db, table); | ||
| 200 | + return _RawDataStorage(table: table, keyColumn: 'time'); | ||
| 201 | + } | ||
| 202 | + | ||
| 203 | + Future<List<OhosHealthRawDataItem>> _filterExistingDuplicates({ | ||
| 204 | + required Database db, | ||
| 205 | + required int dataType, | ||
| 206 | + required _RawDataStorage storage, | ||
| 207 | + required List<OhosHealthRawDataItem> items, | ||
| 208 | + required int? latestTime, | ||
| 209 | + }) async { | ||
| 210 | + final keyedItems = <int, OhosHealthRawDataItem>{}; | ||
| 211 | + final refreshKeys = <int>{}; | ||
| 212 | + for (final item in items) { | ||
| 213 | + final keyTime = _dedupeKeyTime(dataType: dataType, item: item); | ||
| 214 | + keyedItems.putIfAbsent(keyTime, () => item); | ||
| 215 | + if (_dailyItemNeedsRefresh( | ||
| 216 | + dataType: dataType, | ||
| 217 | + item: item, | ||
| 218 | + latestTime: latestTime, | ||
| 219 | + )) { | ||
| 220 | + refreshKeys.add(keyTime); | ||
| 221 | + } | ||
| 222 | + } | ||
| 223 | + if (keyedItems.isEmpty) return const <OhosHealthRawDataItem>[]; | ||
| 224 | + if (latestTime == null) { | ||
| 225 | + return keyedItems.values.toList(growable: false); | ||
| 226 | + } | ||
| 227 | + | ||
| 228 | + final existingKeys = await _queryExistingKeys( | ||
| 229 | + db: db, | ||
| 230 | + table: storage.table, | ||
| 231 | + keyColumn: storage.keyColumn, | ||
| 232 | + keys: keyedItems.keys.where((key) => !refreshKeys.contains(key)), | ||
| 233 | + ); | ||
| 234 | + return <OhosHealthRawDataItem>[ | ||
| 235 | + for (final entry in keyedItems.entries) | ||
| 236 | + if (refreshKeys.contains(entry.key) || | ||
| 237 | + !existingKeys.contains(entry.key)) | ||
| 238 | + entry.value, | ||
| 239 | + ]; | ||
| 240 | + } | ||
| 241 | + | ||
| 242 | + Future<Set<int>> _queryExistingKeys({ | ||
| 243 | + required Database db, | ||
| 244 | + required String table, | ||
| 245 | + required String keyColumn, | ||
| 246 | + required Iterable<int> keys, | ||
| 247 | + }) async { | ||
| 248 | + final keyList = keys.toList(growable: false); | ||
| 249 | + if (keyList.isEmpty) return const <int>{}; | ||
| 250 | + final existing = <int>{}; | ||
| 251 | + const chunkSize = 900; | ||
| 252 | + for (var offset = 0; offset < keyList.length; offset += chunkSize) { | ||
| 253 | + final chunk = | ||
| 254 | + keyList.skip(offset).take(chunkSize).toList(growable: false); | ||
| 255 | + final placeholders = List<String>.filled(chunk.length, '?').join(','); | ||
| 256 | + final rows = await db.query( | ||
| 257 | + table, | ||
| 258 | + columns: <String>[keyColumn], | ||
| 259 | + where: '$keyColumn IN ($placeholders)', | ||
| 260 | + whereArgs: chunk, | ||
| 261 | + ); | ||
| 262 | + for (final row in rows) { | ||
| 263 | + final key = row[keyColumn]; | ||
| 264 | + if (key is int) { | ||
| 265 | + existing.add(key); | ||
| 266 | + } else if (key is num) { | ||
| 267 | + existing.add(key.toInt()); | ||
| 268 | + } | ||
| 269 | + } | ||
| 270 | + } | ||
| 271 | + return existing; | ||
| 272 | + } | ||
| 273 | + | ||
| 274 | + Future<int> _commitInsertBatch(Batch batch) async { | ||
| 275 | + final results = await batch.commit(noResult: false); | ||
| 276 | + return results.whereType<int>().where((rowId) => rowId > 0).length; | ||
| 277 | + } | ||
| 278 | + | ||
| 167 | @override | 279 | @override |
| 168 | Future<List<OhosHealthRawDataItem>> queryRawData({ | 280 | Future<List<OhosHealthRawDataItem>> queryRawData({ |
| 169 | required int dataType, | 281 | required int dataType, |
| @@ -606,3 +718,13 @@ CREATE TABLE IF NOT EXISTS $table ( | @@ -606,3 +718,13 @@ CREATE TABLE IF NOT EXISTS $table ( | ||
| 606 | return value is num ? value.toInt() : null; | 718 | return value is num ? value.toInt() : null; |
| 607 | } | 719 | } |
| 608 | } | 720 | } |
| 721 | + | ||
| 722 | +class _RawDataStorage { | ||
| 723 | + const _RawDataStorage({ | ||
| 724 | + required this.table, | ||
| 725 | + required this.keyColumn, | ||
| 726 | + }); | ||
| 727 | + | ||
| 728 | + final String table; | ||
| 729 | + final String keyColumn; | ||
| 730 | +} |
| 1 | +import 'dart:async'; | ||
| 2 | +import 'dart:collection'; | ||
| 1 | import 'dart:math' as math; | 3 | import 'dart:math' as math; |
| 2 | 4 | ||
| 5 | +import 'package:dio/dio.dart'; | ||
| 6 | +import 'package:doublefeel_flutter/core/error/app_error.dart'; | ||
| 3 | import 'package:doublefeel_flutter/core/logging/app_logger.dart'; | 7 | import 'package:doublefeel_flutter/core/logging/app_logger.dart'; |
| 4 | import 'package:doublefeel_flutter/core/result/app_result.dart'; | 8 | import 'package:doublefeel_flutter/core/result/app_result.dart'; |
| 5 | import 'package:doublefeel_flutter/data/models/enums/app_enums.dart'; | 9 | import 'package:doublefeel_flutter/data/models/enums/app_enums.dart'; |
| @@ -10,6 +14,7 @@ import 'package:doublefeel_flutter/data/models/health/health_v2_models.dart'; | @@ -10,6 +14,7 @@ import 'package:doublefeel_flutter/data/models/health/health_v2_models.dart'; | ||
| 10 | import 'package:flutter/foundation.dart'; | 14 | import 'package:flutter/foundation.dart'; |
| 11 | 15 | ||
| 12 | import 'huawei_health_data_type.dart'; | 16 | import 'huawei_health_data_type.dart'; |
| 17 | +import 'ohos_health_raw_data_events.dart'; | ||
| 13 | 18 | ||
| 14 | class OhosHealthRawDataType { | 19 | class OhosHealthRawDataType { |
| 15 | const OhosHealthRawDataType._(); | 20 | const OhosHealthRawDataType._(); |
| @@ -25,17 +30,20 @@ class OhosHealthRawDataSyncService { | @@ -25,17 +30,20 @@ class OhosHealthRawDataSyncService { | ||
| 25 | OhosHealthRawDataLocalStore? localStore, | 30 | OhosHealthRawDataLocalStore? localStore, |
| 26 | DateTime Function()? nowProvider, | 31 | DateTime Function()? nowProvider, |
| 27 | void Function(String message)? logSink, | 32 | void Function(String message)? logSink, |
| 33 | + int maxConcurrentFetches = defaultMaxConcurrentFetches, | ||
| 28 | }) : _remoteDataSource = | 34 | }) : _remoteDataSource = |
| 29 | remoteDataSource ?? const TodoOhosHealthRawDataRemoteDataSource(), | 35 | remoteDataSource ?? const TodoOhosHealthRawDataRemoteDataSource(), |
| 30 | _localStore = localStore ?? const TodoOhosHealthRawDataLocalStore(), | 36 | _localStore = localStore ?? const TodoOhosHealthRawDataLocalStore(), |
| 31 | _nowProvider = nowProvider ?? DateTime.now, | 37 | _nowProvider = nowProvider ?? DateTime.now, |
| 32 | - _logSink = logSink; | 38 | + _logSink = logSink, |
| 39 | + _fetchLimiter = _AsyncLimiter(maxConcurrentFetches); | ||
| 33 | 40 | ||
| 34 | - static const int defaultLookbackDays = 183; | ||
| 35 | - static const int incrementalBackfillDays = 7; | 41 | + static const int defaultLookbackMonths = 2; |
| 36 | static const int heartRateFetchChunkDays = 10; | 42 | static const int heartRateFetchChunkDays = 10; |
| 37 | static const int defaultFetchChunkDays = 30; | 43 | static const int defaultFetchChunkDays = 30; |
| 44 | + static const int defaultMaxConcurrentFetches = 10; | ||
| 38 | static const String logMarker = '[OHOS_HEALTH_RAW_SYNC]'; | 45 | static const String logMarker = '[OHOS_HEALTH_RAW_SYNC]'; |
| 46 | + static const String profileLogMarker = '[OHOS_HEALTH_RAW_PROFILE]'; | ||
| 39 | static final List<int> calculationDataTypes = List<int>.unmodifiable( | 47 | static final List<int> calculationDataTypes = List<int>.unmodifiable( |
| 40 | <int>[ | 48 | <int>[ |
| 41 | for (final type in HuaweiHealthDataType.values) type.dataType, | 49 | for (final type in HuaweiHealthDataType.values) type.dataType, |
| @@ -48,6 +56,7 @@ class OhosHealthRawDataSyncService { | @@ -48,6 +56,7 @@ class OhosHealthRawDataSyncService { | ||
| 48 | final OhosHealthRawDataLocalStore _localStore; | 56 | final OhosHealthRawDataLocalStore _localStore; |
| 49 | final DateTime Function() _nowProvider; | 57 | final DateTime Function() _nowProvider; |
| 50 | final void Function(String message)? _logSink; | 58 | final void Function(String message)? _logSink; |
| 59 | + final _AsyncLimiter _fetchLimiter; | ||
| 51 | final Map<String, Future<OhosHealthRawDataSyncResult>> _runningSyncs = | 60 | final Map<String, Future<OhosHealthRawDataSyncResult>> _runningSyncs = |
| 52 | <String, Future<OhosHealthRawDataSyncResult>>{}; | 61 | <String, Future<OhosHealthRawDataSyncResult>>{}; |
| 53 | 62 | ||
| @@ -56,17 +65,22 @@ class OhosHealthRawDataSyncService { | @@ -56,17 +65,22 @@ class OhosHealthRawDataSyncService { | ||
| 56 | int? startTime, | 65 | int? startTime, |
| 57 | int? endTime, | 66 | int? endTime, |
| 58 | }) async { | 67 | }) async { |
| 68 | + final totalStopwatch = Stopwatch()..start(); | ||
| 59 | final resolvedEndTime = endTime ?? _unixSeconds(_nowProvider()); | 69 | final resolvedEndTime = endTime ?? _unixSeconds(_nowProvider()); |
| 60 | final resolvedStartTime = await _resolveStartTime( | 70 | final resolvedStartTime = await _resolveStartTime( |
| 61 | dataType: dataType, | 71 | dataType: dataType, |
| 62 | requestedStartTime: startTime, | 72 | requestedStartTime: startTime, |
| 63 | endTime: resolvedEndTime, | 73 | endTime: resolvedEndTime, |
| 64 | ); | 74 | ); |
| 75 | + _profileLog( | ||
| 76 | + 'syncRawData_resolved dataType=$dataType ' | ||
| 77 | + 'requestedStartTime=${startTime ?? ''} startTime=$resolvedStartTime ' | ||
| 78 | + 'endTime=$resolvedEndTime elapsedMs=${totalStopwatch.elapsedMilliseconds}', | ||
| 79 | + ); | ||
| 65 | 80 | ||
| 66 | if (resolvedEndTime < resolvedStartTime) { | 81 | if (resolvedEndTime < resolvedStartTime) { |
| 67 | throw ArgumentError.value(endTime, 'endTime'); | 82 | throw ArgumentError.value(endTime, 'endTime'); |
| 68 | } | 83 | } |
| 69 | - | ||
| 70 | final syncKey = '$dataType:$resolvedStartTime:$resolvedEndTime'; | 84 | final syncKey = '$dataType:$resolvedStartTime:$resolvedEndTime'; |
| 71 | final running = _runningSyncs[syncKey]; | 85 | final running = _runningSyncs[syncKey]; |
| 72 | if (running != null) { | 86 | if (running != null) { |
| @@ -74,9 +88,21 @@ class OhosHealthRawDataSyncService { | @@ -74,9 +88,21 @@ class OhosHealthRawDataSyncService { | ||
| 74 | 'sync_duplicate_join dataType=$dataType ' | 88 | 'sync_duplicate_join dataType=$dataType ' |
| 75 | 'startTime=$resolvedStartTime endTime=$resolvedEndTime', | 89 | 'startTime=$resolvedStartTime endTime=$resolvedEndTime', |
| 76 | ); | 90 | ); |
| 91 | + _profileLog( | ||
| 92 | + 'syncRawData_duplicate_join dataType=$dataType ' | ||
| 93 | + 'startTime=$resolvedStartTime endTime=$resolvedEndTime', | ||
| 94 | + ); | ||
| 77 | return running; | 95 | return running; |
| 78 | } | 96 | } |
| 79 | 97 | ||
| 98 | + _publishSyncEvent( | ||
| 99 | + type: OhosHealthRawDataPipelineEventType.syncStarted, | ||
| 100 | + flow: 'syncRawData', | ||
| 101 | + dataType: dataType, | ||
| 102 | + startTime: resolvedStartTime, | ||
| 103 | + endTime: resolvedEndTime, | ||
| 104 | + ); | ||
| 105 | + | ||
| 80 | final task = _syncResolvedRawData( | 106 | final task = _syncResolvedRawData( |
| 81 | dataType: dataType, | 107 | dataType: dataType, |
| 82 | startTime: resolvedStartTime, | 108 | startTime: resolvedStartTime, |
| @@ -84,12 +110,43 @@ class OhosHealthRawDataSyncService { | @@ -84,12 +110,43 @@ class OhosHealthRawDataSyncService { | ||
| 84 | ); | 110 | ); |
| 85 | _runningSyncs[syncKey] = task; | 111 | _runningSyncs[syncKey] = task; |
| 86 | try { | 112 | try { |
| 87 | - return await task; | 113 | + final result = await task; |
| 114 | + _profileLog( | ||
| 115 | + 'syncRawData_finish dataType=$dataType ' | ||
| 116 | + 'storedCount=${result.storedCount} pageCount=${result.pageCount} ' | ||
| 117 | + 'segmentCount=${result.segmentCount} ' | ||
| 118 | + 'elapsedMs=${totalStopwatch.elapsedMilliseconds}', | ||
| 119 | + ); | ||
| 120 | + _publishSyncEvent( | ||
| 121 | + type: OhosHealthRawDataPipelineEventType.syncSucceeded, | ||
| 122 | + flow: 'syncRawData', | ||
| 123 | + dataType: dataType, | ||
| 124 | + startTime: resolvedStartTime, | ||
| 125 | + endTime: resolvedEndTime, | ||
| 126 | + elapsedMs: totalStopwatch.elapsedMilliseconds, | ||
| 127 | + pageCount: result.pageCount, | ||
| 128 | + storedCount: result.storedCount, | ||
| 129 | + ); | ||
| 130 | + return result; | ||
| 88 | } catch (error, stackTrace) { | 131 | } catch (error, stackTrace) { |
| 89 | _log( | 132 | _log( |
| 90 | 'sync_failed dataType=$dataType ' | 133 | 'sync_failed dataType=$dataType ' |
| 91 | 'startTime=$resolvedStartTime endTime=$resolvedEndTime ' | 134 | 'startTime=$resolvedStartTime endTime=$resolvedEndTime ' |
| 92 | - 'error=$error stackTrace=$stackTrace', | 135 | + 'error=${_describeError(error)} stackTrace=$stackTrace', |
| 136 | + ); | ||
| 137 | + _profileLog( | ||
| 138 | + 'syncRawData_failed dataType=$dataType ' | ||
| 139 | + 'elapsedMs=${totalStopwatch.elapsedMilliseconds} ' | ||
| 140 | + 'error=${_describeError(error)}', | ||
| 141 | + ); | ||
| 142 | + _publishSyncEvent( | ||
| 143 | + type: OhosHealthRawDataPipelineEventType.syncFailed, | ||
| 144 | + flow: 'syncRawData', | ||
| 145 | + dataType: dataType, | ||
| 146 | + startTime: resolvedStartTime, | ||
| 147 | + endTime: resolvedEndTime, | ||
| 148 | + elapsedMs: totalStopwatch.elapsedMilliseconds, | ||
| 149 | + error: _describeError(error), | ||
| 93 | ); | 150 | ); |
| 94 | rethrow; | 151 | rethrow; |
| 95 | } finally { | 152 | } finally { |
| @@ -108,21 +165,34 @@ class OhosHealthRawDataSyncService { | @@ -108,21 +165,34 @@ class OhosHealthRawDataSyncService { | ||
| 108 | int? endTime, | 165 | int? endTime, |
| 109 | List<int>? dataTypes, | 166 | List<int>? dataTypes, |
| 110 | }) async { | 167 | }) async { |
| 168 | + final totalStopwatch = Stopwatch()..start(); | ||
| 111 | final resolvedEndTime = endTime ?? _unixSeconds(_nowProvider()); | 169 | final resolvedEndTime = endTime ?? _unixSeconds(_nowProvider()); |
| 170 | + final earliestStartTime = _twoMonthLookbackStart(resolvedEndTime); | ||
| 171 | + final eventStartTime = startTime == null | ||
| 172 | + ? earliestStartTime | ||
| 173 | + : math.max(startTime, earliestStartTime); | ||
| 112 | final resolvedDataTypes = dataTypes ?? calculationDataTypes; | 174 | final resolvedDataTypes = dataTypes ?? calculationDataTypes; |
| 113 | _log( | 175 | _log( |
| 114 | 'calculation_sync_start dataTypes=${resolvedDataTypes.join(',')} ' | 176 | 'calculation_sync_start dataTypes=${resolvedDataTypes.join(',')} ' |
| 115 | 'startTime=${startTime ?? ''} endTime=$resolvedEndTime', | 177 | 'startTime=${startTime ?? ''} endTime=$resolvedEndTime', |
| 116 | ); | 178 | ); |
| 179 | + _publishSyncEvent( | ||
| 180 | + type: OhosHealthRawDataPipelineEventType.syncStarted, | ||
| 181 | + flow: 'syncCalculationRawData', | ||
| 182 | + dataTypes: resolvedDataTypes, | ||
| 183 | + startTime: eventStartTime, | ||
| 184 | + endTime: resolvedEndTime, | ||
| 185 | + ); | ||
| 117 | final List<OhosHealthRawDataSyncResult> results; | 186 | final List<OhosHealthRawDataSyncResult> results; |
| 118 | try { | 187 | try { |
| 119 | - final activityGoalFuture = _remoteDataSource.fetchActivityGoal(); | ||
| 120 | - final rawFetchesFuture = Future.wait( | 188 | + final activityGoalFuture = _fetchAndStoreActivityGoalForCalculationSync(); |
| 189 | + final rawSyncFuture = Future.wait( | ||
| 121 | resolvedDataTypes.map( | 190 | resolvedDataTypes.map( |
| 122 | - (dataType) => _fetchRawDataForSync( | 191 | + (dataType) => _syncRawDataForCalculation( |
| 123 | dataType: dataType, | 192 | dataType: dataType, |
| 124 | startTime: startTime, | 193 | startTime: startTime, |
| 125 | endTime: resolvedEndTime, | 194 | endTime: resolvedEndTime, |
| 195 | + storeGate: activityGoalFuture, | ||
| 126 | ), | 196 | ), |
| 127 | ), | 197 | ), |
| 128 | eagerError: true, | 198 | eagerError: true, |
| @@ -130,29 +200,30 @@ class OhosHealthRawDataSyncService { | @@ -130,29 +200,30 @@ class OhosHealthRawDataSyncService { | ||
| 130 | final fetched = await Future.wait<Object?>( | 200 | final fetched = await Future.wait<Object?>( |
| 131 | <Future<Object?>>[ | 201 | <Future<Object?>>[ |
| 132 | activityGoalFuture, | 202 | activityGoalFuture, |
| 133 | - rawFetchesFuture, | 203 | + rawSyncFuture, |
| 134 | ], | 204 | ], |
| 135 | eagerError: true, | 205 | eagerError: true, |
| 136 | ); | 206 | ); |
| 137 | - final activityGoal = fetched[0] as V2ActivityTarget?; | ||
| 138 | - final rawFetches = fetched[1] as List<_FetchedRawDataSync>; | ||
| 139 | - if (activityGoal != null) { | ||
| 140 | - await _localStore.upsertActivityGoal(activityGoal); | ||
| 141 | - _log( | ||
| 142 | - 'calculation_sync_activity_goal_stored ' | ||
| 143 | - 'move=${activityGoal.move} step=${activityGoal.step} ' | ||
| 144 | - 'exercise=${activityGoal.exercise} stand=${activityGoal.stand}', | ||
| 145 | - ); | ||
| 146 | - } | ||
| 147 | - results = <OhosHealthRawDataSyncResult>[]; | ||
| 148 | - for (final fetch in rawFetches) { | ||
| 149 | - results.add(await _storeFetchedRawData(fetch)); | ||
| 150 | - } | 207 | + results = fetched[1] as List<OhosHealthRawDataSyncResult>; |
| 151 | } catch (error, stackTrace) { | 208 | } catch (error, stackTrace) { |
| 152 | _log( | 209 | _log( |
| 153 | 'calculation_sync_failed dataTypes=${resolvedDataTypes.join(',')} ' | 210 | 'calculation_sync_failed dataTypes=${resolvedDataTypes.join(',')} ' |
| 154 | 'startTime=${startTime ?? ''} endTime=$resolvedEndTime ' | 211 | 'startTime=${startTime ?? ''} endTime=$resolvedEndTime ' |
| 155 | - 'error=$error stackTrace=$stackTrace', | 212 | + 'error=${_describeError(error)} stackTrace=$stackTrace', |
| 213 | + ); | ||
| 214 | + _profileLog( | ||
| 215 | + 'calculationSync_failed dataTypes=${resolvedDataTypes.join(',')} ' | ||
| 216 | + 'elapsedMs=${totalStopwatch.elapsedMilliseconds} ' | ||
| 217 | + 'error=${_describeError(error)}', | ||
| 218 | + ); | ||
| 219 | + _publishSyncEvent( | ||
| 220 | + type: OhosHealthRawDataPipelineEventType.syncFailed, | ||
| 221 | + flow: 'syncCalculationRawData', | ||
| 222 | + dataTypes: resolvedDataTypes, | ||
| 223 | + startTime: eventStartTime, | ||
| 224 | + endTime: resolvedEndTime, | ||
| 225 | + elapsedMs: totalStopwatch.elapsedMilliseconds, | ||
| 226 | + error: _describeError(error), | ||
| 156 | ); | 227 | ); |
| 157 | rethrow; | 228 | rethrow; |
| 158 | } | 229 | } |
| @@ -168,14 +239,69 @@ class OhosHealthRawDataSyncService { | @@ -168,14 +239,69 @@ class OhosHealthRawDataSyncService { | ||
| 168 | 'calculation_sync_finish dataTypes=${resolvedDataTypes.join(',')} ' | 239 | 'calculation_sync_finish dataTypes=${resolvedDataTypes.join(',')} ' |
| 169 | 'pageCount=$pageCount storedCount=$storedCount', | 240 | 'pageCount=$pageCount storedCount=$storedCount', |
| 170 | ); | 241 | ); |
| 242 | + _profileLog( | ||
| 243 | + 'calculationSync_finish dataTypes=${resolvedDataTypes.join(',')} ' | ||
| 244 | + 'pageCount=$pageCount storedCount=$storedCount ' | ||
| 245 | + 'elapsedMs=${totalStopwatch.elapsedMilliseconds}', | ||
| 246 | + ); | ||
| 247 | + _publishSyncEvent( | ||
| 248 | + type: OhosHealthRawDataPipelineEventType.syncSucceeded, | ||
| 249 | + flow: 'syncCalculationRawData', | ||
| 250 | + dataTypes: resolvedDataTypes, | ||
| 251 | + startTime: eventStartTime, | ||
| 252 | + endTime: resolvedEndTime, | ||
| 253 | + elapsedMs: totalStopwatch.elapsedMilliseconds, | ||
| 254 | + pageCount: pageCount, | ||
| 255 | + storedCount: storedCount, | ||
| 256 | + ); | ||
| 171 | return results; | 257 | return results; |
| 172 | } | 258 | } |
| 173 | 259 | ||
| 174 | - Future<_FetchedRawDataSync> _fetchRawDataForSync({ | 260 | + Future<V2ActivityTarget?> _fetchActivityGoalForCalculationSync() async { |
| 261 | + final stopwatch = Stopwatch()..start(); | ||
| 262 | + try { | ||
| 263 | + final result = await _remoteDataSource.fetchActivityGoal(); | ||
| 264 | + _profileLog( | ||
| 265 | + 'calculationSync_fetchActivityGoal_finish found=${result != null} ' | ||
| 266 | + 'elapsedMs=${stopwatch.elapsedMilliseconds}', | ||
| 267 | + ); | ||
| 268 | + return result; | ||
| 269 | + } catch (error) { | ||
| 270 | + _profileLog( | ||
| 271 | + 'calculationSync_fetchActivityGoal_failed ' | ||
| 272 | + 'elapsedMs=${stopwatch.elapsedMilliseconds} ' | ||
| 273 | + 'error=${_describeError(error)}', | ||
| 274 | + ); | ||
| 275 | + rethrow; | ||
| 276 | + } | ||
| 277 | + } | ||
| 278 | + | ||
| 279 | + Future<V2ActivityTarget?> | ||
| 280 | + _fetchAndStoreActivityGoalForCalculationSync() async { | ||
| 281 | + final activityGoal = await _fetchActivityGoalForCalculationSync(); | ||
| 282 | + if (activityGoal != null) { | ||
| 283 | + final storeGoalStopwatch = Stopwatch()..start(); | ||
| 284 | + await _localStore.upsertActivityGoal(activityGoal); | ||
| 285 | + _profileLog( | ||
| 286 | + 'calculationSync_storeActivityGoal elapsedMs=' | ||
| 287 | + '${storeGoalStopwatch.elapsedMilliseconds}', | ||
| 288 | + ); | ||
| 289 | + _log( | ||
| 290 | + 'calculation_sync_activity_goal_stored ' | ||
| 291 | + 'move=${activityGoal.move} step=${activityGoal.step} ' | ||
| 292 | + 'exercise=${activityGoal.exercise} stand=${activityGoal.stand}', | ||
| 293 | + ); | ||
| 294 | + } | ||
| 295 | + return activityGoal; | ||
| 296 | + } | ||
| 297 | + | ||
| 298 | + Future<OhosHealthRawDataSyncResult> _syncRawDataForCalculation({ | ||
| 175 | required int dataType, | 299 | required int dataType, |
| 176 | required int? startTime, | 300 | required int? startTime, |
| 177 | required int endTime, | 301 | required int endTime, |
| 302 | + required Future<void> storeGate, | ||
| 178 | }) async { | 303 | }) async { |
| 304 | + final stopwatch = Stopwatch()..start(); | ||
| 179 | final resolvedStartTime = await _resolveStartTime( | 305 | final resolvedStartTime = await _resolveStartTime( |
| 180 | dataType: dataType, | 306 | dataType: dataType, |
| 181 | requestedStartTime: startTime, | 307 | requestedStartTime: startTime, |
| @@ -184,11 +310,33 @@ class OhosHealthRawDataSyncService { | @@ -184,11 +310,33 @@ class OhosHealthRawDataSyncService { | ||
| 184 | if (endTime < resolvedStartTime) { | 310 | if (endTime < resolvedStartTime) { |
| 185 | throw ArgumentError.value(endTime, 'endTime'); | 311 | throw ArgumentError.value(endTime, 'endTime'); |
| 186 | } | 312 | } |
| 187 | - return _fetchResolvedRawData( | ||
| 188 | - dataType: dataType, | ||
| 189 | - startTime: resolvedStartTime, | ||
| 190 | - endTime: endTime, | 313 | + _profileLog( |
| 314 | + 'fetchRawDataForSync_resolved dataType=$dataType ' | ||
| 315 | + 'requestedStartTime=${startTime ?? ''} startTime=$resolvedStartTime ' | ||
| 316 | + 'endTime=$endTime resolveElapsedMs=${stopwatch.elapsedMilliseconds}', | ||
| 191 | ); | 317 | ); |
| 318 | + try { | ||
| 319 | + final result = await _syncResolvedRawData( | ||
| 320 | + dataType: dataType, | ||
| 321 | + startTime: resolvedStartTime, | ||
| 322 | + endTime: endTime, | ||
| 323 | + storeGate: storeGate, | ||
| 324 | + ); | ||
| 325 | + _profileLog( | ||
| 326 | + 'fetchRawDataForSync_finish dataType=$dataType ' | ||
| 327 | + 'segments=${result.segmentCount} pageCount=${result.pageCount} ' | ||
| 328 | + 'storedCount=${result.storedCount} ' | ||
| 329 | + 'elapsedMs=${stopwatch.elapsedMilliseconds}', | ||
| 330 | + ); | ||
| 331 | + return result; | ||
| 332 | + } catch (error) { | ||
| 333 | + _profileLog( | ||
| 334 | + 'fetchRawDataForSync_failed dataType=$dataType ' | ||
| 335 | + 'elapsedMs=${stopwatch.elapsedMilliseconds} ' | ||
| 336 | + 'error=${_describeError(error)}', | ||
| 337 | + ); | ||
| 338 | + rethrow; | ||
| 339 | + } | ||
| 192 | } | 340 | } |
| 193 | 341 | ||
| 194 | Future<List<OhosHealthRawDataItem>> queryRawData({ | 342 | Future<List<OhosHealthRawDataItem>> queryRawData({ |
| @@ -218,7 +366,8 @@ class OhosHealthRawDataSyncService { | @@ -218,7 +366,8 @@ class OhosHealthRawDataSyncService { | ||
| 218 | } | 366 | } |
| 219 | } catch (error, stackTrace) { | 367 | } catch (error, stackTrace) { |
| 220 | _log( | 368 | _log( |
| 221 | - 'activity_goal_remote_failed error=$error stackTrace=$stackTrace', | 369 | + 'activity_goal_remote_failed error=${_describeError(error)} ' |
| 370 | + 'stackTrace=$stackTrace', | ||
| 222 | ); | 371 | ); |
| 223 | } | 372 | } |
| 224 | } | 373 | } |
| @@ -235,20 +384,9 @@ class OhosHealthRawDataSyncService { | @@ -235,20 +384,9 @@ class OhosHealthRawDataSyncService { | ||
| 235 | required int dataType, | 384 | required int dataType, |
| 236 | required int startTime, | 385 | required int startTime, |
| 237 | required int endTime, | 386 | required int endTime, |
| 387 | + Future<void>? storeGate, | ||
| 238 | }) async { | 388 | }) async { |
| 239 | - final fetched = await _fetchResolvedRawData( | ||
| 240 | - dataType: dataType, | ||
| 241 | - startTime: startTime, | ||
| 242 | - endTime: endTime, | ||
| 243 | - ); | ||
| 244 | - return _storeFetchedRawData(fetched); | ||
| 245 | - } | ||
| 246 | - | ||
| 247 | - Future<_FetchedRawDataSync> _fetchResolvedRawData({ | ||
| 248 | - required int dataType, | ||
| 249 | - required int startTime, | ||
| 250 | - required int endTime, | ||
| 251 | - }) async { | 389 | + final stopwatch = Stopwatch()..start(); |
| 252 | final fetchRanges = _splitIntoFetchRanges( | 390 | final fetchRanges = _splitIntoFetchRanges( |
| 253 | dataType: dataType, | 391 | dataType: dataType, |
| 254 | startTime: startTime, | 392 | startTime: startTime, |
| @@ -261,9 +399,9 @@ class OhosHealthRawDataSyncService { | @@ -261,9 +399,9 @@ class OhosHealthRawDataSyncService { | ||
| 261 | 'chunkDays=$fetchChunkDays rangeCount=${fetchRanges.length}', | 399 | 'chunkDays=$fetchChunkDays rangeCount=${fetchRanges.length}', |
| 262 | ); | 400 | ); |
| 263 | 401 | ||
| 264 | - final segments = await Future.wait( | ||
| 265 | - <Future<_FetchedRawDataSegment>>[ | ||
| 266 | - for (var index = 0; index < fetchRanges.length; index++) | 402 | + final pending = <Future<_FetchedRawDataSegmentOutcome>>[ |
| 403 | + for (var index = 0; index < fetchRanges.length; index++) | ||
| 404 | + _trackedSegmentFetch( | ||
| 267 | _fetchRawDataSegment( | 405 | _fetchRawDataSegment( |
| 268 | dataType: dataType, | 406 | dataType: dataType, |
| 269 | range: fetchRanges[index], | 407 | range: fetchRanges[index], |
| @@ -271,16 +409,97 @@ class OhosHealthRawDataSyncService { | @@ -271,16 +409,97 @@ class OhosHealthRawDataSyncService { | ||
| 271 | segmentCount: fetchRanges.length, | 409 | segmentCount: fetchRanges.length, |
| 272 | fetchChunkDays: fetchChunkDays, | 410 | fetchChunkDays: fetchChunkDays, |
| 273 | ), | 411 | ), |
| 274 | - ], | ||
| 275 | - eagerError: true, | 412 | + ), |
| 413 | + ]; | ||
| 414 | + var pageCount = 0; | ||
| 415 | + var fetchedItems = 0; | ||
| 416 | + var storedCount = 0; | ||
| 417 | + int? earliestStoredTime; | ||
| 418 | + try { | ||
| 419 | + while (pending.isNotEmpty) { | ||
| 420 | + final outcome = await Future.any(pending); | ||
| 421 | + pending.remove(outcome.task); | ||
| 422 | + final error = outcome.error; | ||
| 423 | + if (error != null) { | ||
| 424 | + Error.throwWithStackTrace( | ||
| 425 | + error, outcome.stackTrace ?? StackTrace.current); | ||
| 426 | + } | ||
| 427 | + final segment = outcome.segment!; | ||
| 428 | + final page = segment.page; | ||
| 429 | + pageCount += 1; | ||
| 430 | + fetchedItems += page.items.length; | ||
| 431 | + if (page.items.isNotEmpty) { | ||
| 432 | + if (storeGate != null) await storeGate; | ||
| 433 | + final pageStoreStopwatch = Stopwatch()..start(); | ||
| 434 | + final pageStoredCount = await _localStore.upsertRawDataBatch( | ||
| 435 | + dataType: dataType, | ||
| 436 | + items: page.items, | ||
| 437 | + ); | ||
| 438 | + storedCount += pageStoredCount; | ||
| 439 | + if (pageStoredCount > 0) { | ||
| 440 | + for (final item in page.items) { | ||
| 441 | + final keyTime = _dedupeKeyTime(dataType: dataType, item: item); | ||
| 442 | + earliestStoredTime = earliestStoredTime == null | ||
| 443 | + ? keyTime | ||
| 444 | + : math.min(earliestStoredTime, keyTime); | ||
| 445 | + } | ||
| 446 | + } | ||
| 447 | + _profileLog( | ||
| 448 | + 'page_store_finish dataType=$dataType ' | ||
| 449 | + 'segment=${segment.segmentIndex + 1}/${fetchRanges.length} ' | ||
| 450 | + 'fetchedItems=${page.items.length} storedItems=$pageStoredCount ' | ||
| 451 | + 'elapsedMs=${pageStoreStopwatch.elapsedMilliseconds}', | ||
| 452 | + ); | ||
| 453 | + _log( | ||
| 454 | + 'page_stored dataType=$dataType ' | ||
| 455 | + 'segment=${segment.segmentIndex + 1}/${fetchRanges.length} ' | ||
| 456 | + 'fetchedItems=${page.items.length} ' | ||
| 457 | + 'storedItems=$pageStoredCount totalStored=$storedCount', | ||
| 458 | + ); | ||
| 459 | + } | ||
| 460 | + | ||
| 461 | + _log( | ||
| 462 | + 'segment_finish dataType=$dataType ' | ||
| 463 | + 'segment=${segment.segmentIndex + 1}/${fetchRanges.length}', | ||
| 464 | + ); | ||
| 465 | + } | ||
| 466 | + } catch (error) { | ||
| 467 | + _profileLog( | ||
| 468 | + 'syncResolvedRawData_failed dataType=$dataType ' | ||
| 469 | + 'rangeCount=${fetchRanges.length} ' | ||
| 470 | + 'elapsedMs=${stopwatch.elapsedMilliseconds} ' | ||
| 471 | + 'error=${_describeError(error)}', | ||
| 472 | + ); | ||
| 473 | + rethrow; | ||
| 474 | + } | ||
| 475 | + | ||
| 476 | + _log( | ||
| 477 | + 'sync_finish dataType=$dataType ' | ||
| 478 | + 'startTime=$startTime endTime=$endTime ' | ||
| 479 | + 'chunkDays=$fetchChunkDays rangeCount=${fetchRanges.length} ' | ||
| 480 | + 'pageCount=$pageCount ' | ||
| 481 | + 'storedCount=$storedCount', | ||
| 276 | ); | 482 | ); |
| 277 | - segments.sort((a, b) => a.segmentIndex.compareTo(b.segmentIndex)); | ||
| 278 | - return _FetchedRawDataSync( | 483 | + _profileLog( |
| 484 | + 'syncResolvedRawData_finish dataType=$dataType ' | ||
| 485 | + 'pageCount=$pageCount fetchedItems=$fetchedItems ' | ||
| 486 | + 'storedCount=$storedCount ' | ||
| 487 | + 'elapsedMs=${stopwatch.elapsedMilliseconds}', | ||
| 488 | + ); | ||
| 489 | + _profileLog( | ||
| 490 | + 'storeFetchedRawData_finish dataType=$dataType ' | ||
| 491 | + 'pageCount=$pageCount fetchedItems=$fetchedItems ' | ||
| 492 | + 'storedCount=$storedCount ' | ||
| 493 | + 'elapsedMs=${stopwatch.elapsedMilliseconds}', | ||
| 494 | + ); | ||
| 495 | + return OhosHealthRawDataSyncResult( | ||
| 279 | dataType: dataType, | 496 | dataType: dataType, |
| 280 | startTime: startTime, | 497 | startTime: startTime, |
| 281 | endTime: endTime, | 498 | endTime: endTime, |
| 282 | - fetchChunkDays: fetchChunkDays, | ||
| 283 | - segments: segments, | 499 | + segmentCount: fetchRanges.length, |
| 500 | + pageCount: pageCount, | ||
| 501 | + storedCount: storedCount, | ||
| 502 | + earliestStoredTime: earliestStoredTime, | ||
| 284 | ); | 503 | ); |
| 285 | } | 504 | } |
| 286 | 505 | ||
| @@ -291,6 +510,8 @@ class OhosHealthRawDataSyncService { | @@ -291,6 +510,8 @@ class OhosHealthRawDataSyncService { | ||
| 291 | required int segmentCount, | 510 | required int segmentCount, |
| 292 | required int fetchChunkDays, | 511 | required int fetchChunkDays, |
| 293 | }) async { | 512 | }) async { |
| 513 | + final stopwatch = Stopwatch()..start(); | ||
| 514 | + var queueWaitMs = 0; | ||
| 294 | _log( | 515 | _log( |
| 295 | 'segment_start dataType=$dataType ' | 516 | 'segment_start dataType=$dataType ' |
| 296 | 'segment=${segmentIndex + 1}/$segmentCount ' | 517 | 'segment=${segmentIndex + 1}/$segmentCount ' |
| @@ -301,16 +522,49 @@ class OhosHealthRawDataSyncService { | @@ -301,16 +522,49 @@ class OhosHealthRawDataSyncService { | ||
| 301 | 'apiEndDate=${_exclusiveEndDateKeyFromUnixSeconds(range.endTime)}', | 522 | 'apiEndDate=${_exclusiveEndDateKeyFromUnixSeconds(range.endTime)}', |
| 302 | ); | 523 | ); |
| 303 | 524 | ||
| 304 | - final page = await _remoteDataSource.fetchRawDataPage( | ||
| 305 | - dataType: dataType, | ||
| 306 | - startTime: range.startTime, | ||
| 307 | - endTime: range.endTime, | ||
| 308 | - ); | 525 | + final OhosHealthRawDataPage page; |
| 526 | + try { | ||
| 527 | + page = await _fetchLimiter.run( | ||
| 528 | + () { | ||
| 529 | + queueWaitMs = stopwatch.elapsedMilliseconds; | ||
| 530 | + _profileLog( | ||
| 531 | + 'segment_fetch_acquired dataType=$dataType ' | ||
| 532 | + 'segment=${segmentIndex + 1}/$segmentCount ' | ||
| 533 | + 'queueWaitMs=$queueWaitMs activeFetches=' | ||
| 534 | + '${_fetchLimiter.activeCount}', | ||
| 535 | + ); | ||
| 536 | + return _remoteDataSource.fetchRawDataPage( | ||
| 537 | + dataType: dataType, | ||
| 538 | + startTime: range.startTime, | ||
| 539 | + endTime: range.endTime, | ||
| 540 | + ); | ||
| 541 | + }, | ||
| 542 | + ); | ||
| 543 | + } catch (error) { | ||
| 544 | + _profileLog( | ||
| 545 | + 'segment_fetch_failed dataType=$dataType ' | ||
| 546 | + 'segment=${segmentIndex + 1}/$segmentCount ' | ||
| 547 | + 'startTime=${range.startTime} endTime=${range.endTime} ' | ||
| 548 | + 'queueWaitMs=$queueWaitMs ' | ||
| 549 | + 'requestElapsedMs=${stopwatch.elapsedMilliseconds - queueWaitMs} ' | ||
| 550 | + 'elapsedMs=${stopwatch.elapsedMilliseconds} ' | ||
| 551 | + 'error=${_describeError(error)}', | ||
| 552 | + ); | ||
| 553 | + rethrow; | ||
| 554 | + } | ||
| 309 | _log( | 555 | _log( |
| 310 | 'page_fetched dataType=$dataType ' | 556 | 'page_fetched dataType=$dataType ' |
| 311 | 'segment=${segmentIndex + 1}/$segmentCount ' | 557 | 'segment=${segmentIndex + 1}/$segmentCount ' |
| 312 | 'items=${page.items.length}', | 558 | 'items=${page.items.length}', |
| 313 | ); | 559 | ); |
| 560 | + _profileLog( | ||
| 561 | + 'segment_fetch_finish dataType=$dataType ' | ||
| 562 | + 'segment=${segmentIndex + 1}/$segmentCount ' | ||
| 563 | + 'startTime=${range.startTime} endTime=${range.endTime} ' | ||
| 564 | + 'items=${page.items.length} queueWaitMs=$queueWaitMs ' | ||
| 565 | + 'requestElapsedMs=${stopwatch.elapsedMilliseconds - queueWaitMs} ' | ||
| 566 | + 'elapsedMs=${stopwatch.elapsedMilliseconds}', | ||
| 567 | + ); | ||
| 314 | 568 | ||
| 315 | return _FetchedRawDataSegment( | 569 | return _FetchedRawDataSegment( |
| 316 | segmentIndex: segmentIndex, | 570 | segmentIndex: segmentIndex, |
| @@ -319,52 +573,23 @@ class OhosHealthRawDataSyncService { | @@ -319,52 +573,23 @@ class OhosHealthRawDataSyncService { | ||
| 319 | ); | 573 | ); |
| 320 | } | 574 | } |
| 321 | 575 | ||
| 322 | - Future<OhosHealthRawDataSyncResult> _storeFetchedRawData( | ||
| 323 | - _FetchedRawDataSync fetched, | ||
| 324 | - ) async { | ||
| 325 | - var pageCount = 0; | ||
| 326 | - var storedCount = 0; | ||
| 327 | - | ||
| 328 | - for (final segment in fetched.segments) { | ||
| 329 | - final page = segment.page; | ||
| 330 | - pageCount += 1; | ||
| 331 | - if (page.items.isNotEmpty) { | ||
| 332 | - final pageStoredCount = await _localStore.upsertRawDataBatch( | ||
| 333 | - dataType: fetched.dataType, | ||
| 334 | - items: page.items, | ||
| 335 | - ); | ||
| 336 | - storedCount += pageStoredCount; | ||
| 337 | - _log( | ||
| 338 | - 'page_stored dataType=${fetched.dataType} ' | ||
| 339 | - 'segment=${segment.segmentIndex + 1}/${fetched.segmentCount} ' | ||
| 340 | - 'fetchedItems=${page.items.length} ' | ||
| 341 | - 'storedItems=$pageStoredCount totalStored=$storedCount', | ||
| 342 | - ); | ||
| 343 | - } | ||
| 344 | - | ||
| 345 | - _log( | ||
| 346 | - 'segment_finish dataType=${fetched.dataType} ' | ||
| 347 | - 'segment=${segment.segmentIndex + 1}/${fetched.segmentCount}', | ||
| 348 | - ); | ||
| 349 | - } | ||
| 350 | - | ||
| 351 | - _log( | ||
| 352 | - 'sync_finish dataType=${fetched.dataType} ' | ||
| 353 | - 'startTime=${fetched.startTime} endTime=${fetched.endTime} ' | ||
| 354 | - 'chunkDays=${fetched.fetchChunkDays} rangeCount=${fetched.segmentCount} ' | ||
| 355 | - 'pageCount=$pageCount ' | ||
| 356 | - 'storedCount=$storedCount', | ||
| 357 | - ); | ||
| 358 | - | ||
| 359 | - return OhosHealthRawDataSyncResult( | ||
| 360 | - dataType: fetched.dataType, | ||
| 361 | - startTime: fetched.startTime, | ||
| 362 | - endTime: fetched.endTime, | ||
| 363 | - segmentCount: fetched.segmentCount, | ||
| 364 | - pageCount: pageCount, | ||
| 365 | - storedCount: storedCount, | ||
| 366 | - earliestStoredTime: storedCount > 0 ? fetched.earliestItemTime : null, | 576 | + Future<_FetchedRawDataSegmentOutcome> _trackedSegmentFetch( |
| 577 | + Future<_FetchedRawDataSegment> future, | ||
| 578 | + ) { | ||
| 579 | + late final Future<_FetchedRawDataSegmentOutcome> tracked; | ||
| 580 | + tracked = future.then( | ||
| 581 | + (segment) => _FetchedRawDataSegmentOutcome( | ||
| 582 | + task: tracked, | ||
| 583 | + segment: segment, | ||
| 584 | + ), | ||
| 585 | + onError: (Object error, StackTrace stackTrace) => | ||
| 586 | + _FetchedRawDataSegmentOutcome( | ||
| 587 | + task: tracked, | ||
| 588 | + error: error, | ||
| 589 | + stackTrace: stackTrace, | ||
| 590 | + ), | ||
| 367 | ); | 591 | ); |
| 592 | + return tracked; | ||
| 368 | } | 593 | } |
| 369 | 594 | ||
| 370 | Future<int> _resolveStartTime({ | 595 | Future<int> _resolveStartTime({ |
| @@ -373,22 +598,18 @@ class OhosHealthRawDataSyncService { | @@ -373,22 +598,18 @@ class OhosHealthRawDataSyncService { | ||
| 373 | required int endTime, | 598 | required int endTime, |
| 374 | }) async { | 599 | }) async { |
| 375 | final latestDataTime = await _localStore.latestDataTime(dataType: dataType); | 600 | final latestDataTime = await _localStore.latestDataTime(dataType: dataType); |
| 601 | + final earliestStartTime = _twoMonthLookbackStart(endTime); | ||
| 376 | final int baseStartTime; | 602 | final int baseStartTime; |
| 377 | if (latestDataTime != null) { | 603 | if (latestDataTime != null) { |
| 378 | - final backfillStart = math.max( | ||
| 379 | - 0, | ||
| 380 | - latestDataTime - incrementalBackfillDays * Duration.secondsPerDay, | ||
| 381 | - ); | ||
| 382 | baseStartTime = requestedStartTime == null | 604 | baseStartTime = requestedStartTime == null |
| 383 | - ? backfillStart | ||
| 384 | - : math.min(requestedStartTime, backfillStart); | 605 | + ? latestDataTime |
| 606 | + : math.min(requestedStartTime, latestDataTime); | ||
| 385 | } else { | 607 | } else { |
| 386 | - baseStartTime = requestedStartTime ?? | ||
| 387 | - _unixSeconds( | ||
| 388 | - _nowProvider().subtract(const Duration(days: defaultLookbackDays)), | ||
| 389 | - ); | 608 | + baseStartTime = requestedStartTime ?? earliestStartTime; |
| 390 | } | 609 | } |
| 391 | - final resolvedStartTime = _startOfLocalDay(baseStartTime); | 610 | + final resolvedStartTime = _startOfLocalDay( |
| 611 | + math.max(baseStartTime, earliestStartTime), | ||
| 612 | + ); | ||
| 392 | return resolvedStartTime > endTime ? endTime : resolvedStartTime; | 613 | return resolvedStartTime > endTime ? endTime : resolvedStartTime; |
| 393 | } | 614 | } |
| 394 | 615 | ||
| @@ -401,6 +622,12 @@ class OhosHealthRawDataSyncService { | @@ -401,6 +622,12 @@ class OhosHealthRawDataSyncService { | ||
| 401 | return _unixSeconds(DateTime(dateTime.year, dateTime.month, dateTime.day)); | 622 | return _unixSeconds(DateTime(dateTime.year, dateTime.month, dateTime.day)); |
| 402 | } | 623 | } |
| 403 | 624 | ||
| 625 | + int _twoMonthLookbackStart(int endTime) { | ||
| 626 | + final endDate = DateTime.fromMillisecondsSinceEpoch(endTime * 1000); | ||
| 627 | + return _unixSeconds(DateTime( | ||
| 628 | + endDate.year, endDate.month - defaultLookbackMonths, endDate.day)); | ||
| 629 | + } | ||
| 630 | + | ||
| 404 | List<OhosHealthRawDataFetchRange> _splitIntoFetchRanges({ | 631 | List<OhosHealthRawDataFetchRange> _splitIntoFetchRanges({ |
| 405 | required int dataType, | 632 | required int dataType, |
| 406 | required int startTime, | 633 | required int startTime, |
| @@ -450,6 +677,40 @@ class OhosHealthRawDataSyncService { | @@ -450,6 +677,40 @@ class OhosHealthRawDataSyncService { | ||
| 450 | // AppLogger may not be initialized in isolated test/bootstrap contexts. | 677 | // AppLogger may not be initialized in isolated test/bootstrap contexts. |
| 451 | } | 678 | } |
| 452 | } | 679 | } |
| 680 | + | ||
| 681 | + void _profileLog(String message) { | ||
| 682 | + if (!kDebugMode) return; | ||
| 683 | + _log('$profileLogMarker $message'); | ||
| 684 | + } | ||
| 685 | + | ||
| 686 | + void _publishSyncEvent({ | ||
| 687 | + required OhosHealthRawDataPipelineEventType type, | ||
| 688 | + required String flow, | ||
| 689 | + int? dataType, | ||
| 690 | + List<int>? dataTypes, | ||
| 691 | + int? startTime, | ||
| 692 | + int? endTime, | ||
| 693 | + int? elapsedMs, | ||
| 694 | + int? pageCount, | ||
| 695 | + int? storedCount, | ||
| 696 | + String? error, | ||
| 697 | + }) { | ||
| 698 | + OhosHealthRawDataPipelineEvents.publish( | ||
| 699 | + OhosHealthRawDataPipelineEvent( | ||
| 700 | + type: type, | ||
| 701 | + flow: flow, | ||
| 702 | + occurredAt: _nowProvider(), | ||
| 703 | + dataType: dataType, | ||
| 704 | + dataTypes: dataTypes == null ? null : List<int>.unmodifiable(dataTypes), | ||
| 705 | + startTime: startTime, | ||
| 706 | + endTime: endTime, | ||
| 707 | + elapsedMs: elapsedMs, | ||
| 708 | + pageCount: pageCount, | ||
| 709 | + storedCount: storedCount, | ||
| 710 | + error: error, | ||
| 711 | + ), | ||
| 712 | + ); | ||
| 713 | + } | ||
| 453 | } | 714 | } |
| 454 | 715 | ||
| 455 | abstract class OhosHealthRawDataRemoteDataSource { | 716 | abstract class OhosHealthRawDataRemoteDataSource { |
| @@ -528,7 +789,7 @@ class OhosHarmonyHealthRawDataRemoteDataSource | @@ -528,7 +789,7 @@ class OhosHarmonyHealthRawDataRemoteDataSource | ||
| 528 | ), | 789 | ), |
| 529 | ), | 790 | ), |
| 530 | AppFailure<HmSleepData>(:final error) => throw StateError( | 791 | AppFailure<HmSleepData>(:final error) => throw StateError( |
| 531 | - 'OHOS sleep raw data fetch failed: $error', | 792 | + 'OHOS sleep raw data fetch failed: ${_describeAppError(error)}', |
| 532 | ), | 793 | ), |
| 533 | }; | 794 | }; |
| 534 | } | 795 | } |
| @@ -541,7 +802,7 @@ class OhosHarmonyHealthRawDataRemoteDataSource | @@ -541,7 +802,7 @@ class OhosHarmonyHealthRawDataRemoteDataSource | ||
| 541 | ), | 802 | ), |
| 542 | ), | 803 | ), |
| 543 | AppFailure<HmWorkoutData>(:final error) => throw StateError( | 804 | AppFailure<HmWorkoutData>(:final error) => throw StateError( |
| 544 | - 'OHOS workout raw data fetch failed: $error', | 805 | + 'OHOS workout raw data fetch failed: ${_describeAppError(error)}', |
| 545 | ), | 806 | ), |
| 546 | }; | 807 | }; |
| 547 | } | 808 | } |
| @@ -560,7 +821,7 @@ class OhosHarmonyHealthRawDataRemoteDataSource | @@ -560,7 +821,7 @@ class OhosHarmonyHealthRawDataRemoteDataSource | ||
| 560 | ), | 821 | ), |
| 561 | ), | 822 | ), |
| 562 | AppFailure<HmHealthData>(:final error) => throw StateError( | 823 | AppFailure<HmHealthData>(:final error) => throw StateError( |
| 563 | - 'OHOS health raw data fetch failed: $error', | 824 | + 'OHOS health raw data fetch failed: ${_describeAppError(error)}', |
| 564 | ), | 825 | ), |
| 565 | }; | 826 | }; |
| 566 | } | 827 | } |
| @@ -571,7 +832,7 @@ class OhosHarmonyHealthRawDataRemoteDataSource | @@ -571,7 +832,7 @@ class OhosHarmonyHealthRawDataRemoteDataSource | ||
| 571 | return switch (result) { | 832 | return switch (result) { |
| 572 | AppSuccess<V2ActivityTarget>(:final data) => data, | 833 | AppSuccess<V2ActivityTarget>(:final data) => data, |
| 573 | AppFailure<V2ActivityTarget>(:final error) => throw StateError( | 834 | AppFailure<V2ActivityTarget>(:final error) => throw StateError( |
| 574 | - 'OHOS activity goal fetch failed: $error', | 835 | + 'OHOS activity goal fetch failed: ${_describeAppError(error)}', |
| 575 | ), | 836 | ), |
| 576 | }; | 837 | }; |
| 577 | } | 838 | } |
| @@ -726,35 +987,6 @@ class OhosHealthRawDataFetchRange { | @@ -726,35 +987,6 @@ class OhosHealthRawDataFetchRange { | ||
| 726 | final int endTime; | 987 | final int endTime; |
| 727 | } | 988 | } |
| 728 | 989 | ||
| 729 | -class _FetchedRawDataSync { | ||
| 730 | - const _FetchedRawDataSync({ | ||
| 731 | - required this.dataType, | ||
| 732 | - required this.startTime, | ||
| 733 | - required this.endTime, | ||
| 734 | - required this.fetchChunkDays, | ||
| 735 | - required this.segments, | ||
| 736 | - }); | ||
| 737 | - | ||
| 738 | - final int dataType; | ||
| 739 | - final int startTime; | ||
| 740 | - final int endTime; | ||
| 741 | - final int fetchChunkDays; | ||
| 742 | - final List<_FetchedRawDataSegment> segments; | ||
| 743 | - | ||
| 744 | - int get segmentCount => segments.length; | ||
| 745 | - | ||
| 746 | - int? get earliestItemTime { | ||
| 747 | - int? earliest; | ||
| 748 | - for (final segment in segments) { | ||
| 749 | - for (final item in segment.page.items) { | ||
| 750 | - final keyTime = _dedupeKeyTime(dataType: dataType, item: item); | ||
| 751 | - earliest = earliest == null ? keyTime : math.min(earliest, keyTime); | ||
| 752 | - } | ||
| 753 | - } | ||
| 754 | - return earliest; | ||
| 755 | - } | ||
| 756 | -} | ||
| 757 | - | ||
| 758 | class _FetchedRawDataSegment { | 990 | class _FetchedRawDataSegment { |
| 759 | const _FetchedRawDataSegment({ | 991 | const _FetchedRawDataSegment({ |
| 760 | required this.segmentIndex, | 992 | required this.segmentIndex, |
| @@ -767,6 +999,20 @@ class _FetchedRawDataSegment { | @@ -767,6 +999,20 @@ class _FetchedRawDataSegment { | ||
| 767 | final OhosHealthRawDataPage page; | 999 | final OhosHealthRawDataPage page; |
| 768 | } | 1000 | } |
| 769 | 1001 | ||
| 1002 | +class _FetchedRawDataSegmentOutcome { | ||
| 1003 | + const _FetchedRawDataSegmentOutcome({ | ||
| 1004 | + required this.task, | ||
| 1005 | + this.segment, | ||
| 1006 | + this.error, | ||
| 1007 | + this.stackTrace, | ||
| 1008 | + }); | ||
| 1009 | + | ||
| 1010 | + final Future<_FetchedRawDataSegmentOutcome> task; | ||
| 1011 | + final _FetchedRawDataSegment? segment; | ||
| 1012 | + final Object? error; | ||
| 1013 | + final StackTrace? stackTrace; | ||
| 1014 | +} | ||
| 1015 | + | ||
| 770 | class OhosHealthRawDataSyncResult { | 1016 | class OhosHealthRawDataSyncResult { |
| 771 | const OhosHealthRawDataSyncResult({ | 1017 | const OhosHealthRawDataSyncResult({ |
| 772 | required this.dataType, | 1018 | required this.dataType, |
| @@ -832,3 +1078,79 @@ int _exclusiveEndDateKeyFromUnixSeconds(int seconds) { | @@ -832,3 +1078,79 @@ int _exclusiveEndDateKeyFromUnixSeconds(int seconds) { | ||
| 832 | int _dateKeyFromDateTime(DateTime dateTime) { | 1078 | int _dateKeyFromDateTime(DateTime dateTime) { |
| 833 | return dateTime.year * 10000 + dateTime.month * 100 + dateTime.day; | 1079 | return dateTime.year * 10000 + dateTime.month * 100 + dateTime.day; |
| 834 | } | 1080 | } |
| 1081 | + | ||
| 1082 | +String _describeError(Object error) { | ||
| 1083 | + if (error is AppError) return _describeAppError(error); | ||
| 1084 | + return error.toString(); | ||
| 1085 | +} | ||
| 1086 | + | ||
| 1087 | +String _describeAppError(AppError error) { | ||
| 1088 | + return switch (error) { | ||
| 1089 | + AppNetworkError() => _describeAppNetworkError(error), | ||
| 1090 | + AppHttpError( | ||
| 1091 | + :final statusCode, | ||
| 1092 | + :final businessCode, | ||
| 1093 | + :final businessMessage, | ||
| 1094 | + :final cause, | ||
| 1095 | + ) => | ||
| 1096 | + 'AppHttpError(statusCode=$statusCode, businessCode=$businessCode, ' | ||
| 1097 | + 'businessMessage=$businessMessage, cause=$cause)', | ||
| 1098 | + AppCancelledError() => 'AppCancelledError', | ||
| 1099 | + AppUnknownError(:final cause) => 'AppUnknownError(cause=$cause)', | ||
| 1100 | + }; | ||
| 1101 | +} | ||
| 1102 | + | ||
| 1103 | +String _describeAppNetworkError(AppNetworkError error) { | ||
| 1104 | + final cause = error.cause; | ||
| 1105 | + if (cause is DioException) { | ||
| 1106 | + return 'AppNetworkError(' | ||
| 1107 | + 'dioType=${cause.type}, ' | ||
| 1108 | + 'message=${cause.message}, ' | ||
| 1109 | + 'uri=${cause.requestOptions.uri}, ' | ||
| 1110 | + 'method=${cause.requestOptions.method}, ' | ||
| 1111 | + 'statusCode=${cause.response?.statusCode}' | ||
| 1112 | + ')'; | ||
| 1113 | + } | ||
| 1114 | + return 'AppNetworkError(cause=$cause)'; | ||
| 1115 | +} | ||
| 1116 | + | ||
| 1117 | +class _AsyncLimiter { | ||
| 1118 | + _AsyncLimiter(this._maxConcurrent) { | ||
| 1119 | + if (_maxConcurrent <= 0) { | ||
| 1120 | + throw ArgumentError.value(_maxConcurrent, 'maxConcurrentFetches'); | ||
| 1121 | + } | ||
| 1122 | + } | ||
| 1123 | + | ||
| 1124 | + final int _maxConcurrent; | ||
| 1125 | + final Queue<Completer<void>> _waiters = Queue<Completer<void>>(); | ||
| 1126 | + int _activeCount = 0; | ||
| 1127 | + | ||
| 1128 | + int get activeCount => _activeCount; | ||
| 1129 | + | ||
| 1130 | + Future<T> run<T>(Future<T> Function() action) async { | ||
| 1131 | + await _acquire(); | ||
| 1132 | + try { | ||
| 1133 | + return await action(); | ||
| 1134 | + } finally { | ||
| 1135 | + _release(); | ||
| 1136 | + } | ||
| 1137 | + } | ||
| 1138 | + | ||
| 1139 | + Future<void> _acquire() { | ||
| 1140 | + if (_activeCount < _maxConcurrent) { | ||
| 1141 | + _activeCount += 1; | ||
| 1142 | + return Future<void>.value(); | ||
| 1143 | + } | ||
| 1144 | + final waiter = Completer<void>(); | ||
| 1145 | + _waiters.add(waiter); | ||
| 1146 | + return waiter.future; | ||
| 1147 | + } | ||
| 1148 | + | ||
| 1149 | + void _release() { | ||
| 1150 | + if (_waiters.isNotEmpty) { | ||
| 1151 | + _waiters.removeFirst().complete(); | ||
| 1152 | + return; | ||
| 1153 | + } | ||
| 1154 | + _activeCount -= 1; | ||
| 1155 | + } | ||
| 1156 | +} |
| 1 | +import 'dart:io'; | ||
| 2 | + | ||
| 3 | +import 'package:flutter/foundation.dart'; | ||
| 4 | +import 'package:path_provider/path_provider.dart'; | ||
| 5 | +import 'package:sqflite/sqflite.dart'; | ||
| 6 | + | ||
| 7 | +import '../../../logging/app_logger.dart'; | ||
| 8 | + | ||
| 9 | +class OhosSqliteWriteBenchmark { | ||
| 10 | + const OhosSqliteWriteBenchmark(); | ||
| 11 | + | ||
| 12 | + static const logMarker = '[OHOS_SQLITE_BENCH]'; | ||
| 13 | + | ||
| 14 | + Future<String> run() async { | ||
| 15 | + final totalStopwatch = Stopwatch()..start(); | ||
| 16 | + final tempDir = await getTemporaryDirectory(); | ||
| 17 | + final path = | ||
| 18 | + '${tempDir.path}/ohos_sqlite_write_bench_${DateTime.now().millisecondsSinceEpoch}.sqlite'; | ||
| 19 | + final db = await openDatabase(path); | ||
| 20 | + final results = <_BenchmarkResult>[]; | ||
| 21 | + try { | ||
| 22 | + _log('start path=$path'); | ||
| 23 | + results.add(await _runBatchInsert( | ||
| 24 | + db: db, | ||
| 25 | + table: 'batch_false_500', | ||
| 26 | + rowCount: 500, | ||
| 27 | + noResult: false, | ||
| 28 | + )); | ||
| 29 | + results.add(await _runBatchInsert( | ||
| 30 | + db: db, | ||
| 31 | + table: 'batch_false_1000', | ||
| 32 | + rowCount: 1000, | ||
| 33 | + noResult: false, | ||
| 34 | + )); | ||
| 35 | + results.add(await _runBatchInsert( | ||
| 36 | + db: db, | ||
| 37 | + table: 'batch_false_2000', | ||
| 38 | + rowCount: 2000, | ||
| 39 | + noResult: false, | ||
| 40 | + )); | ||
| 41 | + results.add(await _runBatchInsert( | ||
| 42 | + db: db, | ||
| 43 | + table: 'batch_true_500', | ||
| 44 | + rowCount: 500, | ||
| 45 | + noResult: true, | ||
| 46 | + )); | ||
| 47 | + results.add(await _runBatchInsert( | ||
| 48 | + db: db, | ||
| 49 | + table: 'batch_true_1000', | ||
| 50 | + rowCount: 1000, | ||
| 51 | + noResult: true, | ||
| 52 | + )); | ||
| 53 | + results.add(await _runBatchInsert( | ||
| 54 | + db: db, | ||
| 55 | + table: 'batch_true_2000', | ||
| 56 | + rowCount: 2000, | ||
| 57 | + noResult: true, | ||
| 58 | + )); | ||
| 59 | + results.add(await _runChunkedBatchInsert( | ||
| 60 | + db: db, | ||
| 61 | + table: 'chunked_true_2000', | ||
| 62 | + rowCount: 2000, | ||
| 63 | + chunkSize: 500, | ||
| 64 | + )); | ||
| 65 | + results.add(await _runExistingKeyQuery( | ||
| 66 | + db: db, | ||
| 67 | + table: 'existing_key_query_2000', | ||
| 68 | + rowCount: 2000, | ||
| 69 | + chunkSize: 900, | ||
| 70 | + )); | ||
| 71 | + } finally { | ||
| 72 | + await db.close(); | ||
| 73 | + try { | ||
| 74 | + await File(path).delete(); | ||
| 75 | + } catch (_) { | ||
| 76 | + // Best-effort cleanup for a debug-only benchmark database. | ||
| 77 | + } | ||
| 78 | + } | ||
| 79 | + | ||
| 80 | + final lines = <String>[ | ||
| 81 | + 'OHOS SQLite 写入性能测试完成,总耗时 ${totalStopwatch.elapsedMilliseconds}ms', | ||
| 82 | + for (final result in results) result.summary, | ||
| 83 | + ]; | ||
| 84 | + final report = lines.join('\n'); | ||
| 85 | + _log('finish totalMs=${totalStopwatch.elapsedMilliseconds}'); | ||
| 86 | + return report; | ||
| 87 | + } | ||
| 88 | + | ||
| 89 | + Future<_BenchmarkResult> _runBatchInsert({ | ||
| 90 | + required Database db, | ||
| 91 | + required String table, | ||
| 92 | + required int rowCount, | ||
| 93 | + required bool noResult, | ||
| 94 | + }) async { | ||
| 95 | + await _createRawTable(db, table); | ||
| 96 | + final stopwatch = Stopwatch()..start(); | ||
| 97 | + await db.transaction((txn) async { | ||
| 98 | + final batch = txn.batch(); | ||
| 99 | + for (var index = 0; index < rowCount; index += 1) { | ||
| 100 | + batch.insert( | ||
| 101 | + table, | ||
| 102 | + _row(index), | ||
| 103 | + conflictAlgorithm: ConflictAlgorithm.ignore, | ||
| 104 | + ); | ||
| 105 | + } | ||
| 106 | + await batch.commit(noResult: noResult); | ||
| 107 | + }); | ||
| 108 | + return _finish( | ||
| 109 | + name: 'batch(noResult:$noResult)', | ||
| 110 | + rowCount: rowCount, | ||
| 111 | + elapsedMs: stopwatch.elapsedMilliseconds, | ||
| 112 | + ); | ||
| 113 | + } | ||
| 114 | + | ||
| 115 | + Future<_BenchmarkResult> _runChunkedBatchInsert({ | ||
| 116 | + required Database db, | ||
| 117 | + required String table, | ||
| 118 | + required int rowCount, | ||
| 119 | + required int chunkSize, | ||
| 120 | + }) async { | ||
| 121 | + await _createRawTable(db, table); | ||
| 122 | + final stopwatch = Stopwatch()..start(); | ||
| 123 | + for (var start = 0; start < rowCount; start += chunkSize) { | ||
| 124 | + final end = (start + chunkSize).clamp(0, rowCount); | ||
| 125 | + await db.transaction((txn) async { | ||
| 126 | + final batch = txn.batch(); | ||
| 127 | + for (var index = start; index < end; index += 1) { | ||
| 128 | + batch.insert( | ||
| 129 | + table, | ||
| 130 | + _row(index), | ||
| 131 | + conflictAlgorithm: ConflictAlgorithm.ignore, | ||
| 132 | + ); | ||
| 133 | + } | ||
| 134 | + await batch.commit(noResult: true); | ||
| 135 | + }); | ||
| 136 | + } | ||
| 137 | + return _finish( | ||
| 138 | + name: 'chunkedBatch(noResult:true,chunk:$chunkSize)', | ||
| 139 | + rowCount: rowCount, | ||
| 140 | + elapsedMs: stopwatch.elapsedMilliseconds, | ||
| 141 | + ); | ||
| 142 | + } | ||
| 143 | + | ||
| 144 | + Future<_BenchmarkResult> _runExistingKeyQuery({ | ||
| 145 | + required Database db, | ||
| 146 | + required String table, | ||
| 147 | + required int rowCount, | ||
| 148 | + required int chunkSize, | ||
| 149 | + }) async { | ||
| 150 | + await _createRawTable(db, table); | ||
| 151 | + await db.transaction((txn) async { | ||
| 152 | + final batch = txn.batch(); | ||
| 153 | + for (var index = 0; index < rowCount; index += 1) { | ||
| 154 | + batch.insert(table, _row(index), | ||
| 155 | + conflictAlgorithm: ConflictAlgorithm.ignore); | ||
| 156 | + } | ||
| 157 | + await batch.commit(noResult: true); | ||
| 158 | + }); | ||
| 159 | + | ||
| 160 | + final keys = List<int>.generate(rowCount, (index) => 1780000000 + index); | ||
| 161 | + final stopwatch = Stopwatch()..start(); | ||
| 162 | + var found = 0; | ||
| 163 | + for (var offset = 0; offset < keys.length; offset += chunkSize) { | ||
| 164 | + final chunk = keys.skip(offset).take(chunkSize).toList(growable: false); | ||
| 165 | + final placeholders = List<String>.filled(chunk.length, '?').join(','); | ||
| 166 | + final rows = await db.query( | ||
| 167 | + table, | ||
| 168 | + columns: const <String>['time'], | ||
| 169 | + where: 'time IN ($placeholders)', | ||
| 170 | + whereArgs: chunk, | ||
| 171 | + ); | ||
| 172 | + found += rows.length; | ||
| 173 | + } | ||
| 174 | + final elapsedMs = stopwatch.elapsedMilliseconds; | ||
| 175 | + _log( | ||
| 176 | + 'result name=existingKeyQuery rows=$rowCount found=$found ' | ||
| 177 | + 'chunkSize=$chunkSize elapsedMs=$elapsedMs', | ||
| 178 | + ); | ||
| 179 | + return _BenchmarkResult( | ||
| 180 | + name: 'existingKeyQuery(chunk:$chunkSize)', | ||
| 181 | + rowCount: rowCount, | ||
| 182 | + elapsedMs: elapsedMs, | ||
| 183 | + ); | ||
| 184 | + } | ||
| 185 | + | ||
| 186 | + Future<void> _createRawTable(Database db, String table) async { | ||
| 187 | + await db.execute(''' | ||
| 188 | +CREATE TABLE IF NOT EXISTS $table ( | ||
| 189 | + id INTEGER PRIMARY KEY AUTOINCREMENT, | ||
| 190 | + time INTEGER NOT NULL, | ||
| 191 | + value REAL, | ||
| 192 | + is_asleep INTEGER, | ||
| 193 | + date_key INTEGER NOT NULL DEFAULT 0, | ||
| 194 | + create_time INTEGER NOT NULL, | ||
| 195 | + UNIQUE (time) | ||
| 196 | +) | ||
| 197 | +'''); | ||
| 198 | + await db.execute( | ||
| 199 | + 'CREATE INDEX IF NOT EXISTS idx_${table}_time ON $table(time)'); | ||
| 200 | + } | ||
| 201 | + | ||
| 202 | + Map<String, Object?> _row(int index) { | ||
| 203 | + final time = 1780000000 + index; | ||
| 204 | + return <String, Object?>{ | ||
| 205 | + 'time': time, | ||
| 206 | + 'value': 60 + index % 80, | ||
| 207 | + 'is_asleep': index % 5 == 0 ? 1 : 0, | ||
| 208 | + 'date_key': 20260801 + index ~/ 1000, | ||
| 209 | + 'create_time': 1780000000, | ||
| 210 | + }; | ||
| 211 | + } | ||
| 212 | + | ||
| 213 | + _BenchmarkResult _finish({ | ||
| 214 | + required String name, | ||
| 215 | + required int rowCount, | ||
| 216 | + required int elapsedMs, | ||
| 217 | + }) { | ||
| 218 | + final rowsPerSecond = | ||
| 219 | + elapsedMs == 0 ? rowCount * 1000.0 : rowCount * 1000 / elapsedMs; | ||
| 220 | + _log( | ||
| 221 | + 'result name=$name rows=$rowCount elapsedMs=$elapsedMs ' | ||
| 222 | + 'rowsPerSecond=${rowsPerSecond.toStringAsFixed(1)}', | ||
| 223 | + ); | ||
| 224 | + return _BenchmarkResult( | ||
| 225 | + name: name, | ||
| 226 | + rowCount: rowCount, | ||
| 227 | + elapsedMs: elapsedMs, | ||
| 228 | + rowsPerSecond: rowsPerSecond, | ||
| 229 | + ); | ||
| 230 | + } | ||
| 231 | + | ||
| 232 | + void _log(String message) { | ||
| 233 | + final tagged = '$logMarker $message'; | ||
| 234 | + debugPrint(tagged); | ||
| 235 | + try { | ||
| 236 | + AppLogger.i(tagged); | ||
| 237 | + } catch (_) { | ||
| 238 | + // AppLogger may not be initialized in isolated debug/bootstrap contexts. | ||
| 239 | + } | ||
| 240 | + } | ||
| 241 | +} | ||
| 242 | + | ||
| 243 | +class _BenchmarkResult { | ||
| 244 | + const _BenchmarkResult({ | ||
| 245 | + required this.name, | ||
| 246 | + required this.rowCount, | ||
| 247 | + required this.elapsedMs, | ||
| 248 | + this.rowsPerSecond, | ||
| 249 | + }); | ||
| 250 | + | ||
| 251 | + final String name; | ||
| 252 | + final int rowCount; | ||
| 253 | + final int elapsedMs; | ||
| 254 | + final double? rowsPerSecond; | ||
| 255 | + | ||
| 256 | + String get summary { | ||
| 257 | + final speed = rowsPerSecond; | ||
| 258 | + if (speed == null) return '$name: $rowCount 条,${elapsedMs}ms'; | ||
| 259 | + return '$name: $rowCount 条,${elapsedMs}ms,${speed.toStringAsFixed(1)} 条/s'; | ||
| 260 | + } | ||
| 261 | +} |
| @@ -6,7 +6,7 @@ packages: | @@ -6,7 +6,7 @@ packages: | ||
| 6 | description: | 6 | description: |
| 7 | name: _fe_analyzer_shared | 7 | name: _fe_analyzer_shared |
| 8 | sha256: da0d9209ca76bde579f2da330aeb9df62b6319c834fa7baae052021b0462401f | 8 | sha256: da0d9209ca76bde579f2da330aeb9df62b6319c834fa7baae052021b0462401f |
| 9 | - url: "https://pub.dev" | 9 | + url: "https://pub.flutter-io.cn" |
| 10 | source: hosted | 10 | source: hosted |
| 11 | version: "85.0.0" | 11 | version: "85.0.0" |
| 12 | analyzer: | 12 | analyzer: |
| @@ -14,7 +14,7 @@ packages: | @@ -14,7 +14,7 @@ packages: | ||
| 14 | description: | 14 | description: |
| 15 | name: analyzer | 15 | name: analyzer |
| 16 | sha256: "974859dc0ff5f37bc4313244b3218c791810d03ab3470a579580279ba971a48d" | 16 | sha256: "974859dc0ff5f37bc4313244b3218c791810d03ab3470a579580279ba971a48d" |
| 17 | - url: "https://pub.dev" | 17 | + url: "https://pub.flutter-io.cn" |
| 18 | source: hosted | 18 | source: hosted |
| 19 | version: "7.7.1" | 19 | version: "7.7.1" |
| 20 | archive: | 20 | archive: |
| @@ -22,7 +22,7 @@ packages: | @@ -22,7 +22,7 @@ packages: | ||
| 22 | description: | 22 | description: |
| 23 | name: archive | 23 | name: archive |
| 24 | sha256: ace891da0862b0e4cabbb064ee3fd87b2728b898949fdb366d83fe98342c9f19 | 24 | sha256: ace891da0862b0e4cabbb064ee3fd87b2728b898949fdb366d83fe98342c9f19 |
| 25 | - url: "https://pub.dev" | 25 | + url: "https://pub.flutter-io.cn" |
| 26 | source: hosted | 26 | source: hosted |
| 27 | version: "4.2.0" | 27 | version: "4.2.0" |
| 28 | args: | 28 | args: |
| @@ -30,63 +30,79 @@ packages: | @@ -30,63 +30,79 @@ packages: | ||
| 30 | description: | 30 | description: |
| 31 | name: args | 31 | name: args |
| 32 | sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04 | 32 | sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04 |
| 33 | - url: "https://pub.dev" | 33 | + url: "https://pub.flutter-io.cn" |
| 34 | source: hosted | 34 | source: hosted |
| 35 | version: "2.7.0" | 35 | version: "2.7.0" |
| 36 | async: | 36 | async: |
| 37 | dependency: transitive | 37 | dependency: transitive |
| 38 | description: | 38 | description: |
| 39 | name: async | 39 | name: async |
| 40 | - sha256: e2eb0491ba5ddb6177742d2da23904574082139b07c1e33b8503b9f46f3e1a37 | ||
| 41 | - url: "https://pub.dev" | 40 | + sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" |
| 41 | + url: "https://pub.flutter-io.cn" | ||
| 42 | source: hosted | 42 | source: hosted |
| 43 | - version: "2.13.1" | 43 | + version: "2.11.0" |
| 44 | boolean_selector: | 44 | boolean_selector: |
| 45 | dependency: transitive | 45 | dependency: transitive |
| 46 | description: | 46 | description: |
| 47 | name: boolean_selector | 47 | name: boolean_selector |
| 48 | - sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" | ||
| 49 | - url: "https://pub.dev" | 48 | + sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" |
| 49 | + url: "https://pub.flutter-io.cn" | ||
| 50 | source: hosted | 50 | source: hosted |
| 51 | - version: "2.1.2" | 51 | + version: "2.1.1" |
| 52 | build: | 52 | build: |
| 53 | dependency: transitive | 53 | dependency: transitive |
| 54 | description: | 54 | description: |
| 55 | name: build | 55 | name: build |
| 56 | - sha256: "825fed4d63050252a0b6e74f2d75844c4a85b664814be6993bd3493fb5239779" | ||
| 57 | - url: "https://pub.dev" | 56 | + sha256: cef23f1eda9b57566c81e2133d196f8e3df48f244b317368d65c5943d91148f0 |
| 57 | + url: "https://pub.flutter-io.cn" | ||
| 58 | source: hosted | 58 | source: hosted |
| 59 | - version: "4.0.1" | 59 | + version: "2.4.2" |
| 60 | build_config: | 60 | build_config: |
| 61 | dependency: transitive | 61 | dependency: transitive |
| 62 | description: | 62 | description: |
| 63 | name: build_config | 63 | name: build_config |
| 64 | - sha256: "4f64382b97504dc2fcdf487d5aae33418e08b4703fc21249e4db6d804a4d0187" | ||
| 65 | - url: "https://pub.dev" | 64 | + sha256: "4ae2de3e1e67ea270081eaee972e1bd8f027d459f249e0f1186730784c2e7e33" |
| 65 | + url: "https://pub.flutter-io.cn" | ||
| 66 | source: hosted | 66 | source: hosted |
| 67 | - version: "1.2.0" | 67 | + version: "1.1.2" |
| 68 | build_daemon: | 68 | build_daemon: |
| 69 | dependency: transitive | 69 | dependency: transitive |
| 70 | description: | 70 | description: |
| 71 | name: build_daemon | 71 | name: build_daemon |
| 72 | - sha256: fd754058c342243718d5171a95f352cfc9fcf0cba8cfa26df67cb13a5836db78 | ||
| 73 | - url: "https://pub.dev" | 72 | + sha256: "8e928697a82be082206edb0b9c99c5a4ad6bc31c9e9b8b2f291ae65cd4a25daa" |
| 73 | + url: "https://pub.flutter-io.cn" | ||
| 74 | source: hosted | 74 | source: hosted |
| 75 | - version: "4.1.2" | 75 | + version: "4.0.4" |
| 76 | + build_resolvers: | ||
| 77 | + dependency: transitive | ||
| 78 | + description: | ||
| 79 | + name: build_resolvers | ||
| 80 | + sha256: b9e4fda21d846e192628e7a4f6deda6888c36b5b69ba02ff291a01fd529140f0 | ||
| 81 | + url: "https://pub.flutter-io.cn" | ||
| 82 | + source: hosted | ||
| 83 | + version: "2.4.4" | ||
| 76 | build_runner: | 84 | build_runner: |
| 77 | dependency: "direct dev" | 85 | dependency: "direct dev" |
| 78 | description: | 86 | description: |
| 79 | name: build_runner | 87 | name: build_runner |
| 80 | - sha256: "4e54dbeefdc70691ba80b3bce3976af63b5425c8c07dface348dfee664a0edc1" | ||
| 81 | - url: "https://pub.dev" | 88 | + sha256: "058fe9dce1de7d69c4b84fada934df3e0153dd000758c4d65964d0166779aa99" |
| 89 | + url: "https://pub.flutter-io.cn" | ||
| 90 | + source: hosted | ||
| 91 | + version: "2.4.15" | ||
| 92 | + build_runner_core: | ||
| 93 | + dependency: transitive | ||
| 94 | + description: | ||
| 95 | + name: build_runner_core | ||
| 96 | + sha256: "22e3aa1c80e0ada3722fe5b63fd43d9c8990759d0a2cf489c8c5d7b2bdebc021" | ||
| 97 | + url: "https://pub.flutter-io.cn" | ||
| 82 | source: hosted | 98 | source: hosted |
| 83 | - version: "2.9.0" | 99 | + version: "8.0.0" |
| 84 | built_collection: | 100 | built_collection: |
| 85 | dependency: transitive | 101 | dependency: transitive |
| 86 | description: | 102 | description: |
| 87 | name: built_collection | 103 | name: built_collection |
| 88 | sha256: "376e3dd27b51ea877c28d525560790aee2e6fbb5f20e2f85d5081027d94e2100" | 104 | sha256: "376e3dd27b51ea877c28d525560790aee2e6fbb5f20e2f85d5081027d94e2100" |
| 89 | - url: "https://pub.dev" | 105 | + url: "https://pub.flutter-io.cn" |
| 90 | source: hosted | 106 | source: hosted |
| 91 | version: "5.1.1" | 107 | version: "5.1.1" |
| 92 | built_value: | 108 | built_value: |
| @@ -94,7 +110,7 @@ packages: | @@ -94,7 +110,7 @@ packages: | ||
| 94 | description: | 110 | description: |
| 95 | name: built_value | 111 | name: built_value |
| 96 | sha256: f87ea98192116f7093cb214551ce1929caae0681fdba282b3d8b4462adee7bb7 | 112 | sha256: f87ea98192116f7093cb214551ce1929caae0681fdba282b3d8b4462adee7bb7 |
| 97 | - url: "https://pub.dev" | 113 | + url: "https://pub.flutter-io.cn" |
| 98 | source: hosted | 114 | source: hosted |
| 99 | version: "8.13.0" | 115 | version: "8.13.0" |
| 100 | cached_network_image: | 116 | cached_network_image: |
| @@ -102,7 +118,7 @@ packages: | @@ -102,7 +118,7 @@ packages: | ||
| 102 | description: | 118 | description: |
| 103 | name: cached_network_image | 119 | name: cached_network_image |
| 104 | sha256: "7c1183e361e5c8b0a0f21a28401eecdbde252441106a9816400dd4c2b2424916" | 120 | sha256: "7c1183e361e5c8b0a0f21a28401eecdbde252441106a9816400dd4c2b2424916" |
| 105 | - url: "https://pub.dev" | 121 | + url: "https://pub.flutter-io.cn" |
| 106 | source: hosted | 122 | source: hosted |
| 107 | version: "3.4.1" | 123 | version: "3.4.1" |
| 108 | cached_network_image_platform_interface: | 124 | cached_network_image_platform_interface: |
| @@ -110,7 +126,7 @@ packages: | @@ -110,7 +126,7 @@ packages: | ||
| 110 | description: | 126 | description: |
| 111 | name: cached_network_image_platform_interface | 127 | name: cached_network_image_platform_interface |
| 112 | sha256: "35814b016e37fbdc91f7ae18c8caf49ba5c88501813f73ce8a07027a395e2829" | 128 | sha256: "35814b016e37fbdc91f7ae18c8caf49ba5c88501813f73ce8a07027a395e2829" |
| 113 | - url: "https://pub.dev" | 129 | + url: "https://pub.flutter-io.cn" |
| 114 | source: hosted | 130 | source: hosted |
| 115 | version: "4.1.1" | 131 | version: "4.1.1" |
| 116 | cached_network_image_web: | 132 | cached_network_image_web: |
| @@ -118,103 +134,95 @@ packages: | @@ -118,103 +134,95 @@ packages: | ||
| 118 | description: | 134 | description: |
| 119 | name: cached_network_image_web | 135 | name: cached_network_image_web |
| 120 | sha256: "980842f4e8e2535b8dbd3d5ca0b1f0ba66bf61d14cc3a17a9b4788a3685ba062" | 136 | sha256: "980842f4e8e2535b8dbd3d5ca0b1f0ba66bf61d14cc3a17a9b4788a3685ba062" |
| 121 | - url: "https://pub.dev" | 137 | + url: "https://pub.flutter-io.cn" |
| 122 | source: hosted | 138 | source: hosted |
| 123 | version: "1.3.1" | 139 | version: "1.3.1" |
| 124 | characters: | 140 | characters: |
| 125 | dependency: transitive | 141 | dependency: transitive |
| 126 | description: | 142 | description: |
| 127 | name: characters | 143 | name: characters |
| 128 | - sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 | ||
| 129 | - url: "https://pub.dev" | 144 | + sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" |
| 145 | + url: "https://pub.flutter-io.cn" | ||
| 130 | source: hosted | 146 | source: hosted |
| 131 | - version: "1.4.0" | 147 | + version: "1.3.0" |
| 132 | checked_yaml: | 148 | checked_yaml: |
| 133 | dependency: transitive | 149 | dependency: transitive |
| 134 | description: | 150 | description: |
| 135 | name: checked_yaml | 151 | name: checked_yaml |
| 136 | - sha256: "959525d3162f249993882720d52b7e0c833978df229be20702b33d48d91de70f" | ||
| 137 | - url: "https://pub.dev" | 152 | + sha256: feb6bed21949061731a7a75fc5d2aa727cf160b91af9a3e464c5e3a32e28b5ff |
| 153 | + url: "https://pub.flutter-io.cn" | ||
| 138 | source: hosted | 154 | source: hosted |
| 139 | - version: "2.0.4" | 155 | + version: "2.0.3" |
| 140 | clock: | 156 | clock: |
| 141 | dependency: transitive | 157 | dependency: transitive |
| 142 | description: | 158 | description: |
| 143 | name: clock | 159 | name: clock |
| 144 | - sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b | ||
| 145 | - url: "https://pub.dev" | ||
| 146 | - source: hosted | ||
| 147 | - version: "1.1.2" | ||
| 148 | - code_assets: | ||
| 149 | - dependency: transitive | ||
| 150 | - description: | ||
| 151 | - name: code_assets | ||
| 152 | - sha256: cfd4f5f575a49c5f10ca856e9846073f1e6c3ee94912377eea5f6cefc5272941 | ||
| 153 | - url: "https://pub.dev" | 160 | + sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf |
| 161 | + url: "https://pub.flutter-io.cn" | ||
| 154 | source: hosted | 162 | source: hosted |
| 155 | - version: "2.0.0" | 163 | + version: "1.1.1" |
| 156 | code_builder: | 164 | code_builder: |
| 157 | dependency: transitive | 165 | dependency: transitive |
| 158 | description: | 166 | description: |
| 159 | name: code_builder | 167 | name: code_builder |
| 160 | - sha256: aa5932e94c6c39c2f9ec4e5e06dfdd11a9430a61f6c41b6ba75b28ce0c481baf | ||
| 161 | - url: "https://pub.dev" | 168 | + sha256: "0ec10bf4a89e4c613960bf1e8b42c64127021740fb21640c29c909826a5eea3e" |
| 169 | + url: "https://pub.flutter-io.cn" | ||
| 162 | source: hosted | 170 | source: hosted |
| 163 | - version: "4.12.0" | 171 | + version: "4.10.1" |
| 164 | collection: | 172 | collection: |
| 165 | dependency: transitive | 173 | dependency: transitive |
| 166 | description: | 174 | description: |
| 167 | name: collection | 175 | name: collection |
| 168 | - sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" | ||
| 169 | - url: "https://pub.dev" | 176 | + sha256: a1ace0a119f20aabc852d165077c036cd864315bd99b7eaa10a60100341941bf |
| 177 | + url: "https://pub.flutter-io.cn" | ||
| 170 | source: hosted | 178 | source: hosted |
| 171 | - version: "1.19.1" | 179 | + version: "1.19.0" |
| 172 | convert: | 180 | convert: |
| 173 | dependency: transitive | 181 | dependency: transitive |
| 174 | description: | 182 | description: |
| 175 | name: convert | 183 | name: convert |
| 176 | sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68 | 184 | sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68 |
| 177 | - url: "https://pub.dev" | 185 | + url: "https://pub.flutter-io.cn" |
| 178 | source: hosted | 186 | source: hosted |
| 179 | version: "3.1.2" | 187 | version: "3.1.2" |
| 180 | cross_file: | 188 | cross_file: |
| 181 | dependency: transitive | 189 | dependency: transitive |
| 182 | description: | 190 | description: |
| 183 | name: cross_file | 191 | name: cross_file |
| 184 | - sha256: f141ea4f277af142a0356955707f6556f37b03947d39d55585981a06ca437bd6 | ||
| 185 | - url: "https://pub.dev" | 192 | + sha256: "7caf6a750a0c04effbb52a676dce9a4a592e10ad35c34d6d2d0e4811160d5670" |
| 193 | + url: "https://pub.flutter-io.cn" | ||
| 186 | source: hosted | 194 | source: hosted |
| 187 | - version: "0.3.5+5" | 195 | + version: "0.3.4+2" |
| 188 | crypto: | 196 | crypto: |
| 189 | dependency: "direct main" | 197 | dependency: "direct main" |
| 190 | description: | 198 | description: |
| 191 | name: crypto | 199 | name: crypto |
| 192 | sha256: c8ea0233063ba03258fbcf2ca4d6dadfefe14f02fab57702265467a19f27fadf | 200 | sha256: c8ea0233063ba03258fbcf2ca4d6dadfefe14f02fab57702265467a19f27fadf |
| 193 | - url: "https://pub.dev" | 201 | + url: "https://pub.flutter-io.cn" |
| 194 | source: hosted | 202 | source: hosted |
| 195 | version: "3.0.7" | 203 | version: "3.0.7" |
| 196 | cupertino_icons: | 204 | cupertino_icons: |
| 197 | dependency: "direct main" | 205 | dependency: "direct main" |
| 198 | description: | 206 | description: |
| 199 | name: cupertino_icons | 207 | name: cupertino_icons |
| 200 | - sha256: "41e005c33bd814be4d3096aff55b1908d419fde52ca656c8c47719ec745873cd" | ||
| 201 | - url: "https://pub.dev" | 208 | + sha256: ba631d1c7f7bef6b729a622b7b752645a2d076dba9976925b8f25725a30e1ee6 |
| 209 | + url: "https://pub.flutter-io.cn" | ||
| 202 | source: hosted | 210 | source: hosted |
| 203 | - version: "1.0.9" | 211 | + version: "1.0.8" |
| 204 | dart_style: | 212 | dart_style: |
| 205 | dependency: transitive | 213 | dependency: transitive |
| 206 | description: | 214 | description: |
| 207 | name: dart_style | 215 | name: dart_style |
| 208 | - sha256: "8a0e5fba27e8ee025d2ffb4ee820b4e6e2cf5e4246a6b1a477eb66866947e0bb" | ||
| 209 | - url: "https://pub.dev" | 216 | + sha256: "27eb0ae77836989a3bc541ce55595e8ceee0992807f14511552a898ddd0d88ac" |
| 217 | + url: "https://pub.flutter-io.cn" | ||
| 210 | source: hosted | 218 | source: hosted |
| 211 | - version: "3.1.1" | 219 | + version: "3.0.1" |
| 212 | dio: | 220 | dio: |
| 213 | dependency: "direct main" | 221 | dependency: "direct main" |
| 214 | description: | 222 | description: |
| 215 | name: dio | 223 | name: dio |
| 216 | sha256: "852ec3b48cc431ac04fff978413c541502b67ffc3e26921e74e3d994694192c1" | 224 | sha256: "852ec3b48cc431ac04fff978413c541502b67ffc3e26921e74e3d994694192c1" |
| 217 | - url: "https://pub.dev" | 225 | + url: "https://pub.flutter-io.cn" |
| 218 | source: hosted | 226 | source: hosted |
| 219 | version: "5.11.1" | 227 | version: "5.11.1" |
| 220 | dio_web_adapter: | 228 | dio_web_adapter: |
| @@ -222,7 +230,7 @@ packages: | @@ -222,7 +230,7 @@ packages: | ||
| 222 | description: | 230 | description: |
| 223 | name: dio_web_adapter | 231 | name: dio_web_adapter |
| 224 | sha256: "3a1b2cd7be71086f38504956e3ebcd2837288d231ff454bafa78021244102bfc" | 232 | sha256: "3a1b2cd7be71086f38504956e3ebcd2837288d231ff454bafa78021244102bfc" |
| 225 | - url: "https://pub.dev" | 233 | + url: "https://pub.flutter-io.cn" |
| 226 | source: hosted | 234 | source: hosted |
| 227 | version: "2.2.2" | 235 | version: "2.2.2" |
| 228 | equatable: | 236 | equatable: |
| @@ -230,71 +238,71 @@ packages: | @@ -230,71 +238,71 @@ packages: | ||
| 230 | description: | 238 | description: |
| 231 | name: equatable | 239 | name: equatable |
| 232 | sha256: "3bce007a596ff8b3119c45d68aaef631272537c03d30e5d4534dd24bf4c5eaa2" | 240 | sha256: "3bce007a596ff8b3119c45d68aaef631272537c03d30e5d4534dd24bf4c5eaa2" |
| 233 | - url: "https://pub.dev" | 241 | + url: "https://pub.flutter-io.cn" |
| 234 | source: hosted | 242 | source: hosted |
| 235 | version: "2.1.0" | 243 | version: "2.1.0" |
| 236 | fake_async: | 244 | fake_async: |
| 237 | dependency: transitive | 245 | dependency: transitive |
| 238 | description: | 246 | description: |
| 239 | name: fake_async | 247 | name: fake_async |
| 240 | - sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44" | ||
| 241 | - url: "https://pub.dev" | 248 | + sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" |
| 249 | + url: "https://pub.flutter-io.cn" | ||
| 242 | source: hosted | 250 | source: hosted |
| 243 | - version: "1.3.3" | 251 | + version: "1.3.1" |
| 244 | ffi: | 252 | ffi: |
| 245 | dependency: transitive | 253 | dependency: transitive |
| 246 | description: | 254 | description: |
| 247 | name: ffi | 255 | name: ffi |
| 248 | - sha256: "6d7fd89431262d8f3125e81b50d3847a091d846eafcd4fdb88dd06f36d705a45" | ||
| 249 | - url: "https://pub.dev" | 256 | + sha256: "16ed7b077ef01ad6170a3d0c57caa4a112a38d7a2ed5602e0aca9ca6f3d98da6" |
| 257 | + url: "https://pub.flutter-io.cn" | ||
| 250 | source: hosted | 258 | source: hosted |
| 251 | - version: "2.2.0" | 259 | + version: "2.1.3" |
| 252 | file: | 260 | file: |
| 253 | dependency: transitive | 261 | dependency: transitive |
| 254 | description: | 262 | description: |
| 255 | name: file | 263 | name: file |
| 256 | sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4 | 264 | sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4 |
| 257 | - url: "https://pub.dev" | 265 | + url: "https://pub.flutter-io.cn" |
| 258 | source: hosted | 266 | source: hosted |
| 259 | version: "7.0.1" | 267 | version: "7.0.1" |
| 260 | file_selector_linux: | 268 | file_selector_linux: |
| 261 | dependency: transitive | 269 | dependency: transitive |
| 262 | description: | 270 | description: |
| 263 | name: file_selector_linux | 271 | name: file_selector_linux |
| 264 | - sha256: da76400e7872ce7637ffdce12749ec24169c25f6195c28372208e65a24bcd2ab | ||
| 265 | - url: "https://pub.dev" | 272 | + sha256: "54cbbd957e1156d29548c7d9b9ec0c0ebb6de0a90452198683a7d23aed617a33" |
| 273 | + url: "https://pub.flutter-io.cn" | ||
| 266 | source: hosted | 274 | source: hosted |
| 267 | - version: "0.9.4+1" | 275 | + version: "0.9.3+2" |
| 268 | file_selector_macos: | 276 | file_selector_macos: |
| 269 | dependency: transitive | 277 | dependency: transitive |
| 270 | description: | 278 | description: |
| 271 | name: file_selector_macos | 279 | name: file_selector_macos |
| 272 | - sha256: d57c62362766b5e7ae739448650b66c6aab7a68ba7ecc65e04018652645ae0f4 | ||
| 273 | - url: "https://pub.dev" | 280 | + sha256: "8c9250b2bd2d8d4268e39c82543bacbaca0fda7d29e0728c3c4bbb7c820fd711" |
| 281 | + url: "https://pub.flutter-io.cn" | ||
| 274 | source: hosted | 282 | source: hosted |
| 275 | - version: "0.9.5+1" | 283 | + version: "0.9.4+3" |
| 276 | file_selector_platform_interface: | 284 | file_selector_platform_interface: |
| 277 | dependency: transitive | 285 | dependency: transitive |
| 278 | description: | 286 | description: |
| 279 | name: file_selector_platform_interface | 287 | name: file_selector_platform_interface |
| 280 | - sha256: "35e0bd61ebcdb91a3505813b055b09b79dfdc7d0aee9c09a7ba59ae4bb13dc85" | ||
| 281 | - url: "https://pub.dev" | 288 | + sha256: a3994c26f10378a039faa11de174d7b78eb8f79e4dd0af2a451410c1a5c3f66b |
| 289 | + url: "https://pub.flutter-io.cn" | ||
| 282 | source: hosted | 290 | source: hosted |
| 283 | - version: "2.7.0" | 291 | + version: "2.6.2" |
| 284 | file_selector_windows: | 292 | file_selector_windows: |
| 285 | dependency: transitive | 293 | dependency: transitive |
| 286 | description: | 294 | description: |
| 287 | name: file_selector_windows | 295 | name: file_selector_windows |
| 288 | - sha256: fbefc5fb92c6d3cbe8d284a2cd971b593bb07d2cd6da8557b81a862250b4acec | ||
| 289 | - url: "https://pub.dev" | 296 | + sha256: "320fcfb6f33caa90f0b58380489fc5ac05d99ee94b61aa96ec2bff0ba81d3c2b" |
| 297 | + url: "https://pub.flutter-io.cn" | ||
| 290 | source: hosted | 298 | source: hosted |
| 291 | - version: "0.9.3+6" | 299 | + version: "0.9.3+4" |
| 292 | fixnum: | 300 | fixnum: |
| 293 | dependency: transitive | 301 | dependency: transitive |
| 294 | description: | 302 | description: |
| 295 | name: fixnum | 303 | name: fixnum |
| 296 | sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be | 304 | sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be |
| 297 | - url: "https://pub.dev" | 305 | + url: "https://pub.flutter-io.cn" |
| 298 | source: hosted | 306 | source: hosted |
| 299 | version: "1.1.1" | 307 | version: "1.1.1" |
| 300 | fl_chart: | 308 | fl_chart: |
| @@ -302,7 +310,7 @@ packages: | @@ -302,7 +310,7 @@ packages: | ||
| 302 | description: | 310 | description: |
| 303 | name: fl_chart | 311 | name: fl_chart |
| 304 | sha256: "5276944c6ffc975ae796569a826c38a62d2abcf264e26b88fa6f482e107f4237" | 312 | sha256: "5276944c6ffc975ae796569a826c38a62d2abcf264e26b88fa6f482e107f4237" |
| 305 | - url: "https://pub.dev" | 313 | + url: "https://pub.flutter-io.cn" |
| 306 | source: hosted | 314 | source: hosted |
| 307 | version: "0.70.2" | 315 | version: "0.70.2" |
| 308 | flutter: | 316 | flutter: |
| @@ -314,16 +322,16 @@ packages: | @@ -314,16 +322,16 @@ packages: | ||
| 314 | dependency: transitive | 322 | dependency: transitive |
| 315 | description: | 323 | description: |
| 316 | name: flutter_cache_manager | 324 | name: flutter_cache_manager |
| 317 | - sha256: "1de7849213b4c73c85aca7e0ac687a9a5d82ccdb594366b9dcc26cb6a2189cd2" | ||
| 318 | - url: "https://pub.dev" | 325 | + sha256: "400b6592f16a4409a7f2bb929a9a7e38c72cceb8ffb99ee57bbf2cb2cecf8386" |
| 326 | + url: "https://pub.flutter-io.cn" | ||
| 319 | source: hosted | 327 | source: hosted |
| 320 | - version: "3.4.2" | 328 | + version: "3.4.1" |
| 321 | flutter_lints: | 329 | flutter_lints: |
| 322 | dependency: "direct dev" | 330 | dependency: "direct dev" |
| 323 | description: | 331 | description: |
| 324 | name: flutter_lints | 332 | name: flutter_lints |
| 325 | sha256: "5398f14efa795ffb7a33e9b6a08798b26a180edac4ad7db3f231e40f82ce11e1" | 333 | sha256: "5398f14efa795ffb7a33e9b6a08798b26a180edac4ad7db3f231e40f82ce11e1" |
| 326 | - url: "https://pub.dev" | 334 | + url: "https://pub.flutter-io.cn" |
| 327 | source: hosted | 335 | source: hosted |
| 328 | version: "5.0.0" | 336 | version: "5.0.0" |
| 329 | flutter_localizations: | 337 | flutter_localizations: |
| @@ -335,10 +343,10 @@ packages: | @@ -335,10 +343,10 @@ packages: | ||
| 335 | dependency: transitive | 343 | dependency: transitive |
| 336 | description: | 344 | description: |
| 337 | name: flutter_plugin_android_lifecycle | 345 | name: flutter_plugin_android_lifecycle |
| 338 | - sha256: "3854fe5e3bff0b113c658f260b90c95dea17c92db0f2addeac2e343dd9969785" | ||
| 339 | - url: "https://pub.dev" | 346 | + sha256: "6382ce712ff69b0f719640ce957559dde459e55ecd433c767e06d139ddf16cab" |
| 347 | + url: "https://pub.flutter-io.cn" | ||
| 340 | source: hosted | 348 | source: hosted |
| 341 | - version: "2.0.35" | 349 | + version: "2.0.29" |
| 342 | flutter_test: | 350 | flutter_test: |
| 343 | dependency: "direct dev" | 351 | dependency: "direct dev" |
| 344 | description: flutter | 352 | description: flutter |
| @@ -349,7 +357,7 @@ packages: | @@ -349,7 +357,7 @@ packages: | ||
| 349 | description: | 357 | description: |
| 350 | name: flutter_timezone | 358 | name: flutter_timezone |
| 351 | sha256: "869677426fde92dbe170fb7d2d4929f2a8343c2f5f62f08b0bb64f908630b073" | 359 | sha256: "869677426fde92dbe170fb7d2d4929f2a8343c2f5f62f08b0bb64f908630b073" |
| 352 | - url: "https://pub.dev" | 360 | + url: "https://pub.flutter-io.cn" |
| 353 | source: hosted | 361 | source: hosted |
| 354 | version: "5.1.0" | 362 | version: "5.1.0" |
| 355 | flutter_web_plugins: | 363 | flutter_web_plugins: |
| @@ -366,12 +374,20 @@ packages: | @@ -366,12 +374,20 @@ packages: | ||
| 366 | url: "https://gitcode.com/CPF-Flutter/flutter_fluttertoast.git" | 374 | url: "https://gitcode.com/CPF-Flutter/flutter_fluttertoast.git" |
| 367 | source: git | 375 | source: git |
| 368 | version: "9.0.0" | 376 | version: "9.0.0" |
| 377 | + frontend_server_client: | ||
| 378 | + dependency: transitive | ||
| 379 | + description: | ||
| 380 | + name: frontend_server_client | ||
| 381 | + sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694 | ||
| 382 | + url: "https://pub.flutter-io.cn" | ||
| 383 | + source: hosted | ||
| 384 | + version: "4.0.0" | ||
| 369 | get: | 385 | get: |
| 370 | dependency: "direct main" | 386 | dependency: "direct main" |
| 371 | description: | 387 | description: |
| 372 | name: get | 388 | name: get |
| 373 | sha256: "5ed34a7925b85336e15d472cc4cfe7d9ebf4ab8e8b9f688585bf6b50f4c3d79a" | 389 | sha256: "5ed34a7925b85336e15d472cc4cfe7d9ebf4ab8e8b9f688585bf6b50f4c3d79a" |
| 374 | - url: "https://pub.dev" | 390 | + url: "https://pub.flutter-io.cn" |
| 375 | source: hosted | 391 | source: hosted |
| 376 | version: "4.7.3" | 392 | version: "4.7.3" |
| 377 | glob: | 393 | glob: |
| @@ -379,7 +395,7 @@ packages: | @@ -379,7 +395,7 @@ packages: | ||
| 379 | description: | 395 | description: |
| 380 | name: glob | 396 | name: glob |
| 381 | sha256: "218aeb56050c714f62a3182775320dfa04602b55074873e24e31bbd39bda96fb" | 397 | sha256: "218aeb56050c714f62a3182775320dfa04602b55074873e24e31bbd39bda96fb" |
| 382 | - url: "https://pub.dev" | 398 | + url: "https://pub.flutter-io.cn" |
| 383 | source: hosted | 399 | source: hosted |
| 384 | version: "2.2.0" | 400 | version: "2.2.0" |
| 385 | graphs: | 401 | graphs: |
| @@ -387,23 +403,15 @@ packages: | @@ -387,23 +403,15 @@ packages: | ||
| 387 | description: | 403 | description: |
| 388 | name: graphs | 404 | name: graphs |
| 389 | sha256: "741bbf84165310a68ff28fe9e727332eef1407342fca52759cb21ad8177bb8d0" | 405 | sha256: "741bbf84165310a68ff28fe9e727332eef1407342fca52759cb21ad8177bb8d0" |
| 390 | - url: "https://pub.dev" | 406 | + url: "https://pub.flutter-io.cn" |
| 391 | source: hosted | 407 | source: hosted |
| 392 | version: "2.3.2" | 408 | version: "2.3.2" |
| 393 | - hooks: | ||
| 394 | - dependency: transitive | ||
| 395 | - description: | ||
| 396 | - name: hooks | ||
| 397 | - sha256: eaac480a35ec0814146c2c48d96aaa829e0e44a7662c88ae84c9edf4bc35651f | ||
| 398 | - url: "https://pub.dev" | ||
| 399 | - source: hosted | ||
| 400 | - version: "2.2.0" | ||
| 401 | http: | 409 | http: |
| 402 | dependency: transitive | 410 | dependency: transitive |
| 403 | description: | 411 | description: |
| 404 | name: http | 412 | name: http |
| 405 | sha256: "87721a4a50b19c7f1d49001e51409bddc46303966ce89a65af4f4e6004896412" | 413 | sha256: "87721a4a50b19c7f1d49001e51409bddc46303966ce89a65af4f4e6004896412" |
| 406 | - url: "https://pub.dev" | 414 | + url: "https://pub.flutter-io.cn" |
| 407 | source: hosted | 415 | source: hosted |
| 408 | version: "1.6.0" | 416 | version: "1.6.0" |
| 409 | http_multi_server: | 417 | http_multi_server: |
| @@ -411,7 +419,7 @@ packages: | @@ -411,7 +419,7 @@ packages: | ||
| 411 | description: | 419 | description: |
| 412 | name: http_multi_server | 420 | name: http_multi_server |
| 413 | sha256: aa6199f908078bb1c5efb8d8638d4ae191aac11b311132c3ef48ce352fb52ef8 | 421 | sha256: aa6199f908078bb1c5efb8d8638d4ae191aac11b311132c3ef48ce352fb52ef8 |
| 414 | - url: "https://pub.dev" | 422 | + url: "https://pub.flutter-io.cn" |
| 415 | source: hosted | 423 | source: hosted |
| 416 | version: "3.2.2" | 424 | version: "3.2.2" |
| 417 | http_parser: | 425 | http_parser: |
| @@ -419,7 +427,7 @@ packages: | @@ -419,7 +427,7 @@ packages: | ||
| 419 | description: | 427 | description: |
| 420 | name: http_parser | 428 | name: http_parser |
| 421 | sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571" | 429 | sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571" |
| 422 | - url: "https://pub.dev" | 430 | + url: "https://pub.flutter-io.cn" |
| 423 | source: hosted | 431 | source: hosted |
| 424 | version: "4.1.2" | 432 | version: "4.1.2" |
| 425 | image_cropper: | 433 | image_cropper: |
| @@ -435,7 +443,7 @@ packages: | @@ -435,7 +443,7 @@ packages: | ||
| 435 | dependency: transitive | 443 | dependency: transitive |
| 436 | description: | 444 | description: |
| 437 | path: image_cropper_for_web | 445 | path: image_cropper_for_web |
| 438 | - ref: b1b45b1a5333571095569d53d01720c505fff983 | 446 | + ref: "9.1.0-ohos-1.0.0-beta.1" |
| 439 | resolved-ref: b1b45b1a5333571095569d53d01720c505fff983 | 447 | resolved-ref: b1b45b1a5333571095569d53d01720c505fff983 |
| 440 | url: "https://gitcode.com/CPF-Flutter/fluttertpc_image_cropper.git" | 448 | url: "https://gitcode.com/CPF-Flutter/fluttertpc_image_cropper.git" |
| 441 | source: git | 449 | source: git |
| @@ -444,7 +452,7 @@ packages: | @@ -444,7 +452,7 @@ packages: | ||
| 444 | dependency: transitive | 452 | dependency: transitive |
| 445 | description: | 453 | description: |
| 446 | path: image_cropper_platform_interface | 454 | path: image_cropper_platform_interface |
| 447 | - ref: b1b45b1a5333571095569d53d01720c505fff983 | 455 | + ref: "9.1.0-ohos-1.0.0-beta.1" |
| 448 | resolved-ref: b1b45b1a5333571095569d53d01720c505fff983 | 456 | resolved-ref: b1b45b1a5333571095569d53d01720c505fff983 |
| 449 | url: "https://gitcode.com/CPF-Flutter/fluttertpc_image_cropper.git" | 457 | url: "https://gitcode.com/CPF-Flutter/fluttertpc_image_cropper.git" |
| 450 | source: git | 458 | source: git |
| @@ -462,42 +470,42 @@ packages: | @@ -462,42 +470,42 @@ packages: | ||
| 462 | dependency: transitive | 470 | dependency: transitive |
| 463 | description: | 471 | description: |
| 464 | name: image_picker_android | 472 | name: image_picker_android |
| 465 | - sha256: d5b3e1774af29c9ab00103afb0d4614070f924d2e0057ac867ec98800114793f | ||
| 466 | - url: "https://pub.dev" | 473 | + sha256: e83b2b05141469c5e19d77e1dfa11096b6b1567d09065b2265d7c6904560050c |
| 474 | + url: "https://pub.flutter-io.cn" | ||
| 467 | source: hosted | 475 | source: hosted |
| 468 | - version: "0.8.13+17" | 476 | + version: "0.8.13" |
| 469 | image_picker_for_web: | 477 | image_picker_for_web: |
| 470 | dependency: transitive | 478 | dependency: transitive |
| 471 | description: | 479 | description: |
| 472 | name: image_picker_for_web | 480 | name: image_picker_for_web |
| 473 | - sha256: "66257a3191ab360d23a55c8241c91a6e329d31e94efa7be9cf7a212e65850214" | ||
| 474 | - url: "https://pub.dev" | 481 | + sha256: "40c2a6a0da15556dc0f8e38a3246064a971a9f512386c3339b89f76db87269b6" |
| 482 | + url: "https://pub.flutter-io.cn" | ||
| 475 | source: hosted | 483 | source: hosted |
| 476 | - version: "3.1.1" | 484 | + version: "3.1.0" |
| 477 | image_picker_ios: | 485 | image_picker_ios: |
| 478 | dependency: transitive | 486 | dependency: transitive |
| 479 | description: | 487 | description: |
| 480 | name: image_picker_ios | 488 | name: image_picker_ios |
| 481 | - sha256: ee3885b6fcd71958fbc79770dd194c63371439d536d69c47b279171a486482ae | ||
| 482 | - url: "https://pub.dev" | 489 | + sha256: eb06fe30bab4c4497bad449b66448f50edcc695f1c59408e78aa3a8059eb8f0e |
| 490 | + url: "https://pub.flutter-io.cn" | ||
| 483 | source: hosted | 491 | source: hosted |
| 484 | - version: "0.8.13+7" | 492 | + version: "0.8.13" |
| 485 | image_picker_linux: | 493 | image_picker_linux: |
| 486 | dependency: transitive | 494 | dependency: transitive |
| 487 | description: | 495 | description: |
| 488 | name: image_picker_linux | 496 | name: image_picker_linux |
| 489 | sha256: "1f81c5f2046b9ab724f85523e4af65be1d47b038160a8c8deed909762c308ed4" | 497 | sha256: "1f81c5f2046b9ab724f85523e4af65be1d47b038160a8c8deed909762c308ed4" |
| 490 | - url: "https://pub.dev" | 498 | + url: "https://pub.flutter-io.cn" |
| 491 | source: hosted | 499 | source: hosted |
| 492 | version: "0.2.2" | 500 | version: "0.2.2" |
| 493 | image_picker_macos: | 501 | image_picker_macos: |
| 494 | dependency: transitive | 502 | dependency: transitive |
| 495 | description: | 503 | description: |
| 496 | name: image_picker_macos | 504 | name: image_picker_macos |
| 497 | - sha256: "86f0f15a309de7e1a552c12df9ce5b59fe927e71385329355aec4776c6a8ec91" | ||
| 498 | - url: "https://pub.dev" | 505 | + sha256: d58cd9d67793d52beefd6585b12050af0a7663c0c2a6ece0fb110a35d6955e04 |
| 506 | + url: "https://pub.flutter-io.cn" | ||
| 499 | source: hosted | 507 | source: hosted |
| 500 | - version: "0.2.2+1" | 508 | + version: "0.2.2" |
| 501 | image_picker_ohos: | 509 | image_picker_ohos: |
| 502 | dependency: transitive | 510 | dependency: transitive |
| 503 | description: | 511 | description: |
| @@ -511,96 +519,80 @@ packages: | @@ -511,96 +519,80 @@ packages: | ||
| 511 | dependency: transitive | 519 | dependency: transitive |
| 512 | description: | 520 | description: |
| 513 | name: image_picker_platform_interface | 521 | name: image_picker_platform_interface |
| 514 | - sha256: "567e056716333a1647c64bb6bd873cff7622233a5c3f694be28a583d4715690c" | ||
| 515 | - url: "https://pub.dev" | 522 | + sha256: "9f143b0dba3e459553209e20cc425c9801af48e6dfa4f01a0fcf927be3f41665" |
| 523 | + url: "https://pub.flutter-io.cn" | ||
| 516 | source: hosted | 524 | source: hosted |
| 517 | - version: "2.11.1" | 525 | + version: "2.11.0" |
| 518 | image_picker_windows: | 526 | image_picker_windows: |
| 519 | dependency: transitive | 527 | dependency: transitive |
| 520 | description: | 528 | description: |
| 521 | name: image_picker_windows | 529 | name: image_picker_windows |
| 522 | sha256: d248c86554a72b5495a31c56f060cf73a41c7ff541689327b1a7dbccc33adfae | 530 | sha256: d248c86554a72b5495a31c56f060cf73a41c7ff541689327b1a7dbccc33adfae |
| 523 | - url: "https://pub.dev" | 531 | + url: "https://pub.flutter-io.cn" |
| 524 | source: hosted | 532 | source: hosted |
| 525 | version: "0.2.2" | 533 | version: "0.2.2" |
| 526 | intl: | 534 | intl: |
| 527 | dependency: "direct main" | 535 | dependency: "direct main" |
| 528 | description: | 536 | description: |
| 529 | name: intl | 537 | name: intl |
| 530 | - sha256: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5" | ||
| 531 | - url: "https://pub.dev" | 538 | + sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf |
| 539 | + url: "https://pub.flutter-io.cn" | ||
| 532 | source: hosted | 540 | source: hosted |
| 533 | - version: "0.20.2" | 541 | + version: "0.19.0" |
| 534 | io: | 542 | io: |
| 535 | dependency: transitive | 543 | dependency: transitive |
| 536 | description: | 544 | description: |
| 537 | name: io | 545 | name: io |
| 538 | sha256: "2635216ca6a737e60de577ffa1a48a0bec76ca8a62917cfc1bb88c14c570646f" | 546 | sha256: "2635216ca6a737e60de577ffa1a48a0bec76ca8a62917cfc1bb88c14c570646f" |
| 539 | - url: "https://pub.dev" | 547 | + url: "https://pub.flutter-io.cn" |
| 540 | source: hosted | 548 | source: hosted |
| 541 | version: "1.1.0" | 549 | version: "1.1.0" |
| 542 | - jni: | ||
| 543 | - dependency: transitive | ||
| 544 | - description: | ||
| 545 | - name: jni | ||
| 546 | - sha256: f038e58b4dc2c9037f50e233175086337e0b305e356d28211bf55f21c504cbd3 | ||
| 547 | - url: "https://pub.dev" | ||
| 548 | - source: hosted | ||
| 549 | - version: "1.0.3" | ||
| 550 | - jni_flutter: | 550 | + js: |
| 551 | dependency: transitive | 551 | dependency: transitive |
| 552 | description: | 552 | description: |
| 553 | - name: jni_flutter | ||
| 554 | - sha256: b2310cdd4c18c65c081ab141a41efa94aa26c65431803703ece51996f174f351 | ||
| 555 | - url: "https://pub.dev" | 553 | + name: js |
| 554 | + sha256: c1b2e9b5ea78c45e1a0788d29606ba27dc5f71f019f32ca5140f61ef071838cf | ||
| 555 | + url: "https://pub.flutter-io.cn" | ||
| 556 | source: hosted | 556 | source: hosted |
| 557 | - version: "1.0.3" | ||
| 558 | - jni_util: | ||
| 559 | - dependency: transitive | ||
| 560 | - description: | ||
| 561 | - name: jni_util | ||
| 562 | - sha256: "1ba86da04a5f2bf18fde2edb235587e70c5b0fc5bd4ba955f46b00942c3fc35f" | ||
| 563 | - url: "https://pub.dev" | ||
| 564 | - source: hosted | ||
| 565 | - version: "1.0.0" | 557 | + version: "0.7.1" |
| 566 | json_annotation: | 558 | json_annotation: |
| 567 | dependency: transitive | 559 | dependency: transitive |
| 568 | description: | 560 | description: |
| 569 | name: json_annotation | 561 | name: json_annotation |
| 570 | - sha256: "2a743920d81b7910627f68ee2c9ac1fc0bfee32b9fc3403587d7c6791ca12f80" | ||
| 571 | - url: "https://pub.dev" | 562 | + sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1" |
| 563 | + url: "https://pub.flutter-io.cn" | ||
| 572 | source: hosted | 564 | source: hosted |
| 573 | - version: "4.12.0" | 565 | + version: "4.9.0" |
| 574 | leak_tracker: | 566 | leak_tracker: |
| 575 | dependency: transitive | 567 | dependency: transitive |
| 576 | description: | 568 | description: |
| 577 | name: leak_tracker | 569 | name: leak_tracker |
| 578 | - sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de" | ||
| 579 | - url: "https://pub.dev" | 570 | + sha256: "7bb2830ebd849694d1ec25bf1f44582d6ac531a57a365a803a6034ff751d2d06" |
| 571 | + url: "https://pub.flutter-io.cn" | ||
| 580 | source: hosted | 572 | source: hosted |
| 581 | - version: "11.0.2" | 573 | + version: "10.0.7" |
| 582 | leak_tracker_flutter_testing: | 574 | leak_tracker_flutter_testing: |
| 583 | dependency: transitive | 575 | dependency: transitive |
| 584 | description: | 576 | description: |
| 585 | name: leak_tracker_flutter_testing | 577 | name: leak_tracker_flutter_testing |
| 586 | - sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1" | ||
| 587 | - url: "https://pub.dev" | 578 | + sha256: "9491a714cca3667b60b5c420da8217e6de0d1ba7a5ec322fab01758f6998f379" |
| 579 | + url: "https://pub.flutter-io.cn" | ||
| 588 | source: hosted | 580 | source: hosted |
| 589 | - version: "3.0.10" | 581 | + version: "3.0.8" |
| 590 | leak_tracker_testing: | 582 | leak_tracker_testing: |
| 591 | dependency: transitive | 583 | dependency: transitive |
| 592 | description: | 584 | description: |
| 593 | name: leak_tracker_testing | 585 | name: leak_tracker_testing |
| 594 | - sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1" | ||
| 595 | - url: "https://pub.dev" | 586 | + sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" |
| 587 | + url: "https://pub.flutter-io.cn" | ||
| 596 | source: hosted | 588 | source: hosted |
| 597 | - version: "3.0.2" | 589 | + version: "3.0.1" |
| 598 | lints: | 590 | lints: |
| 599 | dependency: transitive | 591 | dependency: transitive |
| 600 | description: | 592 | description: |
| 601 | name: lints | 593 | name: lints |
| 602 | sha256: c35bb79562d980e9a453fc715854e1ed39e24e7d0297a880ef54e17f9874a9d7 | 594 | sha256: c35bb79562d980e9a453fc715854e1ed39e24e7d0297a880ef54e17f9874a9d7 |
| 603 | - url: "https://pub.dev" | 595 | + url: "https://pub.flutter-io.cn" |
| 604 | source: hosted | 596 | source: hosted |
| 605 | version: "5.1.1" | 597 | version: "5.1.1" |
| 606 | logger: | 598 | logger: |
| @@ -608,7 +600,7 @@ packages: | @@ -608,7 +600,7 @@ packages: | ||
| 608 | description: | 600 | description: |
| 609 | name: logger | 601 | name: logger |
| 610 | sha256: "25aee487596a6257655a1e091ec2ae66bc30e7af663592cc3a27e6591e05035c" | 602 | sha256: "25aee487596a6257655a1e091ec2ae66bc30e7af663592cc3a27e6591e05035c" |
| 611 | - url: "https://pub.dev" | 603 | + url: "https://pub.flutter-io.cn" |
| 612 | source: hosted | 604 | source: hosted |
| 613 | version: "2.7.0" | 605 | version: "2.7.0" |
| 614 | logging: | 606 | logging: |
| @@ -616,7 +608,7 @@ packages: | @@ -616,7 +608,7 @@ packages: | ||
| 616 | description: | 608 | description: |
| 617 | name: logging | 609 | name: logging |
| 618 | sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61 | 610 | sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61 |
| 619 | - url: "https://pub.dev" | 611 | + url: "https://pub.flutter-io.cn" |
| 620 | source: hosted | 612 | source: hosted |
| 621 | version: "1.3.0" | 613 | version: "1.3.0" |
| 622 | lottie: | 614 | lottie: |
| @@ -624,23 +616,23 @@ packages: | @@ -624,23 +616,23 @@ packages: | ||
| 624 | description: | 616 | description: |
| 625 | name: lottie | 617 | name: lottie |
| 626 | sha256: c5fa04a80a620066c15cf19cc44773e19e9b38e989ff23ea32e5903ef1015950 | 618 | sha256: c5fa04a80a620066c15cf19cc44773e19e9b38e989ff23ea32e5903ef1015950 |
| 627 | - url: "https://pub.dev" | 619 | + url: "https://pub.flutter-io.cn" |
| 628 | source: hosted | 620 | source: hosted |
| 629 | version: "3.3.1" | 621 | version: "3.3.1" |
| 630 | matcher: | 622 | matcher: |
| 631 | dependency: transitive | 623 | dependency: transitive |
| 632 | description: | 624 | description: |
| 633 | name: matcher | 625 | name: matcher |
| 634 | - sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 | ||
| 635 | - url: "https://pub.dev" | 626 | + sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb |
| 627 | + url: "https://pub.flutter-io.cn" | ||
| 636 | source: hosted | 628 | source: hosted |
| 637 | - version: "0.12.17" | 629 | + version: "0.12.16+1" |
| 638 | material_color_utilities: | 630 | material_color_utilities: |
| 639 | dependency: transitive | 631 | dependency: transitive |
| 640 | description: | 632 | description: |
| 641 | name: material_color_utilities | 633 | name: material_color_utilities |
| 642 | sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec | 634 | sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec |
| 643 | - url: "https://pub.dev" | 635 | + url: "https://pub.flutter-io.cn" |
| 644 | source: hosted | 636 | source: hosted |
| 645 | version: "0.11.1" | 637 | version: "0.11.1" |
| 646 | meta: | 638 | meta: |
| @@ -648,7 +640,7 @@ packages: | @@ -648,7 +640,7 @@ packages: | ||
| 648 | description: | 640 | description: |
| 649 | name: meta | 641 | name: meta |
| 650 | sha256: "307249ce4ff29d58a18e97f6345f539382eb9c9c29ecda628900f31de0443dd9" | 642 | sha256: "307249ce4ff29d58a18e97f6345f539382eb9c9c29ecda628900f31de0443dd9" |
| 651 | - url: "https://pub.dev" | 643 | + url: "https://pub.flutter-io.cn" |
| 652 | source: hosted | 644 | source: hosted |
| 653 | version: "1.19.0" | 645 | version: "1.19.0" |
| 654 | mime: | 646 | mime: |
| @@ -656,23 +648,15 @@ packages: | @@ -656,23 +648,15 @@ packages: | ||
| 656 | description: | 648 | description: |
| 657 | name: mime | 649 | name: mime |
| 658 | sha256: bd47de35f07e27267e69c8c8b22edf9473bfee170a60d60fcc93730c5144b7f6 | 650 | sha256: bd47de35f07e27267e69c8c8b22edf9473bfee170a60d60fcc93730c5144b7f6 |
| 659 | - url: "https://pub.dev" | 651 | + url: "https://pub.flutter-io.cn" |
| 660 | source: hosted | 652 | source: hosted |
| 661 | version: "2.1.0" | 653 | version: "2.1.0" |
| 662 | - objective_c: | ||
| 663 | - dependency: transitive | ||
| 664 | - description: | ||
| 665 | - name: objective_c | ||
| 666 | - sha256: ad56fd53a78ff6b1472fa59ff2a4e8b8ccabafc586fc263a1dfad0b99b5553e3 | ||
| 667 | - url: "https://pub.dev" | ||
| 668 | - source: hosted | ||
| 669 | - version: "9.6.0" | ||
| 670 | octo_image: | 654 | octo_image: |
| 671 | dependency: transitive | 655 | dependency: transitive |
| 672 | description: | 656 | description: |
| 673 | name: octo_image | 657 | name: octo_image |
| 674 | sha256: "34faa6639a78c7e3cbe79be6f9f96535867e879748ade7d17c9b1ae7536293bd" | 658 | sha256: "34faa6639a78c7e3cbe79be6f9f96535867e879748ade7d17c9b1ae7536293bd" |
| 675 | - url: "https://pub.dev" | 659 | + url: "https://pub.flutter-io.cn" |
| 676 | source: hosted | 660 | source: hosted |
| 677 | version: "2.1.0" | 661 | version: "2.1.0" |
| 678 | package_config: | 662 | package_config: |
| @@ -680,17 +664,17 @@ packages: | @@ -680,17 +664,17 @@ packages: | ||
| 680 | description: | 664 | description: |
| 681 | name: package_config | 665 | name: package_config |
| 682 | sha256: f096c55ebb7deb7e384101542bfba8c52696c1b56fca2eb62827989ef2353bbc | 666 | sha256: f096c55ebb7deb7e384101542bfba8c52696c1b56fca2eb62827989ef2353bbc |
| 683 | - url: "https://pub.dev" | 667 | + url: "https://pub.flutter-io.cn" |
| 684 | source: hosted | 668 | source: hosted |
| 685 | version: "2.2.0" | 669 | version: "2.2.0" |
| 686 | path: | 670 | path: |
| 687 | dependency: transitive | 671 | dependency: transitive |
| 688 | description: | 672 | description: |
| 689 | name: path | 673 | name: path |
| 690 | - sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" | ||
| 691 | - url: "https://pub.dev" | 674 | + sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" |
| 675 | + url: "https://pub.flutter-io.cn" | ||
| 692 | source: hosted | 676 | source: hosted |
| 693 | - version: "1.9.1" | 677 | + version: "1.9.0" |
| 694 | path_provider: | 678 | path_provider: |
| 695 | dependency: "direct main" | 679 | dependency: "direct main" |
| 696 | description: | 680 | description: |
| @@ -704,26 +688,26 @@ packages: | @@ -704,26 +688,26 @@ packages: | ||
| 704 | dependency: transitive | 688 | dependency: transitive |
| 705 | description: | 689 | description: |
| 706 | name: path_provider_android | 690 | name: path_provider_android |
| 707 | - sha256: "69cbd515a62b94d32a7944f086b2f82b4ac40a1d45bebfc00813a430ab2dabcd" | ||
| 708 | - url: "https://pub.dev" | 691 | + sha256: d0d310befe2c8ab9e7f393288ccbb11b60c019c6b5afc21973eeee4dda2b35e9 |
| 692 | + url: "https://pub.flutter-io.cn" | ||
| 709 | source: hosted | 693 | source: hosted |
| 710 | - version: "2.3.1" | 694 | + version: "2.2.17" |
| 711 | path_provider_foundation: | 695 | path_provider_foundation: |
| 712 | dependency: transitive | 696 | dependency: transitive |
| 713 | description: | 697 | description: |
| 714 | name: path_provider_foundation | 698 | name: path_provider_foundation |
| 715 | - sha256: "2a376b7d6392d80cd3705782d2caa734ca4727776db0b6ec36ef3f1855197699" | ||
| 716 | - url: "https://pub.dev" | 699 | + sha256: "4843174df4d288f5e29185bd6e72a6fbdf5a4a4602717eed565497429f179942" |
| 700 | + url: "https://pub.flutter-io.cn" | ||
| 717 | source: hosted | 701 | source: hosted |
| 718 | - version: "2.6.0" | 702 | + version: "2.4.1" |
| 719 | path_provider_linux: | 703 | path_provider_linux: |
| 720 | dependency: transitive | 704 | dependency: transitive |
| 721 | description: | 705 | description: |
| 722 | name: path_provider_linux | 706 | name: path_provider_linux |
| 723 | - sha256: "58c2005f147315b11e9b4a7bc889cd5203e250cba8e3f012dae259b4972b5c16" | ||
| 724 | - url: "https://pub.dev" | 707 | + sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279 |
| 708 | + url: "https://pub.flutter-io.cn" | ||
| 725 | source: hosted | 709 | source: hosted |
| 726 | - version: "2.2.2" | 710 | + version: "2.2.1" |
| 727 | path_provider_ohos: | 711 | path_provider_ohos: |
| 728 | dependency: transitive | 712 | dependency: transitive |
| 729 | description: | 713 | description: |
| @@ -737,16 +721,16 @@ packages: | @@ -737,16 +721,16 @@ packages: | ||
| 737 | dependency: transitive | 721 | dependency: transitive |
| 738 | description: | 722 | description: |
| 739 | name: path_provider_platform_interface | 723 | name: path_provider_platform_interface |
| 740 | - sha256: "484838772624c3a4b94f1e44a3e19897fee738f2d5c4ce448443b0417f7c9dda" | ||
| 741 | - url: "https://pub.dev" | 724 | + sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334" |
| 725 | + url: "https://pub.flutter-io.cn" | ||
| 742 | source: hosted | 726 | source: hosted |
| 743 | - version: "2.1.3" | 727 | + version: "2.1.2" |
| 744 | path_provider_windows: | 728 | path_provider_windows: |
| 745 | dependency: transitive | 729 | dependency: transitive |
| 746 | description: | 730 | description: |
| 747 | name: path_provider_windows | 731 | name: path_provider_windows |
| 748 | sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7 | 732 | sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7 |
| 749 | - url: "https://pub.dev" | 733 | + url: "https://pub.flutter-io.cn" |
| 750 | source: hosted | 734 | source: hosted |
| 751 | version: "2.3.0" | 735 | version: "2.3.0" |
| 752 | permission_handler: | 736 | permission_handler: |
| @@ -754,7 +738,7 @@ packages: | @@ -754,7 +738,7 @@ packages: | ||
| 754 | description: | 738 | description: |
| 755 | name: permission_handler | 739 | name: permission_handler |
| 756 | sha256: "59adad729136f01ea9e35a48f5d1395e25cba6cea552249ddbe9cf950f5d7849" | 740 | sha256: "59adad729136f01ea9e35a48f5d1395e25cba6cea552249ddbe9cf950f5d7849" |
| 757 | - url: "https://pub.dev" | 741 | + url: "https://pub.flutter-io.cn" |
| 758 | source: hosted | 742 | source: hosted |
| 759 | version: "11.4.0" | 743 | version: "11.4.0" |
| 760 | permission_handler_android: | 744 | permission_handler_android: |
| @@ -762,7 +746,7 @@ packages: | @@ -762,7 +746,7 @@ packages: | ||
| 762 | description: | 746 | description: |
| 763 | name: permission_handler_android | 747 | name: permission_handler_android |
| 764 | sha256: d3971dcdd76182a0c198c096b5db2f0884b0d4196723d21a866fc4cdea057ebc | 748 | sha256: d3971dcdd76182a0c198c096b5db2f0884b0d4196723d21a866fc4cdea057ebc |
| 765 | - url: "https://pub.dev" | 749 | + url: "https://pub.flutter-io.cn" |
| 766 | source: hosted | 750 | source: hosted |
| 767 | version: "12.1.0" | 751 | version: "12.1.0" |
| 768 | permission_handler_apple: | 752 | permission_handler_apple: |
| @@ -770,7 +754,7 @@ packages: | @@ -770,7 +754,7 @@ packages: | ||
| 770 | description: | 754 | description: |
| 771 | name: permission_handler_apple | 755 | name: permission_handler_apple |
| 772 | sha256: f49cb15a064ea9d974fc7fbb302099353b7b170d07284e86e264561579e5bcf8 | 756 | sha256: f49cb15a064ea9d974fc7fbb302099353b7b170d07284e86e264561579e5bcf8 |
| 773 | - url: "https://pub.dev" | 757 | + url: "https://pub.flutter-io.cn" |
| 774 | source: hosted | 758 | source: hosted |
| 775 | version: "9.6.1" | 759 | version: "9.6.1" |
| 776 | permission_handler_html: | 760 | permission_handler_html: |
| @@ -778,7 +762,7 @@ packages: | @@ -778,7 +762,7 @@ packages: | ||
| 778 | description: | 762 | description: |
| 779 | name: permission_handler_html | 763 | name: permission_handler_html |
| 780 | sha256: "6ea98b3f17f60d3b527f2647ed2ab4dc0f6bfe25b22cb1c363f5d8f62252f6ac" | 764 | sha256: "6ea98b3f17f60d3b527f2647ed2ab4dc0f6bfe25b22cb1c363f5d8f62252f6ac" |
| 781 | - url: "https://pub.dev" | 765 | + url: "https://pub.flutter-io.cn" |
| 782 | source: hosted | 766 | source: hosted |
| 783 | version: "0.1.4+1" | 767 | version: "0.1.4+1" |
| 784 | permission_handler_ohos: | 768 | permission_handler_ohos: |
| @@ -795,7 +779,7 @@ packages: | @@ -795,7 +779,7 @@ packages: | ||
| 795 | description: | 779 | description: |
| 796 | name: permission_handler_platform_interface | 780 | name: permission_handler_platform_interface |
| 797 | sha256: a5c8a97ecf5616112a5b16d4b8e9ec0e5ae90ef63ac69c0d7b8ae240be760b23 | 781 | sha256: a5c8a97ecf5616112a5b16d4b8e9ec0e5ae90ef63ac69c0d7b8ae240be760b23 |
| 798 | - url: "https://pub.dev" | 782 | + url: "https://pub.flutter-io.cn" |
| 799 | source: hosted | 783 | source: hosted |
| 800 | version: "4.4.0" | 784 | version: "4.4.0" |
| 801 | permission_handler_windows: | 785 | permission_handler_windows: |
| @@ -803,7 +787,7 @@ packages: | @@ -803,7 +787,7 @@ packages: | ||
| 803 | description: | 787 | description: |
| 804 | name: permission_handler_windows | 788 | name: permission_handler_windows |
| 805 | sha256: caeae01858a0a7d2df67a445ac98e1ad95e55a0e77c73044f4e9b1c8c2289cbd | 789 | sha256: caeae01858a0a7d2df67a445ac98e1ad95e55a0e77c73044f4e9b1c8c2289cbd |
| 806 | - url: "https://pub.dev" | 790 | + url: "https://pub.flutter-io.cn" |
| 807 | source: hosted | 791 | source: hosted |
| 808 | version: "0.2.2" | 792 | version: "0.2.2" |
| 809 | pigeon: | 793 | pigeon: |
| @@ -820,7 +804,7 @@ packages: | @@ -820,7 +804,7 @@ packages: | ||
| 820 | description: | 804 | description: |
| 821 | name: platform | 805 | name: platform |
| 822 | sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984" | 806 | sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984" |
| 823 | - url: "https://pub.dev" | 807 | + url: "https://pub.flutter-io.cn" |
| 824 | source: hosted | 808 | source: hosted |
| 825 | version: "3.1.6" | 809 | version: "3.1.6" |
| 826 | plugin_platform_interface: | 810 | plugin_platform_interface: |
| @@ -828,7 +812,7 @@ packages: | @@ -828,7 +812,7 @@ packages: | ||
| 828 | description: | 812 | description: |
| 829 | name: plugin_platform_interface | 813 | name: plugin_platform_interface |
| 830 | sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" | 814 | sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" |
| 831 | - url: "https://pub.dev" | 815 | + url: "https://pub.flutter-io.cn" |
| 832 | source: hosted | 816 | source: hosted |
| 833 | version: "2.1.8" | 817 | version: "2.1.8" |
| 834 | pool: | 818 | pool: |
| @@ -836,7 +820,7 @@ packages: | @@ -836,7 +820,7 @@ packages: | ||
| 836 | description: | 820 | description: |
| 837 | name: pool | 821 | name: pool |
| 838 | sha256: "4177f68c237ea2128d1bee66ac17b2ce05ba3dbaafcbdd54c5d40a39d0b6b11c" | 822 | sha256: "4177f68c237ea2128d1bee66ac17b2ce05ba3dbaafcbdd54c5d40a39d0b6b11c" |
| 839 | - url: "https://pub.dev" | 823 | + url: "https://pub.flutter-io.cn" |
| 840 | source: hosted | 824 | source: hosted |
| 841 | version: "1.5.3" | 825 | version: "1.5.3" |
| 842 | posix: | 826 | posix: |
| @@ -844,7 +828,7 @@ packages: | @@ -844,7 +828,7 @@ packages: | ||
| 844 | description: | 828 | description: |
| 845 | name: posix | 829 | name: posix |
| 846 | sha256: bc1bad54ad2b735816e31f8d4600cfde6c7839975085ddfbca48b6c9f7c4044e | 830 | sha256: bc1bad54ad2b735816e31f8d4600cfde6c7839975085ddfbca48b6c9f7c4044e |
| 847 | - url: "https://pub.dev" | 831 | + url: "https://pub.flutter-io.cn" |
| 848 | source: hosted | 832 | source: hosted |
| 849 | version: "6.5.2" | 833 | version: "6.5.2" |
| 850 | pretty_dio_logger: | 834 | pretty_dio_logger: |
| @@ -852,7 +836,7 @@ packages: | @@ -852,7 +836,7 @@ packages: | ||
| 852 | description: | 836 | description: |
| 853 | name: pretty_dio_logger | 837 | name: pretty_dio_logger |
| 854 | sha256: "36f2101299786d567869493e2f5731de61ce130faa14679473b26905a92b6407" | 838 | sha256: "36f2101299786d567869493e2f5731de61ce130faa14679473b26905a92b6407" |
| 855 | - url: "https://pub.dev" | 839 | + url: "https://pub.flutter-io.cn" |
| 856 | source: hosted | 840 | source: hosted |
| 857 | version: "1.4.0" | 841 | version: "1.4.0" |
| 858 | pub_semver: | 842 | pub_semver: |
| @@ -860,31 +844,23 @@ packages: | @@ -860,31 +844,23 @@ packages: | ||
| 860 | description: | 844 | description: |
| 861 | name: pub_semver | 845 | name: pub_semver |
| 862 | sha256: "261236774e8b1d69cfc6b9eabbc96c40f25e7a2d6b171f3385d4f65d5734fb24" | 846 | sha256: "261236774e8b1d69cfc6b9eabbc96c40f25e7a2d6b171f3385d4f65d5734fb24" |
| 863 | - url: "https://pub.dev" | 847 | + url: "https://pub.flutter-io.cn" |
| 864 | source: hosted | 848 | source: hosted |
| 865 | version: "2.2.1" | 849 | version: "2.2.1" |
| 866 | pubspec_parse: | 850 | pubspec_parse: |
| 867 | dependency: transitive | 851 | dependency: transitive |
| 868 | description: | 852 | description: |
| 869 | name: pubspec_parse | 853 | name: pubspec_parse |
| 870 | - sha256: c38b81cbf34450b67e0265d73433569d12e34782e30ed769c9cc99c9d5f2e796 | ||
| 871 | - url: "https://pub.dev" | ||
| 872 | - source: hosted | ||
| 873 | - version: "1.6.0" | ||
| 874 | - record_use: | ||
| 875 | - dependency: transitive | ||
| 876 | - description: | ||
| 877 | - name: record_use | ||
| 878 | - sha256: "1cb8564af8d43b464294411db9217f5ec04891c6f22ee2c32d73ae05e88a6bd2" | ||
| 879 | - url: "https://pub.dev" | 854 | + sha256: "0560ba233314abbed0a48a2956f7f022cce7c3e1e73df540277da7544cad4082" |
| 855 | + url: "https://pub.flutter-io.cn" | ||
| 880 | source: hosted | 856 | source: hosted |
| 881 | - version: "1.1.1" | 857 | + version: "1.5.0" |
| 882 | rxdart: | 858 | rxdart: |
| 883 | dependency: transitive | 859 | dependency: transitive |
| 884 | description: | 860 | description: |
| 885 | name: rxdart | 861 | name: rxdart |
| 886 | sha256: "5c3004a4a8dbb94bd4bf5412a4def4acdaa12e12f269737a5751369e12d1a962" | 862 | sha256: "5c3004a4a8dbb94bd4bf5412a4def4acdaa12e12f269737a5751369e12d1a962" |
| 887 | - url: "https://pub.dev" | 863 | + url: "https://pub.flutter-io.cn" |
| 888 | source: hosted | 864 | source: hosted |
| 889 | version: "0.28.0" | 865 | version: "0.28.0" |
| 890 | share_plus: | 866 | share_plus: |
| @@ -900,7 +876,7 @@ packages: | @@ -900,7 +876,7 @@ packages: | ||
| 900 | dependency: transitive | 876 | dependency: transitive |
| 901 | description: | 877 | description: |
| 902 | path: "packages/share_plus/share_plus_platform_interface" | 878 | path: "packages/share_plus/share_plus_platform_interface" |
| 903 | - ref: "55de300a8627c55cd45ac86e6a26bcae8e0ca4cf" | 879 | + ref: "br_share_plus-v10.1.1_ohos" |
| 904 | resolved-ref: "55de300a8627c55cd45ac86e6a26bcae8e0ca4cf" | 880 | resolved-ref: "55de300a8627c55cd45ac86e6a26bcae8e0ca4cf" |
| 905 | url: "https://gitcode.com/CPF-Flutter/flutter_plus_plugins.git" | 881 | url: "https://gitcode.com/CPF-Flutter/flutter_plus_plugins.git" |
| 906 | source: git | 882 | source: git |
| @@ -918,24 +894,24 @@ packages: | @@ -918,24 +894,24 @@ packages: | ||
| 918 | dependency: transitive | 894 | dependency: transitive |
| 919 | description: | 895 | description: |
| 920 | name: shared_preferences_android | 896 | name: shared_preferences_android |
| 921 | - sha256: e8d4762b1e2e8578fc4d0fd548cebf24afd24f49719c08974df92834565e2c53 | ||
| 922 | - url: "https://pub.dev" | 897 | + sha256: "5bcf0772a761b04f8c6bf814721713de6f3e5d9d89caf8d3fe031b02a342379e" |
| 898 | + url: "https://pub.flutter-io.cn" | ||
| 923 | source: hosted | 899 | source: hosted |
| 924 | - version: "2.4.23" | 900 | + version: "2.4.11" |
| 925 | shared_preferences_foundation: | 901 | shared_preferences_foundation: |
| 926 | dependency: transitive | 902 | dependency: transitive |
| 927 | description: | 903 | description: |
| 928 | name: shared_preferences_foundation | 904 | name: shared_preferences_foundation |
| 929 | - sha256: "2ec3934efa51e46117f23031cc141b8fc878e8525b94ec1ea4f7f586cf1b47ea" | ||
| 930 | - url: "https://pub.dev" | 905 | + sha256: "6a52cfcdaeac77cad8c97b539ff688ccfc458c007b4db12be584fbe5c0e49e03" |
| 906 | + url: "https://pub.flutter-io.cn" | ||
| 931 | source: hosted | 907 | source: hosted |
| 932 | - version: "2.5.7" | 908 | + version: "2.5.4" |
| 933 | shared_preferences_linux: | 909 | shared_preferences_linux: |
| 934 | dependency: transitive | 910 | dependency: transitive |
| 935 | description: | 911 | description: |
| 936 | name: shared_preferences_linux | 912 | name: shared_preferences_linux |
| 937 | sha256: "580abfd40f415611503cae30adf626e6656dfb2f0cee8f465ece7b6defb40f2f" | 913 | sha256: "580abfd40f415611503cae30adf626e6656dfb2f0cee8f465ece7b6defb40f2f" |
| 938 | - url: "https://pub.dev" | 914 | + url: "https://pub.flutter-io.cn" |
| 939 | source: hosted | 915 | source: hosted |
| 940 | version: "2.4.1" | 916 | version: "2.4.1" |
| 941 | shared_preferences_ohos: | 917 | shared_preferences_ohos: |
| @@ -951,16 +927,16 @@ packages: | @@ -951,16 +927,16 @@ packages: | ||
| 951 | dependency: transitive | 927 | dependency: transitive |
| 952 | description: | 928 | description: |
| 953 | name: shared_preferences_platform_interface | 929 | name: shared_preferences_platform_interface |
| 954 | - sha256: "649dc798a33931919ea356c4305c2d1f81619ea6e92244070b520187b5140ef9" | ||
| 955 | - url: "https://pub.dev" | 930 | + sha256: "57cbf196c486bc2cf1f02b85784932c6094376284b3ad5779d1b1c6c6a816b80" |
| 931 | + url: "https://pub.flutter-io.cn" | ||
| 956 | source: hosted | 932 | source: hosted |
| 957 | - version: "2.4.2" | 933 | + version: "2.4.1" |
| 958 | shared_preferences_web: | 934 | shared_preferences_web: |
| 959 | dependency: transitive | 935 | dependency: transitive |
| 960 | description: | 936 | description: |
| 961 | name: shared_preferences_web | 937 | name: shared_preferences_web |
| 962 | sha256: c49bd060261c9a3f0ff445892695d6212ff603ef3115edbb448509d407600019 | 938 | sha256: c49bd060261c9a3f0ff445892695d6212ff603ef3115edbb448509d407600019 |
| 963 | - url: "https://pub.dev" | 939 | + url: "https://pub.flutter-io.cn" |
| 964 | source: hosted | 940 | source: hosted |
| 965 | version: "2.4.3" | 941 | version: "2.4.3" |
| 966 | shared_preferences_windows: | 942 | shared_preferences_windows: |
| @@ -968,7 +944,7 @@ packages: | @@ -968,7 +944,7 @@ packages: | ||
| 968 | description: | 944 | description: |
| 969 | name: shared_preferences_windows | 945 | name: shared_preferences_windows |
| 970 | sha256: "94ef0f72b2d71bc3e700e025db3710911bd51a71cefb65cc609dd0d9a982e3c1" | 946 | sha256: "94ef0f72b2d71bc3e700e025db3710911bd51a71cefb65cc609dd0d9a982e3c1" |
| 971 | - url: "https://pub.dev" | 947 | + url: "https://pub.flutter-io.cn" |
| 972 | source: hosted | 948 | source: hosted |
| 973 | version: "2.4.1" | 949 | version: "2.4.1" |
| 974 | shelf: | 950 | shelf: |
| @@ -976,7 +952,7 @@ packages: | @@ -976,7 +952,7 @@ packages: | ||
| 976 | description: | 952 | description: |
| 977 | name: shelf | 953 | name: shelf |
| 978 | sha256: e7dd780a7ffb623c57850b33f43309312fc863fb6aa3d276a754bb299839ef12 | 954 | sha256: e7dd780a7ffb623c57850b33f43309312fc863fb6aa3d276a754bb299839ef12 |
| 979 | - url: "https://pub.dev" | 955 | + url: "https://pub.flutter-io.cn" |
| 980 | source: hosted | 956 | source: hosted |
| 981 | version: "1.4.2" | 957 | version: "1.4.2" |
| 982 | shelf_web_socket: | 958 | shelf_web_socket: |
| @@ -984,7 +960,7 @@ packages: | @@ -984,7 +960,7 @@ packages: | ||
| 984 | description: | 960 | description: |
| 985 | name: shelf_web_socket | 961 | name: shelf_web_socket |
| 986 | sha256: "3632775c8e90d6c9712f883e633716432a27758216dfb61bd86a8321c0580925" | 962 | sha256: "3632775c8e90d6c9712f883e633716432a27758216dfb61bd86a8321c0580925" |
| 987 | - url: "https://pub.dev" | 963 | + url: "https://pub.flutter-io.cn" |
| 988 | source: hosted | 964 | source: hosted |
| 989 | version: "3.0.0" | 965 | version: "3.0.0" |
| 990 | simple_gesture_detector: | 966 | simple_gesture_detector: |
| @@ -992,7 +968,7 @@ packages: | @@ -992,7 +968,7 @@ packages: | ||
| 992 | description: | 968 | description: |
| 993 | name: simple_gesture_detector | 969 | name: simple_gesture_detector |
| 994 | sha256: ba2cd5af24ff20a0b8d609cec3f40e5b0744d2a71804a2616ae086b9c19d19a3 | 970 | sha256: ba2cd5af24ff20a0b8d609cec3f40e5b0744d2a71804a2616ae086b9c19d19a3 |
| 995 | - url: "https://pub.dev" | 971 | + url: "https://pub.flutter-io.cn" |
| 996 | source: hosted | 972 | source: hosted |
| 997 | version: "0.2.1" | 973 | version: "0.2.1" |
| 998 | sky_engine: | 974 | sky_engine: |
| @@ -1004,10 +980,10 @@ packages: | @@ -1004,10 +980,10 @@ packages: | ||
| 1004 | dependency: transitive | 980 | dependency: transitive |
| 1005 | description: | 981 | description: |
| 1006 | name: source_span | 982 | name: source_span |
| 1007 | - sha256: "56a02f1f4cd1a2d96303c0144c93bd6d909eea6bee6bf5a0e0b685edbd4c47ab" | ||
| 1008 | - url: "https://pub.dev" | 983 | + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" |
| 984 | + url: "https://pub.flutter-io.cn" | ||
| 1009 | source: hosted | 985 | source: hosted |
| 1010 | - version: "1.10.2" | 986 | + version: "1.10.0" |
| 1011 | sqflite: | 987 | sqflite: |
| 1012 | dependency: "direct main" | 988 | dependency: "direct main" |
| 1013 | description: | 989 | description: |
| @@ -1021,31 +997,31 @@ packages: | @@ -1021,31 +997,31 @@ packages: | ||
| 1021 | dependency: transitive | 997 | dependency: transitive |
| 1022 | description: | 998 | description: |
| 1023 | name: sqflite_android | 999 | name: sqflite_android |
| 1024 | - sha256: "881e28efdcc9950fd8e9bb42713dcf1103e62a2e7168f23c9338d82db13dec40" | ||
| 1025 | - url: "https://pub.dev" | 1000 | + sha256: "78f489aab276260cdd26676d2169446c7ecd3484bbd5fead4ca14f3ed4dd9ee3" |
| 1001 | + url: "https://pub.flutter-io.cn" | ||
| 1026 | source: hosted | 1002 | source: hosted |
| 1027 | - version: "2.4.2+3" | 1003 | + version: "2.4.0" |
| 1028 | sqflite_common: | 1004 | sqflite_common: |
| 1029 | dependency: transitive | 1005 | dependency: transitive |
| 1030 | description: | 1006 | description: |
| 1031 | name: sqflite_common | 1007 | name: sqflite_common |
| 1032 | - sha256: "1581ffbf7a0e333b380d6a30737d78516b826cb35beb7fb0bf8a3ea0c678b465" | ||
| 1033 | - url: "https://pub.dev" | 1008 | + sha256: "761b9740ecbd4d3e66b8916d784e581861fd3c3553eda85e167bc49fdb68f709" |
| 1009 | + url: "https://pub.flutter-io.cn" | ||
| 1034 | source: hosted | 1010 | source: hosted |
| 1035 | - version: "2.5.8" | 1011 | + version: "2.5.4+6" |
| 1036 | sqflite_darwin: | 1012 | sqflite_darwin: |
| 1037 | dependency: transitive | 1013 | dependency: transitive |
| 1038 | description: | 1014 | description: |
| 1039 | name: sqflite_darwin | 1015 | name: sqflite_darwin |
| 1040 | - sha256: "279832e5cde3fe99e8571879498c9211f3ca6391b0d818df4e17d9fff5c6ccb3" | ||
| 1041 | - url: "https://pub.dev" | 1016 | + sha256: "22adfd9a2c7d634041e96d6241e6e1c8138ca6817018afc5d443fef91dcefa9c" |
| 1017 | + url: "https://pub.flutter-io.cn" | ||
| 1042 | source: hosted | 1018 | source: hosted |
| 1043 | - version: "2.4.2" | 1019 | + version: "2.4.1+1" |
| 1044 | sqflite_ohos: | 1020 | sqflite_ohos: |
| 1045 | dependency: transitive | 1021 | dependency: transitive |
| 1046 | description: | 1022 | description: |
| 1047 | path: sqflite_ohos | 1023 | path: sqflite_ohos |
| 1048 | - ref: "8cc162bdd90adad2e8c8054b52fc14c1e86000ee" | 1024 | + ref: "2.4.2-ohos-1.0.0-beta.2" |
| 1049 | resolved-ref: "8cc162bdd90adad2e8c8054b52fc14c1e86000ee" | 1025 | resolved-ref: "8cc162bdd90adad2e8c8054b52fc14c1e86000ee" |
| 1050 | url: "https://gitcode.com/CPF-Flutter/flutter_sqflite.git" | 1026 | url: "https://gitcode.com/CPF-Flutter/flutter_sqflite.git" |
| 1051 | source: git | 1027 | source: git |
| @@ -1055,159 +1031,167 @@ packages: | @@ -1055,159 +1031,167 @@ packages: | ||
| 1055 | description: | 1031 | description: |
| 1056 | name: sqflite_platform_interface | 1032 | name: sqflite_platform_interface |
| 1057 | sha256: "8dd4515c7bdcae0a785b0062859336de775e8c65db81ae33dd5445f35be61920" | 1033 | sha256: "8dd4515c7bdcae0a785b0062859336de775e8c65db81ae33dd5445f35be61920" |
| 1058 | - url: "https://pub.dev" | 1034 | + url: "https://pub.flutter-io.cn" |
| 1059 | source: hosted | 1035 | source: hosted |
| 1060 | version: "2.4.0" | 1036 | version: "2.4.0" |
| 1061 | stack_trace: | 1037 | stack_trace: |
| 1062 | dependency: transitive | 1038 | dependency: transitive |
| 1063 | description: | 1039 | description: |
| 1064 | name: stack_trace | 1040 | name: stack_trace |
| 1065 | - sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" | ||
| 1066 | - url: "https://pub.dev" | 1041 | + sha256: "9f47fd3630d76be3ab26f0ee06d213679aa425996925ff3feffdec504931c377" |
| 1042 | + url: "https://pub.flutter-io.cn" | ||
| 1067 | source: hosted | 1043 | source: hosted |
| 1068 | - version: "1.12.1" | 1044 | + version: "1.12.0" |
| 1069 | stream_channel: | 1045 | stream_channel: |
| 1070 | dependency: transitive | 1046 | dependency: transitive |
| 1071 | description: | 1047 | description: |
| 1072 | name: stream_channel | 1048 | name: stream_channel |
| 1073 | - sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" | ||
| 1074 | - url: "https://pub.dev" | 1049 | + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 |
| 1050 | + url: "https://pub.flutter-io.cn" | ||
| 1075 | source: hosted | 1051 | source: hosted |
| 1076 | - version: "2.1.4" | 1052 | + version: "2.1.2" |
| 1077 | stream_transform: | 1053 | stream_transform: |
| 1078 | dependency: transitive | 1054 | dependency: transitive |
| 1079 | description: | 1055 | description: |
| 1080 | name: stream_transform | 1056 | name: stream_transform |
| 1081 | sha256: a00e5f18bffc764f923e7dec1038527f7fe7a1791361a7117f0358193f13d53a | 1057 | sha256: a00e5f18bffc764f923e7dec1038527f7fe7a1791361a7117f0358193f13d53a |
| 1082 | - url: "https://pub.dev" | 1058 | + url: "https://pub.flutter-io.cn" |
| 1083 | source: hosted | 1059 | source: hosted |
| 1084 | version: "2.1.2" | 1060 | version: "2.1.2" |
| 1085 | string_scanner: | 1061 | string_scanner: |
| 1086 | dependency: transitive | 1062 | dependency: transitive |
| 1087 | description: | 1063 | description: |
| 1088 | name: string_scanner | 1064 | name: string_scanner |
| 1089 | - sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" | ||
| 1090 | - url: "https://pub.dev" | 1065 | + sha256: "688af5ed3402a4bde5b3a6c15fd768dbf2621a614950b17f04626c431ab3c4c3" |
| 1066 | + url: "https://pub.flutter-io.cn" | ||
| 1091 | source: hosted | 1067 | source: hosted |
| 1092 | - version: "1.4.1" | 1068 | + version: "1.3.0" |
| 1093 | synchronized: | 1069 | synchronized: |
| 1094 | dependency: transitive | 1070 | dependency: transitive |
| 1095 | description: | 1071 | description: |
| 1096 | name: synchronized | 1072 | name: synchronized |
| 1097 | - sha256: c254ade258ec8282947a0acbbc90b9575b4f19673533ee46f2f6e9b3aeefd7c0 | ||
| 1098 | - url: "https://pub.dev" | 1073 | + sha256: "69fe30f3a8b04a0be0c15ae6490fc859a78ef4c43ae2dd5e8a623d45bfcf9225" |
| 1074 | + url: "https://pub.flutter-io.cn" | ||
| 1099 | source: hosted | 1075 | source: hosted |
| 1100 | - version: "3.4.0" | 1076 | + version: "3.3.0+3" |
| 1101 | table_calendar: | 1077 | table_calendar: |
| 1102 | dependency: "direct main" | 1078 | dependency: "direct main" |
| 1103 | description: | 1079 | description: |
| 1104 | name: table_calendar | 1080 | name: table_calendar |
| 1105 | - sha256: f276347cad425ef837a41e8d9ad43f3ee7d59227aa4c36d7430607a5a18fa3b3 | ||
| 1106 | - url: "https://pub.dev" | 1081 | + sha256: b2896b7c86adf3a4d9c911d860120fe3dbe03c85db43b22fd61f14ee78cdbb63 |
| 1082 | + url: "https://pub.flutter-io.cn" | ||
| 1107 | source: hosted | 1083 | source: hosted |
| 1108 | - version: "3.2.1" | 1084 | + version: "3.1.3" |
| 1109 | term_glyph: | 1085 | term_glyph: |
| 1110 | dependency: transitive | 1086 | dependency: transitive |
| 1111 | description: | 1087 | description: |
| 1112 | name: term_glyph | 1088 | name: term_glyph |
| 1113 | - sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" | ||
| 1114 | - url: "https://pub.dev" | 1089 | + sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 |
| 1090 | + url: "https://pub.flutter-io.cn" | ||
| 1115 | source: hosted | 1091 | source: hosted |
| 1116 | - version: "1.2.2" | 1092 | + version: "1.2.1" |
| 1117 | test_api: | 1093 | test_api: |
| 1118 | dependency: transitive | 1094 | dependency: transitive |
| 1119 | description: | 1095 | description: |
| 1120 | name: test_api | 1096 | name: test_api |
| 1121 | - sha256: ab2726c1a94d3176a45960b6234466ec367179b87dd74f1611adb1f3b5fb9d55 | ||
| 1122 | - url: "https://pub.dev" | 1097 | + sha256: "664d3a9a64782fcdeb83ce9c6b39e78fd2971d4e37827b9b06c3aa1edc5e760c" |
| 1098 | + url: "https://pub.flutter-io.cn" | ||
| 1123 | source: hosted | 1099 | source: hosted |
| 1124 | - version: "0.7.7" | 1100 | + version: "0.7.3" |
| 1125 | thinking_analytics: | 1101 | thinking_analytics: |
| 1126 | dependency: "direct main" | 1102 | dependency: "direct main" |
| 1127 | description: | 1103 | description: |
| 1128 | name: thinking_analytics | 1104 | name: thinking_analytics |
| 1129 | sha256: b01cac0b5482e71c1d75c44c77d27f427662cc65a77b7bc3c8b49617d7a01e02 | 1105 | sha256: b01cac0b5482e71c1d75c44c77d27f427662cc65a77b7bc3c8b49617d7a01e02 |
| 1130 | - url: "https://pub.dev" | 1106 | + url: "https://pub.flutter-io.cn" |
| 1131 | source: hosted | 1107 | source: hosted |
| 1132 | version: "3.3.3" | 1108 | version: "3.3.3" |
| 1109 | + timing: | ||
| 1110 | + dependency: transitive | ||
| 1111 | + description: | ||
| 1112 | + name: timing | ||
| 1113 | + sha256: "62ee18aca144e4a9f29d212f5a4c6a053be252b895ab14b5821996cff4ed90fe" | ||
| 1114 | + url: "https://pub.flutter-io.cn" | ||
| 1115 | + source: hosted | ||
| 1116 | + version: "1.0.2" | ||
| 1133 | typed_data: | 1117 | typed_data: |
| 1134 | dependency: transitive | 1118 | dependency: transitive |
| 1135 | description: | 1119 | description: |
| 1136 | name: typed_data | 1120 | name: typed_data |
| 1137 | sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006 | 1121 | sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006 |
| 1138 | - url: "https://pub.dev" | 1122 | + url: "https://pub.flutter-io.cn" |
| 1139 | source: hosted | 1123 | source: hosted |
| 1140 | version: "1.4.0" | 1124 | version: "1.4.0" |
| 1141 | url_launcher_linux: | 1125 | url_launcher_linux: |
| 1142 | dependency: transitive | 1126 | dependency: transitive |
| 1143 | description: | 1127 | description: |
| 1144 | name: url_launcher_linux | 1128 | name: url_launcher_linux |
| 1145 | - sha256: "10f86fef4c2c43563fa6c211ff9cf757adf4d3ab762c56bd430664a947d70cd0" | ||
| 1146 | - url: "https://pub.dev" | 1129 | + sha256: "4e9ba368772369e3e08f231d2301b4ef72b9ff87c31192ef471b380ef29a4935" |
| 1130 | + url: "https://pub.flutter-io.cn" | ||
| 1147 | source: hosted | 1131 | source: hosted |
| 1148 | - version: "3.2.3" | 1132 | + version: "3.2.1" |
| 1149 | url_launcher_platform_interface: | 1133 | url_launcher_platform_interface: |
| 1150 | dependency: transitive | 1134 | dependency: transitive |
| 1151 | description: | 1135 | description: |
| 1152 | name: url_launcher_platform_interface | 1136 | name: url_launcher_platform_interface |
| 1153 | sha256: "552f8a1e663569be95a8190206a38187b531910283c3e982193e4f2733f01029" | 1137 | sha256: "552f8a1e663569be95a8190206a38187b531910283c3e982193e4f2733f01029" |
| 1154 | - url: "https://pub.dev" | 1138 | + url: "https://pub.flutter-io.cn" |
| 1155 | source: hosted | 1139 | source: hosted |
| 1156 | version: "2.3.2" | 1140 | version: "2.3.2" |
| 1157 | url_launcher_web: | 1141 | url_launcher_web: |
| 1158 | dependency: transitive | 1142 | dependency: transitive |
| 1159 | description: | 1143 | description: |
| 1160 | name: url_launcher_web | 1144 | name: url_launcher_web |
| 1161 | - sha256: "85c81589622fbc87c1c683aaea164d3604a7777495a79d91e39ffcdec39ddb34" | ||
| 1162 | - url: "https://pub.dev" | 1145 | + sha256: "4bd2b7b4dc4d4d0b94e5babfffbca8eac1a126c7f3d6ecbc1a11013faa3abba2" |
| 1146 | + url: "https://pub.flutter-io.cn" | ||
| 1163 | source: hosted | 1147 | source: hosted |
| 1164 | - version: "2.4.3" | 1148 | + version: "2.4.1" |
| 1165 | url_launcher_windows: | 1149 | url_launcher_windows: |
| 1166 | dependency: transitive | 1150 | dependency: transitive |
| 1167 | description: | 1151 | description: |
| 1168 | name: url_launcher_windows | 1152 | name: url_launcher_windows |
| 1169 | - sha256: "6c5ad3f22cd4c38e089b81963b3cd7bb83b111b2df5dce008bb066162f42e429" | ||
| 1170 | - url: "https://pub.dev" | 1153 | + sha256: "3284b6d2ac454cf34f114e1d3319866fdd1e19cdc329999057e44ffe936cfa77" |
| 1154 | + url: "https://pub.flutter-io.cn" | ||
| 1171 | source: hosted | 1155 | source: hosted |
| 1172 | - version: "3.1.6" | 1156 | + version: "3.1.4" |
| 1173 | uuid: | 1157 | uuid: |
| 1174 | dependency: transitive | 1158 | dependency: transitive |
| 1175 | description: | 1159 | description: |
| 1176 | name: uuid | 1160 | name: uuid |
| 1177 | sha256: "9b129329f58692f6e6578329498a8fe9fbe98f090beb764ffbb8ee2eadd01dcd" | 1161 | sha256: "9b129329f58692f6e6578329498a8fe9fbe98f090beb764ffbb8ee2eadd01dcd" |
| 1178 | - url: "https://pub.dev" | 1162 | + url: "https://pub.flutter-io.cn" |
| 1179 | source: hosted | 1163 | source: hosted |
| 1180 | version: "4.6.0" | 1164 | version: "4.6.0" |
| 1181 | vector_math: | 1165 | vector_math: |
| 1182 | dependency: transitive | 1166 | dependency: transitive |
| 1183 | description: | 1167 | description: |
| 1184 | name: vector_math | 1168 | name: vector_math |
| 1185 | - sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b | ||
| 1186 | - url: "https://pub.dev" | 1169 | + sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" |
| 1170 | + url: "https://pub.flutter-io.cn" | ||
| 1187 | source: hosted | 1171 | source: hosted |
| 1188 | - version: "2.2.0" | 1172 | + version: "2.1.4" |
| 1189 | video_thumbnail: | 1173 | video_thumbnail: |
| 1190 | dependency: "direct main" | 1174 | dependency: "direct main" |
| 1191 | description: | 1175 | description: |
| 1192 | name: video_thumbnail | 1176 | name: video_thumbnail |
| 1193 | sha256: "181a0c205b353918954a881f53a3441476b9e301641688a581e0c13f00dc588b" | 1177 | sha256: "181a0c205b353918954a881f53a3441476b9e301641688a581e0c13f00dc588b" |
| 1194 | - url: "https://pub.dev" | 1178 | + url: "https://pub.flutter-io.cn" |
| 1195 | source: hosted | 1179 | source: hosted |
| 1196 | version: "0.5.6" | 1180 | version: "0.5.6" |
| 1197 | vm_service: | 1181 | vm_service: |
| 1198 | dependency: transitive | 1182 | dependency: transitive |
| 1199 | description: | 1183 | description: |
| 1200 | name: vm_service | 1184 | name: vm_service |
| 1201 | - sha256: "5f37239c4851efcef929cea7824e76df7f2f0970aef85d66bbc430afa40e72f0" | ||
| 1202 | - url: "https://pub.dev" | 1185 | + sha256: f6be3ed8bd01289b34d679c2b62226f63c0e69f9fd2e50a6b3c1c729a961041b |
| 1186 | + url: "https://pub.flutter-io.cn" | ||
| 1203 | source: hosted | 1187 | source: hosted |
| 1204 | - version: "15.3.0" | 1188 | + version: "14.3.0" |
| 1205 | watcher: | 1189 | watcher: |
| 1206 | dependency: transitive | 1190 | dependency: transitive |
| 1207 | description: | 1191 | description: |
| 1208 | name: watcher | 1192 | name: watcher |
| 1209 | sha256: "1398c9f081a753f9226febe8900fce8f7d0a67163334e1c94a2438339d79d635" | 1193 | sha256: "1398c9f081a753f9226febe8900fce8f7d0a67163334e1c94a2438339d79d635" |
| 1210 | - url: "https://pub.dev" | 1194 | + url: "https://pub.flutter-io.cn" |
| 1211 | source: hosted | 1195 | source: hosted |
| 1212 | version: "1.2.1" | 1196 | version: "1.2.1" |
| 1213 | web: | 1197 | web: |
| @@ -1215,7 +1199,7 @@ packages: | @@ -1215,7 +1199,7 @@ packages: | ||
| 1215 | description: | 1199 | description: |
| 1216 | name: web | 1200 | name: web |
| 1217 | sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a" | 1201 | sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a" |
| 1218 | - url: "https://pub.dev" | 1202 | + url: "https://pub.flutter-io.cn" |
| 1219 | source: hosted | 1203 | source: hosted |
| 1220 | version: "1.1.1" | 1204 | version: "1.1.1" |
| 1221 | web_socket: | 1205 | web_socket: |
| @@ -1223,7 +1207,7 @@ packages: | @@ -1223,7 +1207,7 @@ packages: | ||
| 1223 | description: | 1207 | description: |
| 1224 | name: web_socket | 1208 | name: web_socket |
| 1225 | sha256: "34d64019aa8e36bf9842ac014bb5d2f5586ca73df5e4d9bf5c936975cae6982c" | 1209 | sha256: "34d64019aa8e36bf9842ac014bb5d2f5586ca73df5e4d9bf5c936975cae6982c" |
| 1226 | - url: "https://pub.dev" | 1210 | + url: "https://pub.flutter-io.cn" |
| 1227 | source: hosted | 1211 | source: hosted |
| 1228 | version: "1.0.1" | 1212 | version: "1.0.1" |
| 1229 | web_socket_channel: | 1213 | web_socket_channel: |
| @@ -1231,7 +1215,7 @@ packages: | @@ -1231,7 +1215,7 @@ packages: | ||
| 1231 | description: | 1215 | description: |
| 1232 | name: web_socket_channel | 1216 | name: web_socket_channel |
| 1233 | sha256: d645757fb0f4773d602444000a8131ff5d48c9e47adfe9772652dd1a4f2d45c8 | 1217 | sha256: d645757fb0f4773d602444000a8131ff5d48c9e47adfe9772652dd1a4f2d45c8 |
| 1234 | - url: "https://pub.dev" | 1218 | + url: "https://pub.flutter-io.cn" |
| 1235 | source: hosted | 1219 | source: hosted |
| 1236 | version: "3.0.3" | 1220 | version: "3.0.3" |
| 1237 | webview_flutter: | 1221 | webview_flutter: |
| @@ -1247,8 +1231,8 @@ packages: | @@ -1247,8 +1231,8 @@ packages: | ||
| 1247 | dependency: transitive | 1231 | dependency: transitive |
| 1248 | description: | 1232 | description: |
| 1249 | path: "packages/webview_flutter/webview_flutter_android" | 1233 | path: "packages/webview_flutter/webview_flutter_android" |
| 1250 | - ref: "5a9d0055772c7973712c0eaaf2de0e5f0749f8b9" | ||
| 1251 | - resolved-ref: "5a9d0055772c7973712c0eaaf2de0e5f0749f8b9" | 1234 | + ref: "br_webview_flutter-v4.13.0_ohos" |
| 1235 | + resolved-ref: "3f44aceb6b076a6ec58f3570435f1907411511fe" | ||
| 1252 | url: "https://gitcode.com/openharmony-tpc/flutter_packages.git" | 1236 | url: "https://gitcode.com/openharmony-tpc/flutter_packages.git" |
| 1253 | source: git | 1237 | source: git |
| 1254 | version: "4.7.0" | 1238 | version: "4.7.0" |
| @@ -1256,8 +1240,8 @@ packages: | @@ -1256,8 +1240,8 @@ packages: | ||
| 1256 | dependency: transitive | 1240 | dependency: transitive |
| 1257 | description: | 1241 | description: |
| 1258 | path: "packages/webview_flutter/webview_flutter_ohos" | 1242 | path: "packages/webview_flutter/webview_flutter_ohos" |
| 1259 | - ref: "5a9d0055772c7973712c0eaaf2de0e5f0749f8b9" | ||
| 1260 | - resolved-ref: "5a9d0055772c7973712c0eaaf2de0e5f0749f8b9" | 1243 | + ref: "br_webview_flutter-v4.13.0_ohos" |
| 1244 | + resolved-ref: "3f44aceb6b076a6ec58f3570435f1907411511fe" | ||
| 1261 | url: "https://gitcode.com/openharmony-tpc/flutter_packages.git" | 1245 | url: "https://gitcode.com/openharmony-tpc/flutter_packages.git" |
| 1262 | source: git | 1246 | source: git |
| 1263 | version: "4.7.0" | 1247 | version: "4.7.0" |
| @@ -1265,8 +1249,8 @@ packages: | @@ -1265,8 +1249,8 @@ packages: | ||
| 1265 | dependency: transitive | 1249 | dependency: transitive |
| 1266 | description: | 1250 | description: |
| 1267 | path: "packages/webview_flutter/webview_flutter_platform_interface" | 1251 | path: "packages/webview_flutter/webview_flutter_platform_interface" |
| 1268 | - ref: "5a9d0055772c7973712c0eaaf2de0e5f0749f8b9" | ||
| 1269 | - resolved-ref: "5a9d0055772c7973712c0eaaf2de0e5f0749f8b9" | 1252 | + ref: "br_webview_flutter-v4.13.0_ohos" |
| 1253 | + resolved-ref: "3f44aceb6b076a6ec58f3570435f1907411511fe" | ||
| 1270 | url: "https://gitcode.com/openharmony-tpc/flutter_packages.git" | 1254 | url: "https://gitcode.com/openharmony-tpc/flutter_packages.git" |
| 1271 | source: git | 1255 | source: git |
| 1272 | version: "2.13.1" | 1256 | version: "2.13.1" |
| @@ -1274,8 +1258,8 @@ packages: | @@ -1274,8 +1258,8 @@ packages: | ||
| 1274 | dependency: transitive | 1258 | dependency: transitive |
| 1275 | description: | 1259 | description: |
| 1276 | path: "packages/webview_flutter/webview_flutter_wkwebview" | 1260 | path: "packages/webview_flutter/webview_flutter_wkwebview" |
| 1277 | - ref: "5a9d0055772c7973712c0eaaf2de0e5f0749f8b9" | ||
| 1278 | - resolved-ref: "5a9d0055772c7973712c0eaaf2de0e5f0749f8b9" | 1261 | + ref: "br_webview_flutter-v4.13.0_ohos" |
| 1262 | + resolved-ref: "3f44aceb6b076a6ec58f3570435f1907411511fe" | ||
| 1279 | url: "https://gitcode.com/openharmony-tpc/flutter_packages.git" | 1263 | url: "https://gitcode.com/openharmony-tpc/flutter_packages.git" |
| 1280 | source: git | 1264 | source: git |
| 1281 | version: "3.22.0" | 1265 | version: "3.22.0" |
| @@ -1283,16 +1267,16 @@ packages: | @@ -1283,16 +1267,16 @@ packages: | ||
| 1283 | dependency: transitive | 1267 | dependency: transitive |
| 1284 | description: | 1268 | description: |
| 1285 | name: win32 | 1269 | name: win32 |
| 1286 | - sha256: d7cb55e04cd34096cd3a79b3330245f54cb96a370a1c27adb3c84b917de8b08e | ||
| 1287 | - url: "https://pub.dev" | 1270 | + sha256: daf97c9d80197ed7b619040e86c8ab9a9dad285e7671ee7390f9180cc828a51e |
| 1271 | + url: "https://pub.flutter-io.cn" | ||
| 1288 | source: hosted | 1272 | source: hosted |
| 1289 | - version: "5.15.0" | 1273 | + version: "5.10.1" |
| 1290 | xdg_directories: | 1274 | xdg_directories: |
| 1291 | dependency: transitive | 1275 | dependency: transitive |
| 1292 | description: | 1276 | description: |
| 1293 | name: xdg_directories | 1277 | name: xdg_directories |
| 1294 | sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15" | 1278 | sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15" |
| 1295 | - url: "https://pub.dev" | 1279 | + url: "https://pub.flutter-io.cn" |
| 1296 | source: hosted | 1280 | source: hosted |
| 1297 | version: "1.1.0" | 1281 | version: "1.1.0" |
| 1298 | yaml: | 1282 | yaml: |
| @@ -1300,9 +1284,9 @@ packages: | @@ -1300,9 +1284,9 @@ packages: | ||
| 1300 | description: | 1284 | description: |
| 1301 | name: yaml | 1285 | name: yaml |
| 1302 | sha256: f67cdd8e07d3c6329146aaef1ba043542b3134c12489f553ca9a7435d1068aea | 1286 | sha256: f67cdd8e07d3c6329146aaef1ba043542b3134c12489f553ca9a7435d1068aea |
| 1303 | - url: "https://pub.dev" | 1287 | + url: "https://pub.flutter-io.cn" |
| 1304 | source: hosted | 1288 | source: hosted |
| 1305 | version: "3.1.4" | 1289 | version: "3.1.4" |
| 1306 | sdks: | 1290 | sdks: |
| 1307 | - dart: ">=3.10.3 <4.0.0" | ||
| 1308 | - flutter: ">=3.38.4" | 1291 | + dart: ">=3.6.2 <4.0.0" |
| 1292 | + flutter: ">=3.27.0" |
| @@ -2,6 +2,7 @@ import 'package:doublefeel_flutter/core/services/raw_data_service/health_raw_dat | @@ -2,6 +2,7 @@ import 'package:doublefeel_flutter/core/services/raw_data_service/health_raw_dat | ||
| 2 | import 'package:doublefeel_flutter/core/services/raw_data_service/health_raw_models.dart'; | 2 | import 'package:doublefeel_flutter/core/services/raw_data_service/health_raw_models.dart'; |
| 3 | import 'package:doublefeel_flutter/core/services/raw_data_service/platform_ios/apple_health_raw_data_core_service.dart'; | 3 | import 'package:doublefeel_flutter/core/services/raw_data_service/platform_ios/apple_health_raw_data_core_service.dart'; |
| 4 | import 'package:doublefeel_flutter/core/services/raw_data_service/platform_ohos/ohos_health_raw_data_core_service.dart'; | 4 | import 'package:doublefeel_flutter/core/services/raw_data_service/platform_ohos/ohos_health_raw_data_core_service.dart'; |
| 5 | +import 'package:doublefeel_flutter/core/services/raw_data_service/platform_ohos/ohos_health_raw_data_events.dart'; | ||
| 5 | import 'package:doublefeel_flutter/core/services/raw_data_service/platform_ohos/ohos_health_raw_data_sync_service.dart'; | 6 | import 'package:doublefeel_flutter/core/services/raw_data_service/platform_ohos/ohos_health_raw_data_sync_service.dart'; |
| 6 | import 'package:doublefeel_flutter/data/models/enums/app_enums.dart'; | 7 | import 'package:doublefeel_flutter/data/models/enums/app_enums.dart'; |
| 7 | import 'package:doublefeel_flutter/data/models/health/health_v2_models.dart'; | 8 | import 'package:doublefeel_flutter/data/models/health/health_v2_models.dart'; |
| @@ -249,7 +250,7 @@ void main() { | @@ -249,7 +250,7 @@ void main() { | ||
| 249 | store.dailyStressPoints.map((point) => point.date), contains(20260830)); | 250 | store.dailyStressPoints.map((point) => point.date), contains(20260830)); |
| 250 | }); | 251 | }); |
| 251 | 252 | ||
| 252 | - test('OHOS core backfills missing daily stress from local raw data anchor', | 253 | + test('OHOS core calculates from latest local raw data day without backfill', |
| 253 | () async { | 254 | () async { |
| 254 | final backfillDay = DateTime(2026, 8, 30); | 255 | final backfillDay = DateTime(2026, 8, 30); |
| 255 | final backfillBase = | 256 | final backfillBase = |
| @@ -308,20 +309,63 @@ void main() { | @@ -308,20 +309,63 @@ void main() { | ||
| 308 | call.dataType == HealthDataUploadType.hrv.type || | 309 | call.dataType == HealthDataUploadType.hrv.type || |
| 309 | call.dataType == HealthDataUploadType.heartRate.type) | 310 | call.dataType == HealthDataUploadType.heartRate.type) |
| 310 | .map((call) => _dateKey(call.startTime)), | 311 | .map((call) => _dateKey(call.startTime)), |
| 311 | - everyElement(lessThanOrEqualTo(20260827)), | 312 | + everyElement(20260903), |
| 312 | ); | 313 | ); |
| 313 | expect( | 314 | expect( |
| 314 | store.hrvStressPoints.any((point) => point.rawEndTime == backfillBase), | 315 | store.hrvStressPoints.any((point) => point.rawEndTime == backfillBase), |
| 315 | - isTrue, | 316 | + isFalse, |
| 316 | ); | 317 | ); |
| 317 | expect( | 318 | expect( |
| 318 | store.realtimeStressPoints.any( | 319 | store.realtimeStressPoints.any( |
| 319 | (point) => _dateKey(point.rawEndTime) == 20260830, | 320 | (point) => _dateKey(point.rawEndTime) == 20260830, |
| 320 | ), | 321 | ), |
| 321 | - isTrue, | 322 | + isFalse, |
| 322 | ); | 323 | ); |
| 323 | expect(result.dailyStressPoints.map((point) => point.date), | 324 | expect(result.dailyStressPoints.map((point) => point.date), |
| 324 | - contains(20260830)); | 325 | + isNot(contains(20260830))); |
| 326 | + }); | ||
| 327 | + | ||
| 328 | + test('OHOS core publishes calculation start and success events', () async { | ||
| 329 | + final events = <OhosHealthRawDataPipelineEvent>[]; | ||
| 330 | + final subscription = OhosHealthRawDataPipelineEvents.stream.listen( | ||
| 331 | + events.add, | ||
| 332 | + ); | ||
| 333 | + addTearDown(subscription.cancel); | ||
| 334 | + final base = DateTime.now() | ||
| 335 | + .subtract(const Duration(days: 2)) | ||
| 336 | + .millisecondsSinceEpoch ~/ | ||
| 337 | + 1000; | ||
| 338 | + final service = OHOSHealthRawDataCoreService( | ||
| 339 | + rawDataSource: _FakeHealthRawDataSource( | ||
| 340 | + pointsByDataType: { | ||
| 341 | + HealthDataUploadType.hrv.type: [_point(1, base, 60)], | ||
| 342 | + HealthDataUploadType.heartRate.type: [_point(2, base, 80)], | ||
| 343 | + }, | ||
| 344 | + ), | ||
| 345 | + localStore: _FakeHealthRawStressLocalStore(), | ||
| 346 | + userIdProvider: () => 42, | ||
| 347 | + uploadResultsAfterCalculation: false, | ||
| 348 | + healthReadAuthorizationChecker: () async => false, | ||
| 349 | + ); | ||
| 350 | + | ||
| 351 | + await service.syncAndStore( | ||
| 352 | + startTime: base - Duration.secondsPerHour, | ||
| 353 | + endTime: base + Duration.secondsPerHour, | ||
| 354 | + readChunkDays: 1, | ||
| 355 | + ); | ||
| 356 | + await Future<void>.delayed(Duration.zero); | ||
| 357 | + | ||
| 358 | + final calculationEvents = | ||
| 359 | + events.where((event) => event.flow == 'calculateAndStore').toList(); | ||
| 360 | + expect( | ||
| 361 | + calculationEvents.map((event) => event.type), | ||
| 362 | + containsAllInOrder([ | ||
| 363 | + OhosHealthRawDataPipelineEventType.calculationStarted, | ||
| 364 | + OhosHealthRawDataPipelineEventType.calculationSucceeded, | ||
| 365 | + ]), | ||
| 366 | + ); | ||
| 367 | + expect(calculationEvents.last.userId, 42); | ||
| 368 | + expect(calculationEvents.last.hrvCount, 1); | ||
| 325 | }); | 369 | }); |
| 326 | 370 | ||
| 327 | test('OHOS core shows debug timing toast after sync and calculation', | 371 | test('OHOS core shows debug timing toast after sync and calculation', |
| @@ -2,6 +2,7 @@ import 'dart:async'; | @@ -2,6 +2,7 @@ import 'dart:async'; | ||
| 2 | 2 | ||
| 3 | import 'package:doublefeel_flutter/core/result/app_result.dart'; | 3 | import 'package:doublefeel_flutter/core/result/app_result.dart'; |
| 4 | import 'package:doublefeel_flutter/core/services/raw_data_service/platform_ohos/huawei_health_data_type.dart'; | 4 | import 'package:doublefeel_flutter/core/services/raw_data_service/platform_ohos/huawei_health_data_type.dart'; |
| 5 | +import 'package:doublefeel_flutter/core/services/raw_data_service/platform_ohos/ohos_health_raw_data_events.dart'; | ||
| 5 | import 'package:doublefeel_flutter/core/services/raw_data_service/platform_ohos/ohos_health_raw_data_sync_service.dart'; | 6 | import 'package:doublefeel_flutter/core/services/raw_data_service/platform_ohos/ohos_health_raw_data_sync_service.dart'; |
| 6 | import 'package:doublefeel_flutter/data/models/harmony/hm_health_data.dart'; | 7 | import 'package:doublefeel_flutter/data/models/harmony/hm_health_data.dart'; |
| 7 | import 'package:doublefeel_flutter/data/models/harmony/hm_sleep_data.dart'; | 8 | import 'package:doublefeel_flutter/data/models/harmony/hm_sleep_data.dart'; |
| @@ -47,7 +48,7 @@ void main() { | @@ -47,7 +48,7 @@ void main() { | ||
| 47 | expect(local.storedBatches.map((e) => e.length), [2]); | 48 | expect(local.storedBatches.map((e) => e.length), [2]); |
| 48 | }); | 49 | }); |
| 49 | 50 | ||
| 50 | - test('syncRawData backfills recent days when local data already exists', | 51 | + test('syncRawData starts from latest local data day without backfill', |
| 51 | () async { | 52 | () async { |
| 52 | final latest = _unixSeconds(DateTime(2026, 9, 3, 10)); | 53 | final latest = _unixSeconds(DateTime(2026, 9, 3, 10)); |
| 53 | final remote = _FakeOhosHealthRawDataRemoteDataSource([ | 54 | final remote = _FakeOhosHealthRawDataRemoteDataSource([ |
| @@ -72,7 +73,7 @@ void main() { | @@ -72,7 +73,7 @@ void main() { | ||
| 72 | endTime: _unixSeconds(DateTime(2026, 9, 3, 11)), | 73 | endTime: _unixSeconds(DateTime(2026, 9, 3, 11)), |
| 73 | ); | 74 | ); |
| 74 | 75 | ||
| 75 | - expect(_dateKey(remote.calls.single.startTime), 20260827); | 76 | + expect(_dateKey(remote.calls.single.startTime), 20260903); |
| 76 | expect(result.storedCount, 1); | 77 | expect(result.storedCount, 1); |
| 77 | expect(result.earliestStoredTime, 1788019232); | 78 | expect(result.earliestStoredTime, 1788019232); |
| 78 | }); | 79 | }); |
| @@ -111,12 +112,12 @@ void main() { | @@ -111,12 +112,12 @@ void main() { | ||
| 111 | endTime: _unixSeconds(DateTime(2026, 9, 4)), | 112 | endTime: _unixSeconds(DateTime(2026, 9, 4)), |
| 112 | ); | 113 | ); |
| 113 | 114 | ||
| 114 | - expect(_dateKey(remote.calls[0].startTime), 20260827); | ||
| 115 | - expect(_dateKey(remote.calls[1].startTime), 20260826); | ||
| 116 | - expect(_dateKey(remote.calls[2].startTime), 20260825); | 115 | + expect(_dateKey(remote.calls[0].startTime), 20260903); |
| 116 | + expect(_dateKey(remote.calls[1].startTime), 20260902); | ||
| 117 | + expect(_dateKey(remote.calls[2].startTime), 20260901); | ||
| 117 | }); | 118 | }); |
| 118 | 119 | ||
| 119 | - test('syncRawData falls back to half-year lookback when local data is empty', | 120 | + test('syncRawData falls back to two-month lookback when local data is empty', |
| 120 | () async { | 121 | () async { |
| 121 | final now = DateTime(2026, 8, 13, 12); | 122 | final now = DateTime(2026, 8, 13, 12); |
| 122 | final remote = _FakeOhosHealthRawDataRemoteDataSource([ | 123 | final remote = _FakeOhosHealthRawDataRemoteDataSource([ |
| @@ -131,13 +132,7 @@ void main() { | @@ -131,13 +132,7 @@ void main() { | ||
| 131 | 132 | ||
| 132 | final result = await service.syncRawData(dataType: 7); | 133 | final result = await service.syncRawData(dataType: 7); |
| 133 | 134 | ||
| 134 | - final fallback = now.subtract( | ||
| 135 | - const Duration( | ||
| 136 | - days: OhosHealthRawDataSyncService.defaultLookbackDays, | ||
| 137 | - ), | ||
| 138 | - ); | ||
| 139 | - final expectedStart = | ||
| 140 | - _unixSeconds(DateTime(fallback.year, fallback.month, fallback.day)); | 135 | + final expectedStart = _unixSeconds(DateTime(2026, 6, 13)); |
| 141 | final expectedEnd = now.millisecondsSinceEpoch ~/ 1000; | 136 | final expectedEnd = now.millisecondsSinceEpoch ~/ 1000; |
| 142 | 137 | ||
| 143 | expect(result.startTime, expectedStart); | 138 | expect(result.startTime, expectedStart); |
| @@ -151,6 +146,76 @@ void main() { | @@ -151,6 +146,76 @@ void main() { | ||
| 151 | expect(local.storedBatches, isEmpty); | 146 | expect(local.storedBatches, isEmpty); |
| 152 | }); | 147 | }); |
| 153 | 148 | ||
| 149 | + test('syncRawData clamps requested and latest anchors to two months', | ||
| 150 | + () async { | ||
| 151 | + final endTime = _unixSeconds(DateTime(2026, 9, 4, 12)); | ||
| 152 | + final remote = _FakeOhosHealthRawDataRemoteDataSource([ | ||
| 153 | + const OhosHealthRawDataPage(items: <OhosHealthRawDataItem>[]), | ||
| 154 | + const OhosHealthRawDataPage(items: <OhosHealthRawDataItem>[]), | ||
| 155 | + const OhosHealthRawDataPage(items: <OhosHealthRawDataItem>[]), | ||
| 156 | + ]); | ||
| 157 | + final local = _FakeOhosHealthRawDataLocalStore( | ||
| 158 | + latestDataTime: _unixSeconds(DateTime(2026, 1, 1, 8)), | ||
| 159 | + ); | ||
| 160 | + final service = OhosHealthRawDataSyncService( | ||
| 161 | + remoteDataSource: remote, | ||
| 162 | + localStore: local, | ||
| 163 | + ); | ||
| 164 | + | ||
| 165 | + final result = await service.syncRawData( | ||
| 166 | + dataType: 7, | ||
| 167 | + startTime: _unixSeconds(DateTime(2026, 1, 1)), | ||
| 168 | + endTime: endTime, | ||
| 169 | + ); | ||
| 170 | + | ||
| 171 | + final expectedStart = _unixSeconds(DateTime(2026, 7, 4)); | ||
| 172 | + expect(result.startTime, expectedStart); | ||
| 173 | + expect(remote.calls.first.startTime, expectedStart); | ||
| 174 | + expect(remote.calls.last.endTime, endTime); | ||
| 175 | + }); | ||
| 176 | + | ||
| 177 | + test('syncRawData publishes start and success events', () async { | ||
| 178 | + final events = <OhosHealthRawDataPipelineEvent>[]; | ||
| 179 | + final subscription = OhosHealthRawDataPipelineEvents.stream.listen( | ||
| 180 | + events.add, | ||
| 181 | + ); | ||
| 182 | + addTearDown(subscription.cancel); | ||
| 183 | + final remote = _FakeOhosHealthRawDataRemoteDataSource([ | ||
| 184 | + const OhosHealthRawDataPage( | ||
| 185 | + items: [ | ||
| 186 | + OhosHealthRawDataItem( | ||
| 187 | + dataType: 1, | ||
| 188 | + dataTime: 1001, | ||
| 189 | + payload: {'time': 1001, 'value': 45}, | ||
| 190 | + ), | ||
| 191 | + ], | ||
| 192 | + ), | ||
| 193 | + ]); | ||
| 194 | + final service = OhosHealthRawDataSyncService( | ||
| 195 | + remoteDataSource: remote, | ||
| 196 | + localStore: _FakeOhosHealthRawDataLocalStore(), | ||
| 197 | + nowProvider: () => DateTime(2026, 9, 4), | ||
| 198 | + ); | ||
| 199 | + | ||
| 200 | + await service.syncRawData( | ||
| 201 | + dataType: 1, | ||
| 202 | + startTime: 1000, | ||
| 203 | + endTime: 2000, | ||
| 204 | + ); | ||
| 205 | + await Future<void>.delayed(Duration.zero); | ||
| 206 | + | ||
| 207 | + expect( | ||
| 208 | + events.map((event) => event.type), | ||
| 209 | + containsAllInOrder([ | ||
| 210 | + OhosHealthRawDataPipelineEventType.syncStarted, | ||
| 211 | + OhosHealthRawDataPipelineEventType.syncSucceeded, | ||
| 212 | + ]), | ||
| 213 | + ); | ||
| 214 | + expect(events.last.flow, 'syncRawData'); | ||
| 215 | + expect(events.last.dataType, 1); | ||
| 216 | + expect(events.last.storedCount, 1); | ||
| 217 | + }); | ||
| 218 | + | ||
| 154 | test('syncRawData splits non-heart-rate ranges every 30 days and logs marker', | 219 | test('syncRawData splits non-heart-rate ranges every 30 days and logs marker', |
| 155 | () async { | 220 | () async { |
| 156 | final start = _unixSeconds(DateTime(2026, 1, 30, 10)); | 221 | final start = _unixSeconds(DateTime(2026, 1, 30, 10)); |
| @@ -310,6 +375,38 @@ void main() { | @@ -310,6 +375,38 @@ void main() { | ||
| 310 | expect(result.pageCount, 3); | 375 | expect(result.pageCount, 3); |
| 311 | }); | 376 | }); |
| 312 | 377 | ||
| 378 | + test('syncRawData limits segment fetch concurrency to ten', () async { | ||
| 379 | + final start = _unixSeconds(DateTime(2026, 1)); | ||
| 380 | + final end = _unixSeconds(DateTime(2026, 4, 30)); | ||
| 381 | + final gate = Completer<void>(); | ||
| 382 | + final remote = _FakeOhosHealthRawDataRemoteDataSource( | ||
| 383 | + const <OhosHealthRawDataPage>[], | ||
| 384 | + gate: gate, | ||
| 385 | + ); | ||
| 386 | + final service = OhosHealthRawDataSyncService( | ||
| 387 | + remoteDataSource: remote, | ||
| 388 | + localStore: _FakeOhosHealthRawDataLocalStore(), | ||
| 389 | + ); | ||
| 390 | + | ||
| 391 | + final sync = service.syncRawData( | ||
| 392 | + dataType: HuaweiHealthDataType.heartRate.dataType, | ||
| 393 | + startTime: start, | ||
| 394 | + endTime: end, | ||
| 395 | + ); | ||
| 396 | + for (var i = 0; i < 20 && remote.calls.length < 10; i += 1) { | ||
| 397 | + await Future<void>.delayed(const Duration(milliseconds: 1)); | ||
| 398 | + } | ||
| 399 | + | ||
| 400 | + expect(remote.calls, hasLength(10)); | ||
| 401 | + | ||
| 402 | + gate.complete(); | ||
| 403 | + final result = await sync; | ||
| 404 | + | ||
| 405 | + expect(result.segmentCount, 12); | ||
| 406 | + expect(result.pageCount, 12); | ||
| 407 | + expect(remote.calls, hasLength(12)); | ||
| 408 | + }); | ||
| 409 | + | ||
| 313 | test('syncRawData joins duplicate in-flight syncs', () async { | 410 | test('syncRawData joins duplicate in-flight syncs', () async { |
| 314 | final gate = Completer<void>(); | 411 | final gate = Completer<void>(); |
| 315 | final remote = _FakeOhosHealthRawDataRemoteDataSource( | 412 | final remote = _FakeOhosHealthRawDataRemoteDataSource( |
-
Please register or login to post a comment