FunyMeeAI/lib/core/auth/auth_service.dart
2026-04-10 15:36:08 +08:00

124 lines
4.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:convert';
import 'dart:math';
import 'package:android_id/android_id.dart';
import 'package:client_proxy_framework/client_proxy_framework.dart';
import 'package:crypto/crypto.dart' show md5;
import 'package:device_info_plus/device_info_plus.dart';
import 'package:flutter/foundation.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../user/user_state.dart';
const _prefsKeyFallbackDeviceId = 'persisted_device_id';
Future<String> _persistedFallbackDeviceId() async {
final prefs = await SharedPreferences.getInstance();
var id = prefs.getString(_prefsKeyFallbackDeviceId);
if (id != null && id.isNotEmpty) return id;
final random = Random.secure();
final bytes = List<int>.generate(16, (_) => random.nextInt(256));
id = base64UrlEncode(bytes).replaceAll('=', '');
await prefs.setString(_prefsKeyFallbackDeviceId, id);
return id;
}
class AppAuthCallbacks implements AuthServiceCallbacks {
/// 与 app_client 一致Android 用 Settings.Secure.ANDROID_IDandroid_id 包,非 Build.ID
/// iOS 用 identifierForVendor失败或其它平台用 SharedPreferences 持久化随机 id。
@override
Future<String> getDeviceId() async {
switch (defaultTargetPlatform) {
case TargetPlatform.android:
final androidId = await const AndroidId().getId();
if (androidId != null && androidId.isNotEmpty) {
return androidId;
}
return _persistedFallbackDeviceId();
case TargetPlatform.iOS:
final ios = await DeviceInfoPlugin().iosInfo;
final idfv = ios.identifierForVendor;
if (idfv != null && idfv.isNotEmpty) return idfv;
return _persistedFallbackDeviceId();
default:
return _persistedFallbackDeviceId();
}
}
@override
String computeSign(String deviceId) {
return md5.convert(utf8.encode(deviceId)).toString().toUpperCase();
}
@override
void onLoginSuccess(FastLoginResponse data) {
UserState.applyLogin(
userId: data.userId,
credits: data.credits,
avatar: data.avatar,
userName: data.userName,
);
}
@override
void onCommonInfoLoaded(CommonInfoResponse data) {
if (data.credits != null) UserState.setCredits(data.credits!);
if (data.avatar != null) UserState.setAvatar(data.avatar!);
if (data.userName != null) UserState.setUserName(data.userName!);
}
@override
void onLoginFailed(String msg) {
debugPrint('[AuthService] Login failed: $msg');
}
}
/// 应用侧登录封装Adjust 归因桥已由框架 [FrameworkAuthService.init] 默认注册。
class AuthService {
static final _authCallbacks = AppAuthCallbacks();
static Future<void> init() async {
FrameworkAuthService.init(_authCallbacks);
await FrameworkAuthService.start();
}
static Future<void> get loginComplete => FrameworkAuthService.loginComplete;
/// 登录流程是否已结束(含 fast_login + common_info 链路);用于遮罩,勿用 [loginComplete] 的 Future首帧可能为 null
static ValueNotifier<bool> get isLoginComplete =>
FrameworkAuthService.isLoginComplete;
/// 在 **登录成功**[UserState.userId] 非空)且 [isLoginComplete] 为 `true` 之后执行 [onReady]。
///
/// 若登录流程已结束但无用户(失败),调用一次 [onFailed]。若 [isLoginComplete] 已为 `true`,同步执行。
///
/// 返回在 [State.dispose] 中调用的取消函数,避免界面已销毁仍监听。
static VoidCallback whenLoginSucceeded({
required VoidCallback onReady,
VoidCallback? onFailed,
}) {
void runOnce() {
final uid = UserState.userId.value;
if (uid != null && uid.isNotEmpty) {
onReady();
} else {
onFailed?.call();
}
}
void listener() {
if (!isLoginComplete.value) return;
isLoginComplete.removeListener(listener);
runOnce();
}
if (isLoginComplete.value) {
runOnce();
return () {};
}
isLoginComplete.addListener(listener);
return () => isLoginComplete.removeListener(listener);
}
}