优化:登录流程,错误上报

This commit is contained in:
ivan 2026-04-22 23:08:39 +08:00
parent e44a09e7e1
commit 982bed4802
11 changed files with 370 additions and 36 deletions

View File

@ -96,7 +96,7 @@ AES 加密
发送请求 发送请求
``` ```
**请求头**`ProxyClient` 将 [AppConfig.packageName] 写入映射后的包名字段(原始名 `pkg`);若已设置用户 token默认还会写入 `User_token`。**`UserApi.fast_login` 等无需登录态接口**内部使用 `includeUserTokenInHeader: false`,避免把旧 token 打进 `filter_type`。其余请求也可在直接调用 `ProxyClient.request` 时传入该参数 **请求头**`ProxyClient` 默认将 [AppConfig.packageName] 写入映射后的包名字段(原始名 `pkg`);若已设置用户 token默认还会写入 `User_token`。**`UserApi.fastLogin`** 对内层请求使用 `includeUserTokenInHeader: false`(不传 token`pkg` 仍按默认行为传递)。其余接口可按需在 `ProxyClient.request` 上传入上述开关
**请求体**:各 `*Api` 方法使用与《客户端指南》解密表一致的**原始字段名**(如 `referer``deviceId``fileUrls` / `contentType` / `content`)。 **请求体**:各 `*Api` 方法使用与《客户端指南》解密表一致的**原始字段名**(如 `referer``deviceId``fileUrls` / `contentType` / `content`)。
@ -146,9 +146,9 @@ final res = await UserApi.fastLogin(
deviceId: '设备ID', deviceId: '设备ID',
sign: 'MD5(deviceId)大写', sign: 'MD5(deviceId)大写',
app: 'HAndroid', // 必填HIOS / HAndroid app: 'HAndroid', // 必填HIOS / HAndroid
referer: '归因来源', // 可选 referer: '归因来源', // 可选`gg` 时常为 Play Install Referrer框架 start 取不到时用 utm_source=google-play&utm_medium=organic
ch: '渠道号', // 可选 ch: '渠道号', // 可选
type: 'fb', // 可选;未传时默认 fb type: 'gg', // 可选;未传时默认 ggGoogle Play 归因)
); );
if (res.isSuccess) { if (res.isSuccess) {

View File

@ -49,7 +49,7 @@ sequenceDiagram
### 3.1 启动与快速登录 ### 3.1 启动与快速登录
- 延迟与重试策略由 `FrameworkAuthService.start` 控制(默认启动延迟、登录重试等)。 - 延迟与重试策略由 `FrameworkAuthService.start` 控制(默认启动延迟、登录重试等)。
- **`UserApi.fastLogin`**`POST /v1/user/fast_login`请求头**仅**带 `pkg`**不带** `User_token`(见 `UserApi` 文档注释) - **`UserApi.fastLogin`**`POST /v1/user/fast_login`线网内层 headers 保留 `pkg`,但**不注入** `User_token`(见 `UserApi` / `ProxyClient` 注释。Query `type` 未传时默认为 **`gg`**Google Play 归因)。`FrameworkAuthService` 强制 `type=gg``referer` 优先 Play Install Referrer取不到时用 `utm_source=google-play&utm_medium=organic`
- 成功后框架将返回的 `userToken` 写入 **`ApiClient.instance.setUserToken`**,此后代理请求自动附带 `pkg``User_token`(见 `ProxyClient` 行为与 `UserApi` 说明)。 - 成功后框架将返回的 `userToken` 写入 **`ApiClient.instance.setUserToken`**,此后代理请求自动附带 `pkg``User_token`(见 `ProxyClient` 行为与 `UserApi` 说明)。
### 3.2 归因上报(与首页数据并行准备) ### 3.2 归因上报(与首页数据并行准备)

View File

@ -32,12 +32,14 @@ export 'src/services/analytics_attribution_callbacks.dart';
export 'src/services/analytics_events.dart'; export 'src/services/analytics_events.dart';
export 'src/services/analytics_service.dart'; export 'src/services/analytics_service.dart';
export 'src/services/auth_service.dart'; export 'src/services/auth_service.dart';
export 'src/services/login_identity_cache.dart';
export 'src/services/facebook_service.dart'; export 'src/services/facebook_service.dart';
export 'src/services/feedback_api.dart'; export 'src/services/feedback_api.dart';
export 'src/services/image_api.dart'; export 'src/services/image_api.dart';
export 'src/services/image_progress_poll.dart'; export 'src/services/image_progress_poll.dart';
export 'src/services/image_compress.dart'; export 'src/services/image_compress.dart';
export 'src/services/image_presigned_upload_create_flow.dart'; export 'src/services/image_presigned_upload_create_flow.dart';
export 'src/services/image_upload_expected_size_cache.dart';
export 'src/services/image_task_history.dart'; export 'src/services/image_task_history.dart';
export 'src/services/task_upload_cover_store.dart'; export 'src/services/task_upload_cover_store.dart';
export 'src/services/user_account_refresh.dart'; export 'src/services/user_account_refresh.dart';

View File

@ -104,11 +104,11 @@ class ProxyClient {
/// data 线 /// data 线
/// ///
/// **** /// ****
/// - [AppConfig.packageName] `pkg`线 /// - [includePackageInHeader] true[AppConfig.packageName] `pkg`线
/// - [userToken] [includeUserTokenInHeader] true `User_token` /// - [userToken] [includeUserTokenInHeader] true `User_token`
/// ///
/// **** `includeUserTokenInHeader: false` /// **** `includeUserTokenInHeader: false`
/// token `filter_type` /// token `filter_type` `includePackageInHeader: false` headers `pkg` query
Future<ApiResponse> request({ Future<ApiResponse> request({
required String path, required String path,
required String method, required String method,
@ -116,12 +116,13 @@ class ProxyClient {
Map<String, String>? queryParams, Map<String, String>? queryParams,
Map<String, dynamic>? body, Map<String, dynamic>? body,
bool includeUserTokenInHeader = true, bool includeUserTokenInHeader = true,
bool includePackageInHeader = true,
}) async { }) async {
final pk = config.proxyKeys; final pk = config.proxyKeys;
final mapping = config.fieldMapping; final mapping = config.fieldMapping;
var headersMap = Map<String, dynamic>.from(headers ?? {}); var headersMap = Map<String, dynamic>.from(headers ?? {});
if (config.packageName.isNotEmpty) { if (includePackageInHeader && config.packageName.isNotEmpty) {
headersMap[mapping.headerPackageNameField] = config.packageName; headersMap[mapping.headerPackageNameField] = config.packageName;
} }
if (includeUserTokenInHeader && if (includeUserTokenInHeader &&
@ -179,7 +180,7 @@ class ProxyClient {
/// [headers][queryParams][body] 使**** /// [headers][queryParams][body] 使****
/// [entityFactory] data /// [entityFactory] data
/// ///
/// [request] [includeUserTokenInHeader] /// [request] [includeUserTokenInHeader][includePackageInHeader]
Future<EntityResponse<T>> requestEntity<T extends Entity>({ Future<EntityResponse<T>> requestEntity<T extends Entity>({
required String path, required String path,
required String method, required String method,
@ -188,6 +189,7 @@ class ProxyClient {
Map<String, String>? queryParams, Map<String, String>? queryParams,
Map<String, dynamic>? body, Map<String, dynamic>? body,
bool includeUserTokenInHeader = true, bool includeUserTokenInHeader = true,
bool includePackageInHeader = true,
}) async { }) async {
final response = await request( final response = await request(
path: path, path: path,
@ -196,6 +198,7 @@ class ProxyClient {
queryParams: queryParams, queryParams: queryParams,
body: body, body: body,
includeUserTokenInHeader: includeUserTokenInHeader, includeUserTokenInHeader: includeUserTokenInHeader,
includePackageInHeader: includePackageInHeader,
); );
if (response.isSuccess) { if (response.isSuccess) {

View File

@ -8,6 +8,7 @@ class FeedbackUploadPresignedUrlResponse extends Entity {
this.uploadUrl, this.uploadUrl,
this.filePath, this.filePath,
this.putHeaders, this.putHeaders,
this.expectedSize,
}); });
final String? uploadUrl; final String? uploadUrl;
@ -16,6 +17,9 @@ class FeedbackUploadPresignedUrlResponse extends Entity {
/// [UploadPresignedUrlResponse.putHeaders] PUT /// [UploadPresignedUrlResponse.putHeaders] PUT
final Map<String, String>? putHeaders; final Map<String, String>? putHeaders;
/// `expectedSize` `null`
final int? expectedSize;
static String _headerValueToString(dynamic v) { static String _headerValueToString(dynamic v) {
if (v == null) return ''; if (v == null) return '';
if (v is String) return v; if (v is String) return v;
@ -57,6 +61,14 @@ class FeedbackUploadPresignedUrlResponse extends Entity {
return out.isEmpty ? null : out; return out.isEmpty ? null : out;
} }
static int? _readIntField(Map<String, dynamic> json, String key) {
final v = json[key];
if (v == null) return null;
if (v is int) return v;
if (v is num) return v.toInt();
return int.tryParse(v.toString().trim());
}
@override @override
factory FeedbackUploadPresignedUrlResponse.fromJson( factory FeedbackUploadPresignedUrlResponse.fromJson(
Map<String, dynamic> json) { Map<String, dynamic> json) {
@ -64,6 +76,9 @@ class FeedbackUploadPresignedUrlResponse extends Entity {
uploadUrl: json['uploadUrl'] as String?, uploadUrl: json['uploadUrl'] as String?,
filePath: json['filePath'] as String?, filePath: json['filePath'] as String?,
putHeaders: _parsePutHeaders(json), putHeaders: _parsePutHeaders(json),
expectedSize: _readIntField(json, 'expectedSize') ??
_readIntField(json, 'maxFileSize') ??
_readIntField(json, 'maxSize'),
); );
} }
@ -72,6 +87,7 @@ class FeedbackUploadPresignedUrlResponse extends Entity {
'uploadUrl': uploadUrl, 'uploadUrl': uploadUrl,
'filePath': filePath, 'filePath': filePath,
if (putHeaders != null) 'putHeaders': putHeaders, if (putHeaders != null) 'putHeaders': putHeaders,
if (expectedSize != null) 'expectedSize': expectedSize,
}; };
} }

View File

@ -405,6 +405,7 @@ class UploadPresignedUrlResponse extends Entity {
this.uploadUrl, this.uploadUrl,
this.filePath, this.filePath,
this.putHeaders, this.putHeaders,
this.expectedSize,
}); });
final String? uploadUrl; final String? uploadUrl;
@ -413,6 +414,9 @@ class UploadPresignedUrlResponse extends Entity {
/// business /// business
final Map<String, String>? putHeaders; final Map<String, String>? putHeaders;
/// `expectedSize` `null`
final int? expectedSize;
/// JSON [http] [String] `TypeError` /// JSON [http] [String] `TypeError`
static String _headerValueToString(dynamic v) { static String _headerValueToString(dynamic v) {
if (v == null) return ''; if (v == null) return '';
@ -462,6 +466,14 @@ class UploadPresignedUrlResponse extends Entity {
return v.toString(); return v.toString();
} }
static int? _readIntField(Map<String, dynamic> json, String key) {
final v = json[key];
if (v == null) return null;
if (v is int) return v;
if (v is num) return v.toInt();
return int.tryParse(v.toString().trim());
}
@override @override
factory UploadPresignedUrlResponse.fromJson(Map<String, dynamic> json) { factory UploadPresignedUrlResponse.fromJson(Map<String, dynamic> json) {
// FunyMee `uploadUrl1`/`filePath1` wireharden / generate // FunyMee `uploadUrl1`/`filePath1` wireharden / generate
@ -476,6 +488,9 @@ class UploadPresignedUrlResponse extends Entity {
uploadUrl: upload, uploadUrl: upload,
filePath: path, filePath: path,
putHeaders: _parsePutHeaders(json), putHeaders: _parsePutHeaders(json),
expectedSize: _readIntField(json, 'expectedSize') ??
_readIntField(json, 'maxFileSize') ??
_readIntField(json, 'maxSize'),
); );
} }
@ -484,6 +499,7 @@ class UploadPresignedUrlResponse extends Entity {
'uploadUrl': uploadUrl, 'uploadUrl': uploadUrl,
'filePath': filePath, 'filePath': filePath,
if (putHeaders != null) 'putHeaders': putHeaders, if (putHeaders != null) 'putHeaders': putHeaders,
if (expectedSize != null) 'expectedSize': expectedSize,
}; };
} }

View File

@ -11,8 +11,14 @@ import '../config/video_home_runtime.dart';
import '../entities/user_entities.dart'; import '../entities/user_entities.dart';
import 'adjust_service.dart'; import 'adjust_service.dart';
import 'analytics_attribution_callbacks.dart'; import 'analytics_attribution_callbacks.dart';
import 'facebook_service.dart';
import 'login_identity_cache.dart';
import 'user_api.dart'; import 'user_api.dart';
/// [FrameworkAuthService.start] `fast_login` Play Install Referrer 使 `referer`
const String _fastLoginPlayReferrerFallback =
'utm_source=google-play&utm_medium=organic';
/// ///
/// ///
abstract class AuthServiceCallbacks { abstract class AuthServiceCallbacks {
@ -37,6 +43,14 @@ abstract class AuthServiceCallbacks {
/// 1. /// 1.
/// 2. /// 2.
/// 3. /// 3.
///
/// ****宿
/// - `fast_login``type` **** `gg`Google `referer` Google Play Install Referrer使 [_fastLoginPlayReferrerFallback] Adjust/Facebook type
/// - `userId` `userId` `deviceId` [LoginIdentityCache]
/// - `code != 0` `userId` Facebook [facebookLoginFailedEventName]
/// `server_error_code``user_id``device_id` ID
/// ID `register_faild` = `register faild`
/// - [UserApi.getCommonInfo] `extConfig` Facebook [facebookExtConfigFailedEventName]`user_id``device_id``server_error_code` `server_error_msg`
abstract class FrameworkAuthService { abstract class FrameworkAuthService {
static AuthServiceCallbacks? _callbacks; static AuthServiceCallbacks? _callbacks;
static Future<void>? _loginFuture; static Future<void>? _loginFuture;
@ -91,10 +105,14 @@ abstract class FrameworkAuthService {
debugPrint('[AuthService] start: 开始登录流程'); debugPrint('[AuthService] start: 开始登录流程');
} }
/// [catch] Facebook 使 [getDeviceId]
var deviceIdForFacebookFailure = '';
try { try {
await Future<void>.delayed(Duration(seconds: delaySeconds)); await Future<void>.delayed(Duration(seconds: delaySeconds));
final deviceId = await _callbacks!.getDeviceId(); final deviceId = await _callbacks!.getDeviceId();
deviceIdForFacebookFailure = deviceId;
if (kDebugMode) { if (kDebugMode) {
debugPrint('[AuthService] start: deviceId=$deviceId'); debugPrint('[AuthService] start: deviceId=$deviceId');
} }
@ -104,26 +122,17 @@ abstract class FrameworkAuthService {
debugPrint('[AuthService] start: sign=$sign'); debugPrint('[AuthService] start: sign=$sign');
} }
final referer = await AttributionService.getReferrer(); // fast_login使 Google `gg`referer Play Install Referrer UTM
if (kDebugMode && referer != null) { final playReferrer = AdjustService.cachedPlayReferrer;
debugPrint('[AuthService] start: referer=$referer'); final fastLoginReferer = (playReferrer != null && playReferrer.isNotEmpty)
} ? playReferrer
: _fastLoginPlayReferrerFallback;
// const fastLoginType = 'gg';
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) { if (kDebugMode) {
debugPrint('[AuthService] start: referrerType=$referrerType'); debugPrint(
'[AuthService] start: fast_login type=$fastLoginType refererLen=${fastLoginReferer.length}',
);
} }
// //
@ -143,9 +152,9 @@ abstract class FrameworkAuthService {
res = await UserApi.fastLogin( res = await UserApi.fastLogin(
deviceId: deviceId, deviceId: deviceId,
sign: sign, sign: sign,
referer: referer ?? '', referer: fastLoginReferer,
app: appType, app: appType,
type: referrerType, type: fastLoginType,
); );
break; break;
} catch (e) { } catch (e) {
@ -160,6 +169,12 @@ abstract class FrameworkAuthService {
lastLoggedInUserId = null; lastLoggedInUserId = null;
VideoHomeRuntime.reset(); VideoHomeRuntime.reset();
ExtConfigRuntime.applyCommonInfoFailure(); ExtConfigRuntime.applyCommonInfoFailure();
_logFacebookLoginFailed(
serverCode: -1,
missingUserId: true,
userIdFromServer: null,
deviceId: deviceId,
);
completer.complete(); completer.complete();
return; return;
} }
@ -171,8 +186,7 @@ abstract class FrameworkAuthService {
if (res.isSuccess && res.data != null) { if (res.isSuccess && res.data != null) {
final loginData = res.data!; final loginData = res.data!;
final uid = loginData.userId?.trim(); final uid = loginData.userId?.trim();
lastLoggedInUserId = lastLoggedInUserId = uid != null && uid.isNotEmpty ? uid : null;
uid != null && uid.isNotEmpty ? uid : null;
// Token // Token
if (loginData.userToken != null && loginData.userToken!.isNotEmpty) { if (loginData.userToken != null && loginData.userToken!.isNotEmpty) {
@ -182,6 +196,22 @@ abstract class FrameworkAuthService {
} }
} }
if (uid != null && uid.isNotEmpty) {
unawaited(
LoginIdentityCache.writeUserAndDeviceId(
userId: uid,
deviceId: deviceId,
),
);
} else {
_logFacebookLoginFailed(
serverCode: res.code,
missingUserId: true,
userIdFromServer: loginData.userId,
deviceId: deviceId,
);
}
// //
_callbacks!.onLoginSuccess(loginData); _callbacks!.onLoginSuccess(loginData);
@ -194,6 +224,7 @@ abstract class FrameworkAuthService {
lastLoggedInUserId = null; lastLoggedInUserId = null;
VideoHomeRuntime.reset(); VideoHomeRuntime.reset();
ExtConfigRuntime.applyCommonInfoFailure(); ExtConfigRuntime.applyCommonInfoFailure();
_logFacebookLoginFailedFromResponse(res, deviceId: deviceId);
_callbacks!.onLoginFailed(res.msg); _callbacks!.onLoginFailed(res.msg);
} }
} catch (e, st) { } catch (e, st) {
@ -203,6 +234,12 @@ abstract class FrameworkAuthService {
if (kDebugMode) { if (kDebugMode) {
debugPrint('[AuthService] start: 异常 $e\n$st'); debugPrint('[AuthService] start: 异常 $e\n$st');
} }
_logFacebookLoginFailed(
serverCode: -1,
missingUserId: true,
userIdFromServer: null,
deviceId: deviceIdForFacebookFailure,
);
_callbacks!.onLoginFailed(e.toString()); _callbacks!.onLoginFailed(e.toString());
} finally { } finally {
if (!completer.isCompleted) { if (!completer.isCompleted) {
@ -234,8 +271,14 @@ abstract class FrameworkAuthService {
: config.backendAppTypeAndroid; : config.backendAppTypeAndroid;
// Adjust // Adjust
var adjustReferrerTried = false;
var adjustReferrerOk = false;
var adjustReferrerCode = 0;
var adjustReferrerMsg = '';
final adjustReferer = await AttributionService.getAdjustReferrer(); final adjustReferer = await AttributionService.getAdjustReferrer();
if (adjustReferer != null && adjustReferer.isNotEmpty) { if (adjustReferer != null && adjustReferer.isNotEmpty) {
adjustReferrerTried = true;
final adjustType = defaultTargetPlatform == TargetPlatform.iOS final adjustType = defaultTargetPlatform == TargetPlatform.iOS
? 'ios_adjust' ? 'ios_adjust'
: 'android_adjust'; : 'android_adjust';
@ -247,11 +290,19 @@ abstract class FrameworkAuthService {
deviceId: deviceId, deviceId: deviceId,
type: adjustType, type: adjustType,
); );
if (rAdjust.isSuccess) {
adjustReferrerOk = true;
} else {
adjustReferrerCode = rAdjust.code;
adjustReferrerMsg = rAdjust.msg;
}
if (kDebugMode) { if (kDebugMode) {
debugPrint( debugPrint(
'[AuthService] referrer($adjustType): ${rAdjust.isSuccess ? "成功" : "失败"}'); '[AuthService] referrer($adjustType): ${rAdjust.isSuccess ? "成功" : "失败"}');
} }
} catch (e) { } catch (e) {
adjustReferrerCode = -110;
adjustReferrerMsg = e.toString();
if (kDebugMode) { if (kDebugMode) {
debugPrint('[AuthService] referrer($adjustType): 异常 $e'); debugPrint('[AuthService] referrer($adjustType): 异常 $e');
} }
@ -259,8 +310,14 @@ abstract class FrameworkAuthService {
} }
// Google Play AdjustService referrer // Google Play AdjustService referrer
var ggReferrerTried = false;
var ggReferrerOk = false;
var ggReferrerCode = 0;
var ggReferrerMsg = '';
final playReferrer = AdjustService.cachedPlayReferrer; final playReferrer = AdjustService.cachedPlayReferrer;
if (playReferrer != null && playReferrer.isNotEmpty) { if (playReferrer != null && playReferrer.isNotEmpty) {
ggReferrerTried = true;
try { try {
final rGg = await UserApi.referrer( final rGg = await UserApi.referrer(
app: backendApp, app: backendApp,
@ -269,17 +326,39 @@ abstract class FrameworkAuthService {
deviceId: deviceId, deviceId: deviceId,
type: 'gg', type: 'gg',
); );
if (rGg.isSuccess) {
ggReferrerOk = true;
} else {
ggReferrerCode = rGg.code;
ggReferrerMsg = rGg.msg;
}
if (kDebugMode) { if (kDebugMode) {
debugPrint( debugPrint(
'[AuthService] referrer(gg): ${rGg.isSuccess ? "成功" : "失败"}'); '[AuthService] referrer(gg): ${rGg.isSuccess ? "成功" : "失败"}');
} }
} catch (e) { } catch (e) {
ggReferrerCode = -110;
ggReferrerMsg = e.toString();
if (kDebugMode) { if (kDebugMode) {
debugPrint('[AuthService] referrer(gg): 异常 $e'); debugPrint('[AuthService] referrer(gg): 异常 $e');
} }
} }
} }
if (adjustReferrerTried &&
ggReferrerTried &&
!adjustReferrerOk &&
!ggReferrerOk) {
_logFacebookReferrerBothFailed(
userId: uid,
deviceId: deviceId,
adjustCode: adjustReferrerCode,
adjustMsg: adjustReferrerMsg,
ggCode: ggReferrerCode,
ggMsg: ggReferrerMsg,
);
}
// //
try { try {
final commonRes = await UserApi.getCommonInfo( final commonRes = await UserApi.getCommonInfo(
@ -289,8 +368,11 @@ abstract class FrameworkAuthService {
deviceId: deviceId, deviceId: deviceId,
); );
if (commonRes.isSuccess && commonRes.data != null) { if (commonRes.isSuccess && commonRes.data != null) {
ExtConfigRuntime.applyCommonInfoSuccess(commonRes.data!); final info = commonRes.data!;
_callbacks?.onCommonInfoLoaded(commonRes.data!); final extRaw = info.extConfig?.trim();
final extConfigMissing = extRaw == null || extRaw.isEmpty;
ExtConfigRuntime.applyCommonInfoSuccess(info);
_callbacks?.onCommonInfoLoaded(info);
unawaited( unawaited(
VideoHomeRuntime.hydrateAfterCommonInfo( VideoHomeRuntime.hydrateAfterCommonInfo(
userId: uid, userId: uid,
@ -300,9 +382,23 @@ abstract class FrameworkAuthService {
if (kDebugMode) { if (kDebugMode) {
debugPrint('[AuthService] common_info: 获取成功'); debugPrint('[AuthService] common_info: 获取成功');
} }
if (extConfigMissing) {
_logFacebookExtConfigFailed(
userId: uid,
deviceId: deviceId,
serverCode: 0,
serverMsg: 'ext_config_missing',
);
}
} else { } else {
VideoHomeRuntime.reset(); VideoHomeRuntime.reset();
ExtConfigRuntime.applyCommonInfoFailure(); ExtConfigRuntime.applyCommonInfoFailure();
_logFacebookExtConfigFailed(
userId: uid,
deviceId: deviceId,
serverCode: commonRes.code,
serverMsg: commonRes.msg,
);
if (kDebugMode) { if (kDebugMode) {
debugPrint( debugPrint(
'[AuthService] common_info: 失败 code=${commonRes.code} msg=${commonRes.msg}'); '[AuthService] common_info: 失败 code=${commonRes.code} msg=${commonRes.msg}');
@ -311,6 +407,12 @@ abstract class FrameworkAuthService {
} catch (e) { } catch (e) {
VideoHomeRuntime.reset(); VideoHomeRuntime.reset();
ExtConfigRuntime.applyCommonInfoFailure(); ExtConfigRuntime.applyCommonInfoFailure();
_logFacebookExtConfigFailed(
userId: uid,
deviceId: deviceId,
serverCode: -1,
serverMsg: e.toString(),
);
if (kDebugMode) { if (kDebugMode) {
debugPrint('[AuthService] common_info: 异常 $e'); debugPrint('[AuthService] common_info: 异常 $e');
} }
@ -321,4 +423,108 @@ abstract class FrameworkAuthService {
static Map<String, dynamic>? parseExtConfig(String? extConfigStr) { static Map<String, dynamic>? parseExtConfig(String? extConfigStr) {
return ExtConfigData.parseRawMap(extConfigStr); return ExtConfigData.parseRawMap(extConfigStr);
} }
/// Facebook `LoginFaild`
static const String facebookLoginFailedEventName = 'LoginFaild';
/// Adjust + `gg``Referer_faild`
static const String facebookReferrerBothFailedEventName = 'Referer_faild';
/// `common_info` `extConfig` `ExtConfigFaild`
static const String facebookExtConfigFailedEventName = 'ExtConfigFaild';
static const int _facebookParamMaxLen = 500;
static String _truncateForFacebookParam(String s) {
final t = s.trim();
if (t.length <= _facebookParamMaxLen) return t;
return '${t.substring(0, _facebookParamMaxLen)}';
}
/// Adjust Google Play [UserApi.referrer]
static void _logFacebookReferrerBothFailed({
required String userId,
required String deviceId,
required int adjustCode,
required String adjustMsg,
required int ggCode,
required String ggMsg,
}) {
final parts = <String>[
if (adjustMsg.trim().isNotEmpty)
'adjust: ${_truncateForFacebookParam(adjustMsg)}',
if (ggMsg.trim().isNotEmpty) 'gg: ${_truncateForFacebookParam(ggMsg)}',
];
final combinedMsg = parts.join(' | ');
final params = <String, dynamic>{
'user_id': userId.trim(),
'device_id': deviceId.trim(),
'server_error_code': '$adjustCode/$ggCode',
if (combinedMsg.isNotEmpty) 'server_error_msg': combinedMsg,
};
FacebookService.logEvent(
facebookReferrerBothFailedEventName,
parameters: params,
);
}
/// [UserApi.getCommonInfo] `extConfig`
static void _logFacebookExtConfigFailed({
required String userId,
required String deviceId,
required int serverCode,
String serverMsg = '',
}) {
final msg = serverMsg.trim();
final params = <String, dynamic>{
'user_id': userId.trim(),
'device_id': deviceId.trim(),
'server_error_code': '$serverCode',
if (msg.isNotEmpty) 'server_error_msg': _truncateForFacebookParam(msg),
};
FacebookService.logEvent(
facebookExtConfigFailedEventName,
parameters: params,
);
}
static void _logFacebookLoginFailedFromResponse(
EntityResponse<FastLoginResponse> res, {
required String deviceId,
}) {
final uid = res.data?.userId?.trim();
final missingUserId = uid == null || uid.isEmpty;
_logFacebookLoginFailed(
serverCode: res.code,
missingUserId: missingUserId,
userIdFromServer: res.data?.userId,
deviceId: deviceId,
);
}
/// ID Meta / Facebook App Events
///
/// - [serverCode] [EntityResponse.code] `-1`
/// - [userIdFromServer] `userId`
/// - [deviceId]使 ID
/// - [missingUserId] `true` `register_faild` = `register faild`
static void _logFacebookLoginFailed({
required int serverCode,
required bool missingUserId,
String? userIdFromServer,
required String deviceId,
}) {
final uid = userIdFromServer?.trim() ?? '';
final did = deviceId.trim();
final params = <String, dynamic>{
'server_error_code': '$serverCode',
'user_id': uid,
'device_id': did,
if (missingUserId) 'register_faild': 'register faild',
};
FacebookService.logEvent(
facebookLoginFailedEventName,
parameters: params,
);
}
} }

View File

@ -6,6 +6,7 @@ import '../entities/image_entities.dart';
import '../log/app_logger.dart'; import '../log/app_logger.dart';
import 'image_api.dart'; import 'image_api.dart';
import 'image_compress.dart'; import 'image_compress.dart';
import 'image_upload_expected_size_cache.dart';
import 'task_upload_cover_store.dart'; import 'task_upload_cover_store.dart';
final _presignedPutLog = AppLogger('PresignedUpload'); final _presignedPutLog = AppLogger('PresignedUpload');
@ -99,6 +100,9 @@ abstract final class ImagePresignedUploadCreateTaskFlow {
} }
final presigned = presignedRes.data!; final presigned = presignedRes.data!;
await ImageUploadExpectedSizeCache.writeImageExpectedSize(
presigned.expectedSize,
);
final uploadUrl = presigned.uploadUrl; final uploadUrl = presigned.uploadUrl;
final filePath = presigned.filePath; final filePath = presigned.filePath;
if (uploadUrl == null || if (uploadUrl == null ||

View File

@ -0,0 +1,44 @@
import 'package:shared_preferences/shared_preferences.dart';
/// [ImageApi.getUploadPresignedUrl] / [FeedbackApi.getUploadPresignedUrl]
/// [UploadPresignedUrlResponse.expectedSize] / [FeedbackUploadPresignedUrlResponse.expectedSize]
/// 使 [fallbackMaxBytes]
abstract final class ImageUploadExpectedSizeCache {
ImageUploadExpectedSizeCache._();
/// `expectedSize` 使
static const int fallbackMaxBytes = 20 * 1024 * 1024;
static const String _kImage =
'client_proxy_image_presigned_expected_size_bytes_v1';
static const String _kFeedback =
'client_proxy_feedback_presigned_expected_size_bytes_v1';
static Future<int> readImageMaxBytesForUi() async {
final p = await SharedPreferences.getInstance();
final v = p.getInt(_kImage);
if (v != null && v > 0) return v;
return fallbackMaxBytes;
}
static Future<int> readFeedbackMaxBytesForUi() async {
final p = await SharedPreferences.getInstance();
final v = p.getInt(_kFeedback);
if (v != null && v > 0) return v;
return fallbackMaxBytes;
}
/// [bytes]
static Future<void> writeImageExpectedSize(int? bytes) async {
if (bytes == null || bytes <= 0) return;
final p = await SharedPreferences.getInstance();
await p.setInt(_kImage, bytes);
}
/// [bytes]
static Future<void> writeFeedbackExpectedSize(int? bytes) async {
if (bytes == null || bytes <= 0) return;
final p = await SharedPreferences.getInstance();
await p.setInt(_kFeedback, bytes);
}
}

View File

@ -0,0 +1,43 @@
import 'package:shared_preferences/shared_preferences.dart';
/// [FrameworkAuthService] 使 ID ID线
///
/// token
abstract final class LoginIdentityCache {
LoginIdentityCache._();
static const String _kUserId = 'client_proxy_framework_cached_login_user_id_v1';
static const String _kDeviceId =
'client_proxy_framework_cached_login_device_id_v1';
/// `userId` `null`
static Future<String?> readCachedUserId() async {
final p = await SharedPreferences.getInstance();
final v = p.getString(_kUserId)?.trim();
if (v == null || v.isEmpty) return null;
return v;
}
/// `deviceId` `null`
static Future<String?> readCachedDeviceId() async {
final p = await SharedPreferences.getInstance();
final v = p.getString(_kDeviceId)?.trim();
if (v == null || v.isEmpty) return null;
return v;
}
/// [userId]
static Future<void> writeUserAndDeviceId({
required String userId,
required String deviceId,
}) async {
final uid = userId.trim();
if (uid.isEmpty) return;
final did = deviceId.trim();
final p = await SharedPreferences.getInstance();
await p.setString(_kUserId, uid);
if (did.isNotEmpty) {
await p.setString(_kDeviceId, did);
}
}
}

View File

@ -7,7 +7,7 @@ import '../entities/user_entities.dart';
/// API**** [FieldMapping] 线 /// API**** [FieldMapping] 线
/// ///
/// **** [UserApi.fastLogin] [ProxyClient] /// **** [UserApi.fastLogin] [ProxyClient]
/// `pkg` `User_token` token fast_login `pkg` token /// `pkg` `User_token` token **fast_login** `pkg` `User_token`
/// ///
/// ****使**** `referer``deviceId` /// ****使**** `referer``deviceId`
abstract final class UserApi { abstract final class UserApi {
@ -15,8 +15,8 @@ abstract final class UserApi {
/// ///
/// ///
/// ****`pkg` `User_token` /// **** `pkg` `User_token` [ProxyClient.request] `includeUserTokenInHeader`
/// **Query**`app``type` `fb``pkg``ch` /// **Query**`app``type` `gg`Google Play Install Referrer `pkg``ch`
/// **Body**`referer``sign``deviceId` /// **Body**`referer``sign``deviceId`
static Future<EntityResponse<FastLoginResponse>> fastLogin({ static Future<EntityResponse<FastLoginResponse>> fastLogin({
required String deviceId, required String deviceId,
@ -34,7 +34,7 @@ abstract final class UserApi {
queryParams: { queryParams: {
if (ch != null && ch.isNotEmpty) 'ch': ch, if (ch != null && ch.isNotEmpty) 'ch': ch,
'pkg': config.packageName, 'pkg': config.packageName,
'type': type ?? 'fb', 'type': type ?? 'gg',
'app': app, 'app': app,
}, },
body: { body: {