140 lines
5.0 KiB
Dart
140 lines
5.0 KiB
Dart
import 'dart:async';
|
||
|
||
import 'package:flutter/foundation.dart';
|
||
import 'package:shared_preferences/shared_preferences.dart';
|
||
|
||
import '../bootstrap/client_bootstrap.dart';
|
||
import '../config/skin_config.dart';
|
||
import '../entities/payment_entities.dart';
|
||
import 'analytics_service.dart';
|
||
import 'facebook_service.dart';
|
||
import 'payment_flow/payment_flow_models.dart';
|
||
|
||
/// 宿主侧业务埋点(Adjust token 来自 `skin_config.json` 的 `adjustEvents` + Facebook App Events)。
|
||
///
|
||
/// 须在 [ClientBootstrap.initFromAsset] / [ClientBootstrap.initFromJson] 与 [ClientBootstrap.initAnalytics] 之后调用。
|
||
/// 行为对齐常见 app 线(如 app_client [AdjustEvents]):档位价、首日充值标记、支付成功/失败。
|
||
abstract final class AnalyticsEvents {
|
||
AnalyticsEvents._();
|
||
|
||
static const _prefsRegisterDayKey =
|
||
'client_proxy_framework_skin_analytics_register_day';
|
||
|
||
/// 历史宿主键(FunyMee 曾用),仅用于读取兼容。
|
||
static const _prefsLegacyRegisterDayKey = 'funymee_analytics_register_date';
|
||
|
||
static SkinConfig get _skin => ClientBootstrap.skin;
|
||
|
||
/// 从金额文案解析数字(如 `"¥19.99"` / `"\$9.99"`)。
|
||
static num? parsePrice(String? amount) {
|
||
if (amount == null || amount.trim().isEmpty) return null;
|
||
final m = RegExp(r'[\d.]+').firstMatch(amount);
|
||
if (m == null) return null;
|
||
return num.tryParse(m.group(0)!);
|
||
}
|
||
|
||
static String? _tierLogicalNameForPrice(num price) {
|
||
final s = price.toStringAsFixed(2);
|
||
switch (s) {
|
||
case '5.99':
|
||
return 'price_599';
|
||
case '9.99':
|
||
return 'price_999';
|
||
case '19.99':
|
||
return 'price_1999';
|
||
case '49.99':
|
||
return 'price_4999';
|
||
case '99.99':
|
||
return 'price_9999';
|
||
default:
|
||
return null;
|
||
}
|
||
}
|
||
|
||
/// 用户点击某档位发起支付前(按价格匹配 `adjustEvents.price_*`,否则 `purchase`)。
|
||
static void trackTierSelection(PaymentProductItem item) {
|
||
final p = parsePrice(item.actualAmount);
|
||
if (p == null) return;
|
||
final name = _tierLogicalNameForPrice(p);
|
||
if (name != null) {
|
||
_skin.trackAdjustEvent(name);
|
||
} else {
|
||
_skin.trackAdjustEvent('purchase');
|
||
}
|
||
}
|
||
|
||
/// [FastLoginResponse.firstRegister] 为 true 时调用。
|
||
static Future<void> trackRegisterIfNeeded({required bool firstRegister}) async {
|
||
if (!firstRegister) return;
|
||
_skin.trackAdjustEvent('register');
|
||
AnalyticsService.trackRegister();
|
||
final prefs = await SharedPreferences.getInstance();
|
||
await prefs.setString(
|
||
_prefsRegisterDayKey,
|
||
DateTime.now().toIso8601String().substring(0, 10),
|
||
);
|
||
if (kDebugMode) {
|
||
debugPrint('[AnalyticsEvents] register + first-day marker set');
|
||
}
|
||
}
|
||
|
||
/// 支付成功:Adjust `purchase`;若注册当日则 `firstPurchase` + Facebook `FirstRecharge`(与 app_client [AdjustEvents.trackFirstPurchase] 一致);Facebook 标准购买见 [AnalyticsService.trackPurchase]。
|
||
static Future<void> trackPurchaseSuccess(double amount) async {
|
||
_skin.trackAdjustEvent('purchase');
|
||
final prefs = await SharedPreferences.getInstance();
|
||
final regDate = prefs.getString(_prefsRegisterDayKey) ??
|
||
prefs.getString(_prefsLegacyRegisterDayKey);
|
||
final today = DateTime.now().toIso8601String().substring(0, 10);
|
||
if (regDate != null && regDate == today) {
|
||
_skin.trackAdjustEvent('firstPurchase');
|
||
FacebookService.logEvent(
|
||
'FirstRecharge',
|
||
parameters: <String, dynamic>{'amount': amount},
|
||
);
|
||
}
|
||
if (amount > 0) {
|
||
AnalyticsService.trackPurchase(amount: amount, currency: 'USD');
|
||
}
|
||
}
|
||
|
||
/// 使用 [PaymentProductItem.actualAmount] 解析金额后 [trackPurchaseSuccess]。
|
||
static Future<void> trackPurchaseSuccessForProduct(
|
||
PaymentProductItem? item) async {
|
||
final raw = parsePrice(item?.actualAmount);
|
||
final amount = raw?.toDouble() ?? 0.0;
|
||
await trackPurchaseSuccess(amount);
|
||
}
|
||
|
||
/// 支付失败 / 下单失败(Adjust `paymentFailed` + FB `payment_failed`)。
|
||
static void trackPaymentFailed() {
|
||
_skin.trackAdjustEvent('paymentFailed');
|
||
FacebookService.logEvent('payment_failed');
|
||
}
|
||
|
||
/// 根据支付编排结果 [PaymentSettlement] 自动埋点(成功 → [trackPurchaseSuccessForProduct];失败 → [trackPaymentFailed])。
|
||
///
|
||
/// 取消、超时、[PaymentFlowOutcomeType.nativePendingHostVerification] 不打点。
|
||
static void trackPaymentSettlement(
|
||
PaymentSettlement settlement, {
|
||
PaymentProductItem? product,
|
||
}) {
|
||
switch (settlement.type) {
|
||
case PaymentFlowOutcomeType.success:
|
||
unawaited(trackPurchaseSuccessForProduct(product));
|
||
break;
|
||
case PaymentFlowOutcomeType.failure:
|
||
trackPaymentFailed();
|
||
break;
|
||
case PaymentFlowOutcomeType.cancelled:
|
||
case PaymentFlowOutcomeType.timeout:
|
||
case PaymentFlowOutcomeType.nativePendingHostVerification:
|
||
break;
|
||
}
|
||
}
|
||
|
||
/// Facebook 自定义事件。
|
||
static void trackCustomEvent(String name, {Map<String, dynamic>? parameters}) {
|
||
FacebookService.logEvent(name, parameters: parameters);
|
||
}
|
||
}
|