import 'dart:async'; import 'dart:convert'; import 'package:crypto/crypto.dart'; import 'package:device_info_plus/device_info_plus.dart'; import 'package:flutter/foundation.dart'; import 'package:shared_preferences/shared_preferences.dart'; import '../adjust/adjust_events.dart'; import '../api/api_client.dart'; import '../api/api_config.dart'; import '../api/proxy_client.dart'; import '../api/services/user_api.dart'; import '../log/app_logger.dart'; import '../referrer/referrer_service.dart'; import '../user/user_state.dart'; /// 认证服务:APP 启动时执行快速登录 class AuthService { AuthService._(); static final _log = AppLogger('AuthService'); static Future? _loginFuture; /// 登录完成后的 Future,需鉴权接口应 await 此 Future 再请求 static Future get loginComplete => _loginFuture ?? Future.value(); static void _logMsg(String msg) { _log.d(msg); } /// 获取设备 ID(Android: androidId, iOS: identifierForVendor, Web: fallback) static Future _getDeviceId() async { final deviceInfo = DeviceInfoPlugin(); switch (defaultTargetPlatform) { case TargetPlatform.android: final android = await deviceInfo.androidInfo; return android.id; case TargetPlatform.iOS: final ios = await deviceInfo.iosInfo; return ios.identifierForVendor ?? 'ios-unknown'; default: return 'device-${DateTime.now().millisecondsSinceEpoch}'; } } /// 计算 sign:MD5(deviceId) 大写 32 位 static String _computeSign(String deviceId) { final bytes = utf8.encode(deviceId); final digest = md5.convert(bytes); return digest.toString().toUpperCase(); } /// 将 common_info 响应保存到全局,并解析 surge 中的 enable_third_party_payment static void _saveCommonInfoToState(Map data) { final reveal = data['reveal'] as int?; if (reveal != null) UserState.setCredits(reveal); final realm = data['realm'] as String?; if (realm != null && realm.isNotEmpty) UserState.setAvatar(realm); final terminal = data['terminal'] as String?; if (terminal != null && terminal.isNotEmpty) UserState.setUserName(terminal); final navigate = data['navigate'] as String?; if (navigate != null) UserState.setNavigate(navigate); final surgeStr = data['surge'] as String?; if (surgeStr != null && surgeStr.isNotEmpty) { try { final surge = json.decode(surgeStr) as Map?; if (surge != null) { final enable = surge['enable_third_party_payment'] as bool?; UserState.setEnableThirdPartyPayment(enable); } } catch (e) { _logMsg('surge JSON 解析失败: $e'); } } } /// APP 启动时调用快速登录 /// 启动时网络可能未就绪,会延迟后重试 static Future init() async { if (_loginFuture != null) return _loginFuture!; final completer = Completer(); _loginFuture = completer.future; _logMsg('init: 开始快速登录'); const maxRetries = 3; const retryDelay = Duration(seconds: 2); try { // 等待网络就绪(浏览器能访问但 App 报错时,多为启动时网络未初始化) await Future.delayed(const Duration(seconds: 2)); final deviceId = await _getDeviceId(); _logMsg('init: deviceId=$deviceId'); final sign = _computeSign(deviceId); _logMsg('init: sign=$sign'); final crest = await ReferrerService.getReferrer(); if (crest != null && crest.isNotEmpty) { _logMsg('init: crest(referrer)=$crest'); } ApiResponse? res; for (var i = 0; i < maxRetries; i++) { if (i > 0) { _logMsg('init: 第 ${i + 1} 次重试,等待 ${retryDelay.inSeconds}s...'); await Future.delayed(retryDelay); } try { res = await UserApi.fastLogin( origin: deviceId, resolution: sign, digest: crest ?? '', crest: crest, ); break; } catch (e) { _logMsg('init: 第 ${i + 1} 次请求失败: $e'); if (i == maxRetries - 1) rethrow; } } if (res == null) return; _logMsg('init: 登录结果 code=${res.code} msg=${res.msg}'); if (res.isSuccess && res.data != null) { final data = res.data as Map?; final token = data?['reevaluate'] as String?; if (token != null && token.isNotEmpty) { ApiClient.instance.setUserToken(token); _logMsg('init: 已设置 userToken'); } else { _logMsg('init: 响应中无 reevaluate (userToken)'); } final prefs = await SharedPreferences.getInstance(); final hadLoggedIn = prefs.getBool('adjust_has_logged_in') ?? false; if (!hadLoggedIn) { AdjustEvents.trackRegister(); await prefs.setBool('adjust_has_logged_in', true); await prefs.setString( 'adjust_register_date', DateTime.now().toIso8601String().substring(0, 10), ); _logMsg('init: 首次登录,已上报 register'); } final credits = data?['reveal'] as int?; if (credits != null) { UserState.setCredits(credits); _logMsg('init: 已同步积分 $credits'); } final uid = data?['asset'] as String?; if (uid != null && uid.isNotEmpty) { UserState.setUserId(uid); _logMsg('init: 已设置 userId'); } final avatarUrl = data?['realm'] as String?; if (avatarUrl != null && avatarUrl.isNotEmpty) { UserState.setAvatar(avatarUrl); } final name = data?['terminal'] as String?; if (name != null && name.isNotEmpty) { UserState.setUserName(name); } final countryCode = data?['navigate'] as String?; if (countryCode != null) { UserState.setNavigate(countryCode); } // 3. 归因上报(digest 从 Adjust 取,暂无则用 install referrer) try { final referrerRes = await UserApi.referrer( sentinel: ApiConfig.appId, asset: uid!, digest: crest ?? '', origin: deviceId, ); if (referrerRes.isSuccess) { _logMsg('referrer 上报成功'); } else { _logMsg( 'referrer 上报失败: code=${referrerRes.code} msg=${referrerRes.msg}'); } } catch (e) { _logMsg('referrer 请求异常: $e'); } // 4. 获取用户通用信息,保存到全局并解析 surge try { final commonRes = await UserApi.getCommonInfo( sentinel: ApiConfig.appId, asset: uid, ); if (commonRes.isSuccess && commonRes.data != null) { final commonData = commonRes.data as Map?; if (commonData != null) { _saveCommonInfoToState(commonData); _logMsg('common_info 已保存到全局'); } _logMsg('common_info 响应:'); logWithEmbeddedJson(json.encode(commonRes.data)); } else { _logMsg( 'common_info 失败: code=${commonRes.code} msg=${commonRes.msg}'); } } catch (e) { _logMsg('common_info 请求异常: $e'); } } else { _logMsg('init: 登录失败'); } } catch (e, st) { _logMsg('init: 异常 $e'); _logMsg('init: 堆栈 $st'); } finally { if (!completer.isCompleted) completer.complete(); } } }