hi-client/lib/app/services/kr_subscribe_service.dart

783 lines
28 KiB
Dart
Executable File
Raw 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: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 'package:kaer_with_panels/app/common/app_run_data.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<KRUserAvailableSubscribeItem> kr_availableSubscribes =
<KRUserAvailableSubscribeItem>[].obs;
/// 当前选中的订阅
final Rx<KRUserAvailableSubscribeItem?> kr_currentSubscribe =
Rx<KRUserAvailableSubscribeItem?>(null);
/// 节点分组列表
final RxList<KRNodeGroupListItem> kr_nodeGroups = <KRNodeGroupListItem>[].obs;
/// 服务器分组
final RxList<KRGroupOutboundList> groupOutboundList =
<KRGroupOutboundList>[].obs;
/// 国家分组,包含所有国家
final RxList<KRCountryOutboundList> countryOutboundList =
<KRCountryOutboundList>[].obs;
/// 全部列表
final RxList<KROutboundItem> allList = <KROutboundItem>[].obs;
/// 标签列表
Map<String, KROutboundItem> 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<KRAlreadySubscribe> kr_alreadySubscribe =
<KRAlreadySubscribe>[].obs;
/// 是否处于订阅最后一天
final RxBool kr_isLastDayOfSubscription = false.obs;
/// 定期更新计时器
Timer? _kr_updateTimer;
/// 试用倒计时计时器
Timer? _kr_trialTimer;
/// 订阅倒计时计时器
Timer? _kr_subscriptionTimer;
/// 当前状态
final kr_currentStatus = KRSubscribeServiceStatus.kr_none.obs;
/// ✅ 方案4请求去重标志位 - 防止重复刷新
bool _isRefreshing = false;
/// ✅ 方案5将错误码转换为用户友好的错误信息
String _kr_getFriendlyErrorMessage(int errorCode, String originalMsg) {
switch (errorCode) {
case -90001:
return '连接超时,请检查网络连接';
case -90002:
return '发送数据超时,请稍后重试';
case -90003:
return '接收数据超时,网络较慢,请稍后重试';
case -90004:
return '服务器响应异常:$originalMsg';
case -90006:
return '网络连接失败,请检查网络设置';
case -90007:
return '安全证书验证失败';
case -90008:
return '网络连接被中断,请重试';
case 401:
return '登录已过期,请重新登录';
case 403:
return '没有访问权限';
case 404:
return '请求的资源不存在';
case 500:
return '服务器内部错误,请稍后重试';
case 503:
return '服务暂时不可用,请稍后重试';
default:
if (errorCode >= 500 && errorCode < 600) {
return '服务器错误($errorCode),请稍后重试';
} else if (errorCode >= 400 && errorCode < 500) {
return '请求错误($errorCode$originalMsg';
} else if (errorCode < 0) {
return '网络请求失败,请检查网络连接';
}
return originalMsg.isNotEmpty ? originalMsg : '未知错误,请重试';
}
}
/// 重置订阅周期
Future<void> kr_resetSubscribePeriod() async {
if (kr_currentSubscribe.value == null) {
KRCommonUtil.kr_showToast('subscribe.pleaseSelectFirst'.tr);
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<void> 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<void> 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 没有明确标识时,才检查订阅名称作为备用方案
// 注意:只有当 API 说不是试用,但名称包含"试用"时才覆盖
if (!kr_isTrial.value && currentSubscribe.name.contains('试用')) {
kr_isTrial.value = true;
KRLogUtil.kr_i('步骤2 - 订阅名称包含"试用"关键字,判定为试用', 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<void> kr_refreshAll() async {
try {
// ✅ 方案4请求去重检查 - 防止重复刷新
if (_isRefreshing) {
KRLogUtil.kr_w('⚠️ 正在刷新中,忽略重复请求', tag: 'SubscribeService');
return;
}
// 设置刷新标志
_isRefreshing = true;
KRLogUtil.kr_i('🔄 开始刷新订阅数据...', tag: 'SubscribeService');
// 🔧 修复2: 添加登录状态检查 - 只有已登录用户才能刷新订阅数据
if (!KRAppRunData().kr_isLogin.value) {
KRLogUtil.kr_e('❌ 未登录用户,无法刷新订阅数据', tag: 'SubscribeService');
kr_availableSubscribes.clear();
kr_currentSubscribe.value = null;
kr_currentStatus.value = KRSubscribeServiceStatus.kr_error;
_isRefreshing = false; // 重置标志
return;
}
kr_currentStatus.value = KRSubscribeServiceStatus.kr_loading;
await kr_clearData();
KRLogUtil.kr_i('开始刷新所有数据', tag: 'SubscribeService');
/// 数组有值 ,表示订阅过, 用于判断试用的
final alreadySubscribeResult =
await kr_subscribeApi.kr_getAlreadySubscribe();
alreadySubscribeResult.fold(
(error) {
// ✅ 方案5使用友好的错误信息
final friendlyMsg = _kr_getFriendlyErrorMessage(error.code, error.msg);
KRLogUtil.kr_e('获取已订阅列表失败: $friendlyMsg (错误码: ${error.code})', tag: 'SubscribeService');
throw Exception('获取已订阅列表失败: $friendlyMsg');
},
(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) {
// ✅ 方案5使用友好的错误信息
final friendlyMsg = _kr_getFriendlyErrorMessage(error.code, error.msg);
KRLogUtil.kr_e('获取可用订阅失败: $friendlyMsg (错误码: ${error.code})', tag: 'SubscribeService');
throw Exception('获取可用订阅失败: $friendlyMsg');
},
(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) {
// ✅ 方案5使用友好的错误信息
final friendlyMsg = _kr_getFriendlyErrorMessage(error.code, error.msg);
KRLogUtil.kr_e('获取节点列表失败: $friendlyMsg (错误码: ${error.code})', tag: 'SubscribeService');
throw Exception('获取节点列表失败: $friendlyMsg');
},
(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');
} finally {
// ✅ 方案4无论成功或失败都重置刷新标志
_isRefreshing = false;
KRLogUtil.kr_i('✅ 刷新完成,重置刷新标志', tag: 'SubscribeService');
}
}
//// 清楚
Future<void> kr_clearData() async {
_kr_subscriptionTimer?.cancel();
_kr_trialTimer?.cancel();
_kr_updateTimer?.cancel();
kr_availableSubscribes.clear();
await kr_clearCutNodeData();
}
Future<void> kr_logout() async {
kr_alreadySubscribe.clear();
kr_nodeGroups.clear();
kr_currentSubscribe.value = null;
await kr_clearData();
}
Future<void> kr_clearCutNodeData() async {
kr_isLastDayOfSubscription.value = false;
kr_isTrial.value = false;
kr_hasTrialNodes.value = false;
kr_subscriptionRemainingTime.value = '';
kr_trialRemainingTime.value = '';
/// 停止
// ✅ 关键修复:使用 is 类型检查替代 == 比较
// 原因kr_status 是 Rx<SingboxStatus> 类型,直接 == 比较不可靠
if (KRSingBoxImp.instance.kr_status.value is SingboxStarted) {
KRLogUtil.kr_i('🛑 清理时检测到 VPN 正在运行,停止 VPN...', tag: 'SubscribeService');
try {
// ✅ 不 await改为异步执行防止阻塞清理流程
// 由于主线程已经调用了 kr_stop(),这里不需要再次等待
unawaited(KRSingBoxImp.instance.kr_stop());
} catch (e) {
KRLogUtil.kr_w('⚠️ 清理时停止 VPN 失败: $e', tag: 'SubscribeService');
}
}
// 更新UI数据
groupOutboundList.clear();
countryOutboundList.clear();
allList.clear();
keyList.clear();
// 保存配置
KRSingBoxImp.instance.kr_saveOutbounds([]);
}
/// 获取当前订阅
KRUserAvailableSubscribeItem? get kr_getCurrentSubscribe =>
kr_currentSubscribe.value;
/// 获取免费节点列表(试用节点)
/// 当试用结束时,返回空列表
List<KROutboundItem> 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<KROutboundItem> 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;
}
}