import 'dart:async'; import 'dart:io'; import 'package:get/get.dart'; import 'package:flutter/material.dart'; import 'package:flutter/foundation.dart'; import 'package:logger/web.dart'; import 'package:get_storage/get_storage.dart'; import 'package:device_info_plus/device_info_plus.dart'; import 'package:crypto/crypto.dart'; // 用于生成设备指纹 import 'dart:convert'; import '../../../data/services/db/sqflite_api.dart'; import 'googleLogin.dart'; import 'weixinLogin.dart'; import 'appleLogin.dart'; import '../../../routes/app_routes.dart'; import '../../../data/models/appconfig.dart'; import '../../../data/models/appconfig_model.dart'; import '../../../data/services/network/api.dart'; import '../../../../data/models/user_Info.dart'; import '../../../data/models/channel_app_model.dart'; import '../../../core/utils/device_brand.dart'; import '../../../data/services/version_update_service.dart'; import '../../../modules/common/widgets/privacy_policy_dialog.dart'; import '../constants/country_codes.dart'; class LoginController extends GetxController { static LoginController get to => Get.find(); final GetStorage _storage = GetStorage(); // 添加游客登录显示状态 final _isGuestLoginVisible = false.obs; bool get isGuestLoginVisible => _isGuestLoginVisible.value; String _deviceId = ''; String _deviceModel = ''; String _appVersion = ''; // APP版本号 String _countryCode = ''; // 国家代码 String _languageCode = ''; // 语言代码 UserChannel _userChannel = UserChannel.OFFICIAL; // 默认渠道 final isChinaMainland = false.obs; //是否中国大陆地区 final _hasAcceptedPrivacy = false.obs; //是否同意隐私政策 bool get hasAcceptedPrivacy => _hasAcceptedPrivacy.value; set hasAcceptedPrivacy(bool value) => _hasAcceptedPrivacy.value = value; // 添加国家编码相关状态 final _selectedCountryCode = ''.obs; final _selectedCountryFlag = '✉️'.obs; // 国家编码 Getter String get selectedCountryCode => _selectedCountryCode.value; String get selectedCountryFlag => _selectedCountryFlag.value; // 预设国家编码列表 final List> countryCodes = kCountryCodes; // 初始化默认选择的国家代码 void _initDefaultCountryCode() { if (isChinaMainland.value) { // 中国大陆用户默认选择+86 _selectedCountryCode.value = '+86'; _selectedCountryFlag.value = '🇨🇳'; loginType = 1; // 设置为手机号登录类型 } else { // 非中国大陆用户默认选择邮箱 _selectedCountryCode.value = ''; _selectedCountryFlag.value = '✉️'; loginType = 0; // 设置为邮箱登录类型 } } // 状态管理 final _isLoading = false.obs; final _countdownSeconds = 0.obs; int loginType = 0; // Getters bool get isLoading => _isLoading.value; int get countdownSeconds => _countdownSeconds.value; final versionService = VersionUpdateService.to; // 文本控制器 - 延迟初始化 TextEditingController? _contactController; TextEditingController? _verificationCodeController; // Getter 方法,确保控制器存在 TextEditingController get contactController { _contactController ??= TextEditingController(); return _contactController!; } TextEditingController get verificationCodeController { _verificationCodeController ??= TextEditingController(); return _verificationCodeController!; } @override void onInit() { super.onInit(); // 初始化控制器 _contactController = TextEditingController(); _verificationCodeController = TextEditingController(); _initDeviceInfo(); _initAppInfo(); // 新增:初始化APP信息 _checkGuestLoginConfig(); // 检查游客登录配置 final channel = versionService.cachedChannel; isChinaMainland.value = Get.deviceLocale?.countryCode?.toUpperCase() == 'CN' && channel != UserChannel.GLOBAL_GOOGLE; //是否中国大陆地区 // 初始化默认国家代码 _initDefaultCountryCode(); // 隐私政策是否要重新确认,改由 _initDeviceInfo() 按设备品牌决定(见下方注释): // 单包发五家商店后,这里已经拿不到"用户从哪个商店下载"的信息了。 } @override void onClose() { // 安全地销毁控制器 try { _contactController?.dispose(); _contactController = null; } catch (e) { Logger().e('销毁 contactController 失败: $e'); } try { _verificationCodeController?.dispose(); _verificationCodeController = null; } catch (e) { Logger().e('销毁 verificationCodeController 失败: $e'); } super.onClose(); } // 检查游客登录配置:入口显隐由后台「渠道分发」的游客登录开关决定,可限定只对某个版本生效。 // // 判定结果由服务端算好(见 comm.ChannelApp.ShowTourists),这里只取结论,不做版本比较。 // 必须 await 版本检查:splash 那次 checkForUpdate 是异步发起的,登录页可能先于它建好, // 直接读 cachedShowTourists 会拿到初值 false 而把入口永久藏掉。checkForUpdate 内有 10 // 分钟缓存,重复调用不会多发一次请求。 // // 失败方向刻意偏向隐藏:拿不到配置(无网/接口报错)时 cachedShowTourists 为 false, // 入口不显示。反过来默认显示会在送审期把不该露出的入口露给审核员。 Future _checkGuestLoginConfig() async { try { await versionService.checkForUpdate(); } catch (e) { Logger().e('获取游客登录配置失败,按隐藏处理: $e'); } _isGuestLoginVisible.value = versionService.cachedShowTourists; } // 显示隐私政策弹窗 void _showPrivacyDialog() { PrivacyPolicyDialog.show( onAccept: () { _storage.write('hasAcceptedPrivacy', true); // 存储用户已接受隐私政策 _hasAcceptedPrivacy.value = true; Get.back(); }, onReject: () => exit(0), canDismiss: false, // 禁止通过返回键关闭弹窗 ); } // 初始化设备信息 Future _initDeviceInfo() async { try { DeviceInfoPlugin deviceInfo = DeviceInfoPlugin(); if (GetPlatform.isAndroid) { AndroidDeviceInfo androidInfo = await deviceInfo.androidInfo; _deviceId = androidInfo.id; _deviceModel = androidInfo.model; // 华为/荣耀商店要求每次启动都重新确认隐私政策,所以这两个品牌上不恢复 // 本地的"已同意"标记,登录页的勾选框保持未勾选。 // // 判断依据从"打包渠道"换成了"设备品牌":五家商店共用一个包之后已经无从 // 得知用户是从哪个商店下载的。代价是华为商店的包装在别家手机上不再每次 // 重弹(审核实测都在对应品牌真机上做,能过审),反向则多弹一次。 // // 另一个好处是不再依赖 VersionUpdateService:那边的 cachedChannel 在版本 // 检查跑完前会回退成 GLOBAL_GOOGLE,无网启动时华为包反而会走"恢复已同意" // 分支,等于合规逻辑失效。设备品牌本地就能读到,不看网络也不看初始化顺序。 if (!DeviceBrand.isHuaweiOrHonor( androidInfo.brand, androidInfo.manufacturer)) { _hasAcceptedPrivacy.value = _storage.read('hasAcceptedPrivacy') ?? false; } } else if (GetPlatform.isIOS) { IosDeviceInfo iosInfo = await deviceInfo.iosInfo; //print('lxm---iosInfo: $iosInfo.toString()'); final utsname = iosInfo.utsname; // 包含更详细的设备信息 //print('lxm---utsname: $utsname'); final machineId = utsname.machine.toLowerCase(); // 例如: "iPhone14,2" _deviceModel = machineId; Logger().d('lxm---machineId: $machineId'); final systemVersion = utsname.release; // 例如: "16.4.1" Logger().d('lxm---systemVersion: $systemVersion'); try { // 组合 machine 和 systemVersion 生成指纹 final fingerprint = '$machineId|$systemVersion'; final hashedFingerprint = md5.convert(utf8.encode(fingerprint)).toString(); Logger() .d('生成设备指纹: $hashedFingerprint (基于 $machineId 和 $systemVersion)'); _deviceId = hashedFingerprint; } catch (e) { Logger().e('生成设备指纹失败: $e'); _deviceId = iosInfo.identifierForVendor ?? 'unknown'; } _hasAcceptedPrivacy.value = _storage.read('hasAcceptedPrivacy') ?? false; } } catch (e) { Logger().e('获取设备信息失败: $e'); _deviceId = 'unknown'; } } // 新增:初始化APP信息(版本号、渠道、国家代码) Future _initAppInfo() async { try { _appVersion = versionService.cachedAppVersion; Logger().d('appVersion: $_appVersion'); // 获取国家代码 _countryCode = Get.locale?.countryCode ?? 'Unknown'; Logger().d('countryCode: $_countryCode'); // 获取语言代码 _languageCode = Get.locale?.languageCode ?? 'Unknown'; Logger().d('languageCode: $_languageCode'); // 获取用户渠道(根据平台和设备信息确定) _userChannel = versionService.cachedChannel; Logger().d('userChannel: ${_userChannel.value}'); } catch (e) { Logger().e('获取APP信息失败: $e'); _appVersion = 'Unknown'; _countryCode = 'Unknown'; _languageCode = 'Unknown'; _userChannel = UserChannel.UNKNOWN; } } // 更新选中的国家编码 void updateCountryCode(String code, String flag) { _selectedCountryCode.value = code; _selectedCountryFlag.value = flag; // 根据选择更新输入框类型 if (code == '') { contactController.clear(); // 清空当前输入 loginType = 0; // 设置为邮箱登录类型 } else { loginType = 1; // 设置为手机号登录类型 } _countdownSeconds.value = 0; } void _syncContactLoginType() { loginType = _selectedCountryCode.value.isEmpty ? 0 : 1; } // 获取验证码 void getVerificationCode() { _syncContactLoginType(); if (contactController.text.isEmpty) { Get.snackbar( 'tip'.tr, //提示 'pleaseEnterContactInfo'.tr, //请输入手机号或邮箱 snackPosition: SnackPosition.TOP, backgroundColor: Colors.red.withOpacity(0.1), colorText: Colors.red, ); return; } // 根据登录类型验证格式 if (loginType == 0 && !isEmailContactController()) { Get.snackbar( 'tip'.tr, 'invalidEmailFormat'.tr, snackPosition: SnackPosition.TOP, backgroundColor: Colors.red.withOpacity(0.1), colorText: Colors.red, ); return; } if (loginType == 1 && !isPhoneContactController()) { Get.snackbar( 'tip'.tr, 'invalidPhoneFormat'.tr, snackPosition: SnackPosition.TOP, backgroundColor: Colors.red.withOpacity(0.1), colorText: Colors.red, ); return; } _sendVerificationCode(); } // 开始倒计时 void _startCountdown() { _countdownSeconds.value = 60; Future.doWhile(() async { await Future.delayed(const Duration(seconds: 1)); _countdownSeconds.value--; return _countdownSeconds.value > 0; }); } // 发送验证码 Future _sendVerificationCode() async { // 实际发送验证码的逻辑 if (isEmailContactController() || isPhoneContactController()) { _startCountdown(); Logger().d(contactController.text); var response = await Api.getCode({ "addr": selectedCountryCode + contactController.text, "vtype": loginType, }); if (response != null) { Logger().d("请求后台数据验证码params=$response"); //短信已发送 //EasyLoading.showInfo('codeSend'.tr); } } else { Get.snackbar( 'tip'.tr, //提示 'pleaseEnterContactInfo'.tr, //请输入手机号或邮箱 snackPosition: SnackPosition.TOP, backgroundColor: Colors.red.withOpacity(0.1), colorText: Colors.red, ); return; } } // 检查邮箱格式 bool isEmailContactController() { // 检查每个元素是否为单个字母或数字字符 final text = contactController.text.trim(); if (text.length < 6) return false; // 更严格的邮箱验证 final emailRegex = RegExp(r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'); return emailRegex.hasMatch(text); } // 检查手机号码格式 bool isPhoneContactController() { // 检查每个元素是否为单个字母或数字字符 final text = contactController.text.trim(); if (text.isEmpty) return false; // 根据国家代码验证手机号长度 // 长度检查 if (text.length < 7 || text.length > 15) return false; // 必须是纯数字 if (!RegExp(r'^[0-9]+$').hasMatch(text)) return false; // 特定国家的特殊验证 switch (_selectedCountryCode.value) { case '+86': // 中国 return text.length == 11 && text.startsWith('1'); case '+1': // 美国/加拿大 return text.length == 10; case '+81': // 日本 return text.length == 10 || text.length == 11; default: return true; // 其他国家只做基本验证 } } // 检查验证码是否完整 bool isVerificationCodeComplete() { // 检查每个元素是否为单个字母或数字字符 final text = verificationCodeController.text; Logger().d("数据获取Facebook${text.length}"); return text.length == 4 && RegExp(r'^[0-9]+$').hasMatch(text); } // 登录 Future login({bool fromSocial = false}) async { if (!fromSocial) { _syncContactLoginType(); } // 添加基础验证 if (loginType == 0 || loginType == 1) { // 邮箱或手机号登录 if (contactController.text.isEmpty) { Get.snackbar( 'error'.tr, 'pleaseEnterContactInfo'.tr, snackPosition: SnackPosition.TOP, backgroundColor: Colors.red.withOpacity(0.1), colorText: Colors.red, ); return; } // 验证邮箱格式 if (loginType == 0 && !isEmailContactController()) { Get.snackbar( 'error'.tr, 'invalidEmailFormat'.tr, snackPosition: SnackPosition.TOP, backgroundColor: Colors.red.withOpacity(0.1), colorText: Colors.red, ); return; } // 验证手机号格式 if (loginType == 1 && !isPhoneContactController()) { Get.snackbar( 'error'.tr, 'invalidPhoneFormat'.tr, snackPosition: SnackPosition.TOP, backgroundColor: Colors.red.withOpacity(0.1), colorText: Colors.red, ); return; } } //验证隐私协议勾选状态 if (!_hasAcceptedPrivacy.value) { _showPrivacyDialog(); return; } _isLoading.value = true; // 模拟验证过程 // Future.delayed(const Duration(seconds: 2), () { // _isLoading.value = false; // // 验证成功,跳转到配对页面 // Get.offAllNamed(Routes.pairing); // }); try { switch (loginType) { case 0: //邮箱登录 { if (!isVerificationCodeComplete()) { Get.snackbar( 'tip'.tr, 'pleaseEnterCompleteCode'.tr, snackPosition: SnackPosition.TOP, backgroundColor: Colors.red.withOpacity(0.1), colorText: Colors.red, ); break; } await requestLogin({ "mail": contactController.text, "phone": "", "stype": loginType, "vcode": verificationCodeController.text, "ttoken": "", "phonemodel": "", "phonemac": _deviceId, "name": "", "avatar": "", }); break; } case 1: //手机号登录 { if (!isVerificationCodeComplete()) { Get.snackbar( 'tip'.tr, 'pleaseEnterVerificationCode'.tr, snackPosition: SnackPosition.TOP, backgroundColor: Colors.red.withOpacity(0.1), colorText: Colors.red, ); break; } await requestLogin({ "mail": "", "phone": selectedCountryCode + contactController.text, "stype": loginType, "vcode": verificationCodeController.text, "ttoken": "", "phonemodel": "", "phonemac": _deviceId, "name": "", "avatar": "", }); break; } case 4: //Facebook登录(已移除) { Logger().d("Facebook登录已移除"); break; } case 3: //Google登录 { Logger().d("数据获取_Google()"); final google = Google(); bool authResult = await google.signInWithGoogle(); Logger().d("数据获取_Google().token${authResult}"); if (authResult) { Logger().d("数据获取_Google.token${google.token}"); Logger().d("数据获取_Google.pictureurl${google.pictureurl}"); Logger().d("数据获取_Google.email${google.email}"); Logger().d("数据获取_Google.email${google.displayName}"); // 验证成功,跳转到配对页面 // SettingsController().userName = Google().displayName!; // //SettingsController().avatar = Google().email; // SettingsController().userId = Google().id!; await requestLogin({ "mail": google.email, "phone": "", "stype": loginType, "vcode": "", "ttoken": google.token, "phonemodel": "", "phonemac": _deviceId, "name": google.displayName, "avatar": google.pictureurl, }); } break; } case 2: //微信登录 { Logger().d("数据获取_FlWX()"); bool authResult = await FlWX().authBy( NormalAuth( scope: "snsapi_userinfo", state: "wechat_sdk_demo_test", ), getUserInfo: true, // 按需获取用户信息 ); if (authResult) { // 确保 FlWX() 返回的是同一个实例,并且 token 已经被设置 // 这里假设 FlWX().token 在 authBy 成功后被设置 Logger().d("数据获取_FlWX().token${FlWX().token}"); // SettingsController().userName = FlWX().nickname; // SettingsController().avatar = FlWX().headimgurl; // SettingsController().userId = FlWX().openid; await requestLogin({ "mail": "", "phone": "", "stype": loginType, "vcode": "", "ttoken": FlWX().code, "phonemodel": "", "phonemac": _deviceId, "name": FlWX().nickname, "avatar": FlWX().headimgurl, }); // 验证成功,跳转到配对页面 } break; } case 5: //Apple登录 { Logger().d("开始Apple登录流程"); final apple = Apple(); // 先检查环境(调试用) if (kDebugMode) { await Apple.checkAppleSignInEnvironment(); } bool authResult = await apple.signInWithApple(); if (authResult) { Logger().d("Apple登录成功"); Logger().d("认证方式: ${apple.getAuthMethodName()}"); Logger().d("Token: ${apple.token}"); Logger().d("Email: ${apple.email}"); Logger().d("Name: ${apple.name}"); Logger().d("ID: ${apple.id}"); // 获取登录数据并发送到后端 final loginData = apple.getLoginData(_deviceId); Logger().d("发送登录数据: $loginData"); // 直接调用现有的requestLogin方法 await requestLogin(loginData); } else { Logger().e("Apple登录失败"); Get.snackbar( 'error'.tr, 'Apple登录失败,请重试', snackPosition: SnackPosition.TOP, backgroundColor: Colors.red.withValues(alpha: 0.1), colorText: Colors.red, ); } break; } case 6: //游客登录 { // 走服务端 api_sgin 的 Tourists 分支拿真实账号与 JWT。 // 早先这里是本地伪造会话(token 形如 guest-xxx),网关解不出 uid, // 游客一个后端请求都发不出去——实名认证接口自然也调不通, // 而「实名后就能用功能」这个需求要求游客必须能调后端。 // phonemac 是服务端按设备找回同一个游客账号的唯一依据,不能为空。 // _initDeviceInfo() 在 onInit 里是不等待的,用户手快时可能还没跑完, // 这里补一次——拿不到设备 id 就建不成能找回的账号,宁可不让登。 if (_deviceId.isEmpty) await _initDeviceInfo(); if (_deviceId.isEmpty) { Get.snackbar( 'error'.tr, 'loginFailed'.tr, snackPosition: SnackPosition.TOP, backgroundColor: Colors.red.withOpacity(0.1), colorText: Colors.red, ); break; } await requestLogin({ "mail": "", "phone": "", "stype": loginType, "vcode": "", "ttoken": "", "phonemodel": "", "phonemac": _deviceId, "name": "", "avatar": "", }); break; } default: { Get.snackbar( 'error'.tr, 'loginFailed'.tr, snackPosition: SnackPosition.TOP, backgroundColor: Colors.red.withOpacity(0.1), colorText: Colors.red, ); break; } } } catch (e) { Get.snackbar( 'error'.tr, 'loginFailed'.tr, snackPosition: SnackPosition.TOP, backgroundColor: Colors.red.withOpacity(0.1), colorText: Colors.red, ); } finally { _isLoading.value = false; } } // 使用登陆请求服务器 Future requestLogin(Map params) async { try { Logger().d("请求后台数据login=$params"); Logger().d('lxm---phonemodel: $_deviceModel'); // 在Api.login调用时添加超时 var response = await Api.login({ "mail": params["mail"], "phone": params["phone"], "stype": params["stype"], "vcode": params["vcode"], "ttoken": params["ttoken"], "phonemodel": _deviceModel, "phonemac": params["phonemac"], "name": params["name"], "avatar": params["avatar"], "language": _languageCode, "channel": _userChannel, "version": _appVersion, }).timeout( const Duration(seconds: 30), // 30秒超时 onTimeout: () { throw TimeoutException('登录请求超时', const Duration(seconds: 30)); }, ); if (response != null) { Logger().d("请求后台数据login回复数据=$response"); await _storage.write('loginInfo', jsonEncode(response)); try { Logger().d('响应数据: ${response.toString()}'); if (response['token'].toString() != '') { final user = User.fromJson(response); Logger().d('用户初始化成功: ${response['token'].toString()}'); await _storage.write("logintoken", response['token'].toString()); await _storage.write('user_info', user.toJson()); // 记下这次是不是游客登录:改走服务端之后游客拿到的是正经雪花 uid 和 // JWT,光看 uid/token 已经认不出游客了。每次登录都会覆写, // 所以游客之后改用手机号登录,这个标记会自动清掉。 await _storage.write(User.kLoginIsTourist, params["stype"] == 6); // 获取app的配置信息 await getAppConfig(); // 会议模板不挡登录:它只被会议模板页读取(落 sqlite), // await 它等于让用户在登录按钮上多等一次网络往返才跳主页。 // 与 splash 的处理一致。 unawaited(_initMeetingTemplate()); // 登录成功 → 直接进入首页 Get.offAllNamed(Routes.home); } else { throw Exception('接口返回错误: ${response['msg']}'); } } catch (e, stack) { Logger().d('用户数据处理失败: $e'); Logger().d('堆栈跟踪: $stack'); Get.snackbar( 'error'.tr, 'userDataParseFailed'.tr, // 用户数据解析失败 snackPosition: SnackPosition.TOP, backgroundColor: Colors.red.withOpacity(0.1), colorText: Colors.red, ); } } else { // API返回null的情况 Get.snackbar( 'error'.tr, 'networkError'.tr, // 网络错误,请重试 snackPosition: SnackPosition.TOP, backgroundColor: Colors.red.withOpacity(0.1), colorText: Colors.red, ); } } catch (e) { // 网络请求异常 Logger().e('登录请求失败: $e'); Get.snackbar( 'error'.tr, 'loginFailed'.tr, // 登录失败 snackPosition: SnackPosition.TOP, backgroundColor: Colors.red.withOpacity(0.1), colorText: Colors.red, ); } finally { // 确保无论成功还是失败都重置加载状态 _isLoading.value = false; } } // 使用密码登录 void usePasswordLogin() { // 这里可以实现密码登录的逻辑 Get.snackbar( '提示', '密码登录功能尚未实现', snackPosition: SnackPosition.TOP, backgroundColor: Colors.blue.withOpacity(0.1), colorText: Colors.blue, ); } Future getAppConfig() async { try { Logger().d('getAppConfig:进来了'); //if (User.getname() != "") { final response = await Api.getappconfig(); Logger().d('getAppConfig: $response'); if (response == null) return false; Logger().d('getAppConfig:进来了'); // 根据API设计调整 final resp = UserGetAppConfigResp.fromJson(response); // 详细记录解析后的配置信息 Logger().d('getAppConfig: 配置解析成功'); Logger().d('getAppConfig: 环境变量数量 = ${resp.env.length}'); Logger().d('getAppConfig: 智能体数量 = ${resp.agents.length}'); Logger().d('getAppConfig: MCP服务器数量 = ${resp.mcps.length}'); Logger().d('getAppConfig: 产品设备数量 = ${resp.products.length}'); // 详细记录产品设备信息 if (resp.products.isNotEmpty) { Logger().d('getAppConfig: ========== 产品设备详细信息 =========='); for (int i = 0; i < resp.products.length; i++) { final product = resp.products[i]; Logger().d( 'getAppConfig: 产品[$i] ID=${product.id}, 设备名称="${product.devicename}"'); Logger().d( 'getAppConfig: 产品[$i] 厂商ID=${product.factoryid}, 设备类型=${product.devicetype}'); Logger().d('getAppConfig: 产品[$i] 扫描UUID="${product.scanuuid}"'); Logger().d( 'getAppConfig: 产品[$i] 产品图片="${product.productimage ?? "未设置"}"'); Logger().d('getAppConfig: 产品[$i] 版本="${product.version ?? "未设置"}"'); Logger().d( 'getAppConfig: 产品[$i] 更新描述="${product.updatedescription ?? "未设置"}"'); Logger().d( 'getAppConfig: 产品[$i] 更新包地址="${product.updatepackageaddress ?? "未设置"}"'); Logger().d('getAppConfig: ----------------------------------------'); } Logger().d('getAppConfig: ========== 产品设备信息结束 =========='); } else { Logger().w('getAppConfig: 警告 - 未获取到任何产品设备信息'); } // 记录环境变量信息 if (resp.env.isNotEmpty) { Logger().d('getAppConfig: ========== 环境变量信息 =========='); resp.env.forEach((key, value) { Logger().d('getAppConfig: 环境变量 $key = "$value"'); }); Logger().d('getAppConfig: ========== 环境变量信息结束 =========='); } AppConfig.initialize(resp); Logger().d( 'getAppConfig: AppConfig初始化完成,产品数量验证 = ${AppConfig.config.products.length}'); // 验证AppConfig中的产品数据 final productNames = AppConfig.allproductNames(); final productUuids = AppConfig.allproductUuids(); Logger().d('getAppConfig: 可用产品名称列表 = $productNames'); Logger().d('getAppConfig: 可用产品UUID列表 = $productUuids'); Logger().d('getAppConfig: 应用配置获取和初始化成功完成'); return true; } catch (e) { Logger().d('getAppConfig 取失败: $e'); return false; } } Future _initMeetingTemplate() async { try { var response = await Api.getEchomeetTemplates(); if (response != null) { await SqfliteApi.initMeetingTemplate(response['templates']); } } catch (e) { Logger().d('initMeetingTemplate 获取失败: $e'); } } }