petsHero-AI/lib/core/referrer/referrer_service.dart
2026-03-30 01:39:47 +08:00

139 lines
4.8 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:adjust_sdk/adjust_attribution.dart';
import 'package:adjust_sdk/adjust.dart';
import 'package:flutter/foundation.dart';
import 'package:play_install_referrer/play_install_referrer.dart';
/// 归因信息服务(优先从 Adjust 获取fallback 使用 Play Install Referrer
class ReferrerService {
ReferrerService._();
static String? _cachedReferrer;
static String _referrerSource = 'gg';
static final Completer<String?> _completer = Completer<String?>();
/// attribution 回调的 Completer与 getAttributionWithTimeout 竞速,谁先返回用谁
static final Completer<AdjustAttribution?> _attributionCallbackCompleter =
Completer<AdjustAttribution?>();
/// 过长会拖慢 [AuthService.init] 内首次 getReferrer过短可能拿不到 Adjust仍有 Play Install Referrer fallback
static const int _adjustTimeoutMs = 4500;
/// 由 Adjust attributionCallback 调用,首次安装时归因往往通过此回调返回(晚于 getAttributionWithTimeout
static void receiveAttributionFromCallback(AdjustAttribution attribution) {
if (!_attributionCallbackCompleter.isCompleted) {
_attributionCallbackCompleter.complete(attribution);
}
}
/// 归因来源Adjust 为 android_adjustPlay Install Referrer 为 gg
static String get referrerSource => _referrerSource;
/// JSON 可编码的 costAmount保留字段非有限数字用字符串避免 encode 抛错
static Object? _jsonEncodableCostAmount(num? v) {
if (v == null) return null;
if (v is double && !v.isFinite) return v.toString();
return v;
}
/// 将完整 AdjustAttribution 序列化为 JSON供 digest / Base64不省略任何键
static String _attributionToDigest(AdjustAttribution attr) {
final map = <String, dynamic>{
'trackerToken': attr.trackerToken,
'trackerName': attr.trackerName,
'network': attr.network,
'campaign': attr.campaign,
'adgroup': attr.adgroup,
'creative': attr.creative,
'clickLabel': attr.clickLabel,
'costType': attr.costType,
'costAmount': _jsonEncodableCostAmount(attr.costAmount),
'costCurrency': attr.costCurrency,
'jsonResponse': attr.jsonResponse,
'fbInstallReferrer': attr.fbInstallReferrer,
};
try {
return jsonEncode(map);
} catch (_) {
return '';
}
}
/// 获取 referrer/digest优先从 Adjust 归因获取fallback 使用 Play Install Referrer
/// 同时监听 attributionCallback首次安装时归因往往通过回调返回可能晚于 getAttributionWithTimeout
static Future<String?> getReferrer() async {
if (_cachedReferrer != null) return _cachedReferrer;
if (_completer.isCompleted) return _completer.future;
var digest = '';
try {
// 竞速getAttributionWithTimeout 与 attribution 回调,谁先返回用谁
final attribution = await Future.any<AdjustAttribution?>([
(() async {
try {
return await Adjust.getAttributionWithTimeout(_adjustTimeoutMs);
} catch (_) {
return null;
}
})(),
_attributionCallbackCompleter.future,
]);
if (attribution != null) {
final raw = _attributionToDigest(attribution);
if (raw.isNotEmpty) {
_referrerSource = 'android_adjust';
digest = base64Encode(utf8.encode(raw));
}
}
} catch (_) {
digest = '';
}
if (digest.isEmpty) {
_referrerSource = 'gg';
if (defaultTargetPlatform == TargetPlatform.android) {
try {
final details = await PlayInstallReferrer.installReferrer;
digest = details.installReferrer ?? '';
} catch (_) {
digest = '';
}
}
}
_cachedReferrer = digest;
if (!_completer.isCompleted) _completer.complete(_cachedReferrer);
return _cachedReferrer;
}
/// 初始化并缓存 referrer建议在 app 启动时调用)
static Future<void> init() async {
await getReferrer();
}
/// 仅 Adjust 归因 digestBase64 JSON用于 /v1/user/referrer accolade=android_adjust
static Future<String> getAdjustReferrerDigest() async {
try {
final attr = await Adjust.getAttribution();
final raw = _attributionToDigest(attr);
if (raw.isEmpty) return '';
return base64Encode(utf8.encode(raw));
} catch (_) {
return '';
}
}
/// 仅 Google Play Install Referrer 字符串,用于 /v1/user/referrer accolade=gg
static Future<String> getGgReferrerDigest() async {
if (defaultTargetPlatform != TargetPlatform.android) return '';
try {
final details = await PlayInstallReferrer.installReferrer;
return details.installReferrer ?? '';
} catch (_) {
return '';
}
}
}