omnAPP/lib/app/services/kr_subscribe_service.dart
2025-09-23 16:23:15 +08:00

552 lines
17 KiB
Dart
Executable File
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: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<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 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;
/// 重置订阅周期
Future<void> 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<void> _kr_fetchAvailableSubscribes() async {
try {
KRLogUtil.kr_i('开始获取可用订阅列表', 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) {
kr_currentSubscribe.value = subscribes.first;
KRLogUtil.kr_i('设置新的订阅: ${subscribes.first.name}',
tag: 'SubscribeService');
} 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) {
// 处理节点列表
final listModel = KrOutboundsList();
listModel.processOutboundItems(nodes.list, kr_nodeGroups);
// 更新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();
// 检查试用状态
final bool kr_isSubscribed = kr_currentSubscribe.value != null &&
kr_alreadySubscribe.any((subscribe) =>
kr_currentSubscribe.value?.id == subscribe.userSubscribeId);
KRLogUtil.kr_i('当前订阅状态: ${kr_isSubscribed ? "已订阅" : "未订阅"}',
tag: 'SubscribeService');
KRLogUtil.kr_i('当前订阅ID: ${kr_currentSubscribe.value?.id}',
tag: 'SubscribeService');
KRLogUtil.kr_i(
'已订阅记录: ${kr_alreadySubscribe.map((s) => s.userSubscribeId).join(', ')}',
tag: 'SubscribeService');
// 设置试用状态
kr_isTrial.value = kr_currentSubscribe.value != null && !kr_isSubscribed;
KRLogUtil.kr_i('试用状态: ${kr_isTrial.value ? "" : ""}',
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 {
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');
},
);
final result = await kr_subscribeApi.kr_nodeGroupList();
result.fold(
(error) {
throw Exception('获取节点分组失败: ${error.msg}');
},
(groups) {
kr_nodeGroups.value = groups;
},
);
// 保存当前选中的订阅名称
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) {
kr_currentStatus.value = KRSubscribeServiceStatus.kr_none;
kr_availableSubscribes.clear();
return;
}
// 更新订阅列表
kr_availableSubscribes.assignAll(subscribes);
KRLogUtil.kr_i('获取可用订阅列表成功: ${subscribes.length} 个订阅',
tag: 'SubscribeService');
// 如果之前的订阅名称在可用列表中,保持选中
if (subscribes.isNotEmpty) {
final previousSubscribe = subscribes.firstWhere(
(subscribe) => subscribe.id == currentSubscribeID,
orElse: () => subscribes.first,
);
if (previousSubscribe.id != currentSubscribeID) {
kr_currentSubscribe.value = previousSubscribe;
KRLogUtil.kr_i('切换订阅: ${previousSubscribe.name}',
tag: 'SubscribeService');
} else {
kr_currentSubscribe.value = previousSubscribe;
}
// 获取节点列表
final nodeResult =
await kr_subscribeApi.kr_nodeList(kr_currentSubscribe.value!.id);
// 处理节点列表结果
final nodes = await nodeResult.fold(
(error) {
throw Exception('获取节点列表失败: ${error.msg}');
},
(nodes) => nodes,
);
// 处理节点列表
final listModel = KrOutboundsList();
listModel.processOutboundItems(nodes.list, kr_nodeGroups);
// 更新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<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_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;
}