client_framework/lib/src/services/analytics_events.dart
2026-04-16 17:00:55 +08:00

140 lines
5.0 KiB
Dart
Raw Permalink 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 '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);
}
}