petsHero-AI/lib/core/auth/auth_service.dart
2026-03-09 16:39:54 +08:00

120 lines
3.6 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:crypto/crypto.dart';
import 'package:device_info_plus/device_info_plus.dart';
import 'package:flutter/foundation.dart';
import '../api/api_client.dart';
import '../api/proxy_client.dart';
import '../api/services/user_api.dart';
import '../user/user_state.dart';
/// 认证服务APP 启动时执行快速登录
class AuthService {
AuthService._();
static const _tag = '[AuthService]';
static Future<void>? _loginFuture;
/// 登录完成后的 Future需鉴权接口应 await 此 Future 再请求
static Future<void> get loginComplete =>
_loginFuture ?? Future<void>.value();
static void _log(String msg) {
debugPrint('$_tag $msg');
}
/// 获取设备 IDAndroid: androidId, iOS: identifierForVendor, Web: fallback
static Future<String> _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}';
}
}
/// 计算 signMD5(deviceId) 大写 32 位
static String _computeSign(String deviceId) {
final bytes = utf8.encode(deviceId);
final digest = md5.convert(bytes);
return digest.toString().toUpperCase();
}
/// APP 启动时调用快速登录
/// 启动时网络可能未就绪,会延迟后重试
static Future<void> init() async {
if (_loginFuture != null) return _loginFuture!;
final completer = Completer<void>();
_loginFuture = completer.future;
_log('init: 开始快速登录');
const maxRetries = 3;
const retryDelay = Duration(seconds: 2);
try {
// 等待网络就绪(浏览器能访问但 App 报错时,多为启动时网络未初始化)
await Future<void>.delayed(const Duration(seconds: 2));
final deviceId = await _getDeviceId();
_log('init: deviceId=$deviceId');
final sign = _computeSign(deviceId);
_log('init: sign=$sign');
ApiResponse? res;
for (var i = 0; i < maxRetries; i++) {
if (i > 0) {
_log('init: 第 ${i + 1} 次重试,等待 ${retryDelay.inSeconds}s...');
await Future<void>.delayed(retryDelay);
}
try {
res = await UserApi.fastLogin(
origin: deviceId,
resolution: sign,
digest: '',
);
break;
} catch (e) {
_log('init: 第 ${i + 1} 次请求失败: $e');
if (i == maxRetries - 1) rethrow;
}
}
if (res == null) return;
_log('init: 登录结果 code=${res.code} msg=${res.msg}');
if (res.isSuccess && res.data != null) {
final data = res.data as Map<String, dynamic>?;
final token = data?['reevaluate'] as String?;
if (token != null && token.isNotEmpty) {
ApiClient.instance.setUserToken(token);
_log('init: 已设置 userToken');
} else {
_log('init: 响应中无 reevaluate (userToken)');
}
final credits = data?['reveal'] as int?;
if (credits != null) {
UserState.setCredits(credits);
_log('init: 已同步积分 $credits');
}
} else {
_log('init: 登录失败');
}
} catch (e, st) {
_log('init: 异常 $e');
_log('init: 堆栈 $st');
} finally {
if (!completer.isCompleted) completer.complete();
}
}
}