Commit 2166905b7a2dea583159da9cba3fca8aaadb16a0

Authored by 常守达
1 parent ffbc6b92

feat(today): 首页接口修改

@@ -81,4 +81,7 @@ class PlatformHostApiImpl(private val application: android.app.Application) : Pl @@ -81,4 +81,7 @@ class PlatformHostApiImpl(private val application: android.app.Application) : Pl
81 callback: (Result<AppleProductPaymentResult?>) -> Unit 81 callback: (Result<AppleProductPaymentResult?>) -> Unit
82 ) { 82 ) {
83 } 83 }
  84 +
  85 + override fun performRestore(callback: (Result<Boolean>) -> Unit) {
  86 + }
84 } 87 }
@@ -6,15 +6,16 @@ import 'package:doublefeel_flutter/core/logging/app_logger.dart'; @@ -6,15 +6,16 @@ import 'package:doublefeel_flutter/core/logging/app_logger.dart';
6 import 'package:doublefeel_flutter/core/network/api/health_api.dart'; 6 import 'package:doublefeel_flutter/core/network/api/health_api.dart';
7 import 'package:doublefeel_flutter/core/network/api/user_api.dart'; 7 import 'package:doublefeel_flutter/core/network/api/user_api.dart';
8 import 'package:doublefeel_flutter/core/result/app_result.dart'; 8 import 'package:doublefeel_flutter/core/result/app_result.dart';
9 -import 'package:doublefeel_flutter/core/error/app_error.dart';  
10 import 'package:doublefeel_flutter/core/services/health_kit_upload_service.dart'; 9 import 'package:doublefeel_flutter/core/services/health_kit_upload_service.dart';
11 import 'package:doublefeel_flutter/core/services/user_state_service.dart'; 10 import 'package:doublefeel_flutter/core/services/user_state_service.dart';
12 import 'package:doublefeel_flutter/data/models/enums/app_enums.dart'; 11 import 'package:doublefeel_flutter/data/models/enums/app_enums.dart';
13 import 'package:doublefeel_flutter/data/models/health/health_models.dart'; 12 import 'package:doublefeel_flutter/data/models/health/health_models.dart';
14 import 'package:doublefeel_flutter/data/models/health/health_upload_models.dart'; 13 import 'package:doublefeel_flutter/data/models/health/health_upload_models.dart';
  14 +import 'package:doublefeel_flutter/data/models/health/health_v2_models.dart';
15 import 'package:doublefeel_flutter/pigeon/health_kit_api.g.dart'; 15 import 'package:doublefeel_flutter/pigeon/health_kit_api.g.dart';
16 import 'package:flutter/material.dart'; 16 import 'package:flutter/material.dart';
17 import 'package:get/get.dart'; 17 import 'package:get/get.dart';
  18 +import 'package:intl/intl.dart';
