Commit 4c43a3d4b23566b79627ba30998ec9459b804188
1 parent
ac583fa7
feat(ui):开放线上隐藏的日志入口
(cherry picked from commit da29459c) # Conflicts: # lib/core/platform/pigeon_api_facade.dart # pubspec.lock
Showing
6 changed files
with
241 additions
and
79 deletions
| @@ -31,10 +31,8 @@ class DeveloperOptionsItem { | @@ -31,10 +31,8 @@ class DeveloperOptionsItem { | ||
| 31 | 31 | ||
| 32 | enum DeveloperOptionsAction { | 32 | enum DeveloperOptionsAction { |
| 33 | shareHealthRawDataDatabase, | 33 | shareHealthRawDataDatabase, |
| 34 | - shareHealthRawUploadLog, | ||
| 35 | shareNativeAppleHealthObserverRecord, | 34 | shareNativeAppleHealthObserverRecord, |
| 36 | shareFlutterObserverRecord, | 35 | shareFlutterObserverRecord, |
| 37 | - shareUploadTaskRecord, | ||
| 38 | shareLocalNotificationDebugRecord, | 36 | shareLocalNotificationDebugRecord, |
| 39 | sendTestHrvLocalNotification, | 37 | sendTestHrvLocalNotification, |
| 40 | clearHealthRawDataDatabaseAndUploadLog, | 38 | clearHealthRawDataDatabaseAndUploadLog, |
| @@ -52,61 +50,84 @@ class DeveloperOptionsController extends GetxController { | @@ -52,61 +50,84 @@ class DeveloperOptionsController extends GetxController { | ||
| 52 | final PlatformHostApi _platformHostApi = PlatformHostApi(); | 50 | final PlatformHostApi _platformHostApi = PlatformHostApi(); |
| 53 | final TextEditingController tokenController = TextEditingController(); | 51 | final TextEditingController tokenController = TextEditingController(); |
| 54 | 52 | ||
| 55 | - final List<DeveloperOptionsItem> options = const [ | ||
| 56 | - DeveloperOptionsItem( | ||
| 57 | - title: 'User Information', | ||
| 58 | - routeName: Routes.USER_INFORMATION, | ||
| 59 | - ), | ||
| 60 | - DeveloperOptionsItem( | ||
| 61 | - title: 'Route List', | ||
| 62 | - routeName: Routes.ROUTE_LIST, | ||
| 63 | - ), | ||
| 64 | - DeveloperOptionsItem( | ||
| 65 | - title: 'Token Login', | ||
| 66 | - routeName: Routes.TOKEN_LOGIN, | ||
| 67 | - ), | ||
| 68 | - DeveloperOptionsItem( | ||
| 69 | - title: 'Share HealthRawData Database', | ||
| 70 | - action: DeveloperOptionsAction.shareHealthRawDataDatabase, | ||
| 71 | - ), | ||
| 72 | - DeveloperOptionsItem( | ||
| 73 | - title: '本地上传日志', | ||
| 74 | - action: DeveloperOptionsAction.shareHealthRawUploadLog, | ||
| 75 | - ), | ||
| 76 | - DeveloperOptionsItem( | ||
| 77 | - title: '分享原生AppleHealthObserver记录', | ||
| 78 | - action: DeveloperOptionsAction.shareNativeAppleHealthObserverRecord, | ||
| 79 | - ), | ||
| 80 | - DeveloperOptionsItem( | ||
| 81 | - title: '分享Flutter Observer-Caculate记录', | ||
| 82 | - action: DeveloperOptionsAction.shareFlutterObserverRecord, | ||
| 83 | - ), | ||
| 84 | - DeveloperOptionsItem( | ||
| 85 | - title: '分享上传Task日志', | ||
| 86 | - action: DeveloperOptionsAction.shareUploadTaskRecord, | ||
| 87 | - ), | ||
| 88 | - DeveloperOptionsItem( | ||
| 89 | - title: '分享本地推送诊断日志', | ||
| 90 | - action: DeveloperOptionsAction.shareLocalNotificationDebugRecord, | ||
| 91 | - ), | ||
| 92 | - DeveloperOptionsItem( | ||
| 93 | - title: '测试本地推送 HRV', | ||
| 94 | - action: DeveloperOptionsAction.sendTestHrvLocalNotification, | ||
| 95 | - ), | ||
| 96 | - DeveloperOptionsItem( | ||
| 97 | - title: '清除本地数据库、上传日志', | ||
| 98 | - action: DeveloperOptionsAction.clearHealthRawDataDatabaseAndUploadLog, | ||
| 99 | - ), | ||
| 100 | - DeveloperOptionsItem( | ||
| 101 | - title: 'Global Env', | ||
| 102 | - action: DeveloperOptionsAction.globalEnv, | ||
| 103 | - ), | ||
| 104 | - ]; | 53 | + late final List<DeveloperOptionsItem> options; |
| 105 | 54 | ||
| 106 | @override | 55 | @override |
| 107 | void onInit() { | 56 | void onInit() { |
| 108 | super.onInit(); | 57 | super.onInit(); |
| 109 | tokenController.text = _userPreferencesStorage.accessToken; | 58 | tokenController.text = _userPreferencesStorage.accessToken; |
| 59 | + final config = Get.find<AppEnvironmentConfig>(); | ||
| 60 | + if (config.isDebug) { | ||
| 61 | + options = const [ | ||
| 62 | + DeveloperOptionsItem( | ||
| 63 | + title: 'User Information', | ||
| 64 | + routeName: Routes.USER_INFORMATION, | ||
| 65 | + ), | ||
| 66 | + DeveloperOptionsItem( | ||
| 67 | + title: 'Route List', | ||
| 68 | + routeName: Routes.ROUTE_LIST, | ||
| 69 | + ), | ||
| 70 | + DeveloperOptionsItem( | ||
| 71 | + title: 'Token Login', | ||
| 72 | + routeName: Routes.TOKEN_LOGIN, | ||
| 73 | + ), | ||
| 74 | + DeveloperOptionsItem( | ||
| 75 | + title: '分享本地计算数据库文件', | ||
| 76 | + action: DeveloperOptionsAction.shareHealthRawDataDatabase, | ||
| 77 | + ), | ||
| 78 | + DeveloperOptionsItem( | ||
| 79 | + title: '分享原生响应AppleHealth记录', | ||
| 80 | + action: DeveloperOptionsAction.shareNativeAppleHealthObserverRecord, | ||
| 81 | + ), | ||
| 82 | + DeveloperOptionsItem( | ||
| 83 | + title: '分享Flutter 响应AppleHealth回调记录', | ||
| 84 | + action: DeveloperOptionsAction.shareFlutterObserverRecord, | ||
| 85 | + ), | ||
| 86 | + DeveloperOptionsItem( | ||
| 87 | + title: '分享本地推送诊断日志', | ||
| 88 | + action: DeveloperOptionsAction.shareLocalNotificationDebugRecord, | ||
| 89 | + ), | ||
| 90 | + DeveloperOptionsItem( | ||
| 91 | + title: '测试本地推送 HRV', | ||
| 92 | + action: DeveloperOptionsAction.sendTestHrvLocalNotification, | ||
| 93 | + ), | ||
| 94 | + DeveloperOptionsItem( | ||
| 95 | + title: '清除本地数据库、上传日志', | ||
| 96 | + action: DeveloperOptionsAction.clearHealthRawDataDatabaseAndUploadLog, | ||
| 97 | + ), | ||
| 98 | + DeveloperOptionsItem( | ||
| 99 | + title: 'Global Env', | ||
| 100 | + action: DeveloperOptionsAction.globalEnv, | ||
| 101 | + ), | ||
| 102 | + ]; | ||
| 103 | + } else { | ||
| 104 | + options = const [ | ||
| 105 | + DeveloperOptionsItem( | ||
| 106 | + title: 'User Information', | ||
| 107 | + routeName: Routes.USER_INFORMATION, | ||
| 108 | + ), | ||
| 109 | + DeveloperOptionsItem( | ||
| 110 | + title: '分享本地计算数据库文件', | ||
| 111 | + action: DeveloperOptionsAction.shareHealthRawDataDatabase, | ||
| 112 | + ), | ||
| 113 | + DeveloperOptionsItem( | ||
| 114 | + title: '分享原生响应AppleHealth记录', | ||
| 115 | + action: DeveloperOptionsAction.shareNativeAppleHealthObserverRecord, | ||
| 116 | + ), | ||
| 117 | + DeveloperOptionsItem( | ||
| 118 | + title: '分享Flutter 响应AppleHealth回调记录', | ||
| 119 | + action: DeveloperOptionsAction.shareFlutterObserverRecord, | ||
| 120 | + ), | ||
| 121 | + DeveloperOptionsItem( | ||
| 122 | + title: '分享本地推送诊断日志', | ||
| 123 | + action: DeveloperOptionsAction.shareLocalNotificationDebugRecord, | ||
| 124 | + ), | ||
| 125 | + DeveloperOptionsItem( | ||
| 126 | + title: '清除本地数据库、上传日志', | ||
| 127 | + action: DeveloperOptionsAction.clearHealthRawDataDatabaseAndUploadLog, | ||
| 128 | + ), | ||
| 129 | + ]; | ||
| 130 | + } | ||
| 110 | } | 131 | } |
| 111 | 132 | ||
| 112 | Future<void> optionItemOnTap(DeveloperOptionsItem item) async { | 133 | Future<void> optionItemOnTap(DeveloperOptionsItem item) async { |
| @@ -120,18 +141,12 @@ class DeveloperOptionsController extends GetxController { | @@ -120,18 +141,12 @@ class DeveloperOptionsController extends GetxController { | ||
| 120 | case DeveloperOptionsAction.shareHealthRawDataDatabase: | 141 | case DeveloperOptionsAction.shareHealthRawDataDatabase: |
| 121 | await shareHealthRawDataDatabase(); | 142 | await shareHealthRawDataDatabase(); |
| 122 | break; | 143 | break; |
| 123 | - case DeveloperOptionsAction.shareHealthRawUploadLog: | ||
| 124 | - await shareHealthRawUploadLog(); | ||
| 125 | - break; | ||
| 126 | case DeveloperOptionsAction.shareNativeAppleHealthObserverRecord: | 144 | case DeveloperOptionsAction.shareNativeAppleHealthObserverRecord: |
| 127 | await shareNativeAppleHealthObserverRecord(); | 145 | await shareNativeAppleHealthObserverRecord(); |
| 128 | break; | 146 | break; |
| 129 | case DeveloperOptionsAction.shareFlutterObserverRecord: | 147 | case DeveloperOptionsAction.shareFlutterObserverRecord: |
| 130 | await shareFlutterObserverRecord(); | 148 | await shareFlutterObserverRecord(); |
| 131 | break; | 149 | break; |
| 132 | - case DeveloperOptionsAction.shareUploadTaskRecord: | ||
| 133 | - await shareUploadTaskRecord(); | ||
| 134 | - break; | ||
| 135 | case DeveloperOptionsAction.shareLocalNotificationDebugRecord: | 150 | case DeveloperOptionsAction.shareLocalNotificationDebugRecord: |
| 136 | await shareLocalNotificationDebugRecord(); | 151 | await shareLocalNotificationDebugRecord(); |
| 137 | break; | 152 | break; |
| @@ -15,6 +15,9 @@ import '../models/watch_theme_models.dart'; | @@ -15,6 +15,9 @@ import '../models/watch_theme_models.dart'; | ||
| 15 | class WatchThemeController extends GetxController { | 15 | class WatchThemeController extends GetxController { |
| 16 | WatchThemeController(this._themeApi, this._vipApi); | 16 | WatchThemeController(this._themeApi, this._vipApi); |
| 17 | 17 | ||
| 18 | + static const int _developerOptionsTapCount = 8; | ||
| 19 | + static const Duration _developerOptionsTapWindow = Duration(seconds: 3); | ||
| 20 | + | ||
| 18 | final ThemeApi _themeApi; | 21 | final ThemeApi _themeApi; |
| 19 | final VipApi _vipApi; | 22 | final VipApi _vipApi; |
| 20 | 23 | ||
| @@ -23,6 +26,8 @@ class WatchThemeController extends GetxController { | @@ -23,6 +26,8 @@ class WatchThemeController extends GetxController { | ||
| 23 | final officialThemeItems = <WatchThemeItem>[].obs; | 26 | final officialThemeItems = <WatchThemeItem>[].obs; |
| 24 | final customThemeItems = <WatchThemeItem>[].obs; | 27 | final customThemeItems = <WatchThemeItem>[].obs; |
| 25 | final selectedThemeItem = Rx<WatchThemeItem?>(null); | 28 | final selectedThemeItem = Rx<WatchThemeItem?>(null); |
| 29 | + int _headerTapCount = 0; | ||
| 30 | + DateTime? _firstHeaderTapAt; | ||
| 26 | 31 | ||
| 27 | @override | 32 | @override |
| 28 | void onInit() { | 33 | void onInit() { |
| @@ -63,6 +68,26 @@ class WatchThemeController extends GetxController { | @@ -63,6 +68,26 @@ class WatchThemeController extends GetxController { | ||
| 63 | Get.back(); | 68 | Get.back(); |
| 64 | } | 69 | } |
| 65 | 70 | ||
| 71 | + void onHeaderTap() { | ||
| 72 | + final now = DateTime.now(); | ||
| 73 | + final firstTapAt = _firstHeaderTapAt; | ||
| 74 | + if (firstTapAt == null || | ||
| 75 | + now.difference(firstTapAt) > _developerOptionsTapWindow) { | ||
| 76 | + _firstHeaderTapAt = now; | ||
| 77 | + _headerTapCount = 1; | ||
| 78 | + return; | ||
| 79 | + } | ||
| 80 | + | ||
| 81 | + _headerTapCount += 1; | ||
| 82 | + if (_headerTapCount < _developerOptionsTapCount) { | ||
| 83 | + return; | ||
| 84 | + } | ||
| 85 | + | ||
| 86 | + _firstHeaderTapAt = null; | ||
| 87 | + _headerTapCount = 0; | ||
| 88 | + Get.toNamed(Routes.DEVELOPER_OPTIONS); | ||
| 89 | + } | ||
| 90 | + | ||
| 66 | Future<void> selectOfficialTheme(int index) async { | 91 | Future<void> selectOfficialTheme(int index) async { |
| 67 | await Get.toNamed(Routes.WATCH_THEME_PREVIEW, arguments: { | 92 | await Get.toNamed(Routes.WATCH_THEME_PREVIEW, arguments: { |
| 68 | 'theme': officialThemeItems[index], | 93 | 'theme': officialThemeItems[index], |
| @@ -23,9 +23,13 @@ class WatchThemeView extends GetView<WatchThemeController> { | @@ -23,9 +23,13 @@ class WatchThemeView extends GetView<WatchThemeController> { | ||
| 23 | padding: EdgeInsets.only(top: topPadding, bottom: 24), | 23 | padding: EdgeInsets.only(top: topPadding, bottom: 24), |
| 24 | child: Column( | 24 | child: Column( |
| 25 | children: [ | 25 | children: [ |
| 26 | - WatchThemeHeader( | ||
| 27 | - themeImageUrl: | ||
| 28 | - controller.selectedThemeItem.value?.energeticImage, | 26 | + GestureDetector( |
| 27 | + behavior: HitTestBehavior.opaque, | ||
| 28 | + onTap: controller.onHeaderTap, | ||
| 29 | + child: WatchThemeHeader( | ||
| 30 | + themeImageUrl: | ||
| 31 | + controller.selectedThemeItem.value?.energeticImage, | ||
| 32 | + ), | ||
| 29 | ), | 33 | ), |
| 30 | const SizedBox(height: 26), | 34 | const SizedBox(height: 26), |
| 31 | OfficialThemeGrid( | 35 | OfficialThemeGrid( |
| @@ -193,26 +193,22 @@ class AppleHealthRawDataCoreService { | @@ -193,26 +193,22 @@ class AppleHealthRawDataCoreService { | ||
| 193 | } | 193 | } |
| 194 | 194 | ||
| 195 | Future<bool> shareNativeAppleHealthObserverRecord() async { | 195 | Future<bool> shareNativeAppleHealthObserverRecord() async { |
| 196 | - if (!_isDebug) return false; | ||
| 197 | return _rawDataApi.shareNativeAppleHealthObserverRecord(); | 196 | return _rawDataApi.shareNativeAppleHealthObserverRecord(); |
| 198 | } | 197 | } |
| 199 | 198 | ||
| 200 | Future<bool> shareFlutterObserverRecord() async { | 199 | Future<bool> shareFlutterObserverRecord() async { |
| 201 | - if (!_isDebug) return false; | ||
| 202 | return _rawDataApi.shareFlutterObserverRecord(); | 200 | return _rawDataApi.shareFlutterObserverRecord(); |
| 203 | } | 201 | } |
| 204 | 202 | ||
| 205 | Future<bool> shareUploadTaskRecord() async { | 203 | Future<bool> shareUploadTaskRecord() async { |
| 206 | - if (!_isDebug) return false; | ||
| 207 | return _rawDataApi.shareUploadTaskRecord(); | 204 | return _rawDataApi.shareUploadTaskRecord(); |
| 208 | } | 205 | } |
| 209 | 206 | ||
| 210 | Future<String> readLocalNotificationDebugLogText() { | 207 | Future<String> readLocalNotificationDebugLogText() { |
| 211 | - return _localNotificationDebugStore.readText(isDebug: _isDebug); | 208 | + return _localNotificationDebugStore.readText(); |
| 212 | } | 209 | } |
| 213 | 210 | ||
| 214 | Future<bool> shareLocalNotificationDebugRecord() async { | 211 | Future<bool> shareLocalNotificationDebugRecord() async { |
| 215 | - if (!_isDebug) return false; | ||
| 216 | return _shareNativeLocalNotificationDebugRecord(); | 212 | return _shareNativeLocalNotificationDebugRecord(); |
| 217 | } | 213 | } |
| 218 | 214 | ||
| @@ -1477,7 +1473,6 @@ class AppleHealthRawDataCoreService { | @@ -1477,7 +1473,6 @@ class AppleHealthRawDataCoreService { | ||
| 1477 | required String apiName, | 1473 | required String apiName, |
| 1478 | required Object? result, | 1474 | required Object? result, |
| 1479 | }) async { | 1475 | }) async { |
| 1480 | - if (!_isDebug) return; | ||
| 1481 | final file = await _uploadApiLogFile(); | 1476 | final file = await _uploadApiLogFile(); |
| 1482 | await file.parent.create(recursive: true); | 1477 | await file.parent.create(recursive: true); |
| 1483 | final line = | 1478 | final line = |
| @@ -1486,7 +1481,6 @@ class AppleHealthRawDataCoreService { | @@ -1486,7 +1481,6 @@ class AppleHealthRawDataCoreService { | ||
| 1486 | } | 1481 | } |
| 1487 | 1482 | ||
| 1488 | Future<void> _saveNativeCallFlutterChange(List<int>? dataTypes) async { | 1483 | Future<void> _saveNativeCallFlutterChange(List<int>? dataTypes) async { |
| 1489 | - if (!_isDebug) return; | ||
| 1490 | try { | 1484 | try { |
| 1491 | await _rawDataApi.saveNativeCallFlutterChange( | 1485 | await _rawDataApi.saveNativeCallFlutterChange( |
| 1492 | relativeDataTypes: dataTypes ?? const <int>[], | 1486 | relativeDataTypes: dataTypes ?? const <int>[], |
| @@ -1497,7 +1491,6 @@ class AppleHealthRawDataCoreService { | @@ -1497,7 +1491,6 @@ class AppleHealthRawDataCoreService { | ||
| 1497 | } | 1491 | } |
| 1498 | 1492 | ||
| 1499 | Future<void> _saveFlutterCaculateFinished(List<int> dataTypes) async { | 1493 | Future<void> _saveFlutterCaculateFinished(List<int> dataTypes) async { |
| 1500 | - if (!_isDebug) return; | ||
| 1501 | try { | 1494 | try { |
| 1502 | await _rawDataApi.saveFlutterCaculateFinished( | 1495 | await _rawDataApi.saveFlutterCaculateFinished( |
| 1503 | relativeDataTypes: dataTypes, | 1496 | relativeDataTypes: dataTypes, |
| @@ -1510,7 +1503,6 @@ class AppleHealthRawDataCoreService { | @@ -1510,7 +1503,6 @@ class AppleHealthRawDataCoreService { | ||
| 1510 | Future<void> _saveLocalNotificationDebugEvent( | 1503 | Future<void> _saveLocalNotificationDebugEvent( |
| 1511 | Map<String, Object?> event, | 1504 | Map<String, Object?> event, |
| 1512 | ) async { | 1505 | ) async { |
| 1513 | - if (!_isDebug) return; | ||
| 1514 | try { | 1506 | try { |
| 1515 | await _localNotificationDebugStore.append(event); | 1507 | await _localNotificationDebugStore.append(event); |
| 1516 | await _saveNativeLocalNotificationDebugEvent(event); | 1508 | await _saveNativeLocalNotificationDebugEvent(event); |
| @@ -2229,7 +2221,7 @@ class HealthRawStressLocalStore { | @@ -2229,7 +2221,7 @@ class HealthRawStressLocalStore { | ||
| 2229 | final db = await factory.openDatabase( | 2221 | final db = await factory.openDatabase( |
| 2230 | path, | 2222 | path, |
| 2231 | options: OpenDatabaseOptions( | 2223 | options: OpenDatabaseOptions( |
| 2232 | - version: 7, | 2224 | + version: 8, |
| 2233 | onCreate: (db, version) async { | 2225 | onCreate: (db, version) async { |
| 2234 | await _createTables(db); | 2226 | await _createTables(db); |
| 2235 | }, | 2227 | }, |
| @@ -2253,6 +2245,9 @@ class HealthRawStressLocalStore { | @@ -2253,6 +2245,9 @@ class HealthRawStressLocalStore { | ||
| 2253 | if (oldVersion < 7) { | 2245 | if (oldVersion < 7) { |
| 2254 | await _createSleepResultsTable(db); | 2246 | await _createSleepResultsTable(db); |
| 2255 | } | 2247 | } |
| 2248 | + if (oldVersion < 8) { | ||
| 2249 | + await _addUpdateTimeColumns(db); | ||
| 2250 | + } | ||
| 2256 | }, | 2251 | }, |
| 2257 | ), | 2252 | ), |
| 2258 | ); | 2253 | ); |
| @@ -2278,7 +2273,8 @@ CREATE TABLE IF NOT EXISTS $hrvResultsTable ( | @@ -2278,7 +2273,8 @@ CREATE TABLE IF NOT EXISTS $hrvResultsTable ( | ||
| 2278 | is_workout_recovery INTEGER NOT NULL DEFAULT 0, | 2273 | is_workout_recovery INTEGER NOT NULL DEFAULT 0, |
| 2279 | is_sleep_likely INTEGER NOT NULL DEFAULT 0, | 2274 | is_sleep_likely INTEGER NOT NULL DEFAULT 0, |
| 2280 | is_suspected_activity INTEGER NOT NULL DEFAULT 0, | 2275 | is_suspected_activity INTEGER NOT NULL DEFAULT 0, |
| 2281 | - uploaded INTEGER NOT NULL DEFAULT 0 | 2276 | + uploaded INTEGER NOT NULL DEFAULT 0, |
| 2277 | + update_time INTEGER NOT NULL DEFAULT 0 | ||
| 2282 | ) | 2278 | ) |
| 2283 | '''); | 2279 | '''); |
| 2284 | await db.execute(''' | 2280 | await db.execute(''' |
| @@ -2293,7 +2289,8 @@ CREATE TABLE IF NOT EXISTS $realtimeStressResultsTable ( | @@ -2293,7 +2289,8 @@ CREATE TABLE IF NOT EXISTS $realtimeStressResultsTable ( | ||
| 2293 | is_workout_recovery INTEGER NOT NULL DEFAULT 0, | 2289 | is_workout_recovery INTEGER NOT NULL DEFAULT 0, |
| 2294 | is_sleep_likely INTEGER NOT NULL DEFAULT 0, | 2290 | is_sleep_likely INTEGER NOT NULL DEFAULT 0, |
| 2295 | is_suspected_activity INTEGER NOT NULL DEFAULT 0, | 2291 | is_suspected_activity INTEGER NOT NULL DEFAULT 0, |
| 2296 | - uploaded INTEGER NOT NULL DEFAULT 0 | 2292 | + uploaded INTEGER NOT NULL DEFAULT 0, |
| 2293 | + update_time INTEGER NOT NULL DEFAULT 0 | ||
| 2297 | ) | 2294 | ) |
| 2298 | '''); | 2295 | '''); |
| 2299 | await _createDailyStressTable(db); | 2296 | await _createDailyStressTable(db); |
| @@ -2309,7 +2306,8 @@ CREATE TABLE IF NOT EXISTS $dailyStressResultsTable ( | @@ -2309,7 +2306,8 @@ CREATE TABLE IF NOT EXISTS $dailyStressResultsTable ( | ||
| 2309 | stress_score INTEGER NOT NULL, | 2306 | stress_score INTEGER NOT NULL, |
| 2310 | state INTEGER NOT NULL, | 2307 | state INTEGER NOT NULL, |
| 2311 | data_time INTEGER NOT NULL, | 2308 | data_time INTEGER NOT NULL, |
| 2312 | - uploaded INTEGER NOT NULL DEFAULT 0 | 2309 | + uploaded INTEGER NOT NULL DEFAULT 0, |
| 2310 | + update_time INTEGER NOT NULL DEFAULT 0 | ||
| 2313 | ) | 2311 | ) |
| 2314 | '''); | 2312 | '''); |
| 2315 | } | 2313 | } |
| @@ -2325,11 +2323,29 @@ CREATE TABLE IF NOT EXISTS $sleepResultsTable ( | @@ -2325,11 +2323,29 @@ CREATE TABLE IF NOT EXISTS $sleepResultsTable ( | ||
| 2325 | in_bed_minutes INTEGER NOT NULL, | 2323 | in_bed_minutes INTEGER NOT NULL, |
| 2326 | awak_minutes INTEGER NOT NULL, | 2324 | awak_minutes INTEGER NOT NULL, |
| 2327 | sleep_minutes INTEGER NOT NULL, | 2325 | sleep_minutes INTEGER NOT NULL, |
| 2328 | - uploaded INTEGER NOT NULL DEFAULT 0 | 2326 | + uploaded INTEGER NOT NULL DEFAULT 0, |
| 2327 | + update_time INTEGER NOT NULL DEFAULT 0 | ||
| 2329 | ) | 2328 | ) |
| 2330 | '''); | 2329 | '''); |
| 2331 | } | 2330 | } |
| 2332 | 2331 | ||
| 2332 | + Future<void> _addUpdateTimeColumns(DatabaseExecutor db) async { | ||
| 2333 | + for (final table in const [ | ||
| 2334 | + hrvResultsTable, | ||
| 2335 | + realtimeStressResultsTable, | ||
| 2336 | + dailyStressResultsTable, | ||
| 2337 | + sleepResultsTable, | ||
| 2338 | + ]) { | ||
| 2339 | + try { | ||
| 2340 | + await db.execute( | ||
| 2341 | + 'ALTER TABLE $table ADD COLUMN update_time INTEGER NOT NULL DEFAULT 0', | ||
| 2342 | + ); | ||
| 2343 | + } on DatabaseException catch (error) { | ||
| 2344 | + if (!error.isDuplicateColumnError()) rethrow; | ||
| 2345 | + } | ||
| 2346 | + } | ||
| 2347 | + } | ||
| 2348 | + | ||
| 2333 | Future<void> _addFlagColumns(DatabaseExecutor db, String table) async { | 2349 | Future<void> _addFlagColumns(DatabaseExecutor db, String table) async { |
| 2334 | for (final column in const [ | 2350 | for (final column in const [ |
| 2335 | 'is_workout', | 2351 | 'is_workout', |
| @@ -2491,6 +2507,7 @@ CREATE TABLE IF NOT EXISTS $sleepResultsTable ( | @@ -2491,6 +2507,7 @@ CREATE TABLE IF NOT EXISTS $sleepResultsTable ( | ||
| 2491 | 'baseline_resting_hr': point.baselineRestingHr, | 2507 | 'baseline_resting_hr': point.baselineRestingHr, |
| 2492 | ..._flagsRow(point.flags), | 2508 | ..._flagsRow(point.flags), |
| 2493 | 'uploaded': point.uploaded ? 1 : 0, | 2509 | 'uploaded': point.uploaded ? 1 : 0, |
| 2510 | + 'update_time': _currentUnixSeconds(), | ||
| 2494 | }; | 2511 | }; |
| 2495 | } | 2512 | } |
| 2496 | 2513 | ||
| @@ -2504,6 +2521,7 @@ CREATE TABLE IF NOT EXISTS $sleepResultsTable ( | @@ -2504,6 +2521,7 @@ CREATE TABLE IF NOT EXISTS $sleepResultsTable ( | ||
| 2504 | 'source_end_time': point.sourceEndTime, | 2521 | 'source_end_time': point.sourceEndTime, |
| 2505 | ..._flagsRow(point.flags), | 2522 | ..._flagsRow(point.flags), |
| 2506 | 'uploaded': point.uploaded ? 1 : 0, | 2523 | 'uploaded': point.uploaded ? 1 : 0, |
| 2524 | + 'update_time': _currentUnixSeconds(), | ||
| 2507 | }; | 2525 | }; |
| 2508 | } | 2526 | } |
| 2509 | 2527 | ||
| @@ -2516,6 +2534,7 @@ CREATE TABLE IF NOT EXISTS $sleepResultsTable ( | @@ -2516,6 +2534,7 @@ CREATE TABLE IF NOT EXISTS $sleepResultsTable ( | ||
| 2516 | 'state': point.state.value, | 2534 | 'state': point.state.value, |
| 2517 | 'data_time': point.dataTime, | 2535 | 'data_time': point.dataTime, |
| 2518 | 'uploaded': point.uploaded ? 1 : 0, | 2536 | 'uploaded': point.uploaded ? 1 : 0, |
| 2537 | + 'update_time': _currentUnixSeconds(), | ||
| 2519 | }; | 2538 | }; |
| 2520 | } | 2539 | } |
| 2521 | 2540 | ||
| @@ -2530,6 +2549,7 @@ CREATE TABLE IF NOT EXISTS $sleepResultsTable ( | @@ -2530,6 +2549,7 @@ CREATE TABLE IF NOT EXISTS $sleepResultsTable ( | ||
| 2530 | 'awak_minutes': result.awakMinutes, | 2549 | 'awak_minutes': result.awakMinutes, |
| 2531 | 'sleep_minutes': result.sleepMinutes, | 2550 | 'sleep_minutes': result.sleepMinutes, |
| 2532 | 'uploaded': result.uploaded ? 1 : 0, | 2551 | 'uploaded': result.uploaded ? 1 : 0, |
| 2552 | + 'update_time': _currentUnixSeconds(), | ||
| 2533 | }; | 2553 | }; |
| 2534 | } | 2554 | } |
| 2535 | 2555 | ||
| @@ -2560,6 +2580,12 @@ CREATE TABLE IF NOT EXISTS $sleepResultsTable ( | @@ -2560,6 +2580,12 @@ CREATE TABLE IF NOT EXISTS $sleepResultsTable ( | ||
| 2560 | final existingRow = existing.first; | 2580 | final existingRow = existing.first; |
| 2561 | if (_matchesStoredValues( | 2581 | if (_matchesStoredValues( |
| 2562 | existingRow, row, _rawResultStoredValueKeys(row))) { | 2582 | existingRow, row, _rawResultStoredValueKeys(row))) { |
| 2583 | + await db.update( | ||
| 2584 | + table, | ||
| 2585 | + <String, Object?>{'update_time': row['update_time']}, | ||
| 2586 | + where: 'raw_end_time = ?', | ||
| 2587 | + whereArgs: [row['raw_end_time']], | ||
| 2588 | + ); | ||
| 2563 | return; | 2589 | return; |
| 2564 | } | 2590 | } |
| 2565 | final uploadPayloadChanged = !_matchesStoredValues( | 2591 | final uploadPayloadChanged = !_matchesStoredValues( |
| @@ -2590,6 +2616,7 @@ CREATE TABLE IF NOT EXISTS $sleepResultsTable ( | @@ -2590,6 +2616,7 @@ CREATE TABLE IF NOT EXISTS $sleepResultsTable ( | ||
| 2590 | 'is_sleep_likely': row['is_sleep_likely'], | 2616 | 'is_sleep_likely': row['is_sleep_likely'], |
| 2591 | 'is_suspected_activity': row['is_suspected_activity'], | 2617 | 'is_suspected_activity': row['is_suspected_activity'], |
| 2592 | 'uploaded': uploadPayloadChanged ? 0 : existingRow['uploaded'], | 2618 | 'uploaded': uploadPayloadChanged ? 0 : existingRow['uploaded'], |
| 2619 | + 'update_time': row['update_time'], | ||
| 2593 | }, | 2620 | }, |
| 2594 | where: 'raw_end_time = ?', | 2621 | where: 'raw_end_time = ?', |
| 2595 | whereArgs: [row['raw_end_time']], | 2622 | whereArgs: [row['raw_end_time']], |
| @@ -2629,6 +2656,7 @@ CREATE TABLE IF NOT EXISTS $sleepResultsTable ( | @@ -2629,6 +2656,7 @@ CREATE TABLE IF NOT EXISTS $sleepResultsTable ( | ||
| 2629 | 'state': row['state'], | 2656 | 'state': row['state'], |
| 2630 | 'data_time': row['data_time'], | 2657 | 'data_time': row['data_time'], |
| 2631 | 'uploaded': isToday ? 0 : (valueChanged ? 0 : existingRow['uploaded']), | 2658 | 'uploaded': isToday ? 0 : (valueChanged ? 0 : existingRow['uploaded']), |
| 2659 | + 'update_time': row['update_time'], | ||
| 2632 | }, | 2660 | }, |
| 2633 | where: 'date = ?', | 2661 | where: 'date = ?', |
| 2634 | whereArgs: [row['date']], | 2662 | whereArgs: [row['date']], |
| @@ -2674,12 +2702,17 @@ CREATE TABLE IF NOT EXISTS $sleepResultsTable ( | @@ -2674,12 +2702,17 @@ CREATE TABLE IF NOT EXISTS $sleepResultsTable ( | ||
| 2674 | 'awak_minutes': row['awak_minutes'], | 2702 | 'awak_minutes': row['awak_minutes'], |
| 2675 | 'sleep_minutes': row['sleep_minutes'], | 2703 | 'sleep_minutes': row['sleep_minutes'], |
| 2676 | 'uploaded': valueChanged ? 0 : existingRow['uploaded'], | 2704 | 'uploaded': valueChanged ? 0 : existingRow['uploaded'], |
| 2705 | + 'update_time': row['update_time'], | ||
| 2677 | }, | 2706 | }, |
| 2678 | where: 'date = ?', | 2707 | where: 'date = ?', |
| 2679 | whereArgs: [row['date']], | 2708 | whereArgs: [row['date']], |
| 2680 | ); | 2709 | ); |
| 2681 | } | 2710 | } |
| 2682 | 2711 | ||
| 2712 | + int _currentUnixSeconds() { | ||
| 2713 | + return DateTime.now().millisecondsSinceEpoch ~/ 1000; | ||
| 2714 | + } | ||
| 2715 | + | ||
| 2683 | List<String> _rawResultStoredValueKeys(Map<String, Object?> row) { | 2716 | List<String> _rawResultStoredValueKeys(Map<String, Object?> row) { |
| 2684 | return <String>[ | 2717 | return <String>[ |
| 2685 | 'user_id', | 2718 | 'user_id', |
lib/core/services/raw_data_service/platform_ios/apple_health_raw_local_notification_debug_store.dart
| @@ -35,10 +35,7 @@ class HealthRawLocalNotificationDebugStore { | @@ -35,10 +35,7 @@ class HealthRawLocalNotificationDebugStore { | ||
| 35 | ); | 35 | ); |
| 36 | } | 36 | } |
| 37 | 37 | ||
| 38 | - Future<String> readText({required bool isDebug}) async { | ||
| 39 | - if (!isDebug) { | ||
| 40 | - return '当前非 Debug 模式,未记录本地推送诊断日志'; | ||
| 41 | - } | 38 | + Future<String> readText() async { |
| 42 | final file = await _file(); | 39 | final file = await _file(); |
| 43 | if (!await file.exists()) { | 40 | if (!await file.exists()) { |
| 44 | return '暂无本地推送诊断日志'; | 41 | return '暂无本地推送诊断日志'; |
| @@ -391,6 +391,71 @@ void main() { | @@ -391,6 +391,71 @@ void main() { | ||
| 391 | expect(daily.single.uploaded, isTrue); | 391 | expect(daily.single.uploaded, isTrue); |
| 392 | }); | 392 | }); |
| 393 | 393 | ||
| 394 | + test('local result rows include update time and refresh on upsert', () async { | ||
| 395 | + const base = 1800000000; | ||
| 396 | + final store = _MemoryHealthRawStressLocalStore(); | ||
| 397 | + await store.upsertResult( | ||
| 398 | + HealthRawStressCalculationResult( | ||
| 399 | + userId: 42, | ||
| 400 | + hrvStressPoints: [_hrvStressPoint(base + 10)], | ||
| 401 | + realtimeStressPoints: [_realtimeStressPoint(base + 20)], | ||
| 402 | + dailyStressPoints: const <HealthRawDailyStressPoint>[], | ||
| 403 | + ), | ||
| 404 | + ); | ||
| 405 | + await store.upsertDailyStressPoints( | ||
| 406 | + userId: 42, | ||
| 407 | + points: [ | ||
| 408 | + HealthRawDailyStressPoint( | ||
| 409 | + userId: 42, | ||
| 410 | + date: 20260101, | ||
| 411 | + stressValue: 40, | ||
| 412 | + stressScore: 40, | ||
| 413 | + state: HealthRawStressState.normal, | ||
| 414 | + dataTime: base + 20, | ||
| 415 | + ), | ||
| 416 | + ], | ||
| 417 | + ); | ||
| 418 | + await store.upsertSleepResults( | ||
| 419 | + userId: 42, | ||
| 420 | + results: const [ | ||
| 421 | + HealthRawSleepResult( | ||
| 422 | + userId: 42, | ||
| 423 | + date: base + 30, | ||
| 424 | + startDate: base, | ||
| 425 | + sleepScore: 80, | ||
| 426 | + sleepState: 2, | ||
| 427 | + inBedMinutes: 480, | ||
| 428 | + awakMinutes: 10, | ||
| 429 | + sleepMinutes: 470, | ||
| 430 | + ), | ||
| 431 | + ], | ||
| 432 | + ); | ||
| 433 | + final firstRows = await store.debugQueryAllRows(42); | ||
| 434 | + final firstHrvUpdateTime = firstRows.hrvRows.single['update_time'] as int; | ||
| 435 | + | ||
| 436 | + expect(firstHrvUpdateTime, greaterThan(0)); | ||
| 437 | + expect(firstRows.realtimeRows.single['update_time'], greaterThan(0)); | ||
| 438 | + expect(firstRows.dailyStressRows.single['update_time'], greaterThan(0)); | ||
| 439 | + expect(firstRows.sleepRows.single['update_time'], greaterThan(0)); | ||
| 440 | + | ||
| 441 | + await store.markHrvStressUploaded(rawEndTimes: [base + 10], userId: 42); | ||
| 442 | + await store.upsertResult( | ||
| 443 | + HealthRawStressCalculationResult( | ||
| 444 | + userId: 42, | ||
| 445 | + hrvStressPoints: [_hrvStressPoint(base + 10)], | ||
| 446 | + realtimeStressPoints: const <HealthRawRealtimeStressPoint>[], | ||
| 447 | + dailyStressPoints: const <HealthRawDailyStressPoint>[], | ||
| 448 | + ), | ||
| 449 | + ); | ||
| 450 | + final secondRows = await store.debugQueryAllRows(42); | ||
| 451 | + | ||
| 452 | + expect( | ||
| 453 | + secondRows.hrvRows.single['update_time'], | ||
| 454 | + greaterThan(firstHrvUpdateTime), | ||
| 455 | + ); | ||
| 456 | + expect(secondRows.hrvRows.single['uploaded'], 1); | ||
| 457 | + }); | ||
| 458 | + | ||
| 394 | test( | 459 | test( |
| 395 | 'startCoreCaculate skips raw reads when health auth and local data are missing', | 460 | 'startCoreCaculate skips raw reads when health auth and local data are missing', |
| 396 | () async { | 461 | () async { |
| @@ -1266,7 +1331,12 @@ class _MemoryHealthRawStressLocalStore extends HealthRawStressLocalStore { | @@ -1266,7 +1331,12 @@ class _MemoryHealthRawStressLocalStore extends HealthRawStressLocalStore { | ||
| 1266 | final Map<int, List<HealthRawRealtimeStressPoint>> _realtime = {}; | 1331 | final Map<int, List<HealthRawRealtimeStressPoint>> _realtime = {}; |
| 1267 | final Map<int, List<HealthRawDailyStressPoint>> _daily = {}; | 1332 | final Map<int, List<HealthRawDailyStressPoint>> _daily = {}; |
| 1268 | final Map<int, List<HealthRawSleepResult>> _sleep = {}; | 1333 | final Map<int, List<HealthRawSleepResult>> _sleep = {}; |
| 1334 | + final Map<int, Map<int, int>> _hrvUpdateTimes = {}; | ||
| 1335 | + final Map<int, Map<int, int>> _realtimeUpdateTimes = {}; | ||
| 1336 | + final Map<int, Map<int, int>> _dailyUpdateTimes = {}; | ||
| 1337 | + final Map<int, Map<int, int>> _sleepUpdateTimes = {}; | ||
| 1269 | final List<String> operationLog; | 1338 | final List<String> operationLog; |
| 1339 | + var _updateTimeClock = 1; | ||
| 1270 | var realtimeNotificationWindowQueryCount = 0; | 1340 | var realtimeNotificationWindowQueryCount = 0; |
| 1271 | 1341 | ||
| 1272 | void insertDailyStress(HealthRawDailyStressPoint point) { | 1342 | void insertDailyStress(HealthRawDailyStressPoint point) { |
| @@ -1277,6 +1347,8 @@ class _MemoryHealthRawStressLocalStore extends HealthRawStressLocalStore { | @@ -1277,6 +1347,8 @@ class _MemoryHealthRawStressLocalStore extends HealthRawStressLocalStore { | ||
| 1277 | byDate[point.date] = point; | 1347 | byDate[point.date] = point; |
| 1278 | _daily[point.userId] = byDate.values.toList() | 1348 | _daily[point.userId] = byDate.values.toList() |
| 1279 | ..sort((a, b) => a.date.compareTo(b.date)); | 1349 | ..sort((a, b) => a.date.compareTo(b.date)); |
| 1350 | + _dailyUpdateTimes.putIfAbsent( | ||
| 1351 | + point.userId, () => <int, int>{})[point.date] = _nextUpdateTime(); | ||
| 1280 | } | 1352 | } |
| 1281 | 1353 | ||
| 1282 | @override | 1354 | @override |
| @@ -1313,6 +1385,9 @@ class _MemoryHealthRawStressLocalStore extends HealthRawStressLocalStore { | @@ -1313,6 +1385,9 @@ class _MemoryHealthRawStressLocalStore extends HealthRawStressLocalStore { | ||
| 1313 | flags: point.flags, | 1385 | flags: point.flags, |
| 1314 | uploaded: same ? existing.uploaded : false, | 1386 | uploaded: same ? existing.uploaded : false, |
| 1315 | ); | 1387 | ); |
| 1388 | + _hrvUpdateTimes.putIfAbsent( | ||
| 1389 | + point.userId, () => <int, int>{})[point.rawEndTime] = | ||
| 1390 | + _nextUpdateTime(); | ||
| 1316 | } | 1391 | } |
| 1317 | _hrv[result.userId] = hrvByTime.values.toList() | 1392 | _hrv[result.userId] = hrvByTime.values.toList() |
| 1318 | ..sort((a, b) => a.rawEndTime.compareTo(b.rawEndTime)); | 1393 | ..sort((a, b) => a.rawEndTime.compareTo(b.rawEndTime)); |
| @@ -1336,6 +1411,9 @@ class _MemoryHealthRawStressLocalStore extends HealthRawStressLocalStore { | @@ -1336,6 +1411,9 @@ class _MemoryHealthRawStressLocalStore extends HealthRawStressLocalStore { | ||
| 1336 | flags: point.flags, | 1411 | flags: point.flags, |
| 1337 | uploaded: same ? existing.uploaded : false, | 1412 | uploaded: same ? existing.uploaded : false, |
| 1338 | ); | 1413 | ); |
| 1414 | + _realtimeUpdateTimes.putIfAbsent( | ||
| 1415 | + point.userId, () => <int, int>{})[point.rawEndTime] = | ||
| 1416 | + _nextUpdateTime(); | ||
| 1339 | } | 1417 | } |
| 1340 | _realtime[result.userId] = realtimeByTime.values.toList() | 1418 | _realtime[result.userId] = realtimeByTime.values.toList() |
| 1341 | ..sort((a, b) => a.rawEndTime.compareTo(b.rawEndTime)); | 1419 | ..sort((a, b) => a.rawEndTime.compareTo(b.rawEndTime)); |
| @@ -1368,6 +1446,8 @@ class _MemoryHealthRawStressLocalStore extends HealthRawStressLocalStore { | @@ -1368,6 +1446,8 @@ class _MemoryHealthRawStressLocalStore extends HealthRawStressLocalStore { | ||
| 1368 | dataTime: point.dataTime, | 1446 | dataTime: point.dataTime, |
| 1369 | uploaded: isToday ? false : (same ? existing.uploaded : false), | 1447 | uploaded: isToday ? false : (same ? existing.uploaded : false), |
| 1370 | ); | 1448 | ); |
| 1449 | + _dailyUpdateTimes.putIfAbsent( | ||
| 1450 | + point.userId, () => <int, int>{})[point.date] = _nextUpdateTime(); | ||
| 1371 | } | 1451 | } |
| 1372 | _daily[userId] = byDate.values.toList() | 1452 | _daily[userId] = byDate.values.toList() |
| 1373 | ..sort((a, b) => a.date.compareTo(b.date)); | 1453 | ..sort((a, b) => a.date.compareTo(b.date)); |
| @@ -1384,6 +1464,8 @@ class _MemoryHealthRawStressLocalStore extends HealthRawStressLocalStore { | @@ -1384,6 +1464,8 @@ class _MemoryHealthRawStressLocalStore extends HealthRawStressLocalStore { | ||
| 1384 | }; | 1464 | }; |
| 1385 | for (final result in results) { | 1465 | for (final result in results) { |
| 1386 | byDate[result.date] = result; | 1466 | byDate[result.date] = result; |
| 1467 | + _sleepUpdateTimes.putIfAbsent( | ||
| 1468 | + result.userId, () => <int, int>{})[result.date] = _nextUpdateTime(); | ||
| 1387 | } | 1469 | } |
| 1388 | _sleep[userId] = byDate.values.toList() | 1470 | _sleep[userId] = byDate.values.toList() |
| 1389 | ..sort((a, b) => a.date.compareTo(b.date)); | 1471 | ..sort((a, b) => a.date.compareTo(b.date)); |
| @@ -1711,6 +1793,7 @@ class _MemoryHealthRawStressLocalStore extends HealthRawStressLocalStore { | @@ -1711,6 +1793,7 @@ class _MemoryHealthRawStressLocalStore extends HealthRawStressLocalStore { | ||
| 1711 | 'is_sleep_likely': point.flags.isSleepLikely ? 1 : 0, | 1793 | 'is_sleep_likely': point.flags.isSleepLikely ? 1 : 0, |
| 1712 | 'is_suspected_activity': point.flags.isSuspectedActivity ? 1 : 0, | 1794 | 'is_suspected_activity': point.flags.isSuspectedActivity ? 1 : 0, |
| 1713 | 'uploaded': point.uploaded ? 1 : 0, | 1795 | 'uploaded': point.uploaded ? 1 : 0, |
| 1796 | + 'update_time': _hrvUpdateTimes[point.userId]?[point.rawEndTime] ?? 0, | ||
| 1714 | }; | 1797 | }; |
| 1715 | } | 1798 | } |
| 1716 | 1799 | ||
| @@ -1727,6 +1810,7 @@ class _MemoryHealthRawStressLocalStore extends HealthRawStressLocalStore { | @@ -1727,6 +1810,7 @@ class _MemoryHealthRawStressLocalStore extends HealthRawStressLocalStore { | ||
| 1727 | 'is_sleep_likely': point.flags.isSleepLikely ? 1 : 0, | 1810 | 'is_sleep_likely': point.flags.isSleepLikely ? 1 : 0, |
| 1728 | 'is_suspected_activity': point.flags.isSuspectedActivity ? 1 : 0, | 1811 | 'is_suspected_activity': point.flags.isSuspectedActivity ? 1 : 0, |
| 1729 | 'uploaded': point.uploaded ? 1 : 0, | 1812 | 'uploaded': point.uploaded ? 1 : 0, |
| 1813 | + 'update_time': _realtimeUpdateTimes[point.userId]?[point.rawEndTime] ?? 0, | ||
| 1730 | }; | 1814 | }; |
| 1731 | } | 1815 | } |
| 1732 | 1816 | ||
| @@ -1739,6 +1823,7 @@ class _MemoryHealthRawStressLocalStore extends HealthRawStressLocalStore { | @@ -1739,6 +1823,7 @@ class _MemoryHealthRawStressLocalStore extends HealthRawStressLocalStore { | ||
| 1739 | 'state': point.state.value, | 1823 | 'state': point.state.value, |
| 1740 | 'data_time': point.dataTime, | 1824 | 'data_time': point.dataTime, |
| 1741 | 'uploaded': point.uploaded ? 1 : 0, | 1825 | 'uploaded': point.uploaded ? 1 : 0, |
| 1826 | + 'update_time': _dailyUpdateTimes[point.userId]?[point.date] ?? 0, | ||
| 1742 | }; | 1827 | }; |
| 1743 | } | 1828 | } |
| 1744 | 1829 | ||
| @@ -1753,9 +1838,12 @@ class _MemoryHealthRawStressLocalStore extends HealthRawStressLocalStore { | @@ -1753,9 +1838,12 @@ class _MemoryHealthRawStressLocalStore extends HealthRawStressLocalStore { | ||
| 1753 | 'awak_minutes': result.awakMinutes, | 1838 | 'awak_minutes': result.awakMinutes, |
| 1754 | 'sleep_minutes': result.sleepMinutes, | 1839 | 'sleep_minutes': result.sleepMinutes, |
| 1755 | 'uploaded': result.uploaded ? 1 : 0, | 1840 | 'uploaded': result.uploaded ? 1 : 0, |
| 1841 | + 'update_time': _sleepUpdateTimes[result.userId]?[result.date] ?? 0, | ||
| 1756 | }; | 1842 | }; |
| 1757 | } | 1843 | } |
| 1758 | 1844 | ||
| 1845 | + int _nextUpdateTime() => _updateTimeClock++; | ||
| 1846 | + | ||
| 1759 | int _todayDateKey() { | 1847 | int _todayDateKey() { |
| 1760 | final now = DateTime.now(); | 1848 | final now = DateTime.now(); |
| 1761 | return now.year * 10000 + now.month * 100 + now.day; | 1849 | return now.year * 10000 + now.month * 100 + now.day; |
-
Please register or login to post a comment