1、增加磁盘HDD类型已用空间图形接口。

2、增加磁盘HDD类型总空间字段计算。
3、增加硬盘设备监控列表接口。
This commit is contained in:
gaoyutao
2026-01-16 15:10:31 +08:00
parent f39aa887c8
commit d62fe4b99a
16 changed files with 301 additions and 51 deletions
@@ -53,7 +53,7 @@ public class EchartsDataUtils {
return Math.round(microseconds / 1_000_000.0 * 100.0) / 100.0;
}
/**
* 构建ECharts图表数据(带时间补全和特殊值处理)
* 构建ECharts图表数据(带时间补全和特殊值处理)- 修复版本
*/
public static <T> Map<String, Object> buildEchartsDataAutoPadding(
List<T> list,
@@ -84,57 +84,64 @@ public class EchartsDataUtils {
.sorted(Comparator.comparing(timeExtractor))
.collect(Collectors.toList());
// 自动检测时间间隔(用于数据期间)
// 自动检测时间间隔
long timeInterval = detectTimeInterval(sortedList, timeExtractor);
// 获取数据实际的时间范围
Date actualStartTime = timeExtractor.apply(sortedList.get(0));
Date actualEndTime = timeExtractor.apply(sortedList.get(sortedList.size() - 1));
// 计算稀疏间隔(用于数据期间外)
// 计算稀疏间隔
long totalTimeRange = endDate.getTime() - startDate.getTime();
long sparseInterval = totalTimeRange > 12L * 30 * 24 * 60 * 60 * 1000 ?
30L * 24 * 60 * 60 * 1000 : 2L * 24 * 60 * 60 * 1000;
// 生成三段时间序列
// 生成完整的时间序列
List<Date> fullTimeSeries = new ArrayList<>();
// 1. 开始时间到数据开始时间(稀疏间隔)
if (startDate.before(actualStartTime)) {
List<Date> beforeSeries = generateTimeSeries(startDate, actualStartTime, sparseInterval);
List<Date> beforeSeries = generateSparseTimeSeries(startDate, actualStartTime, sparseInterval);
fullTimeSeries.addAll(beforeSeries);
}
// 2. 数据开始时间到数据结束时间(正常间隔)
List<Date> dataSeries = generateTimeSeries(actualStartTime, actualEndTime, timeInterval);
// 2. 数据开始时间到数据结束时间 - 以第一个数据点的时间为基准生成序列
List<Date> dataSeries = generateTimeSeriesFromDataPoints(actualStartTime, actualEndTime,
timeInterval, actualStartTime);
fullTimeSeries.addAll(dataSeries);
// 3. 数据结束时间到结束时间(稀疏间隔)
if (actualEndTime.before(endDate)) {
// 调整actualEndTime的下一个点开始,避免重复
// actualEndTime的下一个时间点开始
Calendar cal = Calendar.getInstance();
cal.setTime(actualEndTime);
cal.setTimeInMillis(cal.getTimeInMillis() + timeInterval);
cal.add(Calendar.MILLISECOND, (int)timeInterval);
Date nextAfterActualEnd = cal.getTime();
if (nextAfterActualEnd.before(endDate) || nextAfterActualEnd.equals(endDate)) {
List<Date> afterSeries = generateTimeSeries(nextAfterActualEnd, endDate, sparseInterval);
List<Date> afterSeries = generateSparseTimeSeries(nextAfterActualEnd, endDate, sparseInterval);
fullTimeSeries.addAll(afterSeries);
}
}
// 创建时间到数据的映射(考虑时间精度)
Map<Long, T> timeDataMap = sortedList.stream()
.collect(Collectors.toMap(
item -> normalizeTime(timeExtractor.apply(item), timeInterval),
Function.identity(),
(a, b) -> a
));
// 去重并排序
fullTimeSeries = fullTimeSeries.stream()
.distinct()
.sorted()
.collect(Collectors.toList());
// 创建时间到数据的映射
Map<String, T> timeDataMap = new HashMap<>();
for (T item : sortedList) {
Date itemTime = timeExtractor.apply(item);
String timeKey = parseDateToStr(itemTime);
timeDataMap.put(timeKey, item);
}
// 检测整个数据集中是否有真实数据
boolean hasRealData = checkHasRealData(sortedList, dataExtractors);
// 特殊处理:查找percentile95的固定值
// 查找特殊字段的固定值
Object fixedPercentile95Value = findFixedValueForPercentile95(sortedList, dataExtractors);
// 准备X轴和Y轴数据
@@ -145,19 +152,16 @@ public class EchartsDataUtils {
dataExtractors.keySet().forEach(name ->
yData.put(name, new ArrayList<>()));
// 记录当前处理的时间点索引
int timeIndex = 0;
for (Date time : fullTimeSeries) {
// X轴数据
xAxisData.add(parseDateToStr(time));
String timeStr = parseDateToStr(time);
xAxisData.add(timeStr);
// 判断当前时间点是否在数据实际时间范围内
boolean isInDataRange = !time.before(actualStartTime) && !time.after(actualEndTime);
// Y轴数据
Long normalizedTime = normalizeTime(time, timeInterval);
T item = timeDataMap.get(normalizedTime);
// 查找对应的数据
T item = timeDataMap.get(timeStr);
for (Map.Entry<String, Function<T, ?>> entry : dataExtractors.entrySet()) {
String name = entry.getKey();
@@ -169,20 +173,18 @@ public class EchartsDataUtils {
if (item != null) {
// 有真实数据
Object value = extractor.apply(item);
seriesData.add(value != null ? value : getDefaultValue(name, fixedPercentile95Value, timeIndex, hasRealData));
seriesData.add(value != null ? value : null);
} else {
// 智能数据补全
if (isInDataRange) {
// 在数据时间范围内但该时间点无数据:使用智能补全策略
seriesData.add(getDefaultValue(name, fixedPercentile95Value, timeIndex, hasRealData));
// 在数据时间范围内但该时间点无数据(数据缺失点)
seriesData.add(getDefaultValue(name, fixedPercentile95Value, xAxisData.size()-1, hasRealData));
} else {
// 在数据时间范围外(开始时间前或结束时间后):使用空数据补全策略
seriesData.add(getEmptyDataDefaultValue(name, timeIndex));
// 在数据时间范围外
seriesData.add(getEmptyDataDefaultValue(name, xAxisData.size()-1));
}
}
}
timeIndex++;
}
Map<String, Object> result = new HashMap<>();
@@ -191,12 +193,81 @@ public class EchartsDataUtils {
return result;
} catch (Exception e) {
// 记录日志
System.err.println("构建图表数据失败: " + e.getMessage());
e.printStackTrace();
return createEmptyResult(dataExtractors.keySet(), startTime, endTime);
}
}
/**
* 从第一个数据点开始生成时间序列
*/
private static List<Date> generateTimeSeriesFromDataPoints(Date start, Date end,
long interval, Date firstDataPoint) {
List<Date> timeSeries = new ArrayList<>();
if (interval <= 0) {
interval = 300000L; // 默认5分钟
}
// 使用第一个数据点的时间作为基准
Calendar calendar = Calendar.getInstance();
calendar.setTime(firstDataPoint);
calendar.set(Calendar.MILLISECOND, 0);
// 从第一个数据点开始向前找,直到找到小于等于start的时间点
while (calendar.getTime().after(start)) {
calendar.add(Calendar.MILLISECOND, -(int)interval);
}
// 如果当前位置在start之前,前进一个间隔
if (calendar.getTime().before(start)) {
calendar.add(Calendar.MILLISECOND, (int)interval);
}
// 生成时间序列
while (!calendar.getTime().after(end)) {
timeSeries.add(calendar.getTime());
calendar.add(Calendar.MILLISECOND, (int)interval);
}
return timeSeries.stream()
.distinct()
.sorted()
.collect(Collectors.toList());
}
/**
* 生成稀疏时间序列
*/
private static List<Date> generateSparseTimeSeries(Date start, Date end, long interval) {
List<Date> timeSeries = new ArrayList<>();
if (interval <= 0) {
interval = 2L * 24 * 60 * 60 * 1000; // 默认2天
}
Calendar calendar = Calendar.getInstance();
calendar.setTime(start);
calendar.set(Calendar.MILLISECOND, 0);
// 包含开始时间
timeSeries.add(calendar.getTime());
while (true) {
calendar.add(Calendar.MILLISECOND, (int)interval);
if (calendar.getTime().after(end)) {
break;
}
timeSeries.add(calendar.getTime());
}
return timeSeries.stream()
.distinct()
.sorted()
.collect(Collectors.toList());
}
/**
* 获取空数据默认值(用于数据时间范围外的点)
*/
@@ -383,4 +383,39 @@ public class SpeedUtils {
if (mbitValue == null) return BigDecimal.ZERO;
return mbitValue.multiply(new BigDecimal("1000000"));
}
public static String determineUnitByValue(Long value) {
if (value == null || value == 0) {
return "KB";
}
// 注意:这里使用二进制单位(1024)
if (value >= 1024L * 1024 * 1024 * 1024) { // >= 1TB
return "TB";
} else if (value >= 1024L * 1024 * 1024) { // >= 1GB
return "GB";
} else if (value >= 1024L * 1024) { // >= 1MB
return "MB";
} else {
return "KB";
}
}
// 工具方法:获取单位换算除数
public static BigDecimal get1024Divisor(String unit) {
switch (unit) {
case "GB":
case "Gb":
return new BigDecimal(1024L * 1024 * 1024); // 1GB = 1024^3
case "MB":
case "Mb":
return new BigDecimal(1024L * 1024); // 1MB = 1024^2
case "KB":
case "Kb":
return new BigDecimal(1024); // 1KB = 1024
case "TB":
case "Tb":
return new BigDecimal(1024L * 1024 * 1024 * 1024); // 1TB = 1024^4
default:
return new BigDecimal(1024); // 默认返回KB的除数
}
}
}