import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../models/device.dart'; import '../services/device_service.dart'; import '../services/org_service.dart'; import '../utils/session_org_scope.dart'; import 'devices_provider.dart'; /// 仪表盘统计数据。 class DashboardStats { const DashboardStats({ this.deviceTotal = 0, this.storeTotal = 0, this.onlineCount = 0, this.offlineCount = 0, }); final int deviceTotal; final int storeTotal; final int onlineCount; final int offlineCount; DashboardStats copyWith({ int? deviceTotal, int? storeTotal, int? onlineCount, int? offlineCount, }) { return DashboardStats( deviceTotal: deviceTotal ?? this.deviceTotal, storeTotal: storeTotal ?? this.storeTotal, onlineCount: onlineCount ?? this.onlineCount, offlineCount: offlineCount ?? this.offlineCount, ); } } /// 仪表盘统计状态。 class DashboardStatsNotifier extends StateNotifier { DashboardStatsNotifier(this._deviceService, this._orgService) : super(const DashboardStats()); final DeviceService _deviceService; final OrgService _orgService; Future load() async { try { int deviceTotal = 0; int onlineCount = 0; int offlineCount = 0; if (SessionOrgScope.instance.enabled) { // 受限账号:仅统计其组织子树内的设备。 // 设备接口可能忽略 orgId,故改为「拉取列表 + 按 storeId 在范围内收敛」, // 杜绝跨组织设备越权统计(只会少计、不会多计或泄露其他组织数据)。 final ids = await _orgService.scopedOrgIdSet(); if (ids.isNotEmpty) { final devices = await _deviceService.fetchDevices(limit: 1000); final scoped = devices.where((d) => ids.contains(d.storeId)).toList(); deviceTotal = scoped.length; onlineCount = scoped.where((d) => d.status == DeviceStatus.online).length; offlineCount = scoped.where((d) => d.status == DeviceStatus.offline).length; } } else { deviceTotal = await _deviceService.fetchDeviceTotalCount(); // 若后端未返回 totalCount,则拉取一页列表用其长度兜底。 if (deviceTotal <= 0) { final devices = await _deviceService.fetchDevices(limit: 100); deviceTotal = devices.length; } // 在线/离线:分别按 status 拉 totalCount(O(1),无需遍历全量设备)。 final results = await Future.wait([ _deviceService.fetchDeviceCountByStatus(status: 'online'), _deviceService.fetchDeviceCountByStatus(status: 'offline'), ]); onlineCount = results[0]; offlineCount = results[1]; } // 门店总数:优先组织树,失败回退门店接口(fetchStoreCount 内部已含回退链)。 final storeTotal = await _orgService.fetchStoreCount(); state = state.copyWith( deviceTotal: deviceTotal, storeTotal: storeTotal, onlineCount: onlineCount, offlineCount: offlineCount, ); } catch (_) { // 统计失败不阻断页面,保持旧值或 0。 } } } final orgServiceProvider = Provider((ref) => OrgService()); final dashboardStatsProvider = StateNotifierProvider( (ref) => DashboardStatsNotifier( ref.watch(deviceServiceProvider), ref.watch(orgServiceProvider), ), );