diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 6452503..7d0ea3f 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -2,6 +2,7 @@ + ) +#import +#else +@import shared_preferences_foundation; +#endif + #if __has_include() #import #else @@ -55,6 +61,7 @@ [FPPDeviceInfoPlusPlugin registerWithRegistrar:[registry registrarForPlugin:@"FPPDeviceInfoPlusPlugin"]]; [GalPlugin registerWithRegistrar:[registry registrarForPlugin:@"GalPlugin"]]; [FLTImagePickerPlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTImagePickerPlugin"]]; + [SharedPreferencesPlugin registerWithRegistrar:[registry registrarForPlugin:@"SharedPreferencesPlugin"]]; [SqflitePlugin registerWithRegistrar:[registry registrarForPlugin:@"SqflitePlugin"]]; [FVPVideoPlayerPlugin registerWithRegistrar:[registry registrarForPlugin:@"FVPVideoPlayerPlugin"]]; [VideoThumbnailPlugin registerWithRegistrar:[registry registrarForPlugin:@"VideoThumbnailPlugin"]]; diff --git a/lib/core/adjust/adjust_events.dart b/lib/core/adjust/adjust_events.dart new file mode 100644 index 0000000..8c74582 --- /dev/null +++ b/lib/core/adjust/adjust_events.dart @@ -0,0 +1,112 @@ +import 'package:adjust_sdk/adjust.dart'; +import 'package:adjust_sdk/adjust_event.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +/// Adjust 事件埋点(识别码见 docs/adjuest.md) +abstract final class AdjustEvents { + // 购买档位(充值页选择档位时上报) + static const String tier1999 = 'm0r9u9'; // 19.99 + static const String tier4999 = 'aht1ve'; // 49.99 + static const String tier599 = '47hhx9'; // 5.99 + static const String tier999 = 'w775gd'; // 9.99 + static const String tier9999 = 'y7994m'; // 99.99 + + // 首日充值 + static const String firstPurchase = 'r6w4bi'; + + // 支付失败 + static const String orderAbnormal = '4txlo1'; + + // 支付成功 + static const String purchase = 'ek780r'; + + // 注册(首次调用 fast_login 成功时) + static const String register = '2z3dm4'; + + // 其他 + static const String monthlyVip = '96o5ez'; // PetsHero AI Monthly VIP + static const String weeklyVip = '95yg4o'; // PetsHero AI Weekly VIP + + /// 根据金额(如 19.99, 9.99)返回档位事件 token,无法匹配时返回 purchase + static String? tierTokenFromPrice(num price) { + final p = price.toStringAsFixed(2); + switch (p) { + case '5.99': + return tier599; + case '9.99': + return tier999; + case '19.99': + return tier1999; + case '49.99': + return tier4999; + case '99.99': + return tier9999; + default: + return purchase; + } + } + + /// 从金额字符串解析数字(如 "¥19.99" / "\$19.99") + static num? parsePrice(String amount) { + final match = RegExp(r'[\d.]+').firstMatch(amount); + if (match == null) return null; + return num.tryParse(match.group(0)!); + } + + static void _track(String eventToken) { + Adjust.trackEvent(AdjustEvent(eventToken)); + } + + /// 购买档位(用户点击某档位购买时) + static void trackTier(String eventToken) { + _track(eventToken); + } + + /// 首日充值 + static void trackFirstPurchase() { + _track(firstPurchase); + } + + /// 支付失败 + static void trackOrderAbnormal() { + _track(orderAbnormal); + } + + /// 支付成功(若为首日充值需额外调用 trackFirstPurchase) + static void trackPurchase() { + _track(purchase); + } + + /// 注册(首次 fast_login 成功) + static void trackRegister() { + _track(register); + } + + /// PetsHero AI Monthly VIP + static void trackMonthlyVip() { + _track(monthlyVip); + } + + /// PetsHero AI Weekly VIP + static void trackWeeklyVip() { + _track(weeklyVip); + } + + static const String _keyRegisterDate = 'adjust_register_date'; + + /// 支付成功时调用:上报 Purchase,若为首日充值则同时上报 first purchase + static Future trackPurchaseSuccess() async { + _track(purchase); + final prefs = await SharedPreferences.getInstance(); + final registerDate = prefs.getString(_keyRegisterDate); + if (registerDate != null && + registerDate == DateTime.now().toIso8601String().substring(0, 10)) { + _track(firstPurchase); + } + } + + /// 支付失败时调用 + static void trackPaymentFailed() { + _track(orderAbnormal); + } +} diff --git a/lib/core/auth/auth_service.dart b/lib/core/auth/auth_service.dart index 8d82646..808cda6 100644 --- a/lib/core/auth/auth_service.dart +++ b/lib/core/auth/auth_service.dart @@ -4,7 +4,9 @@ import 'dart:convert'; import 'package:crypto/crypto.dart'; import 'package:device_info_plus/device_info_plus.dart'; import 'package:flutter/foundation.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import '../adjust/adjust_events.dart'; import '../api/api_client.dart'; import '../api/proxy_client.dart'; import '../api/services/user_api.dart'; @@ -108,6 +110,17 @@ class AuthService { } else { _log('init: 响应中无 reevaluate (userToken)'); } + final prefs = await SharedPreferences.getInstance(); + final hadLoggedIn = prefs.getBool('adjust_has_logged_in') ?? false; + if (!hadLoggedIn) { + AdjustEvents.trackRegister(); + await prefs.setBool('adjust_has_logged_in', true); + await prefs.setString( + 'adjust_register_date', + DateTime.now().toIso8601String().substring(0, 10), + ); + _log('init: 首次登录,已上报 register'); + } final credits = data?['reveal'] as int?; if (credits != null) { UserState.setCredits(credits); diff --git a/lib/features/home/home_screen.dart b/lib/features/home/home_screen.dart index 1ed84b9..5e4a131 100644 --- a/lib/features/home/home_screen.dart +++ b/lib/features/home/home_screen.dart @@ -88,7 +88,7 @@ class _HomeScreenState extends State { appBar: PreferredSize( preferredSize: const Size.fromHeight(56), child: TopNavBar( - title: 'AI Video', + title: 'PetsHero AI', credits: UserCreditsData.of(context)?.creditsDisplay ?? '--', onCreditsTap: () => Navigator.of(context).pushNamed('/recharge'), ), @@ -101,7 +101,9 @@ class _HomeScreenState extends State { vertical: AppSpacing.xs, ), child: _categoriesLoading - ? const SizedBox(height: 40, child: Center(child: CircularProgressIndicator())) + ? const SizedBox( + height: 40, + child: Center(child: CircularProgressIndicator())) : HomeTabRow( categories: _categories, selectedId: _selectedCategory?.id ?? -1, @@ -137,7 +139,8 @@ class _HomeScreenState extends State { ? task.credits480p.toString() : '50'; return VideoCard( - imageUrl: task.previewImageUrl ?? _placeholderImage, + imageUrl: + task.previewImageUrl ?? _placeholderImage, videoUrl: task.previewVideoUrl, credits: credits, isActive: _activeCardIndex == index, @@ -147,9 +150,9 @@ class _HomeScreenState extends State { setState(() => _activeCardIndex = null), onGenerateSimilar: () => Navigator.of(context).pushNamed( - '/generate', - arguments: task, - ), + '/generate', + arguments: task, + ), ); }, ), @@ -162,5 +165,4 @@ class _HomeScreenState extends State { ), ); } - } diff --git a/lib/features/recharge/recharge_screen.dart b/lib/features/recharge/recharge_screen.dart index 0b7ca15..9cb4813 100644 --- a/lib/features/recharge/recharge_screen.dart +++ b/lib/features/recharge/recharge_screen.dart @@ -2,6 +2,7 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_lucide/flutter_lucide.dart'; +import '../../core/adjust/adjust_events.dart'; import '../../core/api/services/payment_api.dart'; import '../../core/auth/auth_service.dart'; import '../../core/theme/app_colors.dart'; @@ -80,7 +81,20 @@ class _RechargeScreenState extends State { } void _onBuy(ActivityItem item) { - // TODO: 创建支付订单 + final price = AdjustEvents.parsePrice(item.actualAmount); + if (price != null) { + final tierToken = AdjustEvents.tierTokenFromPrice(price); + if (tierToken != null) { + AdjustEvents.trackTier(tierToken); + } + } + final titleLower = item.title.toLowerCase(); + if (titleLower.contains('monthly vip')) { + AdjustEvents.trackMonthlyVip(); + } else if (titleLower.contains('weekly vip')) { + AdjustEvents.trackWeeklyVip(); + } + // TODO: 创建支付订单;成功时调用 AdjustEvents.trackPurchaseSuccess(),失败时调用 AdjustEvents.trackPaymentFailed() } @override diff --git a/pubspec.yaml b/pubspec.yaml index 3c0a3fa..1d86448 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -24,6 +24,7 @@ dependencies: video_thumbnail: ^0.5.3 gal: ^2.3.0 path_provider: ^2.1.2 + shared_preferences: ^2.2.2 flutter_cache_manager: ^3.3.1 dev_dependencies: