feat: 源码提交
Build Windows / build (push) Has been cancelled

This commit is contained in:
2025-10-19 23:30:54 -07:00
parent 54e7df78db
commit 75d4c48e41
6076 changed files with 64655 additions and 0 deletions
+28
View File
@@ -0,0 +1,28 @@
import 'package:get/get.dart';
import 'kr_outbound_item.dart';
/// 表示服务器分组的模型类
class KRGroupOutboundList {
final String tag; // 标签
String icon = ""; // 图标
final List<KROutboundItem> outboundList; // 出站项列表
/// 构造函数,初始化标签和出站项列表
KRGroupOutboundList({
required this.tag,
required this.outboundList,
});
}
class KRCountryOutboundList {
final String country;
final List<KROutboundItem> outboundList;
//// 是否展开
RxBool isExpand = false.obs;
KRCountryOutboundList({
required this.country,
required this.outboundList,
});
}
+319
View File
@@ -0,0 +1,319 @@
import 'dart:convert';
import 'package:get/get.dart';
import '../response/kr_node_list.dart';
/// 表示出站项的模型类
class KROutboundItem {
int selected = 0; // 是否选中(0=未选中,1=选中)
String id = ""; // 标签
String tag = ""; // 标签
String serverAddr = ""; // 服务器地址
/// 初始化配置
Map<String, dynamic> config = {}; // 配置项
String city = ""; // 城市
String country = ""; // 国家
double latitude = 0.0; // 节点纬度
double latitudeCountry = 0.0; // 国家中心纬度
double longitude = 0.0; // 节点经度
double longitudeCountry = 0.0; // 国家中心经度
String protocol = "";
/// 延迟
RxInt urlTestDelay = 0.obs;
/// URL
String url = "";
/// 服务器类型
/// 构造函数,接受 KrNodeListItem 对象并初始化 KROutboundItem
KROutboundItem(KrNodeListItem nodeListItem) {
id = nodeListItem.id.toString();
protocol = nodeListItem.protocol;
latitude = nodeListItem.latitude;
latitudeCountry = nodeListItem.latitudeCountry;
longitude = nodeListItem.longitude;
longitudeCountry = nodeListItem.longitudeCountry;
tag = nodeListItem.name; // 设置标签
serverAddr = nodeListItem.serverAddr; // 设置服务器地址
// 将 config 字符串转换为 Map<String, dynamic>
city = nodeListItem.city; // 设置城市
country = nodeListItem.country; // 设置国家
// 安全解析 config 字段
// 新API格式:config为空,直接使用节点字段构建配置
// 旧API格式:config包含JSON配置
if (nodeListItem.config.isEmpty) {
print('️ 节点 ${nodeListItem.name} 使用直接字段构建配置');
_buildConfigFromFields(nodeListItem);
return;
}
late Map<String, dynamic> json;
try {
json = jsonDecode(nodeListItem.config) as Map<String, dynamic>;
} catch (e) {
print('❌ 节点 ${nodeListItem.name} 的 config 解析失败: $e,尝试使用直接字段');
print('📄 Config 内容: ${nodeListItem.config}');
_buildConfigFromFields(nodeListItem);
return;
}
switch (nodeListItem.protocol) {
case "vless":
final securityConfig =
json["security_config"] as Map<String, dynamic>? ?? {};
// 智能设置 server_name
String serverName = securityConfig["sni"] ?? "";
if (serverName.isEmpty) {
serverName = nodeListItem.serverAddr;
}
config = {
"type": "vless",
"tag": nodeListItem.name,
"server": nodeListItem.serverAddr,
"server_port": json["port"],
"uuid": nodeListItem.uuid,
if (json["flow"] != null && json["flow"] != "none")
"flow": json["flow"],
if (json["transport"] != null && json["transport"] != "tcp")
"transport": _buildTransport(json),
"tls": {
"enabled": json["security"] == "tls",
"server_name": serverName,
"insecure": securityConfig["allow_insecure"] ?? false,
"utls": {
"enabled": true,
"fingerprint": securityConfig["fingerprint"] ?? "chrome"
}
}
};
break;
case "vmess":
final securityConfig =
json["security_config"] as Map<String, dynamic>? ?? {};
// 智能设置 server_name
String serverName = securityConfig["sni"] ?? "";
if (serverName.isEmpty) {
serverName = nodeListItem.serverAddr;
}
config = {
"type": "vmess",
"tag": nodeListItem.name,
"server": nodeListItem.serverAddr,
"server_port": json["port"],
"uuid": nodeListItem.uuid,
"alter_id": 0,
"security": "auto",
if (json["transport"] != null && json["transport"] != "tcp")
"transport": _buildTransport(json),
"tls": {
"enabled": json["security"] == "tls",
"server_name": serverName,
"insecure": securityConfig["allow_insecure"] ?? false,
"utls": {"enabled": true, "fingerprint": "chrome"}
}
};
break;
case "shadowsocks":
config = {
"type": "shadowsocks",
"tag": nodeListItem.name,
"server": nodeListItem.serverAddr,
"server_port": json["port"],
"method": json["method"],
"password": nodeListItem.uuid
};
break;
case "hysteria2":
final securityConfig =
json["security_config"] as Map<String, dynamic>? ?? {};
config = {
"type": "hysteria2",
"tag": nodeListItem.name,
"server": nodeListItem.serverAddr,
"server_port": json["port"],
"password": nodeListItem.uuid,
"up_mbps": 100,
"down_mbps": 100,
"obfs": {
"type": "salamander",
"password": json["obfs_password"] ?? nodeListItem.uuid
},
"tls": {
"enabled": true,
"server_name": securityConfig["sni"] ?? "",
"insecure": securityConfig["allow_insecure"] ?? false,
"alpn": ["h3"]
}
};
break;
case "trojan":
final securityConfig =
json["security_config"] as Map<String, dynamic>? ?? {};
// 智能设置 server_name
String serverName = securityConfig["sni"] ?? "";
if (serverName.isEmpty) {
// 如果没有配置 SNI,使用服务器地址
serverName = nodeListItem.serverAddr;
}
config = {
"type": "trojan",
"tag": nodeListItem.name,
"server": nodeListItem.serverAddr,
"server_port": json["port"],
"password": nodeListItem.uuid,
"tls": {
"enabled": json["security"] == "tls",
"server_name": serverName,
"insecure": securityConfig["allow_insecure"] ?? false,
"utls": {"enabled": true, "fingerprint": "chrome"}
}
};
break;
}
// 检查 relayNode 是否为 JSON 字符串并解析
if (nodeListItem.relayNode.isNotEmpty && nodeListItem.relayMode != "none") {
final relayNodeJson = jsonDecode(nodeListItem.relayNode);
if (relayNodeJson is List && nodeListItem.relayMode != "none") {
// 随机选择一个元素
final randomNode = (relayNodeJson..shuffle()).first;
config["server"] = randomNode["host"]; // 提取 host
config["server_port"] = randomNode["port"]; // 提取 port
}
}
// 解析配置
}
/// 构建传输配置
Map<String, dynamic> _buildTransport(Map<String, dynamic> json) {
final transportType = json["transport"] as String?;
final transportConfig =
json["transport_config"] as Map<String, dynamic>? ?? {};
switch (transportType) {
case "ws":
return {
"type": "ws",
"path": transportConfig["path"] ?? "/",
if (transportConfig["host"] != null)
"headers": {"Host": transportConfig["host"]}
};
case "grpc":
return {
"type": "grpc",
"service_name": transportConfig["service_name"] ?? ""
};
case "http":
return {
"type": "http",
"host": [transportConfig["host"] ?? ""],
"path": transportConfig["path"] ?? "/"
};
default:
return {};
}
}
/// 直接从节点字段构建配置(新API格式)
void _buildConfigFromFields(KrNodeListItem nodeListItem) {
switch (nodeListItem.protocol) {
case "shadowsocks":
config = {
"type": "shadowsocks",
"tag": nodeListItem.name,
"server": nodeListItem.serverAddr,
"server_port": nodeListItem.port,
"method": "chacha20-ietf-poly1305", // 默认加密方法
"password": nodeListItem.uuid
};
print('✅ Shadowsocks 节点配置构建成功: ${nodeListItem.name}');
break;
case "vless":
config = {
"type": "vless",
"tag": nodeListItem.name,
"server": nodeListItem.serverAddr,
"server_port": nodeListItem.port,
"uuid": nodeListItem.uuid,
"tls": {
"enabled": true,
"server_name": nodeListItem.serverAddr,
"insecure": false,
"utls": {
"enabled": true,
"fingerprint": "chrome"
}
}
};
print('✅ VLESS 节点配置构建成功: ${nodeListItem.name}');
break;
case "vmess":
config = {
"type": "vmess",
"tag": nodeListItem.name,
"server": nodeListItem.serverAddr,
"server_port": nodeListItem.port,
"uuid": nodeListItem.uuid,
"alter_id": 0,
"security": "auto",
"tls": {
"enabled": true,
"server_name": nodeListItem.serverAddr,
"insecure": false,
"utls": {"enabled": true, "fingerprint": "chrome"}
}
};
print('✅ VMess 节点配置构建成功: ${nodeListItem.name}');
break;
case "trojan":
config = {
"type": "trojan",
"tag": nodeListItem.name,
"server": nodeListItem.serverAddr,
"server_port": nodeListItem.port,
"password": nodeListItem.uuid,
"tls": {
"enabled": true,
"server_name": nodeListItem.serverAddr,
"insecure": false,
"utls": {"enabled": true, "fingerprint": "chrome"}
}
};
print('✅ Trojan 节点配置构建成功: ${nodeListItem.name}');
break;
case "hysteria2":
config = {
"type": "hysteria2",
"tag": nodeListItem.name,
"server": nodeListItem.serverAddr,
"server_port": nodeListItem.port,
"password": nodeListItem.uuid,
"up_mbps": 100,
"down_mbps": 100,
"tls": {
"enabled": true,
"server_name": nodeListItem.serverAddr,
"insecure": false,
"alpn": ["h3"]
}
};
print('✅ Hysteria2 节点配置构建成功: ${nodeListItem.name}');
break;
default:
print('⚠️ 不支持的协议类型: ${nodeListItem.protocol}');
config = {};
}
}
}
+94
View File
@@ -0,0 +1,94 @@
import '../response/kr_node_group_list.dart';
import 'kr_group_outbound_list.dart';
import '../response/kr_node_list.dart';
import 'kr_outbound_item.dart';
/// 表示出站项列表的模型类
class KrOutboundsList {
/// 服务器分组
final List<KRGroupOutboundList> groupOutboundList = []; // 存储服务器分组的列表
/// 国家分组,包含所有国家
final List<KRCountryOutboundList> countryOutboundList = []; // 存储国家分组的列表
/// 全部列表
final List<KROutboundItem> allList = []; // 存储国家分组的列表
// 配置json
final List<Map<String,dynamic>> configJsonList = [];
/// 标签列表
final Map<String,KROutboundItem> keyList = {}; // 存储国家分组的列表
/// 处理出站项并将其分组
/// [list] 是要处理的出站项列表
void processOutboundItems(List<KrNodeListItem> list,List<KRNodeGroupListItem> groupList) {
final Map<String, List<KROutboundItem>> tagGroups = {};
final Map<String, List<KROutboundItem>> countryGroups = {};
// 用于追踪已使用的标签
final Map<String, int> tagCounter = {};
for (var element in list) {
// 生成唯一标签
var baseName = element.name;
if (tagCounter.containsKey(baseName)) {
tagCounter[baseName] = tagCounter[baseName]! + 1;
element.name = "${baseName}_${tagCounter[baseName]}";
} else {
tagCounter[baseName] = 0;
}
final KROutboundItem item = KROutboundItem(element);
// 检查节点配置是否有效(必须包含 type 字段)
if (item.config.isEmpty || !item.config.containsKey('type')) {
print('⚠️ 跳过无效节点: ${element.name},配置为空或缺少 type 字段');
continue; // 跳过无效节点
}
allList.add(item);
// 根据标签分组出站项
for (var tag in element.tags) {
tagGroups.putIfAbsent(tag, () => []);
tagGroups[tag]?.add(item);
}
// 根据国家分组出站项
countryGroups.putIfAbsent(element.country, () => []);
countryGroups[element.country]?.add(item);
configJsonList.add(item.config);
keyList[item.tag] = item;
}
// 将标签分组转换为 KRGroupOutboundList 并添加到 groupOutboundList
for (var tag in tagGroups.keys) {
final item = KRGroupOutboundList(
tag: tag, outboundList: tagGroups[tag]!);
for (var group in groupList) {
if (item.tag == group.name) {
item.icon = group.icon;
break;
}
}
groupOutboundList.add(item); // 添加标签分组到列表
}
// 将国家分组转换为 KRCountryOutboundList 并添加到 countryOutboundList
for (var country in countryGroups.keys) {
countryOutboundList.add(KRCountryOutboundList(
country: country,
outboundList: countryGroups[country]!)); // 添加国家分组到列表
}
}
}
View File
+64
View File
@@ -0,0 +1,64 @@
import 'package:kaer_with_panels/app/model/response/kr_is_register.dart';
import 'package:kaer_with_panels/app/model/response/kr_login_data.dart';
import 'package:kaer_with_panels/app/model/response/kr_node_list.dart';
import 'package:kaer_with_panels/app/model/response/kr_package_list.dart';
import 'response/kr_already_subscribe.dart';
import 'response/kr_config_data.dart';
import 'response/kr_kr_affiliate_count.dart';
import 'response/kr_message_list.dart';
import 'response/kr_node_group_list.dart';
import 'response/kr_order_status.dart';
import 'response/kr_payment_methods.dart';
import 'response/kr_purchase_order_no.dart';
import 'response/kr_status.dart';
import 'response/kr_user_available_subscribe.dart';
import 'response/kr_user_info.dart';
import 'response/kr_user_online_duration.dart';
import 'response/kr_web_text.dart';
/// json转换成实体类,每新建一个实体类就新增加一个case
abstract class EntityFromJsonUtil {
static T parseJsonToEntity<T>(Map<String, dynamic> json) {
switch (T.toString()) {
case "KRIsRegister":
return KRIsRegister.fromJson(json) as T;
case "KRLoginData":
return KRLoginData.fromJson(json) as T;
case "KRPackageList":
return KRPackageList.fromJson(json) as T;
case "KRNodeList":
return KRNodeList.fromJson(json) as T;
case "KRMessageList":
return KRMessageList.fromJson(json) as T;
case "KRUserInfo":
return KRUserInfo.fromJson(json) as T;
case "KRAffiliateCount":
return KRAffiliateCount.fromJson(json) as T;
case "KRPaymentMethods":
return KRPaymentMethods.fromJson(json) as T;
case "KRConfigData":
return KRConfigData.fromJson(json) as T;
case "KRPurchaseOrderNo":
return KRPurchaseOrderNo.fromJson(json) as T;
case "KRPurchaseOrderUrl":
return KRPurchaseOrderUrl.fromJson(json) as T;
case "KROrderStatus":
return KROrderStatus.fromJson(json) as T;
case "KRAlreadySubscribeList":
return KRAlreadySubscribeList.fromJson(json) as T;
case "KRNodeGroupList":
return KRNodeGroupList.fromJson(json) as T;
case "KRWebText":
return KRWebText.fromJson(json) as T;
case "KRUserOnlineDurationResponse":
return KRUserOnlineDurationResponse.fromJson(json) as T;
case "KRUserAvailableSubscribeList":
return KRUserAvailableSubscribeList.fromJson(json) as T;
case "KRStatus":
return KRStatus.fromJson(json) as T;
default:
throw ("类型转换错误,是否忘记添加了case!");
}
}
}
+7
View File
@@ -0,0 +1,7 @@
enum KRHomeViewsStatus {
kr_nore,
kr_serverList,
kr_subscribeList,
kr_coutrysubscribeList,
kr_serversubscribeList,
}
+4
View File
@@ -0,0 +1,4 @@
enum KRMessageType {
kr_payment,
kr_subscribe_update,
}
+18
View File
@@ -0,0 +1,18 @@
/// 登录类型
enum KRLoginType {
kr_telephone, /// 手机号
kr_email, /// 邮箱
}
extension KRLoginTypeExt on KRLoginType {
String get value {
switch (this) {
case KRLoginType.kr_email:
return "email";
case KRLoginType.kr_telephone:
return "mobile";
}
}
}
+73
View File
@@ -0,0 +1,73 @@
class KRAreaCodeItem {
final String kr_name; // 国家名称
final String kr_code; // 国家代码
final String kr_dialCode; // 国际拨号区号
final String kr_icon; // 图标(国旗)
KRAreaCodeItem({
required this.kr_name,
required this.kr_code,
required this.kr_dialCode,
required this.kr_icon,
});
// 从 Map 转换为模型对象
factory KRAreaCodeItem.fromMap(Map<String, dynamic> map) {
return KRAreaCodeItem(
kr_name: map['name'] ?? '',
kr_code: map['code'] ?? '',
kr_dialCode: map['dial_code'] ?? '',
kr_icon: map['icon'] ?? '',
);
}
// 将模型对象转换为 Map
Map<String, dynamic> toMap() {
return {
'name': kr_name,
'code': kr_code,
'dial_code': kr_dialCode,
'icon': kr_icon,
};
}
}
class KRAreaCode {
// 内部区域编码数据
static final List<Map<String, dynamic>> _kr_codeMap = [
{"name": "China", "code": "CN", "dial_code": "86", "icon": "🇨🇳"},
{"name": "United States", "code": "US", "dial_code": "1", "icon": "🇺🇸"},
{"name": "United Kingdom", "code": "GB", "dial_code": "44", "icon": "🇬🇧"},
{"name": "Canada", "code": "CA", "dial_code": "1", "icon": "🇨🇦"},
{"name": "Australia", "code": "AU", "dial_code": "61", "icon": "🇦🇺"},
{"name": "Germany", "code": "DE", "dial_code": "49", "icon": "🇩🇪"},
{"name": "France", "code": "FR", "dial_code": "33", "icon": "🇫🇷"},
{"name": "India", "code": "IN", "dial_code": "91", "icon": "🇮🇳"},
{"name": "Japan", "code": "JP", "dial_code": "81", "icon": "🇯🇵"},
{"name": "South Korea", "code": "KR", "dial_code": "82", "icon": "🇰🇷"},
{"name": "Russia", "code": "RU", "dial_code": "7", "icon": "🇷🇺"},
{"name": "Brazil", "code": "BR", "dial_code": "55", "icon": "🇧🇷"},
{"name": "South Africa", "code": "ZA", "dial_code": "27", "icon": "🇿🇦"},
{"name": "New Zealand", "code": "NZ", "dial_code": "64", "icon": "🇳🇿"},
{"name": "Singapore", "code": "SG", "dial_code": "65", "icon": "🇸🇬"},
{"name": "Hong Kong", "code": "HK", "dial_code": "852", "icon": "🇭🇰"},
{"name": "Taiwan", "code": "TW", "dial_code": "886", "icon": "🇹🇼"},
{"name": "Mexico", "code": "MX", "dial_code": "52", "icon": "🇲🇽"},
{"name": "Argentina", "code": "AR", "dial_code": "54", "icon": "🇦🇷"},
{"name": "Italy", "code": "IT", "dial_code": "39", "icon": "🇮🇹"},
{"name": "Spain", "code": "ES", "dial_code": "34", "icon": "🇪🇸"},
{"name": "Turkey", "code": "TR", "dial_code": "90", "icon": "🇹🇷"},
{"name": "Saudi Arabia", "code": "SA", "dial_code": "966", "icon": "🇸🇦"},
{
"name": "United Arab Emirates",
"code": "AE",
"dial_code": "971",
"icon": "🇦🇪"
}
];
// 获取区域编码的模型数组
static List<KRAreaCodeItem> kr_getCodeList() {
return _kr_codeMap.map((map) => KRAreaCodeItem.fromMap(map)).toList();
}
}
+33
View File
@@ -0,0 +1,33 @@
// ... existing code ...
class KRAlreadySubscribe {
final int subscribeId;
final int userSubscribeId;
const KRAlreadySubscribe({
required this.subscribeId,
required this.userSubscribeId,
});
factory KRAlreadySubscribe.fromJson(Map<String, dynamic> json) {
return KRAlreadySubscribe(
subscribeId: json['subscribe_id'] ?? 0,
userSubscribeId: json['user_subscribe_id'] ?? 0,
);
}
}
class KRAlreadySubscribeList {
final List<KRAlreadySubscribe> list;
KRAlreadySubscribeList({required this.list});
factory KRAlreadySubscribeList.fromJson(Map<String, dynamic> json) {
final List<dynamic> data = json['data'] ?? [];
return KRAlreadySubscribeList(
list: data.map((item) => KRAlreadySubscribe.fromJson(item)).toList(),
);
}
}
// ... existing code ...
+149
View File
@@ -0,0 +1,149 @@
import 'dart:io';
import 'package:package_info_plus/package_info_plus.dart';
import '../../utils/kr_log_util.dart';
/// 配置数据模型
/// 用于存储应用程序的基础配置信息,包括加密信息、域名、启动图、官方联系方式等
class KRConfigData {
/// 配置信息
final String kr_config;
/// 加密密钥
final String kr_encryption_key;
/// 加密方法
final String kr_encryption_method;
/// 可用域名列表
final List<String> kr_domains;
/// 启动页图片URL
final String kr_startup_picture;
/// 启动页跳过等待时间(秒)
final int kr_startup_picture_skip_time;
/// 应用更新信息
final KRUpdateApplication kr_update_application;
/// 官方邮箱
final String kr_official_email;
/// 官方网站
final String kr_official_website;
/// 官方电报群
final String kr_official_telegram;
/// 官方电话
final String kr_official_telephone;
/// 邀请链接
final String kr_invitation_link;
final String kr_website_id;
KRConfigData({
this.kr_config = '',
this.kr_encryption_key = '',
this.kr_encryption_method = '',
List<String>? kr_domains,
this.kr_startup_picture = '',
this.kr_startup_picture_skip_time = 0,
KRUpdateApplication? kr_update_application,
this.kr_official_email = '',
this.kr_official_website = '',
this.kr_official_telegram = '',
this.kr_official_telephone = '',
this.kr_invitation_link = '',
this.kr_website_id = '',
}) : this.kr_domains = kr_domains ?? [],
this.kr_update_application =
kr_update_application ?? KRUpdateApplication();
factory KRConfigData.fromJson(Map<String, dynamic> json) {
KRLogUtil.kr_e('配置数据: $json', tag: 'KRConfigData');
return KRConfigData(
kr_invitation_link: json['invitation_link'] ?? '',
kr_config: json['kr_config'] ?? '',
kr_encryption_key: json['encryption_key'] ?? '',
kr_encryption_method: json['encryption_method'] ?? '',
kr_domains: List<String>.from(json['domains'] ?? []),
kr_startup_picture: json['startup_picture'] ?? '',
kr_startup_picture_skip_time: json['startup_picture_skip_time'] ?? 0,
kr_update_application:
KRUpdateApplication.fromJson(json['applications'] ?? {}),
kr_official_email: json['official_email'] ?? '',
kr_official_website: json['official_website'] ?? '',
kr_official_telegram: json['official_telegram'] ?? '',
kr_official_telephone: json['official_telephone'] ?? '',
kr_website_id: json['kr_website_id'] ?? '',
);
}
}
/// 应用更新信息模型
/// 用于存储应用程序的更新相关信息,包括版本号、下载地址等
class KRUpdateApplication {
/// 应用ID
final int kr_id;
/// 应用名称
final String kr_name;
/// 应用描述
final String kr_description;
/// 应用下载地址
final String kr_url;
/// 应用版本号
final String kr_version;
/// 版本更新说明
final String kr_version_description;
/// 是否为默认应用
final bool kr_is_default;
final String kr_version_review;
KRUpdateApplication({
this.kr_id = 0,
this.kr_name = '',
this.kr_description = '',
this.kr_url = '',
this.kr_version = '',
this.kr_version_description = '',
this.kr_is_default = false,
this.kr_version_review = '',
});
factory KRUpdateApplication.fromJson(Map<String, dynamic> json) {
return KRUpdateApplication(
kr_id: json['id'] ?? 0,
kr_name: json['name'] ?? '',
kr_description: json['description'] ?? '',
kr_url: json['url'] ?? '',
kr_version: json['version'] ?? '',
kr_version_description: json['version_description'] ?? '',
kr_is_default: json['is_default'] ?? false,
kr_version_review: json['version_review'] ?? '',
);
}
Future<bool> kr_is_daytime() async {
if (Platform.isIOS) {
if (kr_version_review.isNotEmpty) {
// 获取当前应用版本号
final PackageInfo packageInfo = await PackageInfo.fromPlatform();
final String currentVersion = packageInfo.version;
// 比较版本号
return !(currentVersion == kr_version_review);
}
return true;
}
return true;
}
}
+22
View File
@@ -0,0 +1,22 @@
/// 是否注册
class KRIsRegister {
bool kr_isRegister = false;
KRIsRegister({this.kr_isRegister = false});
KRIsRegister.fromJson(Map<String, dynamic> json) {
kr_isRegister = json['Status'] == "true" || json['Status'] == true
? true
: false || json['status'] == "true" || json['status'] == true
? true
: false;
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['Status'] = kr_isRegister;
return data;
}
}
+13
View File
@@ -0,0 +1,13 @@
class KRAffiliateCount {
int registers = -1;
int totalCommission = -1;
KRAffiliateCount({required this.registers, required this.totalCommission});
factory KRAffiliateCount.fromJson(Map<String, dynamic> json) {
return KRAffiliateCount(
registers: json['registers'] ?? -1,
totalCommission: json['total_commission'] ?? -1,
);
}
}
+17
View File
@@ -0,0 +1,17 @@
/// 登录信息
class KRLoginData {
String kr_token = "";
KRLoginData({this.kr_token = ""});
KRLoginData.fromJson(Map<String, dynamic> json) {
kr_token = json["token"].toString();
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['token'] = kr_token;
return data;
}
}
+96
View File
@@ -0,0 +1,96 @@
class KRMessageList {
final int total;
final List<KRMessage> announcements;
KRMessageList({
this.total = 0,
List<KRMessage>? announcements,
}) : announcements = announcements ?? [];
factory KRMessageList.fromJson(Map<String, dynamic> json) {
return KRMessageList(
total: json['total'] as int? ?? 0,
announcements: (json['announcements'] as List<dynamic>?)
?.map((e) => KRMessage.fromJson(e as Map<String, dynamic>))
.toList() ??
[],
);
}
Map<String, dynamic> toJson() {
return {
'total': total,
'announcements': announcements.map((e) => e.toJson()).toList(),
};
}
}
class KRMessage {
final int id;
final String title;
final String content;
final bool show;
final bool pinned;
final bool popup;
final int createdAt;
final int updatedAt;
final String dataStr = "";
// 通用时间格式化方法
String kr_formatDateTime(int timestamp, {String format = 'yyyy-MM-dd HH:mm'}) {
if (timestamp == 0) return '';
final DateTime dateTime = DateTime.fromMillisecondsSinceEpoch(timestamp );
return format
.replaceAll('yyyy', dateTime.year.toString())
.replaceAll('MM', dateTime.month.toString().padLeft(2, '0'))
.replaceAll('dd', dateTime.day.toString().padLeft(2, '0'))
.replaceAll('HH', dateTime.hour.toString().padLeft(2, '0'))
.replaceAll('mm', dateTime.minute.toString().padLeft(2, '0'))
.replaceAll('ss', dateTime.second.toString().padLeft(2, '0'));
}
// 获取格式化的创建时间字符串
String get kr_formattedCreatedAt => kr_formatDateTime(createdAt);
// 获取格式化的更新时间字符串
String get kr_formattedUpdatedAt => kr_formatDateTime(updatedAt);
KRMessage({
this.id = 0,
this.title = '',
this.content = '',
this.show = false,
this.pinned = false,
this.popup = false,
this.createdAt = 0,
this.updatedAt = 0,
});
factory KRMessage.fromJson(Map<String, dynamic> json) {
return KRMessage(
id: json['id'] as int? ?? 0,
title: json['title'] as String? ?? '',
content: json['content'] as String? ?? '',
show: json['show'] as bool? ?? false,
pinned: json['pinned'] as bool? ?? false,
popup: json['popup'] as bool? ?? false,
createdAt: json['created_at'] as int? ?? 0,
updatedAt: json['updated_at'] as int? ?? 0,
);
}
Map<String, dynamic> toJson() {
return {
'id': id,
'title': title,
'content': content,
'show': show,
'pinned': pinned,
'popup': popup,
'created_at': createdAt,
'updated_at': updatedAt,
};
}
}
+40
View File
@@ -0,0 +1,40 @@
class KRNodeGroupList {
final List<KRNodeGroupListItem> list;
const KRNodeGroupList({required this.list});
factory KRNodeGroupList.fromJson(Map<String, dynamic> json) {
final dynamic listData = json['list'];
if (listData == null) return KRNodeGroupList(list: []);
try {
return KRNodeGroupList(
list: (listData as List)
.map((e) => KRNodeGroupListItem.fromJson(e))
.toList(),
);
} catch (e) {
return KRNodeGroupList(list: []);
}
}
}
class KRNodeGroupListItem {
final String id;
final String name;
final String icon;
const KRNodeGroupListItem({
required this.id,
required this.name,
required this.icon,
});
factory KRNodeGroupListItem.fromJson(Map<String, dynamic> json) {
return KRNodeGroupListItem(
id: json['id']?.toString() ?? '',
name: json['name']?.toString() ?? '',
icon: json['icon']?.toString() ?? '',
);
}
}
+187
View File
@@ -0,0 +1,187 @@
import 'package:kaer_with_panels/app/utils/kr_log_util.dart';
class KRNodeList {
final List<KrNodeListItem> list;
final String subscribeId;
final String startTime;
final String expireTime;
final bool isTryOut; // 是否是试用订阅
const KRNodeList({
required this.list,
this.subscribeId = "0",
this.startTime = "",
this.expireTime = "",
this.isTryOut = false,
});
factory KRNodeList.fromJson(Map<String, dynamic> json) {
try {
// 新的 API 返回格式: {"list": [{"id": 24, "is_try_out": true, "nodes": [...]}]}
final List<dynamic>? listData = json['list'] as List<dynamic>?;
if (listData == null || listData.isEmpty) {
KRLogUtil.kr_w('节点列表为空', tag: 'NodeList');
return const KRNodeList(list: []);
}
// 获取第一个订阅对象
final subscribeData = listData[0] as Map<String, dynamic>;
final bool isTryOut = subscribeData['is_try_out'] as bool? ?? false;
final List<dynamic>? nodesData = subscribeData['nodes'] as List<dynamic>?;
KRLogUtil.kr_i('节点列表解析: is_try_out=$isTryOut, 节点数=${nodesData?.length ?? 0}', tag: 'NodeList');
return KRNodeList(
list: nodesData?.map((e) => KrNodeListItem.fromJson(e as Map<String, dynamic>)).toList() ?? [],
subscribeId: subscribeData['id']?.toString() ?? "0",
startTime: subscribeData['start_time']?.toString() ?? "",
expireTime: subscribeData['expire_time']?.toString() ?? "",
isTryOut: isTryOut,
);
} catch (err) {
KRLogUtil.kr_e('KRNodeList解析错误: $err', tag: 'NodeList');
return const KRNodeList(list: []);
}
}
}
class KrNodeListItem {
final int id;
String name;
final String uuid;
final String protocol;
final String relayMode;
final String relayNode;
final String serverAddr;
final int port; // 新增:端口字段
final int speedLimit;
final List<String> tags;
final int traffic;
final double trafficRatio;
final int upload;
final String city;
final String config;
final String country;
final int createdAt;
final int download;
final String startTime;
final String expireTime;
final double latitude;
final double latitudeCountry;
final double longitude;
final double longitudeCountry;
KrNodeListItem({
required this.id,
required this.name,
required this.uuid,
required this.protocol,
this.relayMode = '',
this.relayNode = '',
required this.serverAddr,
this.port = 0, // 默认值
required this.speedLimit,
required this.tags,
required this.traffic,
required this.trafficRatio,
required this.upload,
required this.city,
required this.config,
required this.country,
this.createdAt = 0,
required this.download,
required this.startTime,
required this.expireTime,
required this.latitude,
required this.latitudeCountry,
required this.longitude,
required this.longitudeCountry,
});
factory KrNodeListItem.fromJson(Map<String, dynamic> json) {
try {
// 支持新旧两种API格式
// 新格式: address, port
// 旧格式: server_addr, config 中包含 port
final serverAddr = json['address']?.toString() ?? json['server_addr']?.toString() ?? '';
final port = _parseIntSafely(json['port']);
return KrNodeListItem(
id: _parseIntSafely(json['id']),
name: json['name']?.toString() ?? '',
uuid: json['uuid']?.toString() ?? '',
protocol: json['protocol']?.toString() ?? '',
relayMode: json['relay_mode']?.toString() ?? '',
relayNode: json['relay_node']?.toString() ?? '',
serverAddr: serverAddr,
port: port,
speedLimit: _parseIntSafely(json['speed_limit']),
tags: _parseStringList(json['tags']),
traffic: _parseIntSafely(json['traffic']),
trafficRatio: _parseDoubleSafely(json['traffic_ratio']),
upload: _parseIntSafely(json['upload']),
city: json['city']?.toString() ?? '',
config: json['config']?.toString() ?? '',
country: json['country']?.toString() ?? '',
createdAt: _parseIntSafely(json['created_at']),
download: _parseIntSafely(json['download']),
startTime: json['start_time']?.toString() ?? '',
expireTime: json['expire_time']?.toString() ?? '',
latitude: _parseDoubleSafely(json['latitude']),
latitudeCountry: _parseDoubleSafely(json['latitude_country']),
longitude: _parseDoubleSafely(json['longitude']),
longitudeCountry: _parseDoubleSafely(json['longitude_country']),
);
} catch (err) {
KRLogUtil.kr_e('KrNodeListItem解析错误: $err', tag: 'NodeList');
return KrNodeListItem(
id: 0,
name: '',
uuid: '',
protocol: '',
serverAddr: '',
port: 0,
speedLimit: 0,
tags: [],
traffic: 0,
trafficRatio: 0,
upload: 0,
city: '',
config: '',
country: '',
download: 0,
startTime: '',
expireTime: '',
latitude: 0.0,
latitudeCountry: 0.0,
longitude: 0.0,
longitudeCountry: 0.0,
);
}
}
// 添加安全解析工具方法
static int _parseIntSafely(dynamic value) {
if (value == null) return 0;
if (value is int) return value;
if (value is String) return int.tryParse(value) ?? 0;
return 0;
}
static double _parseDoubleSafely(dynamic value) {
if (value == null) return 0.0;
if (value is double) return value;
if (value is int) return value.toDouble();
if (value is String) return double.tryParse(value) ?? 0.0;
return 0.0;
}
static List<String> _parseStringList(dynamic value) {
if (value == null) return [];
if (value is List) {
return value.map((e) => e?.toString() ?? '').toList();
}
return [];
}
}
+323
View File
@@ -0,0 +1,323 @@
/// 订单状态模型类
class KROrderStatus {
/// 订单ID
final int kr_id;
/// 用户ID
final int kr_userId;
/// 订单编号
final String kr_orderNo;
/// 订单类型
final int kr_type;
/// 购买数量
final int kr_quantity;
/// 单价
final double kr_price;
/// 总金额
final double kr_amount;
/// 赠送金额
final double kr_giftAmount;
/// 折扣
final double kr_discount;
/// 优惠券码
final String? kr_coupon;
/// 优惠券折扣金额
final double kr_couponDiscount;
/// 佣金
final double kr_commission;
/// 支付方式
final String kr_method;
/// 手续费
final double kr_feeAmount;
/// 交易号
final String kr_tradeNo;
/// 订单状态
final int kr_status;
/// 订阅ID
final int kr_subscribeId;
/// 订阅信息
final KRSubscribe? kr_subscribe;
/// 创建时间
final int kr_createdAt;
/// 更新时间
final int kr_updatedAt;
/// 订单状态枚举
static const int kr_statusPending = 0; // 待支付
static const int kr_statusPaid = 1; // 已支付
static const int kr_statusCancelled = 2; // 已取消
static const int kr_statusRefunded = 3; // 已退款
static const int kr_statusFailed = 4; // 支付失败
/// 获取订单状态描述
String get kr_statusText {
switch (kr_status) {
case kr_statusPending:
return '待支付';
case kr_statusPaid:
return '已支付';
case kr_statusCancelled:
return '已取消';
case kr_statusRefunded:
return '已退款';
case kr_statusFailed:
return '支付失败';
default:
return '未知状态';
}
}
/// 判断订单是否待支付
bool get kr_isPending => kr_status == kr_statusPending;
/// 判断订单是否已支付
bool get kr_isPaid => kr_status == kr_statusPaid;
/// 判断订单是否已取消
bool get kr_isCancelled => kr_status == kr_statusCancelled;
/// 判断订单是否已退款
bool get kr_isRefunded => kr_status == kr_statusRefunded;
/// 判断订单是否支付失败
bool get kr_isFailed => kr_status == kr_statusFailed;
const KROrderStatus({
required this.kr_id,
required this.kr_userId,
required this.kr_orderNo,
required this.kr_type,
required this.kr_quantity,
required this.kr_price,
required this.kr_amount,
required this.kr_giftAmount,
required this.kr_discount,
this.kr_coupon,
required this.kr_couponDiscount,
required this.kr_commission,
required this.kr_method,
required this.kr_feeAmount,
required this.kr_tradeNo,
required this.kr_status,
required this.kr_subscribeId,
this.kr_subscribe,
required this.kr_createdAt,
required this.kr_updatedAt,
});
/// 从JSON映射创建订单状态实例
factory KROrderStatus.fromJson(Map<String, dynamic> json) {
return KROrderStatus(
kr_id: json['id'] as int? ?? 0,
kr_userId: json['user_id'] as int? ?? 0,
kr_orderNo: json['order_no'] as String? ?? '',
kr_type: json['type'] as int? ?? 0,
kr_quantity: json['quantity'] as int? ?? 0,
kr_price: (json['price'] as num?)?.toDouble() ?? 0.0,
kr_amount: (json['amount'] as num?)?.toDouble() ?? 0.0,
kr_giftAmount: (json['gift_amount'] as num?)?.toDouble() ?? 0.0,
kr_discount: (json['discount'] as num?)?.toDouble() ?? 0.0,
kr_coupon: json['coupon'] as String?,
kr_couponDiscount: (json['coupon_discount'] as num?)?.toDouble() ?? 0.0,
kr_commission: (json['commission'] as num?)?.toDouble() ?? 0.0,
kr_method: json['method'] as String? ?? '',
kr_feeAmount: (json['fee_amount'] as num?)?.toDouble() ?? 0.0,
kr_tradeNo: json['trade_no'] as String? ?? '',
kr_status: json['status'] as int? ?? 0,
kr_subscribeId: json['subscribe_id'] as int? ?? 0,
kr_subscribe: json['subscribe'] != null
? KRSubscribe.fromJson(json['subscribe'] as Map<String, dynamic>)
: null,
kr_createdAt: json['created_at'] as int? ?? 0,
kr_updatedAt: json['updated_at'] as int? ?? 0,
);
}
/// 转换为JSON映射
Map<String, dynamic> toJson() {
return {
'id': kr_id,
'user_id': kr_userId,
'order_no': kr_orderNo,
'type': kr_type,
'quantity': kr_quantity,
'price': kr_price,
'amount': kr_amount,
'gift_amount': kr_giftAmount,
'discount': kr_discount,
'coupon': kr_coupon,
'coupon_discount': kr_couponDiscount,
'commission': kr_commission,
'method': kr_method,
'fee_amount': kr_feeAmount,
'trade_no': kr_tradeNo,
'status': kr_status,
'subscribe_id': kr_subscribeId,
'subscribe': kr_subscribe?.toJson(),
'created_at': kr_createdAt,
'updated_at': kr_updatedAt,
};
}
}
/// 订阅信息模型类
class KRSubscribe {
final int kr_id;
final String kr_name;
final String kr_description;
final double kr_unitPrice;
final String kr_unitTime;
final List<KRDiscount> kr_discount;
final int kr_replacement;
final int kr_inventory;
final int kr_traffic;
final int kr_speedLimit;
final int kr_deviceLimit;
final int kr_quota;
final int kr_groupId;
final List<int> kr_serverGroup;
final List<int> kr_server;
final bool kr_show;
final bool kr_sell;
final int kr_sort;
final double kr_deductionRatio;
final bool kr_allowDeduction;
final int kr_resetCycle;
final bool kr_renewalReset;
final int kr_createdAt;
final int kr_updatedAt;
const KRSubscribe({
required this.kr_id,
required this.kr_name,
required this.kr_description,
required this.kr_unitPrice,
required this.kr_unitTime,
required this.kr_discount,
required this.kr_replacement,
required this.kr_inventory,
required this.kr_traffic,
required this.kr_speedLimit,
required this.kr_deviceLimit,
required this.kr_quota,
required this.kr_groupId,
required this.kr_serverGroup,
required this.kr_server,
required this.kr_show,
required this.kr_sell,
required this.kr_sort,
required this.kr_deductionRatio,
required this.kr_allowDeduction,
required this.kr_resetCycle,
required this.kr_renewalReset,
required this.kr_createdAt,
required this.kr_updatedAt,
});
factory KRSubscribe.fromJson(Map<String, dynamic> json) {
return KRSubscribe(
kr_id: json['id'] as int? ?? 0,
kr_name: json['name'] as String? ?? '',
kr_description: json['description'] as String? ?? '',
kr_unitPrice: (json['unit_price'] as num?)?.toDouble() ?? 0.0,
kr_unitTime: json['unit_time'] as String? ?? '',
kr_discount: (json['discount'] as List<dynamic>?)
?.map((e) => KRDiscount.fromJson(e as Map<String, dynamic>))
.toList() ?? [],
kr_replacement: json['replacement'] as int? ?? 0,
kr_inventory: json['inventory'] as int? ?? 0,
kr_traffic: json['traffic'] as int? ?? 0,
kr_speedLimit: json['speed_limit'] as int? ?? 0,
kr_deviceLimit: json['device_limit'] as int? ?? 0,
kr_quota: json['quota'] as int? ?? 0,
kr_groupId: json['group_id'] as int? ?? 0,
kr_serverGroup: (json['server_group'] as List<dynamic>?)
?.map((e) => e as int)
.toList() ?? [],
kr_server: (json['server'] as List<dynamic>?)
?.map((e) => e as int)
.toList() ?? [],
kr_show: json['show'] as bool? ?? false,
kr_sell: json['sell'] as bool? ?? false,
kr_sort: json['sort'] as int? ?? 0,
kr_deductionRatio: (json['deduction_ratio'] as num?)?.toDouble() ?? 0.0,
kr_allowDeduction: json['allow_deduction'] as bool? ?? false,
kr_resetCycle: json['reset_cycle'] as int? ?? 0,
kr_renewalReset: json['renewal_reset'] as bool? ?? false,
kr_createdAt: json['created_at'] as int? ?? 0,
kr_updatedAt: json['updated_at'] as int? ?? 0,
);
}
Map<String, dynamic> toJson() {
return {
'id': kr_id,
'name': kr_name,
'description': kr_description,
'unit_price': kr_unitPrice,
'unit_time': kr_unitTime,
'discount': kr_discount.map((e) => e.toJson()).toList(),
'replacement': kr_replacement,
'inventory': kr_inventory,
'traffic': kr_traffic,
'speed_limit': kr_speedLimit,
'device_limit': kr_deviceLimit,
'quota': kr_quota,
'group_id': kr_groupId,
'server_group': kr_serverGroup,
'server': kr_server,
'show': kr_show,
'sell': kr_sell,
'sort': kr_sort,
'deduction_ratio': kr_deductionRatio,
'allow_deduction': kr_allowDeduction,
'reset_cycle': kr_resetCycle,
'renewal_reset': kr_renewalReset,
'created_at': kr_createdAt,
'updated_at': kr_updatedAt,
};
}
}
/// 折扣信息模型类
class KRDiscount {
final int kr_quantity;
final double kr_discount;
const KRDiscount({
required this.kr_quantity,
required this.kr_discount,
});
factory KRDiscount.fromJson(Map<String, dynamic> json) {
return KRDiscount(
kr_quantity: json['quantity'] as int? ?? 0,
kr_discount: (json['discount'] as num?)?.toDouble() ?? 0.0,
);
}
Map<String, dynamic> toJson() {
return {
'quantity': kr_quantity,
'discount': kr_discount,
};
}
}
+332
View File
@@ -0,0 +1,332 @@
import 'dart:convert';
import 'package:get/get_connect/http/src/utils/utils.dart';
import 'package:kaer_with_panels/app/utils/kr_log_util.dart';
import '../../utils/kr_common_util.dart';
class KRDescription {
final List<KRFeature> kr_features;
KRDescription({
required this.kr_features,
});
factory KRDescription.fromJson(Map<String, dynamic> json) {
return KRDescription(
kr_features: (json['features'] as List?)
?.map((e) => KRFeature.fromJson(e as Map<String, dynamic>))
.toList() ?? [],
);
}
}
class KRFeature {
final String kr_label;
final String kr_type;
final List<KRFeatureDetail> kr_details;
KRFeature({
required this.kr_label,
required this.kr_type,
required this.kr_details,
});
factory KRFeature.fromJson(Map<String, dynamic> json) {
return KRFeature(
kr_label: json['label'] as String,
kr_type: json['type'] as String,
kr_details: (json['details'] as List)
.map((e) => KRFeatureDetail.fromJson(e as Map<String, dynamic>))
.toList(),
);
}
}
class KRFeatureDetail {
final String kr_label;
final String kr_description;
KRFeatureDetail({
required this.kr_label,
required this.kr_description,
});
factory KRFeatureDetail.fromJson(Map<String, dynamic> json) {
return KRFeatureDetail(
kr_label: json['label'] as String,
kr_description: json['description'] as String,
);
}
}
class KRPackageList {
final List<KRPackageListItem> kr_list;
final int kr_total;
KRPackageList({required this.kr_list, required this.kr_total});
// 获取所有不同的时间单位
List<String> kr_getUniqueUnitTimes() {
return kr_list.map((item) => item.kr_unitTime).toSet().toList();
}
// 根据时间单位获取套餐列表
List<KRPackageListItem> kr_getPackagesByUnitTime(String unitTime) {
return kr_list.where((item) => item.kr_unitTime == unitTime).toList();
}
// 检查是否有多个时间单位
bool kr_hasMultipleUnitTimes() {
return kr_getUniqueUnitTimes().length > 1;
}
factory KRPackageList.fromJson(Map<String, dynamic> json) {
return KRPackageList(
kr_list: (json['list'] as List? ?? [])
.map((item) => KRPackageListItem.fromJson(item))
.toList(),
kr_total: json['total']);
}
}
class KRPackageListItem {
// 包的唯一标识符
final int kr_id;
// 包的名称
final String kr_name;
// 包的描述信息
final KRDescription kr_description;
// 单位价格
final int kr_unitPrice;
// 单位时间(例如:月、年)
final String kr_unitTime;
// 折扣信息列表
final List<KRDiscount> kr_discount;
// 替换费用
final int kr_replacement;
// 库存数量
final int kr_inventory;
// 流量限制
final int kr_traffic;
// 速度限制
final int kr_speedLimit;
// 设备限制数量
final int kr_deviceLimit;
// 配额
final int kr_quota;
// 组ID
final int kr_groupId;
// 服务器组(可能为空)
final dynamic kr_serverGroup;
// 服务器(可能为空)
final dynamic kr_server;
// 是否显示
final bool kr_show;
// 是否出售
final bool kr_sell;
// 排序顺序
final int kr_sort;
// 扣除比例
final int kr_deductionRatio;
// 是否允许扣除
final bool kr_allowDeduction;
// 重置周期
final int kr_resetCycle;
// 是否在续订时重置
final bool kr_renewalReset;
// 创建时间戳
final int kr_createdAt;
// 更新时间戳
final int kr_updatedAt;
KRPackageListItem({
required this.kr_id,
required this.kr_name,
required this.kr_description,
required this.kr_unitPrice,
required this.kr_unitTime,
required this.kr_discount,
required this.kr_replacement,
required this.kr_inventory,
required this.kr_traffic,
required this.kr_speedLimit,
required this.kr_deviceLimit,
required this.kr_quota,
required this.kr_groupId,
this.kr_serverGroup,
this.kr_server,
required this.kr_show,
required this.kr_sell,
required this.kr_sort,
required this.kr_deductionRatio,
required this.kr_allowDeduction,
required this.kr_resetCycle,
required this.kr_renewalReset,
required this.kr_createdAt,
required this.kr_updatedAt,
});
// 从JSON数据创建KRPackageList实例
factory KRPackageListItem.fromJson(Map<String, dynamic> json) {
KRLogUtil.kr_i('json: ${json['traffic'] ?? 0}');
// 获取原始折扣列表
final List<KRDiscount> originalDiscounts = (json['discount'] as List<dynamic>?)
?.map((e) => KRDiscount.fromJson(e as Map<String, dynamic>))
.toList() ?? [];
// 创建基础选项(数量为1,折扣为100%)
final KRDiscount baseDiscount = KRDiscount(
kr_quantity: 1,
kr_discount: 100, // 折扣为100%,表示原价
);
// 创建完整的折扣列表,确保基础选项在最后
final List<KRDiscount> discounts = List.from(originalDiscounts);
if (!discounts.any((discount) => discount.kr_quantity == 1)) {
discounts.add(baseDiscount);
}
// 解析描述信息
final descriptionJson = json['description'];
KRDescription description;
if (descriptionJson is String) {
// 如果是空字符串,直接返回空描述
if (descriptionJson.isEmpty) {
description = KRDescription(kr_features: []);
} else {
try {
description = KRDescription.fromJson(jsonDecode(descriptionJson));
} catch (e) {
KRLogUtil.kr_e('解析描述信息失败: $e');
description = KRDescription(kr_features: []);
}
}
} else if (descriptionJson is Map<String, dynamic>) {
description = KRDescription.fromJson(descriptionJson);
} else {
description = KRDescription(kr_features: []);
}
return KRPackageListItem(
kr_id: json['id'] as int,
kr_name: json['name'] as String,
kr_description: description,
kr_unitPrice: json['unit_price'] ?? 0,
kr_unitTime: json['unit_time'] as String,
kr_discount: discounts,
kr_replacement: json['replacement'] ?? 0,
kr_inventory: json['inventory'] ?? 0,
kr_traffic: json['traffic'] ?? 0,
kr_speedLimit: json['speed_limit'] ?? 0,
kr_deviceLimit: json['device_limit'] ?? 0,
kr_quota: json['quota'] ?? 0,
kr_groupId: json['group_id'] ?? 0,
kr_serverGroup: json['server_group'],
kr_server: json['server'],
kr_show: json['show'] ?? false,
kr_sell: json['sell'] ?? false,
kr_sort: json['sort'] ?? 0,
kr_deductionRatio: json['deduction_ratio'] ?? 0,
kr_allowDeduction: json['allow_deduction'] ?? false,
kr_resetCycle: json['reset_cycle'] ?? 0,
kr_renewalReset: json['renewal_reset'] ?? false,
kr_createdAt: json['created_at'] ?? 0,
kr_updatedAt: json['updated_at'] ?? 0,
);
}
// 获取包含基础选项的完整折扣列表
List<KRDiscount> kr_getCompleteDiscountList() {
// 创建基础选项(数量为1,折扣为100%)
final KRDiscount baseDiscount = KRDiscount(
kr_quantity: 1,
kr_discount: 100, // 折扣为100%,表示原价
);
// 如果原始折扣列表为空,返回只包含基础选项的列表
if (kr_discount.isEmpty) {
return [baseDiscount];
}
// 检查是否已存在数量为1的折扣
final bool hasBaseDiscount = kr_discount.any((discount) => discount.kr_quantity == 1);
// 创建新的列表,包含所有原始折扣
final List<KRDiscount> completeList = List.from(kr_discount);
// 如果没有数量为1的折扣,添加基础选项到列表末尾
if (!hasBaseDiscount) {
completeList.add(baseDiscount);
}
// 按数量排序
completeList.sort((a, b) => a.kr_quantity.compareTo(b.kr_quantity));
return completeList;
}
// 获取折扣后的价格
double kr_getDiscountedPrice() {
if (kr_discount.isEmpty) return kr_unitPrice / 100.0;
final maxDiscount = kr_discount.reduce((a, b) => a.kr_discount > b.kr_discount ? a : b);
return (kr_unitPrice / 100.0) * (maxDiscount.kr_discount / 100.0);
}
// 获取折扣显示文本
String kr_getDiscountDisplay() {
if (kr_discount.isEmpty) return '';
final maxDiscount = kr_discount.reduce((a, b) => a.kr_discount > b.kr_discount ? a : b);
return '${(maxDiscount.kr_discount / 10).toStringAsFixed(1)}';
}
// 获取最大折扣
KRDiscount? kr_getMaxDiscount() {
if (kr_discount.isEmpty) return null;
return kr_discount.reduce((a, b) =>
a.kr_discount > b.kr_discount ? a : b);
}
// 格式化价格显示(保留两位小数)
String kr_formatPrice(double price) {
return price.toStringAsFixed(2);
}
// 获取套餐描述
String kr_getPackageDescription() {
if (kr_discount.isEmpty) {
return '${kr_name} - ${kr_unitPrice / 100.0}元/${kr_unitTime}';
}
final maxDiscount = kr_discount.reduce((a, b) => a.kr_discount > b.kr_discount ? a : b);
return '${kr_name} - ${kr_getDiscountedPrice()}元/${kr_unitTime}';
}
}
class KRDiscount {
// 折扣数量
final int kr_quantity;
// 折扣百分比
final int kr_discount;
KRDiscount({
required this.kr_quantity,
required this.kr_discount,
});
// 从JSON数据创建KRDiscount实例
factory KRDiscount.fromJson(Map<String, dynamic> json) {
// 确保折扣值在 0-100 之间
int discount = json['discount'] ?? 100;
if (discount < 0) discount = 0;
if (discount > 100) discount = 100;
return KRDiscount(
kr_quantity: json['quantity'] ?? 1,
kr_discount: discount,
);
}
}
+48
View File
@@ -0,0 +1,48 @@
import 'package:kaer_with_panels/app/utils/kr_log_util.dart';
class KRPaymentMethods {
/// 支付方式列表
final List<KRPaymentMethod> list;
KRPaymentMethods({required this.list});
factory KRPaymentMethods.fromJson(Map<String, dynamic> json) {
final List<dynamic> rawList = json['list'] ?? [];
return KRPaymentMethods(
list: rawList.map((item) => KRPaymentMethod.fromJson(item)).toList(),
);
}
}
class KRPaymentMethod {
final int id;
final String name;
final String platform;
final String icon;
final int feeMode;
final int feePercent;
final int feeAmount;
KRPaymentMethod({
required this.id,
required this.name,
required this.platform,
required this.icon,
required this.feeMode,
required this.feePercent,
required this.feeAmount,
});
factory KRPaymentMethod.fromJson(Map<String, dynamic> json) {
KRLogUtil.kr_i(json.toString());
return KRPaymentMethod(
id: (json['id'] ?? 0),
name: json['name'] ?? '',
platform: json['platform'] ?? '',
icon: json['icon'] ?? '',
feeMode: json['fee_mode'] ?? 0,
feePercent: json['fee_percent'] ?? 0,
feeAmount: json['fee_amount'] ?? 0,
);
}
}
+21
View File
@@ -0,0 +1,21 @@
class KRPurchaseOrderNo {
final String orderNo;
KRPurchaseOrderNo({required this.orderNo});
factory KRPurchaseOrderNo.fromJson(Map<String, dynamic> json) {
return KRPurchaseOrderNo(orderNo: json['order_no'] ?? '');
}
}
class KRPurchaseOrderUrl {
final String url;
KRPurchaseOrderUrl({required this.url});
factory KRPurchaseOrderUrl.fromJson(Map<String, dynamic> json) {
return KRPurchaseOrderUrl(url: json['checkout_url'] ?? '');
}
}
+425
View File
@@ -0,0 +1,425 @@
import 'dart:convert';
/// 网站配置信息
class KRSiteConfig {
final KRSiteInfo site;
final KRVerifyConfig verify;
final KRAuthConfig auth;
final KRInviteConfig invite;
final KRCurrencyConfig currency;
final KRSubscribeConfig subscribe;
final KRVerifyCodeConfig verifyCode;
final List<String> oauthMethods;
final bool webAd;
KRSiteConfig({
required this.site,
required this.verify,
required this.auth,
required this.invite,
required this.currency,
required this.subscribe,
required this.verifyCode,
required this.oauthMethods,
required this.webAd,
});
factory KRSiteConfig.fromJson(Map<String, dynamic> json) {
return KRSiteConfig(
site: KRSiteInfo.fromJson(json['site'] ?? {}),
verify: KRVerifyConfig.fromJson(json['verify'] ?? {}),
auth: KRAuthConfig.fromJson(json['auth'] ?? {}),
invite: KRInviteConfig.fromJson(json['invite'] ?? {}),
currency: KRCurrencyConfig.fromJson(json['currency'] ?? {}),
subscribe: KRSubscribeConfig.fromJson(json['subscribe'] ?? {}),
verifyCode: KRVerifyCodeConfig.fromJson(json['verify_code'] ?? {}),
oauthMethods: List<String>.from(json['oauth_methods'] ?? []),
webAd: json['web_ad'] ?? false,
);
}
Map<String, dynamic> toJson() {
return {
'site': site.toJson(),
'verify': verify.toJson(),
'auth': auth.toJson(),
'invite': invite.toJson(),
'currency': currency.toJson(),
'subscribe': subscribe.toJson(),
'verify_code': verifyCode.toJson(),
'oauth_methods': oauthMethods,
'web_ad': webAd,
};
}
}
/// 站点信息
class KRSiteInfo {
final String host;
final String siteName;
final String siteDesc;
final String siteLogo;
final String keywords;
final String customHtml;
final String customData;
final String crispId;
KRSiteInfo({
required this.host,
required this.siteName,
required this.siteDesc,
required this.siteLogo,
required this.keywords,
required this.customHtml,
required this.customData,
required this.crispId,
});
factory KRSiteInfo.fromJson(Map<String, dynamic> json) {
String crispId = '0';
// 尝试解析 custom_data 中的 kr_website_id
try {
final customDataStr = json['custom_data'] ?? '';
if (customDataStr.isNotEmpty) {
final customDataJson = jsonDecode(customDataStr) as Map<String, dynamic>;
crispId = customDataJson['kr_website_id'] ?? '0';
}
} catch (e) {
// 解析失败时使用默认值
}
return KRSiteInfo(
host: json['host'] ?? '',
siteName: json['site_name'] ?? '',
siteDesc: json['site_desc'] ?? '',
siteLogo: json['site_logo'] ?? '',
keywords: json['keywords'] ?? '',
customHtml: json['custom_html'] ?? '',
customData: json['custom_data'] ?? '',
crispId: crispId,
);
}
Map<String, dynamic> toJson() {
return {
'host': host,
'site_name': siteName,
'site_desc': siteDesc,
'site_logo': siteLogo,
'keywords': keywords,
'custom_html': customHtml,
'custom_data': customData,
};
}
}
/// 验证配置
class KRVerifyConfig {
final String turnstileSiteKey;
final bool enableLoginVerify;
final bool enableRegisterVerify;
final bool enableResetPasswordVerify;
KRVerifyConfig({
required this.turnstileSiteKey,
required this.enableLoginVerify,
required this.enableRegisterVerify,
required this.enableResetPasswordVerify,
});
factory KRVerifyConfig.fromJson(Map<String, dynamic> json) {
return KRVerifyConfig(
turnstileSiteKey: json['turnstile_site_key'] ?? '',
enableLoginVerify: json['enable_login_verify'] ?? false,
enableRegisterVerify: json['enable_register_verify'] ?? false,
enableResetPasswordVerify: json['enable_reset_password_verify'] ?? false,
);
}
Map<String, dynamic> toJson() {
return {
'turnstile_site_key': turnstileSiteKey,
'enable_login_verify': enableLoginVerify,
'enable_register_verify': enableRegisterVerify,
'enable_reset_password_verify': enableResetPasswordVerify,
};
}
}
/// 认证配置
class KRAuthConfig {
final KRMobileAuth mobile;
final KREmailAuth email;
final KRDeviceAuth device;
final KRRegisterAuth register;
KRAuthConfig({
required this.mobile,
required this.email,
required this.device,
required this.register,
});
factory KRAuthConfig.fromJson(Map<String, dynamic> json) {
return KRAuthConfig(
mobile: KRMobileAuth.fromJson(json['mobile'] ?? {}),
email: KREmailAuth.fromJson(json['email'] ?? {}),
device: KRDeviceAuth.fromJson(json['device'] ?? {}),
register: KRRegisterAuth.fromJson(json['register'] ?? {}),
);
}
Map<String, dynamic> toJson() {
return {
'mobile': mobile.toJson(),
'email': email.toJson(),
'device': device.toJson(),
'register': register.toJson(),
};
}
}
/// 手机号认证配置
class KRMobileAuth {
final bool enable;
final bool enableWhitelist;
final List<String> whitelist;
KRMobileAuth({
required this.enable,
required this.enableWhitelist,
required this.whitelist,
});
factory KRMobileAuth.fromJson(Map<String, dynamic> json) {
return KRMobileAuth(
enable: json['enable'] ?? false,
enableWhitelist: json['enable_whitelist'] ?? false,
whitelist: List<String>.from(json['whitelist'] ?? []),
);
}
Map<String, dynamic> toJson() {
return {
'enable': enable,
'enable_whitelist': enableWhitelist,
'whitelist': whitelist,
};
}
}
/// 邮箱认证配置
class KREmailAuth {
final bool enable;
final bool enableVerify;
final bool enableDomainSuffix;
final String domainSuffixList;
KREmailAuth({
required this.enable,
required this.enableVerify,
required this.enableDomainSuffix,
required this.domainSuffixList,
});
factory KREmailAuth.fromJson(Map<String, dynamic> json) {
return KREmailAuth(
enable: json['enable'] ?? false,
enableVerify: json['enable_verify'] ?? false,
enableDomainSuffix: json['enable_domain_suffix'] ?? false,
domainSuffixList: json['domain_suffix_list'] ?? '',
);
}
Map<String, dynamic> toJson() {
return {
'enable': enable,
'enable_verify': enableVerify,
'enable_domain_suffix': enableDomainSuffix,
'domain_suffix_list': domainSuffixList,
};
}
}
/// 设备认证配置
class KRDeviceAuth {
final bool enable;
final bool showAds;
final bool enableSecurity;
final bool onlyRealDevice;
KRDeviceAuth({
required this.enable,
required this.showAds,
required this.enableSecurity,
required this.onlyRealDevice,
});
factory KRDeviceAuth.fromJson(Map<String, dynamic> json) {
return KRDeviceAuth(
enable: json['enable'] ?? false,
showAds: json['show_ads'] ?? false,
enableSecurity: json['enable_security'] ?? false,
onlyRealDevice: json['only_real_device'] ?? false,
);
}
Map<String, dynamic> toJson() {
return {
'enable': enable,
'show_ads': showAds,
'enable_security': enableSecurity,
'only_real_device': onlyRealDevice,
};
}
}
/// 注册认证配置
class KRRegisterAuth {
final bool stopRegister;
final bool enableIpRegisterLimit;
final int ipRegisterLimit;
final int ipRegisterLimitDuration;
KRRegisterAuth({
required this.stopRegister,
required this.enableIpRegisterLimit,
required this.ipRegisterLimit,
required this.ipRegisterLimitDuration,
});
factory KRRegisterAuth.fromJson(Map<String, dynamic> json) {
return KRRegisterAuth(
stopRegister: json['stop_register'] ?? false,
enableIpRegisterLimit: json['enable_ip_register_limit'] ?? false,
ipRegisterLimit: json['ip_register_limit'] ?? 0,
ipRegisterLimitDuration: json['ip_register_limit_duration'] ?? 0,
);
}
Map<String, dynamic> toJson() {
return {
'stop_register': stopRegister,
'enable_ip_register_limit': enableIpRegisterLimit,
'ip_register_limit': ipRegisterLimit,
'ip_register_limit_duration': ipRegisterLimitDuration,
};
}
}
/// 邀请配置
class KRInviteConfig {
final bool forcedInvite;
final double referralPercentage;
final bool onlyFirstPurchase;
KRInviteConfig({
required this.forcedInvite,
required this.referralPercentage,
required this.onlyFirstPurchase,
});
factory KRInviteConfig.fromJson(Map<String, dynamic> json) {
return KRInviteConfig(
forcedInvite: json['forced_invite'] ?? false,
referralPercentage: (json['referral_percentage'] ?? 0).toDouble(),
onlyFirstPurchase: json['only_first_purchase'] ?? false,
);
}
Map<String, dynamic> toJson() {
return {
'forced_invite': forcedInvite,
'referral_percentage': referralPercentage,
'only_first_purchase': onlyFirstPurchase,
};
}
}
/// 货币配置
class KRCurrencyConfig {
final String currencyUnit;
final String currencySymbol;
KRCurrencyConfig({
required this.currencyUnit,
required this.currencySymbol,
});
factory KRCurrencyConfig.fromJson(Map<String, dynamic> json) {
return KRCurrencyConfig(
currencyUnit: json['currency_unit'] ?? '',
currencySymbol: json['currency_symbol'] ?? '',
);
}
Map<String, dynamic> toJson() {
return {
'currency_unit': currencyUnit,
'currency_symbol': currencySymbol,
};
}
}
/// 订阅配置
class KRSubscribeConfig {
final bool singleModel;
final String subscribePath;
final String subscribeDomain;
final bool panDomain;
final bool userAgentLimit;
final String userAgentList;
KRSubscribeConfig({
required this.singleModel,
required this.subscribePath,
required this.subscribeDomain,
required this.panDomain,
required this.userAgentLimit,
required this.userAgentList,
});
factory KRSubscribeConfig.fromJson(Map<String, dynamic> json) {
return KRSubscribeConfig(
singleModel: json['single_model'] ?? false,
subscribePath: json['subscribe_path'] ?? '',
subscribeDomain: json['subscribe_domain'] ?? '',
panDomain: json['pan_domain'] ?? false,
userAgentLimit: json['user_agent_limit'] ?? false,
userAgentList: json['user_agent_list'] ?? '',
);
}
Map<String, dynamic> toJson() {
return {
'single_model': singleModel,
'subscribe_path': subscribePath,
'subscribe_domain': subscribeDomain,
'pan_domain': panDomain,
'user_agent_limit': userAgentLimit,
'user_agent_list': userAgentList,
};
}
}
/// 验证码配置
class KRVerifyCodeConfig {
final int verifyCodeInterval;
KRVerifyCodeConfig({
required this.verifyCodeInterval,
});
factory KRVerifyCodeConfig.fromJson(Map<String, dynamic> json) {
return KRVerifyCodeConfig(
verifyCodeInterval: json['verify_code_interval'] ?? 60,
);
}
Map<String, dynamic> toJson() {
return {
'verify_code_interval': verifyCodeInterval,
};
}
}
+22
View File
@@ -0,0 +1,22 @@
/// 是否注册
class KRStatus {
bool kr_bl= false;
KRStatus({this.kr_bl = false});
KRStatus.fromJson(Map<String, dynamic> json) {
kr_bl = json['Status'] == "true" || json['Status'] == true
? true
: false || json['status'] == "true" || json['status'] == true
? true
: false;
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['status'] = kr_bl;
return data;
}
}
+89
View File
@@ -0,0 +1,89 @@
import '../../utils/kr_log_util.dart';
class KRUserAvailableSubscribeItem {
final int id;
final String name;
final int deviceLimit;
final int download;
final int upload;
final int traffic;
final String startTime;
final String expireTime;
final List<dynamic> list;
final bool isTryOut; // 试用标志:true=试用,false=付费
const KRUserAvailableSubscribeItem({
this.id = 0,
this.name = '',
this.deviceLimit = 0,
this.download = 0,
this.upload = 0,
this.traffic = 0,
this.startTime = '',
this.expireTime = '',
this.list = const [],
this.isTryOut = false,
});
factory KRUserAvailableSubscribeItem.fromJson(Map<String, dynamic> json) {
// 从 subscribe 对象中获取订阅信息
final subscribe = json['subscribe'] as Map<String, dynamic>?;
// 时间字段可能是 int (毫秒时间戳) 或 String (ISO 8601)
String convertTime(dynamic value) {
if (value == null) return '';
if (value is String) return value;
if (value is int) {
// 将毫秒时间戳转换为 ISO 8601 字符串
return DateTime.fromMillisecondsSinceEpoch(value).toIso8601String();
}
return value.toString();
}
return KRUserAvailableSubscribeItem(
id: json['id'] as int? ?? 0,
name: subscribe?['name'] as String? ?? '',
deviceLimit: subscribe?['device_limit'] as int? ?? 0,
download: json['download'] as int? ?? 0,
upload: json['upload'] as int? ?? 0,
traffic: json['traffic'] as int? ?? 0,
startTime: convertTime(json['start_time']),
expireTime: convertTime(json['expire_time']),
list: (json['list'] as List<dynamic>?) ?? const [],
isTryOut: subscribe?['is_try_out'] as bool? ?? false,
);
}
Map<String, dynamic> toJson() {
return {
'id': id,
'name': name,
'device_limit': deviceLimit,
'download': download,
'upload': upload,
'traffic': traffic,
'start_time': startTime,
'expire_time': expireTime,
'list': list,
'is_try_out': isTryOut,
};
}
}
class KRUserAvailableSubscribeList {
final List<KRUserAvailableSubscribeItem> list;
const KRUserAvailableSubscribeList({
this.list = const [],
});
factory KRUserAvailableSubscribeList.fromJson(Map<String, dynamic> json) {
KRLogUtil.kr_i('订阅json列表: ${json}', tag: 'KRUserAvailableSubscribeList');
final List<dynamic> listData = (json['list'] as List<dynamic>?) ?? const [];
return KRUserAvailableSubscribeList(
list: listData
.map((item) => KRUserAvailableSubscribeItem.fromJson(item as Map<String, dynamic>))
.toList(),
);
}
}
+34
View File
@@ -0,0 +1,34 @@
class KRUserInfo {
final int id;
final String email;
final int refererId;
final String referCode;
final String avatar;
final String areaCode;
final String telephone;
final int balance;
KRUserInfo({
required this.id,
required this.email,
this.refererId = 0,
this.referCode = '',
this.avatar = '',
this.areaCode = '',
this.telephone = '',
this.balance = 0
});
factory KRUserInfo.fromJson(Map<String, dynamic> json) {
return KRUserInfo(
id: json['id'] ?? 0,
email: json['email'] ?? '',
refererId: json['referer_id'] ?? 0,
referCode: json['refer_code'] ?? '',
avatar: json['avatar'] ?? '',
areaCode: json['area_code'] ?? '',
telephone: json['telephone'] ?? '',
balance: json['balance'] ?? 0,
);
}
}
+63
View File
@@ -0,0 +1,63 @@
/// 每日在线时长统计模型
class KRDailyOnlineStat {
final int day;
final String dayName;
final double hours;
KRDailyOnlineStat({
required this.day,
required this.dayName,
required this.hours,
});
factory KRDailyOnlineStat.fromJson(Map<String, dynamic> json) {
return KRDailyOnlineStat(
day: json['day'] ?? 0,
dayName: json['day_name'] ?? '',
hours: (json['hours'] ?? 0.0).toDouble(),
);
}
}
/// 在线时长记录模型
class KROnlineDurationRecord {
final int currentContinuousDays;
final int historyContinuousDays;
final int longestSingleConnection;
KROnlineDurationRecord({
required this.currentContinuousDays,
required this.historyContinuousDays,
required this.longestSingleConnection,
});
factory KROnlineDurationRecord.fromJson(Map<String, dynamic> json) {
return KROnlineDurationRecord(
currentContinuousDays: json['current_continuous_days'] ?? 0,
historyContinuousDays: json['history_continuous_days'] ?? 0,
longestSingleConnection: json['longest_single_connection'] ?? 0,
);
}
}
/// 用户在线时长统计响应模型
class KRUserOnlineDurationResponse {
final List<KRDailyOnlineStat> weeklyStats;
final KROnlineDurationRecord connectionRecords;
KRUserOnlineDurationResponse({
required this.weeklyStats,
required this.connectionRecords,
});
factory KRUserOnlineDurationResponse.fromJson(Map<String, dynamic> json) {
return KRUserOnlineDurationResponse(
weeklyStats: (json['weekly_stats'] as List?)
?.map((e) => KRDailyOnlineStat.fromJson(e as Map<String, dynamic>))
.toList() ?? [],
connectionRecords: KROnlineDurationRecord.fromJson(
json['connection_records'] as Map<String, dynamic>? ?? {},
),
);
}
}
+30
View File
@@ -0,0 +1,30 @@
/// 网页文本内容响应模型
class KRWebText {
/// 隐私政策内容
final String privacyPolicy;
/// 用户协议内容
final String tosContent;
/// 构造函数
KRWebText({
required this.privacyPolicy,
required this.tosContent,
});
/// 从 JSON 创建实例
factory KRWebText.fromJson(Map<String, dynamic> json) {
return KRWebText(
privacyPolicy: json['privacy_policy'] ?? '',
tosContent: json['tos_content'] ?? '',
);
}
/// 转换为 JSON
Map<String, dynamic> toJson() {
return {
'privacy_policy': privacyPolicy,
'tos_content': tosContent,
};
}
}