Files
CloudHome-Ops/lib/providers/dashboard_provider.dart

107 lines
3.5 KiB
Dart
Raw Normal View History

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<DashboardStats> {
DashboardStatsNotifier(this._deviceService, this._orgService)
: super(const DashboardStats());
final DeviceService _deviceService;
final OrgService _orgService;
Future<void> 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<int>([
_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<OrgService>((ref) => OrgService());
final dashboardStatsProvider =
StateNotifierProvider<DashboardStatsNotifier, DashboardStats>(
(ref) => DashboardStatsNotifier(
ref.watch(deviceServiceProvider),
ref.watch(orgServiceProvider),
),
);