import 'dart:io'; import 'dart:math' as math; import 'package:get/get.dart'; import 'package:package_info_plus/package_info_plus.dart'; import 'package:url_launcher/url_launcher.dart'; import 'package:logger/logger.dart'; import 'package:device_info_plus/device_info_plus.dart'; import 'package:flutter_dotenv/flutter_dotenv.dart'; import '../models/channel_app_model.dart'; import 'network/api.dart'; /// 版本更新服务类 class VersionUpdateService extends GetxService { static VersionUpdateService get to => Get.find(); // 静态应用信息(内存缓存) static String? _appVersion = ""; //应用版本号 static String? _buildNumber = ""; //构建号 static String? _platform = ""; //平台类型 static String? _deviceBrand = ""; //设备品牌 static UserChannel? _channel; //渠道 static String? _latestVersion = ""; //最新版本号+构建号 static bool? _hasUpdate = false; //是否有更新 static bool? _isForceUpdate = false; //是否强制更新 static String? _updateUrl = ""; //更新链接 static AppInfo? _appInfo; //应用信息 static bool? _isShowTourists = false; //是否显示游客登录 static DateTime? _lastCheckTime; //上次检查时间 static const int _cacheIntervalMinutes = 10; // 可配置化 static final String? _countryCode = Get.deviceLocale?.countryCode; //国家地区编码 static final String? _languageCode = Get.deviceLocale?.languageCode; //语言编码 // ignore: non_constant_identifier_names static final String? _SERVER_URL = (_countryCode == 'CN') ? dotenv.env['SERVER_URL'] : dotenv.env['HW_SERVER_URL']; //服务器地址 // 添加 getter 方法来访问缓存的静态数据 /// 获取缓存的应用版本 String get cachedAppVersion => _appVersion ?? ''; /// 获取缓存的构建号 String get cachedBuildNumber => _buildNumber ?? ''; /// 获取缓存的平台信息 String get cachedPlatform => _platform ?? ''; /// 获取缓存的设备品牌 String get cachedDeviceBrand => _deviceBrand ?? ''; /// 获取缓存的渠道信息 UserChannel get cachedChannel => _channel ?? (_platform?.toLowerCase() == 'ios' ? UserChannel.GLOBAL_APPLE : UserChannel.GLOBAL_GOOGLE); /// 获取缓存的最新版本 String get cachedLatestVersion => _latestVersion ?? ''; /// 获取缓存的是否有更新 bool get cachedHasUpdate => _hasUpdate ?? false; /// 获取缓存的是否强制更新 bool get cachedIsForceUpdate => _isForceUpdate ?? false; /// 获取缓存的更新地址 String get cachedUpdateUrl => _updateUrl ?? ''; /// 获取缓存的应用信息 AppInfo? get cachedAppInfo => _appInfo; /// 获取缓存的游客登录状态 bool get cachedShowTourists => _isShowTourists ?? false; /// 获取上次检查时间 DateTime? get lastCheckTime => _lastCheckTime; /// 获取缓存的国家地区编码 String get cachedCountryCode => _countryCode ?? ''; /// 获取缓存的语言编码 String get cachedLanguageCode => _languageCode ?? ''; /// 获取缓存的服务器地址 String get cachedServerUrl => _SERVER_URL ?? ''; final Logger _logger = Logger(); // @override // void onInit() { // getCurrentVersion(); // getCurrentPlatformInfo(); // super.onInit(); // } /// 检查版本更新 Future checkForUpdate() async { if (_lastCheckTime != null && DateTime.now().difference(_lastCheckTime!).inMinutes < _cacheIntervalMinutes) { // [1]没有超过10分钟,返回缓存数据 _logger.d('没有超过10分钟,返回缓存数据'); return VersionUpdateInfo( hasUpdate: _hasUpdate ?? false, currentVersion: _appVersion ?? '1.0.0', latestVersion: (_latestVersion ?? '1.0.0').split('+')[0], appInfo: _appInfo, updateType: _hasUpdate! ? (_isForceUpdate! ? UpdateType.force : UpdateType.optional) : UpdateType.none, ); } try { final currentVersion = await getCurrentVersion(); final platformInfo = await getCurrentPlatformInfo(); _platform = platformInfo.platform; _channel = platformInfo.channel; _lastCheckTime = DateTime.now(); // 调用后端接口获取最新版本信息 final response = await Api.getChannelApp({'channel': platformInfo.channel.value}); _logger.d('lxm--获取渠道包信息:$response'); if (response == null || !response.containsKey('app')) { //[2]获取渠道包信息失败: 响应为空或不包含app字段 _logger.w('获取渠道包信息失败: 响应为空或不包含app字段'); return VersionUpdateInfo( hasUpdate: false, currentVersion: currentVersion, latestVersion: currentVersion, updateType: UpdateType.none, ); } //final channelAppResponse = ChannelAppResponse.fromJson(response); // 直接从response中获取app信息 final appInfo = AppInfo.fromJson(response['app']); _appInfo = appInfo; _logger.d('获取到的最新版本信息: ${appInfo.toJson()}'); _latestVersion = appInfo.version; _updateUrl = appInfo.address; // 设置游客登录显示状态(先用当前渠道结果作为回退,再用.env渠道覆盖以保持一致) _isShowTourists = appInfo.tourists; if (Platform.isAndroid && _channel != platformInfo.channel) { //[4]当前渠道与.env渠道不同,需要从.env渠道获取游客登录配置 await _refreshTouristsFromEnv(); } _logger.d('从服务器获取游客登录配置(最终): ${_isShowTourists}'); // if (channelAppResponse.code != 0 || channelAppResponse.data == null) { // _logger.w('获取版本信息失败: ${channelAppResponse.msg}'); // return VersionUpdateInfo( // hasUpdate: false, // currentVersion: currentVersion, // latestVersion: currentVersion, // updateType: UpdateType.none, // ); // } // final appInfo = channelAppResponse.data!.app; _hasUpdate = _compareVersions(currentVersion, appInfo.version); // 判断更新类型:如果主版本号不同则强制更新 UpdateType updateType = UpdateType.none; if (_hasUpdate!) { _isForceUpdate = _shouldForceUpdate(currentVersion, appInfo.version); updateType = _isForceUpdate! ? UpdateType.force : UpdateType.optional; } //[3]返回版本更新信息 return VersionUpdateInfo( hasUpdate: _hasUpdate!, currentVersion: currentVersion.split('+')[0], latestVersion: appInfo.version.split('+')[0], appInfo: appInfo, updateType: updateType, ); } catch (e) { _logger.e('检查版本更新失败: $e'); // 发生错误时,保持默认值 false _isShowTourists = false; final currentVersion = await getCurrentVersion(); return VersionUpdateInfo( hasUpdate: false, currentVersion: currentVersion, latestVersion: currentVersion, updateType: UpdateType.none, ); } } /// 获取当前应用版本信息 Future getCurrentVersion() async { try { final packageInfo = await PackageInfo.fromPlatform(); _appVersion = packageInfo.version; _buildNumber = packageInfo.buildNumber; return '${packageInfo.version}+${packageInfo.buildNumber}'; } catch (e) { _logger.e('获取当前版本信息失败: $e'); return '1.0.0'; } } /// 获取当前平台信息 Future getCurrentPlatformInfo() async { if (Platform.isIOS) { return const PlatformInfo( platform: 'ios', storeType: AppStoreType.appStore, channel: UserChannel.GLOBAL_APPLE, ); } if (Platform.isAndroid) { // 根据设备品牌判断应用商店类型 return await getAndroidStoreInfoByBrand(); } return const PlatformInfo( platform: 'unknown', storeType: AppStoreType.unknown, channel: UserChannel.UNKNOWN, ); } /// 根据设备品牌获取应用商店信息 /// /// 该方法根据设备品牌判断应用商店类型,并返回对应的 [PlatformInfo] 对象。 /// 如果品牌不在预设列表中,将使用环境变量 [Channel] 或默认值 [UserChannel.GLOBAL_GOOGLE]。 Future getAndroidStoreInfoByBrand() async { try { final deviceInfo = DeviceInfoPlugin(); final androidInfo = await deviceInfo.androidInfo; final brand = androidInfo.brand.toLowerCase(); _deviceBrand = brand; String channelFromEnv = dotenv.env['Channel'] ?? 'OFFICIAL'; Logger().d('channelFromEnv: $channelFromEnv'); _channel = UserChannel.fromName(channelFromEnv); UserChannel brandChannel; //品牌渠道,只用于请求版本更新信息 switch (brand) { case 'huawei': brandChannel = UserChannel.CHINA_HUAWEI; break; case 'xiaomi': case 'redmi': brandChannel = UserChannel.CHINA_XIAOMI; break; case 'oppo': case 'oneplus': brandChannel = UserChannel.CHINA_OPPO; break; case 'vivo': case 'iqoo': brandChannel = UserChannel.CHINA_VIVO; break; case 'honor': brandChannel = UserChannel.CHINA_HONOR; break; default: brandChannel = _channel ?? UserChannel.GLOBAL_GOOGLE; break; } return PlatformInfo( platform: 'android', storeType: AppStoreType.official, channel: brandChannel, ); } catch (e) { _logger.e('获取Android设备信息失败: $e'); return const PlatformInfo( platform: 'android', storeType: AppStoreType.googlePlay, channel: UserChannel.UNKNOWN, ); } } /// 从.env文件刷新游客登录配置 Future _refreshTouristsFromEnv() async { try { final envChannelName = dotenv.env['Channel']; if (envChannelName == null || envChannelName.isEmpty) { _logger.w('未配置.env Channel,沿用当前渠道的游客配置: ${_isShowTourists}'); return; } final envChannel = UserChannel.fromName(envChannelName); final resp = await Api.getChannelApp({'channel': envChannel.value}); if (resp != null && resp.containsKey('app')) { final envAppInfo = AppInfo.fromJson(resp['app']); _isShowTourists = envAppInfo.tourists; _logger.d( '游客登录配置已按.env渠道同步: ${envAppInfo.tourists} (channel=$envChannelName)'); } else { _logger.w('按.env渠道获取游客配置失败:响应为空或不含app字段'); } } catch (e) { _logger.w('按.env渠道同步游客配置失败: $e'); } } /// 判断是否需要强制更新 /// 当主版本号(第一位数字)不同时返回true bool _shouldForceUpdate(String current, String latest) { try { // 拆分版本号与构建号 final currentParts = current.split('+'); final latestParts = latest.split('+'); // 解析版本号 (major.minor.patch) final currentVersions = currentParts[0].split('.').map(int.parse).toList(); final latestVersions = latestParts[0].split('.').map(int.parse).toList(); // 比较主版本号(第一位) if (currentVersions.isNotEmpty && latestVersions.isNotEmpty) { if (currentVersions[0] != latestVersions[0]) { return true; } } // 检查是否有两个或更多的 + 号,如果有则强制更新 if (latest.split('+').length > 2) { return true; } return false; } catch (e) { _logger.e('判断强制更新失败: $e'); return false; } } /// 比较版本号 /// 返回true表示需要更新(远程版本更高) bool _compareVersions(String current, String latest) { // 拆分版本号与构建号 final currentParts = current.split('+')[0]; final latestParts = latest.split('+')[0]; // 解析版本号 (major.minor.patch) final currentVersions = currentParts.split('.').map(int.parse).toList(); final latestVersions = latestParts.split('.').map(int.parse).toList(); // 使用较小的长度进行比较,避免索引越界 final minLength = math.min(currentVersions.length, latestVersions.length); // 逐级比较版本号 for (var i = 0; i < minLength; i++) { if (latestVersions[i] > currentVersions[i]) return true; if (latestVersions[i] < currentVersions[i]) return false; } // 如果前面的版本号都相同,比较长度 // 更长的版本号被认为是更新的版本(如 1.0.1 > 1.0) return latestVersions.length > currentVersions.length; } /// 解析版本号字符串为数字数组 List _parseVersion(String version) { final parts = version.split('.'); final result = []; for (int i = 0; i < 3; i++) { if (i < parts.length) { result.add(int.tryParse(parts[i]) ?? 0); } else { result.add(0); } } return result; } /// 执行更新操作 Future performUpdate(VersionUpdateInfo updateInfo) async { //print('lxm---updateInfo详情:'); //print('lxm---hasUpdate: ${updateInfo.hasUpdate}'); //print('lxm---currentVersion: ${updateInfo.currentVersion}'); //print('lxm---latestVersion: ${updateInfo.latestVersion}'); //print('lxm---updateType: ${updateInfo.updateType}'); //print('lxm---appInfo: ${updateInfo.appInfo?.toJson()}'); if (!updateInfo.hasUpdate || updateInfo.appInfo == null) { return false; } try { _logger.d('lxm---执行更新操作,下载地址: ${updateInfo.appInfo!.address}'); return await _openDownloadUrl(updateInfo.appInfo!.address!); } catch (e) { _logger.e('执行更新失败: $e'); return false; } } /// 打开下载链接 Future _openDownloadUrl(String downloadUrl) async { return await _launchUrl(downloadUrl); } /// 启动URL Future _launchUrl(String url) async { try { final uri = Uri.parse(url); if (await canLaunchUrl(uri)) { return await launchUrl(uri, mode: LaunchMode.externalApplication); } return false; } catch (e) { _logger.e('启动URL失败: $e'); return false; } } }