client_framework/lib/src/services/auth_service.dart
2026-03-26 10:39:39 +08:00

246 lines
7.0 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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;
}
}
}