import 'dart:async'; import 'package:get/get.dart'; import 'package:kaer_with_panels/app/model/response/kr_node_group_list.dart'; import 'package:kaer_with_panels/app/model/response/kr_user_available_subscribe.dart'; import 'package:kaer_with_panels/app/services/api_service/kr_subscribe_api.dart'; import 'package:kaer_with_panels/app/services/singbox_imp/kr_sing_box_imp.dart'; import 'package:kaer_with_panels/app/utils/kr_log_util.dart'; import 'package:kaer_with_panels/app/utils/kr_common_util.dart'; import 'package:kaer_with_panels/app/localization/app_translations.dart'; import '../../singbox/model/singbox_status.dart'; import '../model/business/kr_group_outbound_list.dart'; import '../model/business/kr_outbound_item.dart'; import '../model/business/kr_outbounds_list.dart'; import '../model/response/kr_already_subscribe.dart'; /// 首页列表视图状态枚举 enum KRSubscribeServiceStatus { kr_none, kr_loading, kr_error, kr_success } /// 订阅服务类 /// 用于管理用户订阅相关的所有操作 class KRSubscribeService { /// 单例实例 static final KRSubscribeService _instance = KRSubscribeService._internal(); /// 工厂构造函数 factory KRSubscribeService() => _instance; /// 私有构造函数 KRSubscribeService._internal() {} /// 订阅API final KRSubscribeApi kr_subscribeApi = KRSubscribeApi(); /// 可用订阅列表 final RxList kr_availableSubscribes = [].obs; /// 当前选中的订阅 final Rx kr_currentSubscribe = Rx(null); /// 节点分组列表 final RxList kr_nodeGroups = [].obs; /// 服务器分组 final RxList groupOutboundList = [].obs; /// 国家分组,包含所有国家 final RxList countryOutboundList = [].obs; /// 全部列表 final RxList allList = [].obs; /// 标签列表 Map keyList = {}; // 存储国家分组的列表 /// 试用剩余时间 final RxString kr_trialRemainingTime = ''.obs; /// 订阅剩余时间 final RxString kr_subscriptionRemainingTime = ''.obs; /// 剩余时间 final RxString remainingTime = ''.obs; /// 是否处于试用状态 final RxBool kr_isTrial = false.obs; /// 当前节点列表是否包含试用节点 final RxBool kr_hasTrialNodes = false.obs; /// 订阅记录 final RxList kr_alreadySubscribe = [].obs; /// 是否处于订阅最后一天 final RxBool kr_isLastDayOfSubscription = false.obs; /// 定期更新计时器 Timer? _kr_updateTimer; /// 试用倒计时计时器 Timer? _kr_trialTimer; /// 订阅倒计时计时器 Timer? _kr_subscriptionTimer; /// 当前状态 final kr_currentStatus = KRSubscribeServiceStatus.kr_none.obs; /// 重置订阅周期 Future kr_resetSubscribePeriod() async { if (kr_currentSubscribe.value == null) { KRCommonUtil.kr_showToast('请先选择订阅'); return; } final result = await kr_subscribeApi .kr_resetSubscribePeriod(kr_currentSubscribe.value!.id); result.fold( (error) { KRCommonUtil.kr_showToast(error.msg); KRLogUtil.kr_e('重置订阅周期失败: ${error.msg}', tag: 'SubscribeService'); }, (success) { kr_refreshAll(); KRLogUtil.kr_i('重置订阅周期成功', tag: 'SubscribeService'); }, ); } /// 获取可用订阅列表 Future _kr_fetchAvailableSubscribes() async { try { KRLogUtil.kr_i('开始获取可用订阅列表', tag: 'SubscribeService'); // 🔧 修复:同时更新已订阅记录列表,确保判断准确 final alreadySubscribeResult = await kr_subscribeApi.kr_getAlreadySubscribe(); alreadySubscribeResult.fold( (error) { KRLogUtil.kr_e('获取已订阅列表失败: ${error.msg}', tag: 'SubscribeService'); }, (subscribes) { kr_alreadySubscribe.value = subscribes; KRLogUtil.kr_i('更新已订阅记录: ${subscribes.length} 个订阅', tag: 'SubscribeService'); }, ); final result = await kr_subscribeApi.kr_userAvailableSubscribe(); result.fold( (error) { KRLogUtil.kr_e('获取可用订阅失败: ${error.msg}', tag: 'SubscribeService'); }, (subscribes) { // 如果当前有选中的订阅,检查是否还在可用列表中 if (kr_currentSubscribe.value != null) { final currentSubscribeExists = subscribes.any( (subscribe) => subscribe.id == kr_currentSubscribe.value?.id, ); // 如果当前订阅不在可用列表中,清除当前订阅 if (!currentSubscribeExists) { // 如果当前订阅为null或者已过期,才设置新的订阅 if (kr_currentSubscribe.value == null || DateTime.parse(kr_currentSubscribe.value!.expireTime) .isBefore(DateTime.now())) { kr_availableSubscribes.assignAll(subscribes); if (subscribes.isNotEmpty) { // 🔧 修复:优先选择已购买的套餐 KRUserAvailableSubscribeItem? selectedSubscribe; // 优先选择已购买的套餐(非试用) for (var subscribe in subscribes) { final isSubscribed = kr_alreadySubscribe.any( (alreadySub) => alreadySub.userSubscribeId == subscribe.id, ); if (isSubscribed) { selectedSubscribe = subscribe; KRLogUtil.kr_i('选择已购买的套餐: ${selectedSubscribe.name}', tag: 'SubscribeService'); break; } } // 如果没有已购买的套餐,选择第一个(可能是试用套餐) if (selectedSubscribe == null) { selectedSubscribe = subscribes.first; KRLogUtil.kr_i('没有已购买的套餐,选择第一个: ${selectedSubscribe.name}', tag: 'SubscribeService'); } kr_currentSubscribe.value = selectedSubscribe; } else { kr_currentSubscribe.value = null; KRLogUtil.kr_i('没有可用的订阅,清除选中状态', tag: 'SubscribeService'); } kr_clearCutNodeData(); } else { KRLogUtil.kr_i('当前订阅仍然有效,保持选中状态', tag: 'SubscribeService'); } } else { // 如果当前订阅仍然有效,更新为最新的订阅信息 final updatedSubscribe = subscribes.firstWhere( (subscribe) => subscribe.id == kr_currentSubscribe.value?.id, ); // 检查订阅是否有效(未过期且未超出流量限制) final isExpired = DateTime.parse(updatedSubscribe.expireTime) .isBefore(DateTime.now()); final isOverTraffic = updatedSubscribe.traffic > 0 && (updatedSubscribe.download + updatedSubscribe.upload) >= updatedSubscribe.traffic; if (isExpired || isOverTraffic) { if (KRSingBoxImp.instance.kr_status == SingboxStatus.started()) { KRSingBoxImp.instance.kr_stop(); } } kr_currentSubscribe.value = updatedSubscribe; KRLogUtil.kr_i('更新当前订阅信息', tag: 'SubscribeService'); // 更新可用订阅列表 kr_availableSubscribes.assignAll(subscribes); } } KRLogUtil.kr_i('获取可用订阅列表成功: ${subscribes.length} 个订阅', tag: 'SubscribeService'); KRLogUtil.kr_i( '订阅列表: ${subscribes.map((s) => '${s.name}(${s.id})').join(', ')}', tag: 'SubscribeService'); }, ); } catch (err) { KRLogUtil.kr_e('获取可用订阅异常: $err', tag: 'SubscribeService'); } } /// 切换订阅 Future kr_switchSubscribe( KRUserAvailableSubscribeItem subscribe) async { // 如果切换的是当前订阅,直接返回 if (subscribe.id == kr_currentSubscribe.value?.id) { KRLogUtil.kr_i('切换的订阅与当前订阅相同,无需切换', tag: 'SubscribeService'); return; } try { kr_currentStatus.value = KRSubscribeServiceStatus.kr_loading; await kr_clearCutNodeData(); KRLogUtil.kr_i('开始切换订阅: ${subscribe.name + subscribe.id.toString()}', tag: 'SubscribeService'); // 更新当前订阅 kr_currentSubscribe.value = subscribe; final result = await kr_subscribeApi.kr_nodeList(kr_currentSubscribe.value!.id); result.fold((error) { kr_currentStatus.value = KRSubscribeServiceStatus.kr_error; }, (nodes) { // 记录当前节点列表是否包含试用节点 kr_hasTrialNodes.value = nodes.isTryOut; KRLogUtil.kr_i('切换订阅 - 节点列表包含试用节点: ${kr_hasTrialNodes.value}', tag: 'SubscribeService'); // 处理节点列表(不使用分组) final listModel = KrOutboundsList(); listModel.processOutboundItems(nodes.list, []); // 更新UI数据 groupOutboundList.value = listModel.groupOutboundList; countryOutboundList.value = listModel.countryOutboundList; allList.value = listModel.allList; keyList = listModel.keyList; // 保存配置 KRSingBoxImp.instance.kr_saveOutbounds(listModel.configJsonList); // 更新试用和订阅状态 _kr_updateSubscribeStatus(); kr_currentStatus.value = KRSubscribeServiceStatus.kr_success; }); } catch (e) { kr_currentStatus.value = KRSubscribeServiceStatus.kr_error; KRLogUtil.kr_e('切换订阅失败: $e', tag: 'SubscribeService'); rethrow; } } /// 更新订阅状态 void _kr_updateSubscribeStatus() { // 停止之前的计时器 _kr_trialTimer?.cancel(); _kr_subscriptionTimer?.cancel(); if (kr_currentSubscribe.value == null) { kr_isTrial.value = false; return; } // 优先使用 API 返回的 isTryOut 字段判断试用状态 final currentSubscribe = kr_currentSubscribe.value!; // 1. 优先使用 API 返回的 isTryOut 字段 kr_isTrial.value = currentSubscribe.isTryOut; KRLogUtil.kr_i('步骤1 - API isTryOut 字段: ${currentSubscribe.isTryOut}', tag: 'SubscribeService'); // 2. 如果 API 说不是试用,检查是否有购买记录 if (!kr_isTrial.value) { final bool kr_isSubscribed = kr_alreadySubscribe.any( (subscribe) => currentSubscribe.id == subscribe.userSubscribeId ); KRLogUtil.kr_i('步骤2 - 检查购买记录: $kr_isSubscribed', tag: 'SubscribeService'); // 如果没有购买记录,判断为试用 if (!kr_isSubscribed) { kr_isTrial.value = true; KRLogUtil.kr_i('步骤2 - 没有购买记录,判定为试用', tag: 'SubscribeService'); } } // 3. 最后检查订阅名称是否包含"试用"关键字(最后的备用方案) if (!kr_isTrial.value && currentSubscribe.name.contains('试用')) { kr_isTrial.value = true; KRLogUtil.kr_i('步骤3 - 订阅名称包含"试用"关键字,判定为试用', tag: 'SubscribeService'); } KRLogUtil.kr_i('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━', tag: 'SubscribeService'); KRLogUtil.kr_i('当前订阅: ${currentSubscribe.name}(${currentSubscribe.id})', tag: 'SubscribeService'); KRLogUtil.kr_i('API isTryOut: ${currentSubscribe.isTryOut}', tag: 'SubscribeService'); KRLogUtil.kr_i('已订阅记录数: ${kr_alreadySubscribe.length}', tag: 'SubscribeService'); KRLogUtil.kr_i('已订阅ID列表: ${kr_alreadySubscribe.map((s) => s.userSubscribeId).join(', ')}', tag: 'SubscribeService'); KRLogUtil.kr_i('✅ 最终试用状态: ${kr_isTrial.value ? "试用" : "付费"}', tag: 'SubscribeService'); KRLogUtil.kr_i('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━', tag: 'SubscribeService'); if (kr_isTrial.value) { // 启动试用倒计时 _kr_startTrialTimer(); } // 检查订阅状态 else if (kr_currentSubscribe.value != null) { final expireTime = DateTime.parse(kr_currentSubscribe.value!.expireTime); final now = DateTime.now(); final difference = expireTime.difference(now); // 检查是否最后一天 kr_isLastDayOfSubscription.value = difference.inDays <= 1; if (kr_isLastDayOfSubscription.value) { // 启动订阅倒计时 _kr_startSubscriptionTimer(); KRLogUtil.kr_i('当前订阅最后一天', tag: 'SubscribeService'); } } } /// 启动试用倒计时 void _kr_startTrialTimer() { _kr_trialTimer?.cancel(); // 立即执行一次 _kr_updateTrialTime(); // 设置定时器 _kr_trialTimer = Timer.periodic(const Duration(seconds: 1), (timer) { _kr_updateTrialTime(); }); } /// 更新试用时间 void _kr_updateTrialTime() { if (kr_currentSubscribe.value == null) { _kr_trialTimer?.cancel(); return; } final expireTime = DateTime.parse(kr_currentSubscribe.value!.expireTime); final now = DateTime.now(); final difference = expireTime.difference(now); if (difference.isNegative) { /// 停止 if (KRSingBoxImp.instance.kr_status == SingboxStatus.started()) { KRSingBoxImp.instance.kr_stop(); } _kr_trialTimer?.cancel(); kr_trialRemainingTime.value = 'error.60001'.tr; return; } final days = difference.inDays; final hours = difference.inHours % 24; final minutes = difference.inMinutes % 60; final seconds = difference.inSeconds % 60; kr_trialRemainingTime.value = AppTranslations.kr_home.trialTimeWithDays( days, hours, minutes, seconds, ); } /// 启动订阅倒计时 void _kr_startSubscriptionTimer() { _kr_subscriptionTimer?.cancel(); // 立即执行一次 _kr_updateSubscriptionTime(); // 设置定时器 _kr_subscriptionTimer = Timer.periodic(const Duration(seconds: 1), (timer) { _kr_updateSubscriptionTime(); }); } /// 更新订阅时间 void _kr_updateSubscriptionTime() { if (kr_currentSubscribe.value == null) { _kr_subscriptionTimer?.cancel(); return; } final expireTime = DateTime.parse(kr_currentSubscribe.value!.expireTime); final now = DateTime.now(); final difference = expireTime.difference(now); if (difference.isNegative) { _kr_subscriptionTimer?.cancel(); /// 停止 if (KRSingBoxImp.instance.kr_status == SingboxStatus.started()) { KRSingBoxImp.instance.kr_stop(); } kr_subscriptionRemainingTime.value = 'error.60001'.tr; ; return; } final days = difference.inDays; final hours = difference.inHours % 24; final minutes = difference.inMinutes % 60; final seconds = difference.inSeconds % 60; kr_subscriptionRemainingTime.value = AppTranslations.kr_home.trialTimeWithDays( days, hours, minutes, seconds, ); } /// 启动定期更新 void _kr_startPeriodicUpdate() { // 每5分钟更新一次可用订阅列表 _kr_updateTimer = Timer.periodic(const Duration(seconds: 60), (timer) { _kr_fetchAvailableSubscribes(); }); } /// 刷新所有数据 Future kr_refreshAll() async { try { kr_currentStatus.value = KRSubscribeServiceStatus.kr_loading; await kr_clearData(); KRLogUtil.kr_i('开始刷新所有数据', tag: 'SubscribeService'); /// 数组有值 ,表示订阅过, 用于判断试用的 final alreadySubscribeResult = await kr_subscribeApi.kr_getAlreadySubscribe(); alreadySubscribeResult.fold( (error) { throw Exception('获取已订阅列表失败: ${error.msg}'); }, (subscribes) { kr_alreadySubscribe.value = subscribes; KRLogUtil.kr_i('订阅记录: ${subscribes.length} 个订阅', tag: 'SubscribeService'); }, ); // 🔧 取消节点分组的概念,不再调用 kr_nodeGroupList // 直接使用节点列表,通过 is_try_out 字段区分免费/付费节点 kr_nodeGroups.clear(); KRLogUtil.kr_i('已取消节点分组,将直接使用节点列表', tag: 'SubscribeService'); // 保存当前选中的订阅名称 final currentSubscribeID = kr_currentSubscribe.value?.id; // 获取可用订阅列表 final subscribeResult = await kr_subscribeApi.kr_userAvailableSubscribe(); // 处理订阅列表结果 final subscribes = await subscribeResult.fold( (error) { throw Exception('获取可用订阅失败: ${error.msg}'); }, (subscribes) => subscribes, ); // 如果获取订阅列表为空(试用结束且未购买),设置为成功状态但清空订阅 if (subscribes.isEmpty) { KRLogUtil.kr_i('订阅列表为空(试用结束或未购买),清空订阅数据', tag: 'SubscribeService'); kr_availableSubscribes.clear(); kr_currentSubscribe.value = null; kr_currentStatus.value = KRSubscribeServiceStatus.kr_success; return; } // 更新订阅列表 kr_availableSubscribes.assignAll(subscribes); KRLogUtil.kr_i('获取可用订阅列表成功: ${subscribes.length} 个订阅', tag: 'SubscribeService'); // 如果之前的订阅名称在可用列表中,保持选中 if (subscribes.isNotEmpty) { KRUserAvailableSubscribeItem? selectedSubscribe; // 1. 首先尝试找到之前选中的订阅 if (currentSubscribeID != null) { try { selectedSubscribe = subscribes.firstWhere( (subscribe) => subscribe.id == currentSubscribeID, ); KRLogUtil.kr_i('保持之前选中的订阅: ${selectedSubscribe.name}', tag: 'SubscribeService'); } catch (e) { // 没找到之前的订阅,继续下一步 KRLogUtil.kr_i('之前选中的订阅已不存在,重新选择', tag: 'SubscribeService'); } } // 2. 如果没有找到之前的订阅,优先选择试用套餐 if (selectedSubscribe == null) { KRLogUtil.kr_i('开始查找试用套餐...', tag: 'SubscribeService'); for (var subscribe in subscribes) { KRLogUtil.kr_i('检查订阅: ${subscribe.name}(${subscribe.id}), 是否试用: ${subscribe.isTryOut}', tag: 'SubscribeService'); if (subscribe.isTryOut) { selectedSubscribe = subscribe; KRLogUtil.kr_i('✅ 找到试用套餐,默认选择: ${selectedSubscribe.name}', tag: 'SubscribeService'); break; } } } // 3. 如果没有试用套餐,选择已购买的套餐(非试用) if (selectedSubscribe == null) { KRLogUtil.kr_i('没有试用套餐,查找已购买的套餐...', tag: 'SubscribeService'); KRLogUtil.kr_i('已订阅记录: ${kr_alreadySubscribe.map((s) => s.userSubscribeId).join(', ')}', tag: 'SubscribeService'); for (var subscribe in subscribes) { final isSubscribed = kr_alreadySubscribe.any( (alreadySub) => alreadySub.userSubscribeId == subscribe.id, ); KRLogUtil.kr_i('检查订阅: ${subscribe.name}(${subscribe.id}), 是否已购买: $isSubscribed', tag: 'SubscribeService'); if (isSubscribed) { selectedSubscribe = subscribe; KRLogUtil.kr_i('选择已购买的套餐: ${selectedSubscribe.name}', tag: 'SubscribeService'); break; } } } // 4. 如果都没有找到,选择第一个 if (selectedSubscribe == null) { selectedSubscribe = subscribes.first; KRLogUtil.kr_i('没有找到匹配的套餐,选择第一个: ${selectedSubscribe.name}', tag: 'SubscribeService'); } kr_currentSubscribe.value = selectedSubscribe; // 获取节点列表 final nodeResult = await kr_subscribeApi.kr_nodeList(kr_currentSubscribe.value!.id); // 处理节点列表结果 final nodes = await nodeResult.fold( (error) { throw Exception('获取节点列表失败: ${error.msg}'); }, (nodes) => nodes, ); // 记录当前节点列表是否包含试用节点 kr_hasTrialNodes.value = nodes.isTryOut; KRLogUtil.kr_i('节点列表包含试用节点: ${kr_hasTrialNodes.value}', tag: 'SubscribeService'); // 处理节点列表(不使用分组) final listModel = KrOutboundsList(); listModel.processOutboundItems(nodes.list, []); // 更新UI数据 groupOutboundList.value = listModel.groupOutboundList; countryOutboundList.value = listModel.countryOutboundList; allList.value = listModel.allList; keyList = listModel.keyList; // 保存配置 KRSingBoxImp.instance.kr_saveOutbounds(listModel.configJsonList); // 更新试用和订阅状态 _kr_updateSubscribeStatus(); kr_currentStatus.value = KRSubscribeServiceStatus.kr_success; _kr_startPeriodicUpdate(); } else { KRLogUtil.kr_w('没有可用的订阅', tag: 'SubscribeService'); kr_currentStatus.value = KRSubscribeServiceStatus.kr_error; return; } } catch (err, stackTrace) { kr_currentStatus.value = KRSubscribeServiceStatus.kr_error; KRLogUtil.kr_e('刷新数据异常: $err\n$stackTrace', tag: 'SubscribeService'); } } //// 清楚 Future kr_clearData() async { _kr_subscriptionTimer?.cancel(); _kr_trialTimer?.cancel(); _kr_updateTimer?.cancel(); kr_availableSubscribes.clear(); await kr_clearCutNodeData(); } Future kr_logout() async { kr_alreadySubscribe.clear(); kr_nodeGroups.clear(); kr_currentSubscribe.value = null; await kr_clearData(); } Future kr_clearCutNodeData() async { kr_isLastDayOfSubscription.value = false; kr_isTrial.value = false; kr_hasTrialNodes.value = false; kr_subscriptionRemainingTime.value = ''; kr_trialRemainingTime.value = ''; /// 停止 if (KRSingBoxImp.instance.kr_status == SingboxStatus.started()) { await KRSingBoxImp.instance.kr_stop(); } // 更新UI数据 groupOutboundList.clear(); countryOutboundList.clear(); allList.clear(); keyList.clear(); // 保存配置 KRSingBoxImp.instance.kr_saveOutbounds([]); } /// 获取当前订阅 KRUserAvailableSubscribeItem? get kr_getCurrentSubscribe => kr_currentSubscribe.value; /// 获取免费节点列表(试用节点) /// 当试用结束时,返回空列表 List get kr_freeNodes { if (!kr_hasTrialNodes.value) { return []; } // 如果当前是试用状态或有试用节点,返回所有节点作为免费节点 // 如果试用已结束(kr_isTrial=false 且 kr_hasTrialNodes=true),返回空列表 if (kr_isTrial.value) { KRLogUtil.kr_i('返回免费节点: ${allList.length} 个', tag: 'SubscribeService'); return allList.toList(); } else { KRLogUtil.kr_i('试用已结束,不显示免费节点', tag: 'SubscribeService'); return []; } } /// 获取付费节点列表 /// 如果当前不是试用,返回所有节点 /// 如果当前是试用,返回空列表 List get kr_paidNodes { if (kr_isTrial.value) { KRLogUtil.kr_i('当前是试用状态,不显示付费节点', tag: 'SubscribeService'); return []; } else { KRLogUtil.kr_i('返回付费节点: ${allList.length} 个', tag: 'SubscribeService'); return allList.toList(); } } /// 是否应该显示免费标签页 bool get kr_shouldShowFreeTab { return kr_hasTrialNodes.value && kr_isTrial.value; } /// 是否应该显示付费标签页 bool get kr_shouldShowPaidTab { return !kr_isTrial.value; } }