18 19
19 /// HRV 趋势数据点 20 /// HRV 趋势数据点
20 class HrvDataPoint { 21 class HrvDataPoint {
@@ -115,6 +116,12 @@ class TodayController extends GetxController { @@ -115,6 +116,12 @@ class TodayController extends GetxController {
115 116
116 final hrvAnnotations = <HrvAnnotation>[].obs; 117 final hrvAnnotations = <HrvAnnotation>[].obs;
117 118
  119 + final v2HealthData = Rxn<V2HealthData>();
  120 + final v2StressScore = Rxn<V2StressScore>();
  121 + final v2LatestHrv = Rxn<V2LatestHrvData>();
  122 + final v2HrvTrend = Rxn<V2HrvTrendData>();
  123 + final v2RealtimeStress = Rxn<V2RealtimeStressData>();
  124 +
118 Future<void> requestHealthAuthorization() async { 125 Future<void> requestHealthAuthorization() async {
119 if (GetPlatform.isIOS) { 126 if (GetPlatform.isIOS) {
120 try { 127 try {
@@ -211,89 +218,44 @@ class TodayController extends GetxController { @@ -211,89 +218,44 @@ class TodayController extends GetxController {
211 } 218 }
212 219
213 Future<void> _refreshHealthDataForDate(DateTime date) async { 220 Future<void> _refreshHealthDataForDate(DateTime date) async {
214 - final startYear = date.year;  
215 - final startMonth = date.month;  
216 - final startDate = startYear * 10000 + startMonth * 100 + 1; // e.g. 20260601  
217 - const dateRangeType = 1; 221 + _clearRealTimeData();
  222 + final intDate = int.parse(DateFormat('yyyyMM01').format(date));
218 223
219 final isToday = DateUtils.isSameDay(date, lastSelectableDay); 224 final isToday = DateUtils.isSameDay(date, lastSelectableDay);
220 stressSubtitle.value = isToday ? 'Hi, 你今日的综合压力状态' : '该日压力状态'; 225 stressSubtitle.value = isToday ? 'Hi, 你今日的综合压力状态' : '该日压力状态';
221 -  
222 - final todayResultFuture = isToday  
223 - ? _healthApi.getTodayData(  
224 - isOther: isFriend,  
225 - errorHandlingPolicy: null,  
226 - )  
227 - : Future.value(  
228 - AppFailure<TodayStatusData>(AppUnknownError('Not today')));  
229 -  
230 - final latestHrvResultFuture = isToday && !isFriend  
231 - ? _healthApi.getLatestHrvData(  
232 - errorHandlingPolicy: null,  
233 - )  
234 - : Future.value(AppFailure<LatestHrvData>(AppUnknownError('Not today')));  
235 -  
236 - final hrvStatisticsResultFuture = _healthApi.getHrvStatistics(  
237 - isOther: isFriend,  
238 - dateRangeType: dateRangeType,  
239 - startDate: startDate,  
240 - targetUserId: friendUserId,  
241 - errorHandlingPolicy: null,  
242 - );  
243 - final sleepStatisticsResultFuture = _healthApi.getSleepStateStatistics(  
244 - isOther: isFriend,  
245 - dateRangeType: dateRangeType,  
246 - startDate: startDate,  
247 - targetUserId: friendUserId,  
248 - errorHandlingPolicy: null,  
249 - );  
250 - final activityStatisticsResultFuture = _healthApi.getActivityBurnStatistics(  
251 - isOther: isFriend,  
252 - dateRangeType: dateRangeType,  
253 - startDate: startDate,  
254 - targetUserId: friendUserId,  
255 - errorHandlingPolicy: null,  
256 - );  
257 -  
258 - final todayResult = await todayResultFuture;  
259 - final latestHrvResult = await latestHrvResultFuture;  
260 - final hrvStatisticsResult = await hrvStatisticsResultFuture;  
261 - final sleepStatisticsResult = await sleepStatisticsResultFuture;  
262 - final activityStatisticsResult = await activityStatisticsResultFuture;  
263 -  
264 - if (selectedDate.value != date) return;  
265 -  
266 - if (!isToday) {  
267 - _clearRealTimeData();  
268 - }  
269 -  
270 - if (todayResult case AppSuccess<TodayStatusData>(data: final today)) {  
271 - _applyTodayData(today);  
272 - } else if (todayResult case AppFailure(error: final error)) {  
273 - if (isToday) {  
274 - AppLogger.w('HealthApi.getTodayData failed: $error');  
275 - } 226 + switch (await _healthApi.getV2HealthData(friendUserId, intDate)) {
  227 + case AppSuccess(:final data):
  228 + v2HealthData.value = data;
  229 + case AppFailure():
  230 + v2HealthData.value = null;
276 } 231 }
277 232
278 - if (latestHrvResult case AppSuccess<LatestHrvData>(data: final latestHrv)) {  
279 - _applyLatestHrvData(latestHrv); 233 + switch (await _healthApi.getV2StressScore(friendUserId, intDate)) {
  234 + case AppSuccess(:final data):
  235 + v2StressScore.value = data;
  236 + case AppFailure():
  237 + v2StressScore.value = null;
280 } 238 }
281 239
282 - if (hrvStatisticsResult  
283 - case AppSuccess<HrvStatisticsData>(data: final hrvStatistics)) {  
284 - _applyHrvStatistics(hrvStatistics, date); 240 + switch (await _healthApi.getV2LatestHrv(friendUserId, intDate)) {
  241 + case AppSuccess(:final data):
  242 + v2LatestHrv.value = data;
  243 + case AppFailure():
  244 + v2LatestHrv.value = null;
285 } 245 }
286 246
287 - if (sleepStatisticsResult  
288 - case AppSuccess<SleepStatisticsData>(data: final sleepStatistics)) {  
289 - _applySleepStatistics(sleepStatistics, date); 247 + switch (await _healthApi.getV2HrvTrend(friendUserId, intDate)) {
  248 + case AppSuccess(:final data):
  249 + v2HrvTrend.value = data;
  250 + case AppFailure():
  251 + v2HrvTrend.value = null;
290 } 252 }
291 253
292 - if (activityStatisticsResult  
293 - case AppSuccess<ActivityBurnStatisticsData>(  
294 - data: final activityStatistics  
295 - )) {  
296 - _applyActivityStatistics(activityStatistics, date); 254 + switch (await _healthApi.getV2RealtimeStress(friendUserId, intDate)) {
  255 + case AppSuccess(:final data):
  256 + v2RealtimeStress.value = data;
  257 + case AppFailure():
  258 + v2RealtimeStress.value = null;
297 } 259 }
298 } 260 }
299 261
@@ -477,7 +477,22 @@ class _LatestHrvCard extends StatelessWidget { @@ -477,7 +477,22 @@ class _LatestHrvCard extends StatelessWidget {
477 final colors = context.colors; 477 final colors = context.colors;
478 478
479 return Obx(() { 479 return Obx(() {
480 - controller.avgHrv.value; 480 + final latestHrv = controller.v2LatestHrv.value?.latestHrv;
  481 + final latestDataTime = controller.v2LatestHrv.value?.latestDataTime;
  482 + final state = controller.v2LatestHrv.value?.state;
  483 + final tip = controller.v2LatestHrv.value?.tip;
  484 +
  485 + if (latestHrv == null ||
  486 + latestDataTime == null ||
  487 + state == null ||
  488 + tip == null) {
  489 + return const SizedBox.shrink();
  490 + }
  491 + final latestHrvText = '$latestHrv ms';
  492 + final latestDataTimeText =
  493 + '${DateFormat('HH:mm').format(DateTime.fromMillisecondsSinceEpoch(latestDataTime * 1000))}';
  494 + final stateText =
  495 + '${state == 1 ? '状态优秀' : state == 2 ? '状态' : state == 3 ? '状态一般' : state == 4 ? '状态较差' : '状态极差'}';
481 return Container( 496 return Container(
482 padding: const EdgeInsets.fromLTRB(20, 20, 20, 20), 497 padding: const EdgeInsets.fromLTRB(20, 20, 20, 20),
483 decoration: BoxDecoration( 498 decoration: BoxDecoration(
@@ -488,7 +503,7 @@ class _LatestHrvCard extends StatelessWidget { @@ -488,7 +503,7 @@ class _LatestHrvCard extends StatelessWidget {
488 crossAxisAlignment: CrossAxisAlignment.start, 503 crossAxisAlignment: CrossAxisAlignment.start,
489 children: [ 504 children: [
490 Text( 505 Text(
491 - '最近一次(17:20)', 506 + '最近一次($latestDataTimeText)',
492 style: TextStyle( 507 style: TextStyle(
493 color: colors.textSecondary, 508 color: colors.textSecondary,
494 fontSize: 12, 509 fontSize: 12,
@@ -508,7 +523,7 @@ class _LatestHrvCard extends StatelessWidget { @@ -508,7 +523,7 @@ class _LatestHrvCard extends StatelessWidget {
508 ), 523 ),
509 const SizedBox(width: 8), 524 const SizedBox(width: 8),
510 Text( 525 Text(
511 - '66ms·状态优秀', 526 + '${latestHrvText}ms·$stateText',
512 style: TextStyle( 527 style: TextStyle(
513 color: colors.textPrimary, 528 color: colors.textPrimary,
514 fontSize: 18, 529 fontSize: 18,
@@ -519,7 +534,7 @@ class _LatestHrvCard extends StatelessWidget { @@ -519,7 +534,7 @@ class _LatestHrvCard extends StatelessWidget {
519 ), 534 ),
520 const SizedBox(height: 8), 535 const SizedBox(height: 8),
521 Text( 536 Text(
522 - '你的 HRV 高于平时水平。你目前状态放松,压力控制良好——继续保持这样的平衡状态。', 537 + tip,
523 maxLines: 2, 538 maxLines: 2,
524 overflow: TextOverflow.ellipsis, 539 overflow: TextOverflow.ellipsis,
525 style: TextStyle( 540 style: TextStyle(
@@ -25,7 +25,8 @@ class TodayHrvNumberCard extends StatelessWidget { @@ -25,7 +25,8 @@ class TodayHrvNumberCard extends StatelessWidget {
25 Expanded( 25 Expanded(
26 child: _NumberItem( 26 child: _NumberItem(
27 label: context.l10n.averageHrvForTheDay, 27 label: context.l10n.averageHrvForTheDay,
28 - value: controller.avgHrv.value, 28 + value:
  29 + controller.v2HealthData.value?.hrvAvg?.toString() ?? '--',
29 unit: 'ms', 30 unit: 'ms',
30 ), 31 ),
31 ), 32 ),
@@ -33,7 +34,9 @@ class TodayHrvNumberCard extends StatelessWidget { @@ -33,7 +34,9 @@ class TodayHrvNumberCard extends StatelessWidget {
33 Expanded( 34 Expanded(
34 child: _NumberItem( 35 child: _NumberItem(
35 label: context.l10n.restingHeartRate, 36 label: context.l10n.restingHeartRate,
36 - value: controller.restingHeartRate.value, 37 + value: controller.v2HealthData.value?.lastRestingHrValue
  38 + ?.toString() ??
  39 + '--',
37 unit: 'bpm', 40 unit: 'bpm',
38 ), 41 ),
39 ), 42 ),
1 import 'dart:async'; 1 import 'dart:async';
  2 +import 'dart:convert';
  3 +import 'package:doublefeel_flutter/core/config/app_environment_config.dart';
2 import 'package:doublefeel_flutter/core/constants/app_const.dart'; 4 import 'package:doublefeel_flutter/core/constants/app_const.dart';
  5 +import 'package:doublefeel_flutter/core/logging/app_logger.dart';
  6 +import 'package:doublefeel_flutter/data/models/local/user_preferences.dart';
3 import 'package:doublefeel_flutter/l10n/gen/app_localizations.dart'; 7 import 'package:doublefeel_flutter/l10n/gen/app_localizations.dart';
4 import 'package:doublefeel_flutter/pigeon/platform_api.g.dart'; 8 import 'package:doublefeel_flutter/pigeon/platform_api.g.dart';
5 import 'package:flutter/widgets.dart'; 9 import 'package:flutter/widgets.dart';
@@ -177,11 +181,7 @@ class LoginController extends GetxController { @@ -177,11 +181,7 @@ class LoginController extends GetxController {
177 181
178 Future<void> _loginWithApple(AppleSignInModel appleModel) async { 182 Future<void> _loginWithApple(AppleSignInModel appleModel) async {
179 isLoggingIn.value = true; 183 isLoggingIn.value = true;
180 - final loginRes = await _userApi.loginWithApple(  
181 - identityToken: appleModel.identityToken!,  
182 - email: appleModel.email,  
183 - nickname: appleModel.nickname,  
184 - ); 184 + final loginRes = await _userApi.loginWithApple(appleModel);
185 185
186 if (loginRes is AppSuccess<LoginResponse>) { 186 if (loginRes is AppSuccess<LoginResponse>) {
187 await _handleLoginSuccess(loginRes.data, isApple: true); 187 await _handleLoginSuccess(loginRes.data, isApple: true);
@@ -246,6 +246,24 @@ class LoginController extends GetxController { @@ -246,6 +246,24 @@ class LoginController extends GetxController {
246 vip: vip, 246 vip: vip,
247 ); 247 );
248 248
  249 + try {
  250 + if (Get.isRegistered<AppEnvironmentConfig>()) {
  251 + var env = Get.find<AppEnvironmentConfig>();
  252 + await PlatformHostApi().updateLoginInfo(
  253 + jsonEncode(UserPreferences(
  254 + accessToken: accessToken,
  255 + meUserInfo: me,
  256 + partnerUserInfo: partner,
  257 + vipInfo: vip != null
  258 + ? UserPreferencesVipInfo.fromVipInfo(vip)
  259 + : null,
  260 + )),
  261 + env.serverBaseUrl);
  262 + }
  263 + } on Exception catch (e) {
  264 + AppLogger.e(e);
  265 + }
  266 +
249 await _userStateService.onLogin(); 267 await _userStateService.onLogin();
250 268
251 // 用户登录成功即代表同意了协议,持久化到本地供冷启动时 SDK 初始化判断使用 269 // 用户登录成功即代表同意了协议,持久化到本地供冷启动时 SDK 初始化判断使用
@@ -4,6 +4,7 @@ import '../../result/safe_call.dart'; @@ -4,6 +4,7 @@ import '../../result/safe_call.dart';
4 import '../../../data/models/enums/app_enums.dart'; 4 import '../../../data/models/enums/app_enums.dart';
5 import '../../../data/models/health/health_models.dart'; 5 import '../../../data/models/health/health_models.dart';
6 import '../../../data/models/health/health_upload_models.dart'; 6 import '../../../data/models/health/health_upload_models.dart';
  7 +import '../../../data/models/health/health_v2_models.dart';
7 import '../api_paths.dart'; 8 import '../api_paths.dart';
8 import '../dio_client.dart'; 9 import '../dio_client.dart';
9 10
@@ -248,4 +249,92 @@ class HealthApi { @@ -248,4 +249,92 @@ class HealthApi {
248 errorHandlingPolicy: errorHandlingPolicy, 249 errorHandlingPolicy: errorHandlingPolicy,
249 ); 250 );
250 } 251 }
  252 +
  253 + /// V2
  254 +
  255 + Future<AppResult<V2LatestHrvData>> getV2LatestHrv(
  256 + int? friendUserId,
  257 + int intDate,
  258 + ) {
  259 + return safeCall(
  260 + call: () async {
  261 + final response =
  262 + await _dioClient.dio.get(ApiPaths.v2LastestHrv, queryParameters: {
  263 + if (friendUserId != null) 'query_user_id': friendUserId,
  264 + 'date': intDate,
  265 + });
  266 + return V2LatestHrvData.fromJson(response.data as Map<String, dynamic>);
  267 + },
  268 + errorHandlingPolicy: HttpErrorHandlingPolicy.defaultPolicy,
  269 + );
  270 + }
  271 +
  272 + Future<AppResult<V2HealthData>> getV2HealthData(
  273 + int? friendUserId,
  274 + int intDate,
  275 + ) {
  276 + return safeCall(
  277 + call: () async {
  278 + final response =
  279 + await _dioClient.dio.get(ApiPaths.v2LastestData, queryParameters: {
  280 + if (friendUserId != null) 'query_user_id': friendUserId,
  281 + 'date': intDate,
  282 + });
  283 + return V2HealthData.fromJson(response.data as Map<String, dynamic>);
  284 + },
  285 + errorHandlingPolicy: HttpErrorHandlingPolicy.defaultPolicy,
  286 + );
  287 + }
  288 +
  289 + Future<AppResult<V2StressScore>> getV2StressScore(
  290 + int? friendUserId,
  291 + int intDate,
  292 + ) {
  293 + return safeCall(
  294 + call: () async {
  295 + final response =
  296 + await _dioClient.dio.get(ApiPaths.v2StressScore, queryParameters: {
  297 + if (friendUserId != null) 'query_user_id': friendUserId,
  298 + 'date': intDate,
  299 + });
  300 + return V2StressScore.fromJson(response.data as Map<String, dynamic>);
  301 + },
  302 + errorHandlingPolicy: HttpErrorHandlingPolicy.defaultPolicy,
  303 + );
  304 + }
  305 +
  306 + Future<AppResult<V2HrvTrendData>> getV2HrvTrend(
  307 + int? friendUserId,
  308 + int intDate,
  309 + ) {
  310 + return safeCall(
  311 + call: () async {
  312 + final response =
  313 + await _dioClient.dio.get(ApiPaths.v2HrvTrend, queryParameters: {
  314 + if (friendUserId != null) 'query_user_id': friendUserId,
  315 + 'date': intDate,
  316 + });
  317 + return V2HrvTrendData.fromJson(response.data as Map<String, dynamic>);
  318 + },
  319 + errorHandlingPolicy: HttpErrorHandlingPolicy.defaultPolicy,
  320 + );
  321 + }
  322 +
  323 + Future<AppResult<V2RealtimeStressData>> getV2RealtimeStress(
  324 + int? friendUserId,
  325 + int intDate,
  326 + ) {
  327 + return safeCall(
  328 + call: () async {
  329 + final response = await _dioClient.dio
  330 + .get(ApiPaths.v2RealtimeStress, queryParameters: {
  331 + if (friendUserId != null) 'query_user_id': friendUserId,
  332 + 'date': intDate,
  333 + });
  334 + return V2RealtimeStressData.fromJson(
  335 + response.data as Map<String, dynamic>);
  336 + },
  337 + errorHandlingPolicy: HttpErrorHandlingPolicy.defaultPolicy,
  338 + );
  339 + }
251 } 340 }
  1 +import 'package:doublefeel_flutter/pigeon/platform_api.g.dart';
  2 +
1 import '../../constants/app_const.dart'; 3 import '../../constants/app_const.dart';
2 import '../../error/http_error_handling_policy.dart'; 4 import '../../error/http_error_handling_policy.dart';
3 import '../../result/app_result.dart'; 5 import '../../result/app_result.dart';
@@ -42,20 +44,17 @@ class UserApi { @@ -42,20 +44,17 @@ class UserApi {
42 ); 44 );
43 } 45 }
44 46
45 - Future<AppResult<LoginResponse>> loginWithApple({  
46 - required String identityToken,  
47 - String? email,  
48 - String? nickname,  
49 - }) { 47 + Future<AppResult<LoginResponse>> loginWithApple(AppleSignInModel appleModel) {
50 return safeCall( 48 return safeCall(
51 call: () async { 49 call: () async {
52 final response = await _dioClient.dio.post( 50 final response = await _dioClient.dio.post(
53 ApiPaths.userLogin, 51 ApiPaths.userLogin,
54 data: AppleLoginRequest( 52 data: AppleLoginRequest(
55 - identityToken: identityToken,  
56 - email: email,  
57 - nickname: nickname,  
58 - ).toJson(), 53 + identityToken: appleModel.identityToken ?? '',
  54 + email: appleModel.email ?? '',
  55 + nickname: appleModel.nickname ?? '',
  56 + userId: appleModel.userId)
  57 + .toJson(),
59 options: _dioClient.noTokenOptions(), 58 options: _dioClient.noTokenOptions(),
60 ); 59 );
61 return LoginResponse.fromJson(response.data as Map<String, dynamic>); 60 return LoginResponse.fromJson(response.data as Map<String, dynamic>);
@@ -29,6 +29,13 @@ abstract final class ApiPaths { @@ -29,6 +29,13 @@ abstract final class ApiPaths {
29 static const healthStatsHrv = '/client/doublefeel/health/statistics/hrv/'; 29 static const healthStatsHrv = '/client/doublefeel/health/statistics/hrv/';
30 static const healthPulse = '/client/doublefeel/health/pulse/'; 30 static const healthPulse = '/client/doublefeel/health/pulse/';
31 static const healthPulseLatest = '/client/doublefeel/health/pulse/latest/'; 31 static const healthPulseLatest = '/client/doublefeel/health/pulse/latest/';
  32 + //Health v2
  33 + static const v2LastestHrv = '/client/doublefeel/health/v2/lastest_hrv/';
  34 + static const v2LastestData = '/client/doublefeel/health/v2/health_data/';
  35 + static const v2StressScore = '/client/doublefeel/health/v2/stress_score/';
  36 + static const v2HrvTrend = '/client/doublefeel/health/v2/hrv_trend/';
  37 + static const v2RealtimeStress =
  38 + '/client/doublefeel/health/v2/realtime_stress/';
32 39
33 // Pay 40 // Pay
34 static const paymentProducts = '/client/doublefeel/payment/products/'; 41 static const paymentProducts = '/client/doublefeel/payment/products/';
  1 +int? _parseInt(dynamic value) {
  2 + if (value == null) return null;
  3 + if (value is int) return value;
  4 + if (value is double) return value.toInt();
  5 + if (value is String) return int.tryParse(value);
  6 + return null;
  7 +}
  8 +
  9 +double? _parseDouble(dynamic value) {
  10 + if (value == null) return null;
  11 + if (value is num) return value.toDouble();
  12 + if (value is String) {
  13 + if (value == 'NaN') return double.nan;
  14 + if (value == 'Infinity') return double.infinity;
  15 + if (value == '-Infinity') return double.negativeInfinity;
  16 + return double.tryParse(value);
  17 + }
  18 + return null;
  19 +}
  20 +
  21 +/// v2/lastest_hrv/ response
  22 +class V2LatestHrvData {
  23 + const V2LatestHrvData(
  24 + {this.latestHrv, this.latestDataTime, this.state, this.tip});
  25 +
  26 + final double? latestHrv;
  27 + final int? latestDataTime;
  28 + final int? state;
  29 + final String? tip;
  30 +
  31 + factory V2LatestHrvData.fromJson(Map<String, dynamic> json) {
  32 + return V2LatestHrvData(
  33 + latestHrv: _parseDouble(json['latest_hrv']),
  34 + latestDataTime: _parseInt(json['latest_data_time']),
  35 + state: _parseInt(json['state']),
  36 + tip: json['tip_text'],
  37 + );
  38 + }
  39 +}
  40 +
  41 +/// v2/health_data/ response
  42 +class V2HealthData {
  43 + const V2HealthData({
  44 + this.hrvAvg,
  45 + this.lastRestingHrValue,
  46 + this.hrAvg,
  47 + this.move,
  48 + this.exercise,
  49 + this.stand,
  50 + this.steps,
  51 + this.sleepDuration,
  52 + this.sleepScore,
  53 + this.sleepState,
  54 + });
  55 +
  56 + final int? hrvAvg;
  57 + final int? lastRestingHrValue;
  58 + final int? hrAvg;
  59 + final int? move;
  60 + final int? exercise;
  61 + final int? stand;
  62 + final int? steps;
  63 + final int? sleepDuration;
  64 + final double? sleepScore;
  65 + final int? sleepState;
  66 +
  67 + factory V2HealthData.fromJson(Map<String, dynamic> json) {
  68 + return V2HealthData(
  69 + hrvAvg: _parseInt(json['hrv_avg']),
  70 + lastRestingHrValue: _parseInt(json['last_resting_hr_value']),
  71 + hrAvg: _parseInt(json['hr_avg']),
  72 + move: _parseInt(json['move']),
  73 + exercise: _parseInt(json['exercise']),
  74 + stand: _parseInt(json['stand']),
  75 + steps: _parseInt(json['steps']),
  76 + sleepDuration: _parseInt(json['sleep_duration']),
  77 + sleepScore: _parseDouble(json['sleep_score']),
  78 + sleepState: _parseInt(json['sleep_state']),
  79 + );
  80 + }
  81 +
  82 + Map<String, dynamic> toJson() {
  83 + final val = <String, dynamic>{};
  84 + if (hrvAvg != null) val['hrv_avg'] = hrvAvg;
  85 + if (lastRestingHrValue != null) {
  86 + val['last_resting_hr_value'] = lastRestingHrValue;
  87 + }
  88 + if (hrAvg != null) val['hr_avg'] = hrAvg;
  89 + if (move != null) val['move'] = move;
  90 + if (exercise != null) val['exercise'] = exercise;
  91 + if (stand != null) val['stand'] = stand;
  92 + if (steps != null) val['steps'] = steps;
  93 + if (sleepDuration != null) val['sleep_duration'] = sleepDuration;
  94 + if (sleepScore != null) val['sleep_score'] = sleepScore;
  95 + if (sleepState != null) val['sleep_state'] = sleepState;
  96 + return val;
  97 + }
  98 +}
  99 +
  100 +/// v2/stress_score/ response
  101 +class V2StressScore {
  102 + const V2StressScore({
  103 + this.state,
  104 + this.hrvScore,
  105 + this.hrScore,
  106 + this.latestHr,
  107 + this.comprehensiveScore,
  108 + });
  109 +
  110 + final int? state;
  111 + final int? hrvScore;
  112 + final int? hrScore;
  113 + final int? latestHr;
  114 + final int? comprehensiveScore;
  115 +
  116 + factory V2StressScore.fromJson(Map<String, dynamic> json) {
  117 + return V2StressScore(
  118 + state: _parseInt(json['state']),
  119 + hrvScore: _parseInt(json['hrv_score']),
  120 + hrScore: _parseInt(json['hr_score']),
  121 + latestHr: _parseInt(json['latest_hr']),
  122 + comprehensiveScore: _parseInt(json['comprehensive_score']),
  123 + );
  124 + }
  125 +
  126 + Map<String, dynamic> toJson() {
  127 + final val = <String, dynamic>{};
  128 + if (state != null) val['state'] = state;
  129 + if (hrvScore != null) val['hrv_score'] = hrvScore;
  130 + if (hrScore != null) val['hr_score'] = hrScore;
  131 + if (latestHr != null) val['latest_hr'] = latestHr;
  132 + if (comprehensiveScore != null) {
  133 + val['comprehensive_score'] = comprehensiveScore;
  134 + }
  135 + return val;
  136 + }
  137 +}
  138 +
  139 +/// Single item in v2/hrv_trend/ list
  140 +class V2HrvTrendItem {
  141 + const V2HrvTrendItem({this.time, this.rawHrv, this.trendHrv, this.state});
  142 +
  143 + final int? time;
  144 + final double? rawHrv;
  145 + final int? trendHrv;
  146 + final int? state;
  147 +
  148 + factory V2HrvTrendItem.fromJson(Map<String, dynamic> json) {
  149 + return V2HrvTrendItem(
  150 + time: _parseInt(json['time']),
  151 + rawHrv: _parseDouble(json['raw_hrv']),
  152 + trendHrv: _parseInt(json['trend_hrv']),
  153 + state: _parseInt(json['state']),
  154 + );
  155 + }
  156 +
  157 + Map<String, dynamic> toJson() {
  158 + final val = <String, dynamic>{};
  159 + if (time != null) val['time'] = time;
  160 + if (rawHrv != null) val['raw_hrv'] = rawHrv;
  161 + if (trendHrv != null) val['trend_hrv'] = trendHrv;
  162 + if (state != null) val['state'] = state;
  163 + return val;
  164 + }
  165 +}
  166 +
  167 +/// v2/hrv_trend/ response
  168 +class V2HrvTrendData {
  169 + const V2HrvTrendData({this.list});
  170 +
  171 + final List<V2HrvTrendItem>? list;
  172 +
  173 + factory V2HrvTrendData.fromJson(Map<String, dynamic> json) {
  174 + return V2HrvTrendData(
  175 + list: (json['list'] as List<dynamic>?)
  176 + ?.map((e) => V2HrvTrendItem.fromJson(e as Map<String, dynamic>))
  177 + .toList(),
  178 + );
  179 + }
  180 +
  181 + Map<String, dynamic> toJson() {
  182 + final val = <String, dynamic>{};
  183 + if (list != null) {
  184 + val['list'] = list!.map((e) => e.toJson()).toList();
  185 + }
  186 + return val;
  187 + }
  188 +}
  189 +
  190 +/// Single item in v2/realtime_stress/ list
  191 +class V2RealtimeStressItem {
  192 + const V2RealtimeStressItem({this.time, this.value, this.state});
  193 +
  194 + final int? time;
  195 + final int? value;
  196 + final int? state;
  197 +
  198 + factory V2RealtimeStressItem.fromJson(Map<String, dynamic> json) {
  199 + return V2RealtimeStressItem(
  200 + time: _parseInt(json['time']),
  201 + value: _parseInt(json['value']),
  202 + state: _parseInt(json['state']),
  203 + );
  204 + }
  205 +
  206 + Map<String, dynamic> toJson() {
  207 + final val = <String, dynamic>{};
  208 + if (time != null) val['time'] = time;
  209 + if (value != null) val['value'] = value;
  210 + if (state != null) val['state'] = state;
  211 + return val;
  212 + }
  213 +}
  214 +
  215 +/// v2/realtime_stress/ response
  216 +class V2RealtimeStressData {
  217 + const V2RealtimeStressData({this.list});
  218 +
  219 + final List<V2RealtimeStressItem>? list;
  220 +
  221 + factory V2RealtimeStressData.fromJson(Map<String, dynamic> json) {
  222 + return V2RealtimeStressData(
  223 + list: (json['list'] as List<dynamic>?)
  224 + ?.map((e) => V2RealtimeStressItem.fromJson(e as Map<String, dynamic>))
  225 + .toList(),
  226 + );
  227 + }
  228 +
  229 + Map<String, dynamic> toJson() {
  230 + final val = <String, dynamic>{};
  231 + if (list != null) {
  232 + val['list'] = list!.map((e) => e.toJson()).toList();
  233 + }
  234 + return val;
  235 + }
  236 +}
@@ -22,17 +22,20 @@ class AppleLoginRequest { @@ -22,17 +22,20 @@ class AppleLoginRequest {
22 const AppleLoginRequest({ 22 const AppleLoginRequest({
23 this.loginType = 'apple', 23 this.loginType = 'apple',
24 required this.identityToken, 24 required this.identityToken,
  25 + required this.userId,
25 this.email, 26 this.email,
26 this.nickname, 27 this.nickname,
27 }); 28 });
28 29
29 final String loginType; 30 final String loginType;
30 final String identityToken; 31 final String identityToken;
  32 + final String userId;
31 final String? email; 33 final String? email;
32 final String? nickname; 34 final String? nickname;
33 35
34 Map<String, dynamic> toJson() => { 36 Map<String, dynamic> toJson() => {
35 'login_type': loginType, 37 'login_type': loginType,
  38 + 'apple_user_id': userId,
36 'identity_token': identityToken, 39 'identity_token': identityToken,
37 if (email != null) 'email': email, 40 if (email != null) 'email': email,
38 if (nickname != null) 'nickname': nickname, 41 if (nickname != null) 'nickname': nickname,