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