188 lines
5.8 KiB
Dart
Executable File
188 lines
5.8 KiB
Dart
Executable File
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 [];
|
|
}
|
|
}
|