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

107 lines
3.5 KiB
Dart
Raw 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 '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),
),
);