Files
CloudHome-Ops/lib/models/store_info.dart

220 lines
6.7 KiB
Dart
Raw Normal View History

import '../data/region_data.dart';
/// 门店信息模型。
///
/// 对应后端 /sys/store/* 接口的门店实体。
///
/// 扩展字段([themeId]/[splashUrl]/[logoUrl]/省市区/[addressDetail])均为可空,
/// 向后兼容既有后端响应;这些字段仅在「前端占位」阶段由本地缓存承载,
/// 后续由后端提供后自动填充。
class StoreInfo {
const StoreInfo({
required this.orgId,
required this.name,
required this.address,
required this.contactPhone,
required this.activationCode,
required this.status,
required this.expireTime,
required this.createTime,
this.parentId,
this.themeId,
this.splashUrl,
this.logoUrl,
this.provinceCode,
this.cityCode,
this.districtCode,
this.addressDetail,
int? deviceLimit,
}) : deviceLimit = deviceLimit;
/// 门店组织 ID。
final String orgId;
/// 父级组织 ID(所属渠道/代理商)。
final String? parentId;
/// 门店名称。
final String name;
/// 地址(兼容旧文本地址;新结构优先用省市区拼接)。
final String address;
/// 联系电话。
final String contactPhone;
/// 激活码。
final String activationCode;
/// 状态(0=正常 1=禁用)。
final int status;
/// 到期时间。
final DateTime? expireTime;
/// 创建时间。
final DateTime? createTime;
/// 设备总数上限(组织树 groupLimit / 激活码 maxActivateCount)。
final int? deviceLimit;
/// 主题 id(如 'ocean');前端占位,对应 [StoreThemePresets.byId]。
final String? themeId;
/// 启动图本地路径 / 远程 URL(占位)。
final String? splashUrl;
/// Logo 本地路径 / 远程 URL(占位)。
final String? logoUrl;
/// 省 code(6 位)。
final String? provinceCode;
/// 市 code。
final String? cityCode;
/// 区 code。
final String? districtCode;
/// 街道/门牌等详情文本。
final String? addressDetail;
factory StoreInfo.fromJson(Map<String, dynamic> json) {
return StoreInfo(
orgId: (json['orgId'] ?? json['id'])?.toString() ?? '',
parentId: json['parentId']?.toString() ??
json['parent_id']?.toString() ??
(json['parent'] is Map ? json['parent']['id']?.toString() : null),
name: json['name']?.toString() ?? '',
address: json['address']?.toString() ?? '',
contactPhone: json['contactPhone']?.toString() ?? '',
activationCode: json['activationCode']?.toString() ?? '',
status: int.tryParse(json['status']?.toString() ?? '0') ?? 0,
expireTime: _parseDateTime(
json['expireTime'] ?? json['expire_time'] ?? json['expiredTime'],
),
createTime: _parseDateTime(
json['createTime'] ??
json['create_time'] ??
json['createdTime'] ??
json['createdAt'] ??
json['created_at'],
),
deviceLimit: _parseInt(
json['groupLimit'] ?? json['deviceLimit'] ?? json['device_limit'],
),
themeId: json['themeId']?.toString(),
splashUrl: json['splashUrl']?.toString(),
logoUrl: json['logoUrl']?.toString(),
provinceCode: json['provinceCode']?.toString(),
cityCode: json['cityCode']?.toString(),
districtCode: json['districtCode']?.toString(),
addressDetail: json['addressDetail']?.toString(),
);
}
/// 兼容 `yyyy-MM-dd HH:mm:ss` 与 ISO 8601 两种时间格式。
static DateTime? _parseDateTime(dynamic value) {
if (value == null) return null;
final text = value.toString().trim();
if (text.isEmpty) return null;
return DateTime.tryParse(text) ??
DateTime.tryParse(text.replaceFirst(' ', 'T'));
}
/// 解析可空整型(设备上限等)。
static int? _parseInt(dynamic value) {
if (value == null) return null;
return int.tryParse(value.toString());
}
Map<String, dynamic> toJson() {
return {
'orgId': orgId,
'parentId': parentId,
'name': name,
'address': address,
'contactPhone': contactPhone,
'activationCode': activationCode,
'status': status,
'expireTime': expireTime?.toIso8601String(),
'createTime': createTime?.toIso8601String(),
'deviceLimit': deviceLimit,
'themeId': themeId,
'splashUrl': splashUrl,
'logoUrl': logoUrl,
'provinceCode': provinceCode,
'cityCode': cityCode,
'districtCode': districtCode,
'addressDetail': addressDetail,
};
}
/// 不可变更新副本(仅覆盖传入的非空参数)。
///
/// 用于门店列表加载后,用 [OrgService.getStoreListByLoginOrg] 回填的
/// 到期时间等非组织树字段,避免重建整个对象。
StoreInfo copyWith({
String? orgId,
String? parentId,
String? name,
String? address,
String? contactPhone,
String? activationCode,
int? status,
DateTime? expireTime,
DateTime? createTime,
int? deviceLimit,
String? themeId,
String? splashUrl,
String? logoUrl,
String? provinceCode,
String? cityCode,
String? districtCode,
String? addressDetail,
}) {
return StoreInfo(
orgId: orgId ?? this.orgId,
parentId: parentId ?? this.parentId,
name: name ?? this.name,
address: address ?? this.address,
contactPhone: contactPhone ?? this.contactPhone,
activationCode: activationCode ?? this.activationCode,
status: status ?? this.status,
expireTime: expireTime ?? this.expireTime,
createTime: createTime ?? this.createTime,
deviceLimit: deviceLimit ?? this.deviceLimit,
themeId: themeId ?? this.themeId,
splashUrl: splashUrl ?? this.splashUrl,
logoUrl: logoUrl ?? this.logoUrl,
provinceCode: provinceCode ?? this.provinceCode,
cityCode: cityCode ?? this.cityCode,
districtCode: districtCode ?? this.districtCode,
addressDetail: addressDetail ?? this.addressDetail,
);
}
/// 拼接展示地址:省名 + 市名 + 区名 + 详情文本。
///
/// 若已设置省市区则优先用编码反查名称拼接;否则回退到旧文本 [address]。
String get regionText {
final names = <String>[];
if (provinceCode != null && provinceCode!.isNotEmpty) {
final p = RegionData.instance.nameOf(provinceCode!);
if (p.isNotEmpty) names.add(p);
}
if (cityCode != null && cityCode!.isNotEmpty) {
final c = RegionData.instance.nameOf(cityCode!);
if (c.isNotEmpty) names.add(c);
}
if (districtCode != null && districtCode!.isNotEmpty) {
final d = RegionData.instance.nameOf(districtCode!);
if (d.isNotEmpty) names.add(d);
}
final detail = (addressDetail ?? '').trim();
if (detail.isNotEmpty) names.add(detail);
if (names.isEmpty) return address;
return names.join('');
}
}