diff --git a/lib/src/api/api_crypto.dart b/lib/src/api/api_crypto.dart index 4b5dd57..4f4dfa1 100644 --- a/lib/src/api/api_crypto.dart +++ b/lib/src/api/api_crypto.dart @@ -1,4 +1,5 @@ import 'dart:convert'; +import 'dart:math'; import 'package:encrypt/encrypt.dart'; @@ -28,18 +29,19 @@ class ApiCrypto { } /// 生成随机 Base64 字符串(用于噪音字段) - static String randomBase64([int byteLength = 16]) { - final bytes = List.generate( - byteLength, (_) => DateTime.now().millisecondsSinceEpoch % 256); - return base64Encode(bytes); + static String randomBase64() { + const chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; + final random = Random(); + final length = 4 + random.nextInt(5); // 4-8位 + final randomStr = List.generate(length, (_) => chars[random.nextInt(chars.length)]).join(); + return base64Encode(utf8.encode(randomStr)); } /// 生成 8 位随机字母数字 static String randomAlnum() { - const chars = + const chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; - return List.generate(8, - (_) => chars[DateTime.now().microsecondsSinceEpoch % chars.length]) - .join(); + final random = Random(); + return List.generate(8, (_) => chars[random.nextInt(chars.length)]).join(); } } diff --git a/lib/src/api/proxy_client.dart b/lib/src/api/proxy_client.dart index 1c0142e..cabd0d3 100644 --- a/lib/src/api/proxy_client.dart +++ b/lib/src/api/proxy_client.dart @@ -135,7 +135,7 @@ class ProxyClient { final v2BodyEncoded = jsonEncode(v2Body); final logStr = - '========== 原始入参 ===========\npath: $path\nmethod: $method\nqueryParams: $paramsEncoded\nbody(sanctum): ${jsonEncode(sanctum)}'; + '========== 原始入参 ===========\npath: $path\nmethod: $method \nheaders: $headersEncoded\nqueryParams: $paramsEncoded\nbody(sanctum): ${jsonEncode(sanctum)}'; logWithEmbeddedJson(logStr); final proxyBody = { @@ -153,6 +153,7 @@ class ProxyClient { final url = '$_baseUrl${config.proxyPath}'; + logWithEmbeddedJson('========== 请求信息 ===========\n\n目标URL: $url'); logWithEmbeddedJson('========== 实际请求体 ===========\n${jsonEncode(proxyBody)}'); final response = await http.post( Uri.parse(url), diff --git a/lib/src/config/attribution_config.dart b/lib/src/config/attribution_config.dart index 2a32f6a..1e15eca 100644 --- a/lib/src/config/attribution_config.dart +++ b/lib/src/config/attribution_config.dart @@ -54,6 +54,7 @@ abstract class AttributionCallbacks { Future getReferrer(); Future getAdjustReferrer(); Future getPlatformReferrer(); + Future getFacebookReferrer(); } /// 默认归因实现 @@ -66,6 +67,9 @@ class DefaultAttributionCallbacks implements AttributionCallbacks { @override Future getPlatformReferrer() async => null; + + @override + Future getFacebookReferrer() async => null; } /// 归因服务 @@ -87,4 +91,6 @@ abstract class AttributionService { static Future getAdjustReferrer() => callbacks.getAdjustReferrer(); static Future getPlatformReferrer() => callbacks.getPlatformReferrer(); + static Future getFacebookReferrer() => + callbacks.getFacebookReferrer(); } diff --git a/lib/src/log/app_logger.dart b/lib/src/log/app_logger.dart index 1239f5f..34bf172 100644 --- a/lib/src/log/app_logger.dart +++ b/lib/src/log/app_logger.dart @@ -50,26 +50,48 @@ void _logLong(String text) { _proxyLog.d(text); return; } + final buffer = StringBuffer(); - int chunkIndex = 0; + var chunkIndex = 0; + + void emitBuffer() { + if (buffer.isEmpty) return; + chunkIndex++; + _proxyLog.d( + chunkIndex > 1 ? '(part $chunkIndex)\n$buffer' : buffer.toString(), + ); + buffer.clear(); + } + + /// 单条 print / logcat 往往有长度上限;超长单行必须再切分,否则会截断在任意字符处(例如字符串中间的 `"`)。 + void emitLineInChunks(String line) { + for (var offset = 0; offset < line.length; offset += _maxLogChunk) { + final end = (offset + _maxLogChunk <= line.length) + ? offset + _maxLogChunk + : line.length; + chunkIndex++; + final piece = line.substring(offset, end); + _proxyLog.d(chunkIndex > 1 ? '(part $chunkIndex)\n$piece' : piece); + } + } + for (final line in lines) { + if (line.length > _maxLogChunk) { + emitBuffer(); + emitLineInChunks(line); + continue; + } final lineWithNewline = buffer.isEmpty ? line : '\n$line'; if (buffer.length + lineWithNewline.length > _maxLogChunk && buffer.isNotEmpty) { - chunkIndex++; - _proxyLog.d('(part $chunkIndex)\n$buffer'); - buffer.clear(); + emitBuffer(); buffer.write(line); } else { if (buffer.isNotEmpty) buffer.write('\n'); buffer.write(line); } } - if (buffer.isNotEmpty) { - chunkIndex++; - _proxyLog - .d(chunkIndex > 1 ? '(part $chunkIndex)\n$buffer' : buffer.toString()); - } + emitBuffer(); } /// 格式化输出嵌入在字符串中的 JSON,保持缩进对齐。 diff --git a/lib/src/services/auth_service.dart b/lib/src/services/auth_service.dart index 8a3a55d..cc53562 100644 --- a/lib/src/services/auth_service.dart +++ b/lib/src/services/auth_service.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'dart:convert'; import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; import '../api/api_client.dart'; import '../api/proxy_client.dart'; @@ -90,6 +91,23 @@ abstract class FrameworkAuthService { 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++) { @@ -100,10 +118,14 @@ abstract class FrameworkAuthService { await Future.delayed(Duration(seconds: retryDelaySeconds)); } try { + final appType = + defaultTargetPlatform == TargetPlatform.iOS ? 'HIOS' : 'HAndroid'; res = await UserApi.fastLogin( deviceId: deviceId, sign: sign, referer: referer ?? '', + app: appType, + type: referrerType, ); break; } catch (e) { diff --git a/lib/src/services/user_api.dart b/lib/src/services/user_api.dart index 2067406..b444bfc 100644 --- a/lib/src/services/user_api.dart +++ b/lib/src/services/user_api.dart @@ -8,21 +8,30 @@ abstract final class UserApi { static ProxyClient get _client => ApiClient.instance.proxy; /// 设备快速登录 + /// [deviceId] 设备ID (必填) + /// [sign] 签名,MD5(deviceId)大写32位 (必填) + /// [referer] 归因信息 + /// [ch] 渠道 + /// [type] referrer类型 (af/fb/gg/ios_adjust/android_adjust) + /// [app] 应用类型 (iOS: HIOS / Android: HAndroid) static Future> fastLogin({ required String deviceId, required String sign, String? referer, String? ch, String? type, + String? app, }) async { + final config = ApiClient.instance.config; return _client.requestEntity( path: '/v1/user/fast_login', method: 'POST', entityFactory: FastLoginResponse.fromJson, queryParams: { if (ch != null) 'ch': ch, - 'pkg': ApiClient.instance.config.packageName, + 'pkg': config.packageName, if (type != null) 'type': type, + if (app != null) 'app': app, }, body: { 'referer': referer ?? '',