import 'package:dio/dio.dart'; import 'package:flutter_patcher/flutter_patcher.dart'; import 'package:logger/logger.dart'; import 'package:path_provider/path_provider.dart'; import 'dart:io'; /// 应用版本信息模型 class AppVersionInfo { final String version; final int versionCode; final String updateType; final bool forceUpdate; final String updateDesc; final String? patchUrl; final String? patchMd5; final String? apkUrl; final String? apkMd5; AppVersionInfo({ required this.version, required this.versionCode, required this.updateType, required this.forceUpdate, required this.updateDesc, this.patchUrl, this.patchMd5, this.apkUrl, this.apkMd5, }); factory AppVersionInfo.fromJson(Map json) { final patchData = json['patch'] as Map?; final fullApkData = json['fullApk'] as Map?; return AppVersionInfo( version: json['version'] ?? '', versionCode: json['versionCode'] ?? 0, updateType: json['updateType'] ?? 'full', forceUpdate: json['forceUpdate'] ?? false, updateDesc: json['updateDesc'] ?? '', patchUrl: patchData?['patchUrl'] as String?, patchMd5: patchData?['md5'] as String?, apkUrl: fullApkData?['apkUrl'] as String?, apkMd5: fullApkData?['md5'] as String?, ); } } /// 版本检查服务 class VersionCheckService { static final VersionCheckService _instance = VersionCheckService._internal(); factory VersionCheckService() => _instance; VersionCheckService._internal(); final Logger _logger = Logger(); final Dio _dio = Dio(BaseOptions( connectTimeout: const Duration(seconds: 10), receiveTimeout: const Duration(seconds: 10), )); static const String apiUrl = 'http://8.159.134.0:8012/api/update/check'; static const String _patchVersionFileName = '.patch_version'; /// 检查版本更新 Future checkUpdate({ required String currentVersion, required int currentVersionCode, }) async { try { _logger.i('🔍 检查版本更新'); _logger.i('📱 APK 自带版本: $currentVersion ($currentVersionCode)'); // 从文件读取已应用的补丁版本 final patchInfo = await _loadPatchVersionInfo(); final appliedPatchVersion = patchInfo['version'] as String?; final appliedPatchVersionCode = patchInfo['versionCode'] as int?; _logger.i('📂 读取到的补丁版本: $appliedPatchVersion ($appliedPatchVersionCode)'); // 如果打过补丁,使用补丁的版本;否则使用 APK 自带版本 final requestVersion = appliedPatchVersion ?? currentVersion; final requestVersionCode = appliedPatchVersionCode ?? currentVersionCode; _logger.i('🚀 请求后端版本: $requestVersion ($requestVersionCode)'); // 🔥 打印完整的请求参数 _logger.i('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'); _logger.i('📤 App 发送的请求参数:'); _logger.i(' URL: $apiUrl'); _logger.i(' version: $requestVersion'); _logger.i(' versionCode: $requestVersionCode'); _logger.i('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'); final response = await _dio.get( apiUrl, queryParameters: { 'version': requestVersion, 'versionCode': requestVersionCode, }, ); // 🔥 打印完整的后端返回数据 _logger.i('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'); _logger.i('📥 后端返回的完整数据:'); _logger.i(' statusCode: ${response.statusCode}'); _logger.i(' 原始响应: ${response.data}'); _logger.i('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'); if (response.statusCode == 200) { final data = response.data['data']; if (data['hasUpdate'] == false) { _logger.i('✅ 已是最新版本'); return null; } final versionInfo = AppVersionInfo.fromJson(data); // 🔥 关键修复:无论更新类型是什么,只要本地已应用此版本,就跳过 if (appliedPatchVersion != null && appliedPatchVersion == versionInfo.version) { _logger.i('✅ 本地已应用版本 ${versionInfo.version},跳过更新(updateType: ${versionInfo.updateType})'); return null; } // 🔥 额外检查:如果本地补丁版本code >= 服务端返回的versionCode,也跳过 if (appliedPatchVersionCode != null && appliedPatchVersionCode >= versionInfo.versionCode) { _logger.i('✅ 本地补丁版本code ($appliedPatchVersionCode) >= 服务端versionCode (${versionInfo.versionCode}),跳过更新'); return null; } _logger.i('✅ 发现新版本: ${versionInfo.version}'); _logger.i('🔄 更新类型: ${versionInfo.updateType}'); _logger.i('⚠️ 强制更新: ${versionInfo.forceUpdate}'); return versionInfo; } return null; } catch (e, stack) { _logger.e('❌ 版本检查失败: $e'); _logger.e('❌ 堆栈信息: $stack'); throw Exception('网络请求失败: $e'); } } /// 下载并应用差量补丁 Future applyPatch({ required String patchUrl, required String version, required int targetVersionCode, String? md5, Function(double)? onProgress, }) async { try { _logger.i('⬇️ 开始下载补丁: $patchUrl'); _logger.i('🎯 目标版本: $version ($targetVersionCode)'); final tempDir = await getTemporaryDirectory(); final patchPath = '${tempDir.path}/update_patch.so'; await _dio.download( patchUrl, patchPath, onReceiveProgress: (received, total) { if (total != -1 && onProgress != null) { onProgress(received / total); } }, ); _logger.i('✅ 补丁下载完成,开始应用...'); final result = await FlutterPatcher.applyPatch( PatchInfo( version: version, patchUrl: 'file://$patchPath', targetVersionCode: targetVersionCode, md5: md5 ?? '', ), ); if (result.ok) { _logger.i('✅ 补丁应用成功'); // 保存补丁版本号到文件 await _savePatchVersionInfo(version, targetVersionCode); // 验证保存 final verifyInfo = await _loadPatchVersionInfo(); _logger.i('💾 已保存并验证: ${verifyInfo['version']} (${verifyInfo['versionCode']})'); } else { _logger.e('❌ 补丁应用失败: ${result.error}'); } return result.ok; } catch (e) { _logger.e('❌ 应用补丁异常: $e'); return false; } } /// 下载完整 APK Future downloadApk(String apkUrl, Function(double) onProgress) async { try { _logger.i('⬇️ 开始下载 APK: $apkUrl'); final cacheDir = await getApplicationCacheDirectory(); final apkPath = '${cacheDir.path}/app-update.apk'; _logger.i('📁 APK 下载路径: $apkPath'); await _dio.download( apkUrl, apkPath, onReceiveProgress: (received, total) { if (total != -1) { onProgress(received / total); } }, ); _logger.i('✅ APK 下载完成: $apkPath'); return apkPath; } catch (e) { _logger.e('❌ 下载 APK 失败: $e'); return null; } } /// 回滚到内置版本 Future rollback() async { try { _logger.i('🔄 执行回滚操作'); await FlutterPatcher.rollback(); _logger.i('✅ 回滚成功'); } catch (e) { _logger.e('❌ 回滚失败: $e'); } } /// 清除补丁版本信息(整包更新后调用) Future clearPatchVersionInfo() async { try { final directory = await getApplicationSupportDirectory(); final file = File('${directory.path}/$_patchVersionFileName'); if (await file.exists()) { await file.delete(); _logger.i('🗑️ 已清除补丁版本记录'); } } catch (e) { _logger.e('❌ 清除补丁版本信息失败: $e'); } } /// 保存补丁版本信息到文件 Future _savePatchVersionInfo(String version, int versionCode) async { try { final directory = await getApplicationSupportDirectory(); final file = File('${directory.path}/$_patchVersionFileName'); final content = '$version|$versionCode'; await file.writeAsString(content); // 🔥 立即验证写入 final verifyContent = await file.readAsString(); _logger.i('💾 补丁版本已保存到: ${file.path}'); _logger.i('💾 写入内容: $content'); _logger.i('💾 验证读取: $verifyContent'); _logger.i('💾 文件存在: ${await file.exists()}'); } catch (e) { _logger.e('❌ 保存补丁版本信息失败: $e'); } } /// 从文件加载补丁版本信息 Future> _loadPatchVersionInfo() async { try { final directory = await getApplicationSupportDirectory(); final file = File('${directory.path}/$_patchVersionFileName'); _logger.i('📂 尝试加载补丁版本文件: ${file.path}'); _logger.i('📂 文件存在: ${await file.exists()}'); if (await file.exists()) { final content = await file.readAsString(); _logger.i('📂 文件内容: $content'); final parts = content.split('|'); if (parts.length == 2) { final version = parts[0]; final versionCode = int.tryParse(parts[1]); if (versionCode != null) { _logger.i('📂 解析成功: version=$version, versionCode=$versionCode'); return {'version': version, 'versionCode': versionCode}; } else { _logger.e('📂 versionCode 解析失败: ${parts[1]}'); } } else { _logger.e('📂 文件格式错误,parts.length=${parts.length}'); } } _logger.i('📂 未找到有效的补丁版本信息'); return {'version': null, 'versionCode': null}; } catch (e) { _logger.e('❌ 加载补丁版本信息失败: $e'); return {'version': null, 'versionCode': null}; } } }