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),
|
|||
|
|
),
|
|||
|
|
);
|