246 lines
7.0 KiB
Dart
246 lines
7.0 KiB
Dart
import 'dart:async';
|
||
import 'dart:convert';
|
||
|
||
import 'package:flutter/foundation.dart';
|
||
|
||
import '../api/api_client.dart';
|
||
import '../api/proxy_client.dart';
|
||
import '../config/attribution_config.dart';
|
||
import '../entities/user_entities.dart';
|
||
import 'adjust_service.dart';
|
||
import 'user_api.dart';
|
||
|
||
/// 认证服务回调
|
||
/// 用于在认证流程各阶段通知调用方
|
||
abstract class AuthServiceCallbacks {
|
||
/// 获取设备 ID(由调用方实现)
|
||
Future<String> 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<void>? _loginFuture;
|
||
static bool _isInitialized = false;
|
||
|
||
/// 登录是否已完成
|
||
static final ValueNotifier<bool> isLoginComplete = ValueNotifier(false);
|
||
|
||
/// 登录完成后的 Future,需鉴权接口应 await 此 Future 再请求
|
||
static Future<void> get loginComplete => _loginFuture ?? Future<void>.value();
|
||
|
||
/// 初始化认证服务
|
||
static void init(AuthServiceCallbacks callbacks) {
|
||
_callbacks = callbacks;
|
||
_isInitialized = true;
|
||
}
|
||
|
||
/// 启动登录流程
|
||
/// [delaySeconds] 启动延迟秒数,默认 2 秒
|
||
/// [maxRetries] 最大重试次数,默认 3 次
|
||
/// [retryDelaySeconds] 重试延迟秒数,默认 2 秒
|
||
static Future<void> start({
|
||
int delaySeconds = 2,
|
||
int maxRetries = 3,
|
||
int retryDelaySeconds = 2,
|
||
}) async {
|
||
if (!_isInitialized || _callbacks == null) {
|
||
throw StateError(
|
||
'AuthService not initialized. Call AuthService.init(callbacks) first.');
|
||
}
|
||
if (_loginFuture != null) return _loginFuture!;
|
||
final completer = Completer<void>();
|
||
_loginFuture = completer.future;
|
||
|
||
if (kDebugMode) {
|
||
debugPrint('[AuthService] start: 开始登录流程');
|
||
}
|
||
|
||
try {
|
||
await Future<void>.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');
|
||
}
|
||
|
||
// 尝试快速登录
|
||
EntityResponse<FastLoginResponse>? res;
|
||
for (var i = 0; i < maxRetries; i++) {
|
||
if (i > 0) {
|
||
if (kDebugMode) {
|
||
debugPrint('[AuthService] start: 第 ${i + 1} 次重试...');
|
||
}
|
||
await Future<void>.delayed(Duration(seconds: retryDelaySeconds));
|
||
}
|
||
try {
|
||
res = await UserApi.fastLogin(
|
||
deviceId: deviceId,
|
||
sign: sign,
|
||
referer: referer ?? '',
|
||
);
|
||
break;
|
||
} catch (e) {
|
||
if (kDebugMode) {
|
||
debugPrint('[AuthService] start: 第 ${i + 1} 次请求失败: $e');
|
||
}
|
||
if (i == maxRetries - 1) rethrow;
|
||
}
|
||
}
|
||
|
||
if (res == null) {
|
||
completer.complete();
|
||
return;
|
||
}
|
||
|
||
if (kDebugMode) {
|
||
debugPrint('[AuthService] start: 登录结果 code=${res.code} msg=${res.msg}');
|
||
}
|
||
|
||
if (res.isSuccess && res.data != null) {
|
||
final loginData = res.data!;
|
||
|
||
// 设置 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 {
|
||
_callbacks!.onLoginFailed(res.msg);
|
||
}
|
||
} catch (e, st) {
|
||
if (kDebugMode) {
|
||
debugPrint('[AuthService] start: 异常 $e\n$st');
|
||
}
|
||
_callbacks!.onLoginFailed(e.toString());
|
||
} finally {
|
||
if (!completer.isCompleted) {
|
||
completer.complete();
|
||
isLoginComplete.value = true;
|
||
}
|
||
}
|
||
}
|
||
|
||
/// 上报归因并获取通用信息
|
||
static Future<void> _reportReferrersAndLoadCommonInfo({
|
||
required String uid,
|
||
required String deviceId,
|
||
}) async {
|
||
if (uid.isEmpty) return;
|
||
|
||
final config = ApiClient.instance.config;
|
||
|
||
// 上报 Adjust 归因
|
||
final adjustReferer = await AttributionService.getAdjustReferrer();
|
||
if (adjustReferer != null && adjustReferer.isNotEmpty) {
|
||
try {
|
||
final rAdjust = await UserApi.referrer(
|
||
app: config.appId,
|
||
userId: uid,
|
||
referer: adjustReferer,
|
||
deviceId: deviceId,
|
||
type: 'android_adjust',
|
||
);
|
||
if (kDebugMode) {
|
||
debugPrint(
|
||
'[AuthService] referrer(android_adjust): ${rAdjust.isSuccess ? "成功" : "失败"}');
|
||
}
|
||
} catch (e) {
|
||
if (kDebugMode) {
|
||
debugPrint('[AuthService] referrer(android_adjust): 异常 $e');
|
||
}
|
||
}
|
||
}
|
||
|
||
// 上报 Google Play 归因(从 AdjustService 获取缓存的 referrer)
|
||
final playReferrer = AdjustService.cachedPlayReferrer;
|
||
if (playReferrer != null && playReferrer.isNotEmpty) {
|
||
try {
|
||
final rGg = await UserApi.referrer(
|
||
app: config.appId,
|
||
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: config.appId,
|
||
userId: uid,
|
||
);
|
||
if (commonRes.isSuccess && commonRes.data != null) {
|
||
_callbacks?.onCommonInfoLoaded(commonRes.data!);
|
||
if (kDebugMode) {
|
||
debugPrint('[AuthService] common_info: 获取成功');
|
||
}
|
||
} else if (kDebugMode) {
|
||
debugPrint(
|
||
'[AuthService] common_info: 失败 code=${commonRes.code} msg=${commonRes.msg}');
|
||
}
|
||
} catch (e) {
|
||
if (kDebugMode) {
|
||
debugPrint('[AuthService] common_info: 异常 $e');
|
||
}
|
||
}
|
||
}
|
||
|
||
/// 解析 extConfig JSON 字符串
|
||
static Map<String, dynamic>? parseExtConfig(String? extConfigStr) {
|
||
if (extConfigStr == null || extConfigStr.isEmpty) return null;
|
||
try {
|
||
return json.decode(extConfigStr) as Map<String, dynamic>?;
|
||
} catch (_) {
|
||
return null;
|
||
}
|
||
}
|
||
}
|