import 'dart:async'; import 'package:flutter/foundation.dart'; import '../api/api_client.dart'; import '../api/proxy_client.dart'; import '../config/attribution_config.dart'; import '../config/ext_config_models.dart'; import '../config/ext_config_runtime.dart'; import '../config/video_home_runtime.dart'; import '../entities/user_entities.dart'; import 'adjust_service.dart'; import 'analytics_attribution_callbacks.dart'; import 'user_api.dart'; /// 认证服务回调 /// 用于在认证流程各阶段通知调用方 abstract class AuthServiceCallbacks { /// 获取设备 ID(由调用方实现) Future getDeviceId(); /// 计算签名(由调用方实现) String computeSign(String deviceId); /// 登录成功后回调 void onLoginSuccess(FastLoginResponse data) {} /// 通用信息获取后回调 void onCommonInfoLoaded(CommonInfoResponse data) {} /// 登录失败回调 void onLoginFailed(String msg) {} } /// APP 启动认证服务 /// 封装完整的启动登录流程,包括: /// 1. 快速登录 /// 2. 归因上报 /// 3. 获取通用信息 abstract class FrameworkAuthService { static AuthServiceCallbacks? _callbacks; static Future? _loginFuture; static bool _isInitialized = false; /// 登录是否已完成 static final ValueNotifier isLoginComplete = ValueNotifier(false); /// 最近一次快速登录成功时的 [FastLoginResponse.userId](失败或未登录为空)。 /// /// 宿主在调用需 `userId` 的接口(如生图 `create-task`)前可读取;若为空应提示用户稍后重试。 static String? lastLoggedInUserId; /// 登录完成后的 Future,需鉴权接口应 await 此 Future 再请求 static Future get loginComplete => _loginFuture ?? Future.value(); /// 初始化认证服务。 /// /// 默认注册 [AnalyticsAttributionCallbacks](与 Adjust 缓存一致)。可传入 /// [attributionCallbacks] 覆盖(例如自定义 referer 格式)。 static void init( AuthServiceCallbacks callbacks, { AttributionCallbacks? attributionCallbacks, }) { AttributionService.init( attributionCallbacks ?? AnalyticsAttributionCallbacks(), ); _callbacks = callbacks; _isInitialized = true; } /// 启动登录流程 /// [delaySeconds] 启动延迟秒数,默认 2 秒 /// [maxRetries] 最大重试次数,默认 3 次 /// [retryDelaySeconds] 重试延迟秒数,默认 2 秒 static Future start({ int delaySeconds = 2, int maxRetries = 3, int retryDelaySeconds = 2, }) async { if (!_isInitialized || _callbacks == null) { throw StateError( 'FrameworkAuthService not initialized. ' 'Call FrameworkAuthService.init(callbacks) first.', ); } if (_loginFuture != null) return _loginFuture!; final completer = Completer(); _loginFuture = completer.future; if (kDebugMode) { debugPrint('[AuthService] start: 开始登录流程'); } try { await Future.delayed(Duration(seconds: delaySeconds)); final deviceId = await _callbacks!.getDeviceId(); if (kDebugMode) { debugPrint('[AuthService] start: deviceId=$deviceId'); } final sign = _callbacks!.computeSign(deviceId); if (kDebugMode) { debugPrint('[AuthService] start: sign=$sign'); } final referer = await AttributionService.getReferrer(); if (kDebugMode && referer != null) { debugPrint('[AuthService] start: referer=$referer'); } // 确定归因类型 String? referrerType; final adjustReferrer = await AttributionService.getAdjustReferrer(); final fbReferrer = await AttributionService.getFacebookReferrer(); if (adjustReferrer != null && adjustReferrer.isNotEmpty) { referrerType = defaultTargetPlatform == TargetPlatform.iOS ? 'ios_adjust' : 'android_adjust'; } else if (fbReferrer != null && fbReferrer.isNotEmpty) { referrerType = 'fb'; } if (kDebugMode) { debugPrint('[AuthService] start: referrerType=$referrerType'); } // 尝试快速登录 EntityResponse? res; for (var i = 0; i < maxRetries; i++) { if (i > 0) { if (kDebugMode) { debugPrint('[AuthService] start: 第 ${i + 1} 次重试...'); } await Future.delayed(Duration(seconds: retryDelaySeconds)); } try { final cfg = ApiClient.instance.config; final appType = defaultTargetPlatform == TargetPlatform.iOS ? cfg.backendAppTypeIOS : cfg.backendAppTypeAndroid; res = await UserApi.fastLogin( deviceId: deviceId, sign: sign, referer: referer ?? '', app: appType, type: referrerType, ); break; } catch (e) { if (kDebugMode) { debugPrint('[AuthService] start: 第 ${i + 1} 次请求失败: $e'); } if (i == maxRetries - 1) rethrow; } } if (res == null) { lastLoggedInUserId = null; VideoHomeRuntime.reset(); ExtConfigRuntime.applyCommonInfoFailure(); completer.complete(); return; } if (kDebugMode) { debugPrint('[AuthService] start: 登录结果 code=${res.code} msg=${res.msg}'); } if (res.isSuccess && res.data != null) { final loginData = res.data!; final uid = loginData.userId?.trim(); lastLoggedInUserId = uid != null && uid.isNotEmpty ? uid : null; // 设置 Token if (loginData.userToken != null && loginData.userToken!.isNotEmpty) { ApiClient.instance.setUserToken(loginData.userToken!); if (kDebugMode) { debugPrint('[AuthService] start: 已设置 userToken'); } } // 回调登录成功 _callbacks!.onLoginSuccess(loginData); // 上报归因并获取通用信息 await _reportReferrersAndLoadCommonInfo( uid: loginData.userId ?? '', deviceId: deviceId, ); } else { lastLoggedInUserId = null; VideoHomeRuntime.reset(); ExtConfigRuntime.applyCommonInfoFailure(); _callbacks!.onLoginFailed(res.msg); } } catch (e, st) { lastLoggedInUserId = null; VideoHomeRuntime.reset(); ExtConfigRuntime.applyCommonInfoFailure(); if (kDebugMode) { debugPrint('[AuthService] start: 异常 $e\n$st'); } _callbacks!.onLoginFailed(e.toString()); } finally { if (!completer.isCompleted) { completer.complete(); isLoginComplete.value = true; } } } /// 上报归因并获取通用信息 static Future _reportReferrersAndLoadCommonInfo({ required String uid, required String deviceId, }) async { if (uid.isEmpty) { VideoHomeRuntime.reset(); ExtConfigRuntime.applyCommonInfoFailure(); if (kDebugMode) { debugPrint( '[AuthService] common_info: 跳过(userId 为空),已标记 common_info 失败', ); } return; } final config = ApiClient.instance.config; final backendApp = defaultTargetPlatform == TargetPlatform.iOS ? config.backendAppTypeIOS : config.backendAppTypeAndroid; // 上报 Adjust 归因 final adjustReferer = await AttributionService.getAdjustReferrer(); if (adjustReferer != null && adjustReferer.isNotEmpty) { final adjustType = defaultTargetPlatform == TargetPlatform.iOS ? 'ios_adjust' : 'android_adjust'; try { final rAdjust = await UserApi.referrer( app: backendApp, userId: uid, referer: adjustReferer, deviceId: deviceId, type: adjustType, ); if (kDebugMode) { debugPrint( '[AuthService] referrer($adjustType): ${rAdjust.isSuccess ? "成功" : "失败"}'); } } catch (e) { if (kDebugMode) { debugPrint('[AuthService] referrer($adjustType): 异常 $e'); } } } // 上报 Google Play 归因(从 AdjustService 获取缓存的 referrer) final playReferrer = AdjustService.cachedPlayReferrer; if (playReferrer != null && playReferrer.isNotEmpty) { try { final rGg = await UserApi.referrer( app: backendApp, userId: uid, referer: playReferrer, deviceId: deviceId, type: 'gg', ); if (kDebugMode) { debugPrint( '[AuthService] referrer(gg): ${rGg.isSuccess ? "成功" : "失败"}'); } } catch (e) { if (kDebugMode) { debugPrint('[AuthService] referrer(gg): 异常 $e'); } } } // 获取通用信息 try { final commonRes = await UserApi.getCommonInfo( app: backendApp, pkg: config.packageName, userId: uid, deviceId: deviceId, ); if (commonRes.isSuccess && commonRes.data != null) { ExtConfigRuntime.applyCommonInfoSuccess(commonRes.data!); _callbacks?.onCommonInfoLoaded(commonRes.data!); unawaited( VideoHomeRuntime.hydrateAfterCommonInfo( userId: uid, app: backendApp, ), ); if (kDebugMode) { debugPrint('[AuthService] common_info: 获取成功'); } } else { VideoHomeRuntime.reset(); ExtConfigRuntime.applyCommonInfoFailure(); if (kDebugMode) { debugPrint( '[AuthService] common_info: 失败 code=${commonRes.code} msg=${commonRes.msg}'); } } } catch (e) { VideoHomeRuntime.reset(); ExtConfigRuntime.applyCommonInfoFailure(); if (kDebugMode) { debugPrint('[AuthService] common_info: 异常 $e'); } } } /// 解析 extConfig JSON 字符串为 Map(兼容旧代码;结构化解析请用 [ExtConfigData.parse])。 static Map? parseExtConfig(String? extConfigStr) { return ExtConfigData.parseRawMap(extConfigStr); } }