Files
xianyan/lib/features/file_transfer/models/transfer_device.dart
Developer ae6804e8bd refactor: 兼容后端返回数字类型波动,清理废弃代码
主要变更:
1.  全局修复类型转换问题,将多处`as int?`改为`(num?)?.toInt()`兼容浮点/字符串类型的数字字段
2.  移除废弃的nearby_p2p配对方式和对应的依赖包
3.  优化鸿蒙端快捷方式、引导页、路由导航的稳定性
4.  合并日志输出避免鸿蒙端IDE卡顿
5.  修复安卓端蓝牙权限冗余声明
2026-06-07 08:04:38 +08:00

262 lines
8.3 KiB
Dart
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// ============================================================
// 闲言APP — 传输设备模型
// 创建时间: 2026-05-09
// 更新时间: 2026-05-20
// 作用: 文件传输助手远程设备数据模型 — 设备信息/配对状态/传输偏好
// 上次更新: 删除fromJsonWithDefaults重复代码(与fromJson完全重复且缺少字段)统一使用fromJson
// ============================================================
import 'transfer_enums.dart';
class TransferDevice {
const TransferDevice({
required this.id,
required this.alias,
required this.deviceType,
required this.port,
required this.pairingMethod,
required this.preferredTransport,
required this.lastSeen,
required this.isOnline,
required this.isVerified,
this.deviceModel,
this.ip,
this.publicKey,
this.fingerprint,
this.isFavorite = false,
this.userId,
this.ipCity,
this.ipRange,
this.accountAlias,
});
final String id;
final String alias;
final String? deviceModel;
final DeviceType deviceType;
final String? ip;
final int port;
final PairingMethod pairingMethod;
final TransportType preferredTransport;
final DateTime lastSeen;
final bool isOnline;
final bool isVerified;
final String? publicKey;
final String? fingerprint;
final bool isFavorite;
final String? userId;
final String? ipCity;
final String? ipRange;
final String? accountAlias;
String get displayEmoji => deviceType.emoji;
String get displayType => deviceType.label;
String get displayTransport => preferredTransport.label;
String get displayStatus {
if (isOnline) return '🟢 在线';
return '⚪ 离线';
}
String get displayAlias {
if (accountAlias != null && accountAlias!.isNotEmpty) return accountAlias!;
if (deviceType == DeviceType.web)
return alias.isNotEmpty ? alias : 'Web浏览器';
if (alias != '闲言设备' && alias != '未知设备') return alias;
final parts = <String>[];
if (deviceModel != null && deviceModel!.isNotEmpty && deviceModel != 'localhost') {
parts.add(deviceModel!);
}
if (parts.isNotEmpty) return parts.join(' · ');
return alias;
}
String get displayInfo {
final parts = <String>[];
parts.add(displayType);
if (ip != null) parts.add(ip!);
parts.add('$port');
return parts.join(' · ');
}
bool get hasIp => ip != null && ip!.isNotEmpty;
bool get hasPublicKey => publicKey != null && publicKey!.isNotEmpty;
bool get hasIpCity => ipCity != null && ipCity!.isNotEmpty;
bool get hasIpRange => ipRange != null && ipRange!.isNotEmpty;
bool get isAccountDevice => userId != null && userId!.isNotEmpty;
String get displayLocation => hasIpCity ? '📍 $ipCity' : '';
TransferDevice copyWith({
String? id,
String? alias,
String? deviceModel,
DeviceType? deviceType,
String? ip,
int? port,
PairingMethod? pairingMethod,
TransportType? preferredTransport,
DateTime? lastSeen,
bool? isOnline,
bool? isVerified,
String? publicKey,
String? fingerprint,
bool? isFavorite,
String? userId,
String? ipCity,
String? ipRange,
String? accountAlias,
}) {
return TransferDevice(
id: id ?? this.id,
alias: alias ?? this.alias,
deviceModel: deviceModel ?? this.deviceModel,
deviceType: deviceType ?? this.deviceType,
ip: ip ?? this.ip,
port: port ?? this.port,
pairingMethod: pairingMethod ?? this.pairingMethod,
preferredTransport: preferredTransport ?? this.preferredTransport,
lastSeen: lastSeen ?? this.lastSeen,
isOnline: isOnline ?? this.isOnline,
isVerified: isVerified ?? this.isVerified,
publicKey: publicKey ?? this.publicKey,
fingerprint: fingerprint ?? this.fingerprint,
isFavorite: isFavorite ?? this.isFavorite,
userId: userId ?? this.userId,
ipCity: ipCity ?? this.ipCity,
ipRange: ipRange ?? this.ipRange,
accountAlias: accountAlias ?? this.accountAlias,
);
}
Map<String, dynamic> toJson() => {
'id': id,
'alias': alias,
'deviceModel': deviceModel,
'deviceType': deviceType.id,
'ip': ip,
'port': port,
'pairingMethod': pairingMethod.id,
'preferredTransport': preferredTransport.id,
'lastSeen': lastSeen.toIso8601String(),
'isOnline': isOnline,
'isVerified': isVerified,
'isFavorite': isFavorite,
'publicKey': publicKey,
'fingerprint': fingerprint,
'userId': userId,
'ipCity': ipCity,
'ipRange': ipRange,
'accountAlias': accountAlias,
};
factory TransferDevice.fromJson(Map<String, dynamic> json) {
return TransferDevice(
id: json['id'] as String? ?? '',
alias: json['alias'] as String? ?? '未知设备',
deviceModel: json['deviceModel'] as String?,
deviceType: DeviceType.fromId(json['deviceType'] as String? ?? 'mobile'),
ip: json['ip'] as String?,
port: (json['port'] as num?)?.toInt() ?? 53317,
pairingMethod: PairingMethod.fromId(
json['pairingMethod'] as String? ?? 'lan',
),
preferredTransport: TransportType.fromId(
json['preferredTransport'] as String? ?? 'localsend_http',
),
lastSeen: json['lastSeen'] != null
? DateTime.parse(json['lastSeen'] as String)
: DateTime.now(),
isOnline: json['isOnline'] as bool? ?? false,
isVerified: json['isVerified'] as bool? ?? false,
isFavorite: json['isFavorite'] as bool? ?? false,
publicKey: json['publicKey'] as String?,
fingerprint: json['fingerprint'] as String?,
userId: json['userId'] as String?,
ipCity: json['ipCity'] as String?,
ipRange: json['ipRange'] as String?,
accountAlias: json['accountAlias'] as String?,
);
}
factory TransferDevice.fromAnnounce(
Map<String, dynamic> announce,
String sourceIp,
) {
return TransferDevice(
id: announce['fingerprint'] as String? ?? '',
alias: announce['alias'] as String? ?? '未知设备',
deviceModel: announce['deviceModel'] as String?,
deviceType: DeviceType.fromId(
announce['deviceType'] as String? ?? 'mobile',
),
ip: sourceIp,
port: announce['port'] as int? ?? 53317,
pairingMethod: PairingMethod.lan,
preferredTransport: TransportType.localsendHttp,
lastSeen: DateTime.now(),
isOnline: true,
isVerified: false,
fingerprint: announce['fingerprint'] as String?,
);
}
factory TransferDevice.fromSignaling(Map<String, dynamic> data) {
final fingerprint = data['fingerprint'] as String?;
final serverId = data['id'] as String? ?? '';
final deviceId = (fingerprint != null && fingerprint.isNotEmpty)
? fingerprint
: serverId;
final rawDeviceType =
data['platform'] as String? ??
data['deviceType'] as String? ??
'mobile';
final resolvedDeviceType = DeviceType.fromId(rawDeviceType);
final isWeb =
resolvedDeviceType == DeviceType.web || rawDeviceType == 'browser';
final accountAlias =
data['accountAlias'] as String? ??
data['nickname'] as String? ??
data['username'] as String?;
return TransferDevice(
id: deviceId,
alias:
data['alias'] as String? ??
data['deviceName'] as String? ??
(isWeb ? 'Web浏览器' : '未知设备'),
deviceModel: data['deviceModel'] as String?,
deviceType: isWeb ? DeviceType.web : resolvedDeviceType,
ip: data['ip'] as String?,
port: 53317,
pairingMethod: PairingMethod.account,
preferredTransport: isWeb
? TransportType.wsRelay
: (data['rtcSupported'] as bool? ?? false)
? TransportType.webrtcP2p
: TransportType.wsRelay,
lastSeen: DateTime.now(),
isOnline: data['isOnline'] as bool? ?? true,
isVerified: false,
fingerprint: fingerprint ?? serverId,
userId: data['userId'] as String?,
ipCity: data['ipCity'] as String?,
ipRange: data['ipRange'] as String?,
accountAlias: accountAlias,
);
}
@override
String toString() =>
'TransferDevice(id: $id, alias: $alias, ip: $ip, online: $isOnline)';
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is TransferDevice &&
runtimeType == other.runtimeType &&
id == other.id;
@override
int get hashCode => id.hashCode;
}