import 'dart:async'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:in_app_purchase/in_app_purchase.dart'; import 'package:video_player/video_player.dart'; import '../../../../config/theme/dark_theme_colors.dart'; import '../../../../config/translations/strings_enum.dart'; import '../../../../utils/in_app_purchase_util.dart'; import '../../../../utils/log/logger.dart'; import '../../../components/ix_snackbar.dart'; import '../../../constants/assets.dart'; import '../../../controllers/api_controller.dart'; import '../../../data/models/channelplan/channel_plan_list.dart'; import '../../../data/sp/ix_sp.dart'; import '../../../dialog/loading/loading_dialog.dart'; class SubscriptionController extends GetxController { static const String TAG = 'SubscriptionController'; final ApiController _apiController = Get.find(); // 内购工具实例 final InAppPurchaseUtil _iapUtil = InAppPurchaseUtil.instance; // 视频播放器控制器 late VideoPlayerController videoController; final isVideoInitialized = false.obs; // 套餐列表加载状态 final isLoadingPlans = false.obs; // 产品加载状态 final isLoadingProducts = false.obs; // 购买处理中状态 final isPurchasing = false.obs; // 产品列表(内购产品) final productDetails = [].obs; // 套餐列表(API 返回) final channelPlans = [].obs; // 当前选中的订阅计划索引 final selectedPlanIndex = 0.obs; /// 是否显示套餐变更信息(仅 userLevel == 3 时显示) bool get showPlanChangeInfo { final user = IXSP.getUser(); return user?.userLevel == 3; } // 获取产品价格(优先使用内购价格,否则使用 API 返回的价格) String _getDisplayPrice(ChannelPlan plan) { // 尝试从内购获取价格 final productId = plan.payoutData; if (productId != null && productId.isNotEmpty) { final product = _iapUtil.getProductById(productId); if (product != null) { return product.price; } } // 使用 API 返回的价格 return _formatPrice(plan.price, plan.currency); } // 格式化价格 String _formatPrice(double? price, int? currency) { if (price == null) return ''; // currency: 1=USD, 2=CNY, etc. 根据实际情况调整 final symbol = currency == 2 ? '¥' : '\$'; return '$symbol${price.toStringAsFixed(2)}'; } // 获取订阅周期显示文本 String _getPeriodText(ChannelPlan plan) { if (plan.isSubscribe != true) { return Strings.once.tr; // 一次性购买(终身) } // 根据 subscribeType 和 subscribePeriodValue 确定周期 // subscribeType: 1=天, 2=周, 3=月, 4=年 switch (plan.subscribeType) { case 1: final days = plan.subscribePeriodValue ?? 1; return '/$days day${days > 1 ? 's' : ''}'; case 2: return Strings.perWeek.tr; case 3: return '/month'; case 4: return Strings.perYear.tr; default: return ''; } } // 获取标签背景色 Color? _getBadgeBgColor(ChannelPlan plan) { if (plan.tagType == 1) { return DarkThemeColors.bg1; } if (plan.tag != null && plan.tag!.isNotEmpty) { return DarkThemeColors.primaryColor; } return null; } // 获取标签文字颜色 Color? _getBadgeTextColor(ChannelPlan plan) { if (plan.tagType == 1) { return DarkThemeColors.subscriptionColor; } if (plan.tag != null && plan.tag!.isNotEmpty) { return Colors.white; } return null; } @override void onInit() { super.onInit(); _initializeVideoPlayer(); _initializeInAppPurchase(); _getChannelPlanList(); } @override void onClose() { videoController.dispose(); // 注意: 不要在这里调用 _iapUtil.dispose() // 因为它是单例,可能在其他地方还在使用 super.onClose(); } /// 获取套餐列表 Future _getChannelPlanList() async { isLoadingPlans.value = true; try { final plans = await _apiController.getChannelPlanList(); channelPlans.value = plans; // 设置默认选中项 final defaultIndex = plans.indexWhere((p) => p.isDefault == true); if (defaultIndex >= 0) { selectedPlanIndex.value = defaultIndex; } // 加载对应的内购产品 await _loadProductsFromPlans(); } catch (e) { log(TAG, '获取套餐列表失败: $e'); IXSnackBar.showIXSnackBar(title: Strings.error.tr, message: '获取套餐列表失败'); } finally { isLoadingPlans.value = false; } } /// 购买套餐 Future subscribe() async { await LoadingDialog.show( context: Get.context!, loadingText: 'Purchasing...', successText: 'Purchase successful', onRequest: () async { // 执行你的异步请求 await _apiController.subscribe({ 'channelItemId': selectedPlan?.channelItemId, }); }, onSuccess: () { // 成功后的操作 }, ); } /// 初始化内购 Future _initializeInAppPurchase() async { try { // 初始化内购 final success = await _iapUtil.initialize( onSuccess: _handlePurchaseSuccess, onError: _handlePurchaseError, onCancelled: _handlePurchaseCancelled, onPending: _handlePurchasePending, onRestore: _handleRestoreSuccess, ); if (!success) { IXSnackBar.showIXSnackBar(title: Strings.error.tr, message: '内购功能不可用'); } } catch (e) { debugPrint('初始化内购失败: $e'); } } /// 根据套餐列表加载内购产品 Future _loadProductsFromPlans() async { if (channelPlans.isEmpty) return; isLoadingProducts.value = true; try { // 从套餐列表中提取产品ID final productIdSet = channelPlans .where((p) => p.payoutData != null && p.payoutData!.isNotEmpty) .map((p) => p.payoutData!) .toSet(); if (productIdSet.isEmpty) { debugPrint('没有需要加载的内购产品'); return; } final success = await _iapUtil.loadProducts(productIdSet); if (success) { productDetails.value = _iapUtil.products; debugPrint('加载了 ${productDetails.length} 个产品'); // 刷新套餐列表以更新价格显示 channelPlans.refresh(); } } catch (e) { debugPrint('加载产品失败: $e'); } finally { isLoadingProducts.value = false; } } /// 处理购买成功 void _handlePurchaseSuccess(PurchaseDetails purchaseDetails) { isPurchasing.value = false; IXSnackBar.showIXSnackBar( title: Strings.success.tr, message: '购买成功: ${purchaseDetails.productID}', ); // TODO: 这里添加你的业务逻辑 // 1. 更新用户订阅状态到服务器 // 2. 更新本地存储 // 3. 刷新 UI 显示 } /// 处理购买失败 void _handlePurchaseError(PurchaseDetails purchaseDetails) { isPurchasing.value = false; IXSnackBar.showIXSnackBar( title: Strings.error.tr, message: '购买失败: ${purchaseDetails.error?.message ?? "未知错误"}', ); } /// 处理购买取消 void _handlePurchaseCancelled(PurchaseDetails purchaseDetails) { isPurchasing.value = false; IXSnackBar.showIXSnackBar(title: Strings.info.tr, message: '购买已取消'); } /// 处理购买等待中 void _handlePurchasePending(PurchaseDetails purchaseDetails) { isPurchasing.value = true; IXSnackBar.showIXSnackBar(title: Strings.info.tr, message: '购买处理中...'); } /// 处理恢复购买成功 void _handleRestoreSuccess(PurchaseDetails purchaseDetails) { IXSnackBar.showIXSnackBar( title: Strings.success.tr, message: '恢复购买成功: ${purchaseDetails.productID}', ); // TODO: 更新用户订阅状态 } /// 发起购买 Future _requestPurchase(String productId) async { if (isPurchasing.value) { debugPrint('已有购买正在进行中'); return; } isPurchasing.value = true; try { final success = await _iapUtil.purchaseProductById(productId); if (!success) { isPurchasing.value = false; IXSnackBar.showIXSnackBar( title: Strings.error.tr, message: '发起购买失败,请重试', ); } } catch (e) { isPurchasing.value = false; debugPrint('购买异常: $e'); IXSnackBar.showIXSnackBar(title: Strings.error.tr, message: '购买异常: $e'); } } /// 恢复购买 Future _restorePurchases() async { try { final success = await _iapUtil.restorePurchases(); if (success) { IXSnackBar.showIXSnackBar( title: Strings.info.tr, message: Strings.restoringPurchases.tr, ); } else { IXSnackBar.showIXSnackBar(title: Strings.error.tr, message: '恢复购买失败'); } } catch (e) { debugPrint('恢复购买异常: $e'); IXSnackBar.showIXSnackBar(title: Strings.error.tr, message: '恢复购买异常: $e'); } } // 初始化视频播放器 void _initializeVideoPlayer() { videoController = VideoPlayerController.asset(Assets.subscriptionBg) ..initialize() .then((_) { isVideoInitialized.value = true; videoController.setLooping(true); videoController.setVolume(0); // 静音播放 videoController.play(); }) .catchError((error) { print('视频初始化失败: $error'); }); } // 选择订阅计划 void selectPlan(int index) { if (index >= 0 && index < channelPlans.length) { selectedPlanIndex.value = index; } } // 获取当前选中的套餐 ChannelPlan? get selectedPlan { if (selectedPlanIndex.value < channelPlans.length) { return channelPlans[selectedPlanIndex.value]; } return null; } /// 获取当前选中套餐的设备限制数量 String get selectedPlanDeviceLimit { return (selectedPlan?.deviceLimit ?? 0).toString(); } // 确认变更/购买 void confirmChange() { final plan = selectedPlan; if (plan == null) { IXSnackBar.showIXSnackBar(title: Strings.error.tr, message: '请选择套餐'); return; } final productId = plan.payoutData; if (productId == null || productId.isEmpty) { IXSnackBar.showIXSnackBar(title: Strings.error.tr, message: '产品ID未配置'); return; } _requestPurchase(productId); } // 恢复购买 void restorePurchases() { _restorePurchases(); } // 支付问题 void handlePaymentIssue() { // TODO: 实现支付问题处理逻辑 IXSnackBar.showIXSnackBar( title: Strings.info.tr, message: Strings.openingPaymentSupport.tr, ); } // ==================== 用于 UI 展示的便捷方法 ==================== /// 获取套餐数量 int get planCount => channelPlans.length; /// 获取指定索引的套餐标题 String getPlanTitle(int index) { if (index < channelPlans.length) { return channelPlans[index].title ?? ''; } return ''; } /// 获取指定索引的套餐副标题 String getPlanSubTitle(int index) { if (index < channelPlans.length) { return channelPlans[index].subTitle ?? ''; } return ''; } /// 获取指定索引的套餐介绍 String getPlanIntroduce(int index) { if (index < channelPlans.length) { return channelPlans[index].introduce ?? ''; } return ''; } /// 获取指定索引的套餐价格显示 String getPlanPrice(int index) { if (index < channelPlans.length) { return _getDisplayPrice(channelPlans[index]); } return ''; } /// 获取指定索引的套餐周期显示 String getPlanPeriod(int index) { if (index < channelPlans.length) { return _getPeriodText(channelPlans[index]); } return ''; } /// 获取指定索引的套餐标签 String getPlanBadge(int index) { if (index < channelPlans.length) { return channelPlans[index].tag ?? ''; } return ''; } /// 获取指定索引的标签背景色 Color? getPlanBadgeBgColor(int index) { if (index < channelPlans.length) { return _getBadgeBgColor(channelPlans[index]); } return null; } /// 获取指定索引的标签文字颜色 Color? getPlanBadgeTextColor(int index) { if (index < channelPlans.length) { return _getBadgeTextColor(channelPlans[index]); } return null; } /// 获取指定索引的标签边框颜色 Color? getPlanBadgeBorderColor(int index) { if (index < channelPlans.length) { final plan = channelPlans[index]; if (plan.recommend == true) { return DarkThemeColors.dividerColor; } } return null; } /// 是否显示原价(有折扣时显示) bool showOriginalPrice(int index) { if (index < channelPlans.length) { final plan = channelPlans[index]; return plan.orgPrice != null && plan.price != null && plan.orgPrice! > plan.price!; } return false; } /// 获取原价显示 String getPlanOriginalPrice(int index) { if (index < channelPlans.length) { final plan = channelPlans[index]; return _formatPrice(plan.orgPrice, plan.currency); } return ''; } }