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

220 lines
6.7 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.

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('');
}
}