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