Files
ttstd_family_care/lib/features/device/domain/device_models.dart
TongTongStudio 06cdc77172 feat(android): 支持沉浸式状态栏、地图定位与自定义签名
- 配置 edge-to-edge 沉浸式状态栏与刘海屏适配
- 新增百度地图权限、AK 配置及定位权限说明
- 接入 MethodChannel 实现返回键退后台热启动优化
- 配置自定义签名并更新构建逻辑
- 完善 token 刷新与鉴权失效统一处理
- 新增地图页与截图页路由
- 修复返回键拦截导致的 PopScope 失效问题
2026-08-19 03:43:12 +08:00

257 lines
7.4 KiB
Dart
Raw 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.
/// 设备相关领域实体(家属端对接 client 接口)。
///
/// 以不可变类表达,避免 UI / 仓库直接依赖后台 DTO 结构。
/// 因未运行 build_runner避免改动依赖与生成产物此处手写等价实现
/// 所有字段 final、提供 const 构造与 copyWith并手动实现 JSON 转换。
/// 设备简要信息(「我的设备」列表)。
class DeviceBrief {
const DeviceBrief({
required this.serialno,
this.snName,
this.snModel,
this.snMobile,
this.status,
this.activateTime,
});
final String serialno;
final String? snName;
final String? snModel;
final String? snMobile;
final int? status;
final String? activateTime;
factory DeviceBrief.fromJson(Map<String, dynamic> json) => DeviceBrief(
serialno: json['serialno'] as String,
snName: json['snName'] as String?,
snModel: json['snModel'] as String?,
snMobile: json['snMobile'] as String?,
status: _toInt(json['status']),
activateTime: json['activateTime']?.toString(),
);
}
/// 设备基本信息(系统信息面板)。
class DeviceSystemInfo {
const DeviceSystemInfo({
this.serialno,
this.deviceName,
this.deviceBrand,
this.deviceModel,
this.androidVersion,
this.releaseVersion,
this.screenResolution,
this.osType,
this.osVersion,
this.firmwareVersion,
});
final String? serialno;
final String? deviceName;
final String? deviceBrand;
final String? deviceModel;
final String? androidVersion;
final String? releaseVersion;
final String? screenResolution;
final String? osType;
final String? osVersion;
final String? firmwareVersion;
factory DeviceSystemInfo.fromJson(Map<String, dynamic> json) => DeviceSystemInfo(
serialno: json['serialno'] as String?,
deviceName: json['deviceName'] as String?,
deviceBrand: json['deviceBrand'] as String?,
deviceModel: json['deviceModel'] as String?,
androidVersion: json['androidVersion'] as String?,
releaseVersion: json['releaseVersion'] as String?,
screenResolution: json['screenResolution'] as String?,
osType: json['osType'] as String?,
osVersion: json['osVersion'] as String?,
firmwareVersion: json['firmwareVersion'] as String?,
);
}
/// 设备定位信息。
class DeviceLocation {
const DeviceLocation({
this.sn,
this.country,
this.province,
this.city,
this.district,
this.street,
this.address,
this.locationDescribe,
this.longitude,
this.latitude,
this.lastSuccessfulTime,
});
final String? sn;
final String? country;
final String? province;
final String? city;
final String? district;
final String? street;
final String? address;
final String? locationDescribe;
final String? longitude;
final String? latitude;
final String? lastSuccessfulTime;
factory DeviceLocation.fromJson(Map<String, dynamic> json) => DeviceLocation(
sn: json['sn'] as String?,
country: json['country'] as String?,
province: json['province'] as String?,
city: json['city'] as String?,
district: json['district'] as String?,
street: json['street'] as String?,
address: json['address'] as String?,
locationDescribe: json['locationDescribe'] as String?,
longitude: _toDoubleString(json['longitude']),
latitude: _toDoubleString(json['latitude']),
lastSuccessfulTime: json['lastSuccessfulTime']?.toString(),
);
/// 拼接展示用地址:省+市+区+街道+详细地址。
String get displayAddress {
final parts = [
province,
city,
district,
street,
address,
].where((e) => e != null && e.isNotEmpty);
return parts.join('');
}
}
/// 设备已安装应用信息。
class DeviceApkInfo {
const DeviceApkInfo({
required this.packageName,
this.appName,
this.versionName,
this.versionCode,
this.installTime,
this.lastUpdateTime,
this.apkSize,
this.systemApp = false,
this.iconUrl,
});
final String packageName;
final String? appName;
final String? versionName;
final int? versionCode;
final String? installTime;
final String? lastUpdateTime;
final int? apkSize;
final bool systemApp;
final String? iconUrl;
factory DeviceApkInfo.fromJson(Map<String, dynamic> json) => DeviceApkInfo(
packageName: json['packageName'] as String,
appName: json['appName'] as String?,
versionName: json['versionName'] as String?,
versionCode: _toInt(json['versionCode']),
installTime: json['installTime']?.toString(),
lastUpdateTime: json['lastUpdateTime']?.toString(),
apkSize: _toInt(json['apkSize']),
systemApp: json['systemApp'] as bool? ?? false,
iconUrl: json['iconUrl'] as String?,
);
}
/// 设备截图实体(与后台 SnScreenshot 对齐)。
class SnScreenshot {
const SnScreenshot({
this.id,
this.sn,
this.fileName,
this.filePath,
this.fileSize,
this.fileMd5,
this.fileSha1,
this.fileSha256,
this.uploadTime,
});
final int? id;
final String? sn;
final String? fileName;
final String? filePath;
final int? fileSize;
final String? fileMd5;
final String? fileSha1;
final String? fileSha256;
final String? uploadTime;
factory SnScreenshot.fromJson(Map<String, dynamic> json) => SnScreenshot(
id: _toInt(json['id']),
sn: json['sn'] as String?,
fileName: json['fileName'] as String?,
filePath: json['filePath'] as String?,
fileSize: _toInt(json['fileSize']),
fileMd5: json['fileMd5'] as String?,
fileSha1: json['fileSha1'] as String?,
fileSha256: json['fileSha256'] as String?,
uploadTime: json['uploadTime']?.toString(),
);
}
/// 设备截图视图对象(与后台 ScreenshotVO 对齐)。
class ScreenshotVO {
const ScreenshotVO({
this.id,
this.sn,
this.fileName,
this.fileSize,
this.url,
this.uploadTime,
});
final int? id;
final String? sn;
final String? fileName;
final int? fileSize;
final String? url;
final String? uploadTime;
factory ScreenshotVO.fromJson(Map<String, dynamic> json) => ScreenshotVO(
id: _toInt(json['id']),
sn: json['sn'] as String?,
fileName: json['fileName'] as String?,
fileSize: _toInt(json['fileSize']),
url: json['url'] as String?,
uploadTime: json['uploadTime']?.toString(),
);
}
/// 容错整型解析:后台可能以字符串(如 "123")或数字返回整型字段,
/// 直接用 `as int?` 在字符串场景下会抛出
/// `type 'String' is not a subtype of type 'int'`,故统一在此转换。
int? _toInt(dynamic value) {
if (value == null) return null;
if (value is int) return value;
if (value is num) return value.toInt();
if (value is String) return int.tryParse(value);
return null;
}
/// 容错浮点解析(用于经纬度等带小数的字段),返回字符串形式便于上层 `double.tryParse`。
///
/// 经纬度必须保留小数精度:
/// - 后台若以数字(如 `116.404`)返回,直接截断成整数会偏移数公里;
/// - 后台若以字符串(如 `"39.915"`)返回,`int.tryParse` 会解析失败变成 null。
String? _toDoubleString(dynamic value) {
if (value == null) return null;
if (value is num) return value.toString();
if (value is String) {
final parsed = double.tryParse(value);
return parsed?.toString();
}
return null;
}