Commit 1e225e34da6a3a0f188c699a6c5ec20fe605802e

Authored by 常守达
1 parent 99691eb0

fix(ui): 下发会员优惠

... ... @@ -5,6 +5,7 @@ import 'package:get/get.dart';
import '../../core/config/app_environment_config.dart';
import '../../core/error/app_error_handler.dart';
import '../../core/network/api/config_api.dart';
import '../../core/services/app_config_service.dart';
import '../../core/network/api/health_api.dart';
import '../../core/network/api/interaction_api.dart';
import '../../core/network/api/obs_api.dart';
... ... @@ -89,9 +90,10 @@ void registerPayDeps(DioClient dioClient) {
Get.lazyPut(() => PayApi(dioClient), fenix: true);
}
/// Remote config API.
/// Remote config API + global AppConfig service.
void registerConfigDeps(DioClient dioClient) {
Get.lazyPut(() => ConfigApi(dioClient), fenix: true);
Get.put(AppConfigService(Get.find<ConfigApi>()), permanent: true);
}
/// Watch theme API.
... ...
import 'dart:async';
import 'package:flutter/widgets.dart';
import 'package:get/get.dart';
... ... @@ -5,6 +7,7 @@ import '../../core/config/app_environment_config.dart';
import '../../core/logging/app_logger.dart';
import '../../core/network/user_agent_provider.dart';
import '../../core/services/user_state_service.dart';
import '../../core/services/app_config_service.dart';
import '../../data/local/local_storage.dart';
import '../../data/local/user_account_storage.dart';
import '../../data/local/user_preferences_storage.dart';
... ... @@ -41,6 +44,7 @@ abstract final class AppBootstrap {
if (local.termsAgreed) {
await Get.find<UserStateService>().initializeSdks();
unawaited(AppConfigService.to.fetch());
}
}
}
... ...
... ... @@ -4,6 +4,7 @@ import 'package:doublefeel_flutter/app/modules/home/widgets/today/hrv_measuremen
import 'package:doublefeel_flutter/app/modules/report_common/models/report_period.dart';
import 'package:doublefeel_flutter/app/modules/report_common/widgets/report_bottom_slogan.dart';
import 'package:doublefeel_flutter/app/modules/report_common/widgets/report_date_picker_sheet.dart';
import 'package:doublefeel_flutter/core/services/app_config_service.dart';
import 'package:doublefeel_flutter/core/services/thinking_data_service.dart';
import 'package:doublefeel_flutter/core/services/user_state_service.dart';
... ... @@ -214,7 +215,8 @@ class TodayTabBody extends StatelessWidget {
: const SizedBox.shrink(),
),
Obx(() {
return Get.find<UserStateService>().isVip
return Get.find<UserStateService>().isVip ||
!AppConfigService.to.config.showYearVipDiscount
? const SizedBox.shrink()
: GestureDetector(
onTap: () {
... ... @@ -744,7 +746,8 @@ class _TopDateBar extends StatelessWidget {
],
const Spacer(),
Obx(() {
return Get.find<UserStateService>().isVip
return Get.find<UserStateService>().isVip ||
!AppConfigService.to.config.showYearVipDiscount
? const SizedBox.shrink()
: GestureDetector(
onTap: () {
... ...
... ... @@ -3,6 +3,8 @@ import 'dart:convert';
import 'package:doublefeel_flutter/core/config/app_environment_config.dart';
import 'package:doublefeel_flutter/core/constants/app_const.dart';
import 'package:doublefeel_flutter/core/logging/app_logger.dart';
import 'package:doublefeel_flutter/core/network/api/config_api.dart';
import 'package:doublefeel_flutter/core/services/app_config_service.dart';
import 'package:doublefeel_flutter/core/services/thinking_data_service.dart';
import 'package:doublefeel_flutter/data/models/local/user_preferences.dart';
import 'package:doublefeel_flutter/l10n/gen/app_localizations.dart';
... ... @@ -81,6 +83,7 @@ class LoginController extends GetxController {
ta.track('enter_doublefeel_welcome_page');
needTrackWelcomepage = false;
}
unawaited(AppConfigService.to.fetch());
} catch (e) {
AppLogger.e('initializeSdks error: $e');
}
... ...
... ... @@ -6,6 +6,7 @@ import 'package:doublefeel_flutter/core/constants/intent_keys.dart';
import 'package:doublefeel_flutter/core/network/api/pay_api.dart';
import 'package:doublefeel_flutter/core/network/api/vip_api.dart';
import 'package:doublefeel_flutter/core/result/app_result.dart';
import 'package:doublefeel_flutter/core/services/app_config_service.dart';
import 'package:doublefeel_flutter/core/services/thinking_data_service.dart';
import 'package:doublefeel_flutter/core/util/app_toast.dart';
import 'package:doublefeel_flutter/data/local/user_preferences_storage.dart';
... ... @@ -91,7 +92,8 @@ class PurchaseController extends GetxController {
void onBackPressed() {
if (isFromOnboard) {
if (Get.arguments?['is_membership_offer'] == true) {
if (Get.arguments?['is_membership_offer'] == true ||
!AppConfigService.to.config.showYearVipDiscount) {
Get.offAllNamed(AppRoutes.home, arguments: {'isFromOnboard': true});
} else {
Get.offNamed(
... ...
... ... @@ -22,4 +22,18 @@ class ConfigApi {
},
);
}
Future<AppResult<AppConfig>> getAppConfigList() {
return safeCall(
call: () async {
final response = await _dioClient.dio.get(
ApiPaths.appConfig,
options: _dioClient.noTokenOptions(),
);
return AppConfig.fromJson(
response.data as Map<String, dynamic>,
);
},
);
}
}
... ...
... ... @@ -58,6 +58,7 @@ abstract final class ApiPaths {
// Config
static const dynamicConfigList = '/client/doublefeel/dynamic_config/list/';
static const appConfig = '/client/doublefeel/app_config/';
static const contentCheckText = '/client/doublefeel/content_check/text/';
// OBS
... ...
import 'package:doublefeel_flutter/core/result/app_result.dart';
import 'package:get/get.dart';
import '../logging/app_logger.dart';
import '../network/api/config_api.dart';
import '../../data/models/config/config_models.dart';
/// Globally accessible service that fetches and caches [AppConfig] from the
/// server. Registered with [permanent: true] so GetX never disposes it.
///
/// Usage (anywhere in the app):
/// ```dart
/// // One-off read
/// final show = AppConfigService.to.config.showYearVipDiscount;
///
/// // Reactive (inside Obx / ever)
/// Obx(() => AppConfigService.to.config.showYearVipDiscount ? ... : ...)
/// ```
class AppConfigService extends GetxService {
AppConfigService(this._configApi);
static AppConfigService get to => Get.find();
final ConfigApi _configApi;
final Rx<AppConfig> _config = const AppConfig().obs;
/// Latest remote [AppConfig]. Defaults to [AppConfig()] until [fetch] succeeds.
AppConfig get config => _config.value;
/// Reactive [AppConfig] stream — use inside `Obx` / `ever`.
Rx<AppConfig> get configRx => _config;
/// Fetches [AppConfig] from the server and updates [config].
/// Safe to call multiple times; failures are logged and ignored.
Future<void> fetch() async {
final res = await _configApi.getAppConfigList();
switch (res) {
case AppSuccess(:final data):
{
_config.value = data;
AppLogger.i('AppConfigService: config fetched — $data');
}
break;
case AppFailure():
{
AppLogger.w('AppConfigService: fetch failed', res.error);
}
break;
}
}
}
... ...
... ... @@ -51,3 +51,17 @@ class ConfigData {
return val;
}
}
class AppConfig {
const AppConfig({
this.showYearVipDiscount = true,
});
final bool showYearVipDiscount;
factory AppConfig.fromJson(Map<String, dynamic> json) {
return AppConfig(
showYearVipDiscount: json['show_year_vip_discount'] as bool,
);
}
}
... ...