+28
@@ -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,
|
||||
});
|
||||
}
|
||||
Executable
+319
@@ -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
@@ -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]!)); // 添加国家分组到列表
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
Executable
Executable
+64
@@ -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!");
|
||||
}
|
||||
}
|
||||
}
|
||||
Executable
+7
@@ -0,0 +1,7 @@
|
||||
enum KRHomeViewsStatus {
|
||||
kr_nore,
|
||||
kr_serverList,
|
||||
kr_subscribeList,
|
||||
kr_coutrysubscribeList,
|
||||
kr_serversubscribeList,
|
||||
}
|
||||
Executable
+4
@@ -0,0 +1,4 @@
|
||||
enum KRMessageType {
|
||||
kr_payment,
|
||||
kr_subscribe_update,
|
||||
}
|
||||
Executable
+18
@@ -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";
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Executable
+73
@@ -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
@@ -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 ...
|
||||
Executable
+149
@@ -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;
|
||||
}
|
||||
}
|
||||
Executable
+22
@@ -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
@@ -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,
|
||||
);
|
||||
}
|
||||
}
|
||||
Executable
+17
@@ -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;
|
||||
}
|
||||
}
|
||||
Executable
+96
@@ -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
@@ -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() ?? '',
|
||||
);
|
||||
}
|
||||
}
|
||||
Executable
+187
@@ -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 [];
|
||||
}
|
||||
}
|
||||
Executable
+323
@@ -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,
|
||||
};
|
||||
}
|
||||
}
|
||||
Executable
+332
@@ -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
@@ -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
@@ -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'] ?? '');
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
};
|
||||
}
|
||||
}
|
||||
Executable
+22
@@ -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
@@ -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(),
|
||||
);
|
||||
}
|
||||
}
|
||||
Executable
+34
@@ -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
@@ -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>? ?? {},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Executable
+30
@@ -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,
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user