增加存储子网卡信息、修复子网卡流量递增问题
子网卡流量图合并
This commit is contained in:
+409
@@ -0,0 +1,409 @@
|
||||
package com.tongran.common.core.utils;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class EchartsMoreDataUtils {
|
||||
|
||||
|
||||
/**
|
||||
* 构建多网卡ECharts图表数据(带汇总流量)
|
||||
* 每个网卡会生成两个独立的Y轴属性:{interfaceName}netInTraffic 和 {interfaceName}netOutTraffic
|
||||
* 同时生成汇总流量:totalNetInTraffic 和 totalNetOutTraffic
|
||||
*/
|
||||
public static <T> Map<String, Object> buildMultiInterfaceEchartsDataWithTotal(
|
||||
Map<String, List<T>> interfaceDataMap,
|
||||
Function<T, Date> timeExtractor,
|
||||
Function<T, BigDecimal> inSpeedExtractor,
|
||||
Function<T, BigDecimal> outSpeedExtractor,
|
||||
String startTime,
|
||||
String endTime,
|
||||
BigDecimal divisor) {
|
||||
|
||||
try {
|
||||
// 解析时间字符串
|
||||
Date startDate = parseStringToDate(startTime);
|
||||
Date endDate = parseStringToDate(endTime);
|
||||
|
||||
if (startDate == null || endDate == null) {
|
||||
throw new IllegalArgumentException("开始时间或结束时间格式错误");
|
||||
}
|
||||
|
||||
if (startDate.after(endDate)) {
|
||||
throw new IllegalArgumentException("开始时间不能晚于结束时间");
|
||||
}
|
||||
|
||||
// 收集所有网卡的时间点
|
||||
Set<Date> allTimePoints = new TreeSet<>();
|
||||
Map<String, Map<Long, T>> interfaceTimeMap = new LinkedHashMap<>();
|
||||
|
||||
// 为每个网卡处理数据
|
||||
for (Map.Entry<String, List<T>> entry : interfaceDataMap.entrySet()) {
|
||||
String interfaceName = entry.getKey();
|
||||
List<T> interfaceList = entry.getValue();
|
||||
|
||||
if (interfaceList == null || interfaceList.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 按时间排序
|
||||
List<T> sortedList = interfaceList.stream()
|
||||
.sorted(Comparator.comparing(timeExtractor))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// 自动检测时间间隔
|
||||
long timeInterval = detectTimeInterval(sortedList, timeExtractor);
|
||||
|
||||
// 创建时间到数据的映射
|
||||
Map<Long, T> timeMap = sortedList.stream()
|
||||
.collect(Collectors.toMap(
|
||||
item -> normalizeTime(timeExtractor.apply(item), timeInterval),
|
||||
Function.identity(),
|
||||
(a, b) -> a
|
||||
));
|
||||
|
||||
interfaceTimeMap.put(interfaceName, timeMap);
|
||||
|
||||
// 添加时间点到总集合
|
||||
for (T item : sortedList) {
|
||||
allTimePoints.add(timeExtractor.apply(item));
|
||||
}
|
||||
}
|
||||
|
||||
// 生成完整的时间序列
|
||||
List<Date> fullTimeSeries = generateFullTimeSeriesForMultiInterface(
|
||||
allTimePoints, startDate, endDate);
|
||||
|
||||
// 准备X轴和Y轴数据
|
||||
List<String> xAxisData = new ArrayList<>();
|
||||
Map<String, Object> yData = new LinkedHashMap<>();
|
||||
|
||||
// 初始化每个网卡的Y轴数据结构
|
||||
interfaceDataMap.keySet().forEach(interfaceName -> {
|
||||
yData.put(interfaceName + "netInTraffic", new ArrayList<BigDecimal>());
|
||||
yData.put(interfaceName + "netOutTraffic", new ArrayList<BigDecimal>());
|
||||
});
|
||||
|
||||
// 初始化汇总流量数据结构
|
||||
List<BigDecimal> totalNetInTraffic = new ArrayList<>();
|
||||
List<BigDecimal> totalNetOutTraffic = new ArrayList<>();
|
||||
yData.put("totalNetInTraffic", totalNetInTraffic);
|
||||
yData.put("totalNetOutTraffic", totalNetOutTraffic);
|
||||
|
||||
// 遍历所有时间点
|
||||
for (Date time : fullTimeSeries) {
|
||||
// X轴数据
|
||||
xAxisData.add(parseDateToStr(time));
|
||||
|
||||
// 标准化当前时间
|
||||
long normalizedTime = normalizeTime(time, 300000L); // 5分钟间隔
|
||||
|
||||
// 当前时间点的总流量
|
||||
BigDecimal timeTotalInSpeed = BigDecimal.ZERO;
|
||||
BigDecimal timeTotalOutSpeed = BigDecimal.ZERO;
|
||||
|
||||
// 处理每个网卡的数据
|
||||
for (Map.Entry<String, Map<Long, T>> entry : interfaceTimeMap.entrySet()) {
|
||||
String interfaceName = entry.getKey();
|
||||
Map<Long, T> timeMap = entry.getValue();
|
||||
|
||||
T item = timeMap.get(normalizedTime);
|
||||
|
||||
// 获取入方向数据列表
|
||||
@SuppressWarnings("unchecked")
|
||||
List<BigDecimal> inSpeedList = (List<BigDecimal>) yData.get(interfaceName + "netInTraffic");
|
||||
// 获取出方向数据列表
|
||||
@SuppressWarnings("unchecked")
|
||||
List<BigDecimal> outSpeedList = (List<BigDecimal>) yData.get(interfaceName + "netOutTraffic");
|
||||
|
||||
if (item != null) {
|
||||
// 有真实数据
|
||||
BigDecimal inSpeed = inSpeedExtractor.apply(item);
|
||||
BigDecimal outSpeed = outSpeedExtractor.apply(item);
|
||||
|
||||
// 单位转换
|
||||
BigDecimal convertedInSpeed = inSpeed != null ?
|
||||
inSpeed.divide(divisor, 2, RoundingMode.HALF_UP) : null;
|
||||
BigDecimal convertedOutSpeed = outSpeed != null ?
|
||||
outSpeed.divide(divisor, 2, RoundingMode.HALF_UP) : null;
|
||||
|
||||
inSpeedList.add(convertedInSpeed);
|
||||
outSpeedList.add(convertedOutSpeed);
|
||||
|
||||
// 累加到总流量
|
||||
if (convertedInSpeed != null) {
|
||||
timeTotalInSpeed = timeTotalInSpeed.add(convertedInSpeed);
|
||||
}
|
||||
if (convertedOutSpeed != null) {
|
||||
timeTotalOutSpeed = timeTotalOutSpeed.add(convertedOutSpeed);
|
||||
}
|
||||
} else {
|
||||
// 无数据的时间点,补null
|
||||
inSpeedList.add(null);
|
||||
outSpeedList.add(null);
|
||||
}
|
||||
}
|
||||
|
||||
// 添加当前时间点的总流量
|
||||
totalNetInTraffic.add(timeTotalInSpeed.compareTo(BigDecimal.ZERO) == 0 ? null : timeTotalInSpeed);
|
||||
totalNetOutTraffic.add(timeTotalOutSpeed.compareTo(BigDecimal.ZERO) == 0 ? null : timeTotalOutSpeed);
|
||||
}
|
||||
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("xData", xAxisData);
|
||||
result.put("yData", yData);
|
||||
return result;
|
||||
|
||||
} catch (Exception e) {
|
||||
// 记录日志
|
||||
System.err.println("构建多网卡图表数据失败: " + e.getMessage());
|
||||
return createEmptyMultiInterfaceResultWithTotal(interfaceDataMap.keySet(), startTime, endTime);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 为多网卡生成完整的时间序列
|
||||
*/
|
||||
private static List<Date> generateFullTimeSeriesForMultiInterface(
|
||||
Set<Date> allInterfaceTimePoints,
|
||||
Date startDate,
|
||||
Date endDate) {
|
||||
|
||||
// 获取实际数据的时间范围
|
||||
Date actualStartTime = allInterfaceTimePoints.isEmpty() ? startDate :
|
||||
Collections.min(allInterfaceTimePoints);
|
||||
Date actualEndTime = allInterfaceTimePoints.isEmpty() ? endDate :
|
||||
Collections.max(allInterfaceTimePoints);
|
||||
|
||||
// 计算稀疏间隔
|
||||
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;
|
||||
|
||||
// 使用默认的5分钟作为数据期间间隔
|
||||
long dataInterval = 300000L;
|
||||
|
||||
// 生成三段时间序列
|
||||
List<Date> fullTimeSeries = new ArrayList<>();
|
||||
|
||||
// 1. 开始时间到数据开始时间(稀疏间隔)
|
||||
if (startDate.before(actualStartTime)) {
|
||||
List<Date> beforeSeries = generateTimeSeries(startDate, actualStartTime, sparseInterval);
|
||||
fullTimeSeries.addAll(beforeSeries);
|
||||
}
|
||||
|
||||
// 2. 数据开始时间到数据结束时间(正常间隔)
|
||||
List<Date> dataSeries = generateTimeSeries(actualStartTime, actualEndTime, dataInterval);
|
||||
fullTimeSeries.addAll(dataSeries);
|
||||
|
||||
// 3. 数据结束时间到结束时间(稀疏间隔)
|
||||
if (actualEndTime.before(endDate)) {
|
||||
// 调整actualEndTime的下一个点开始,避免重复
|
||||
Calendar cal = Calendar.getInstance();
|
||||
cal.setTime(actualEndTime);
|
||||
cal.setTimeInMillis(cal.getTimeInMillis() + dataInterval);
|
||||
Date nextAfterActualEnd = cal.getTime();
|
||||
|
||||
if (nextAfterActualEnd.before(endDate) || nextAfterActualEnd.equals(endDate)) {
|
||||
List<Date> afterSeries = generateTimeSeries(nextAfterActualEnd, endDate, sparseInterval);
|
||||
fullTimeSeries.addAll(afterSeries);
|
||||
}
|
||||
}
|
||||
|
||||
return fullTimeSeries;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建多网卡空结果(带汇总流量)
|
||||
*/
|
||||
private static Map<String, Object> createEmptyMultiInterfaceResultWithTotal(
|
||||
Set<String> interfaceNames, String startTime, String endTime) {
|
||||
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
|
||||
try {
|
||||
// 解析时间范围
|
||||
Date startDate = parseStringToDate(startTime);
|
||||
Date endDate = parseStringToDate(endTime);
|
||||
|
||||
if (startDate != null && endDate != null && !startDate.after(endDate)) {
|
||||
// 动态计算时间间隔
|
||||
long timeRange = endDate.getTime() - startDate.getTime();
|
||||
long interval;
|
||||
|
||||
if (timeRange > 12L * 30 * 24 * 60 * 60 * 1000) { // 超过12个月
|
||||
interval = 30L * 24 * 60 * 60 * 1000; // 每月1个点
|
||||
} else {
|
||||
interval = 2L * 24 * 60 * 60 * 1000; // 2天1个点
|
||||
}
|
||||
|
||||
List<Date> fullTimeSeries = generateTimeSeries(startDate, endDate, interval);
|
||||
|
||||
// 构建x轴数据
|
||||
List<String> xAxisData = new ArrayList<>();
|
||||
for (Date date : fullTimeSeries) {
|
||||
xAxisData.add(parseDateToStr(date));
|
||||
}
|
||||
result.put("xData", xAxisData);
|
||||
|
||||
// 构建y轴数据(空数据集)
|
||||
Map<String, Object> yData = new LinkedHashMap<>();
|
||||
int dataSize = xAxisData.size();
|
||||
|
||||
// 为每个网卡创建空数据系列
|
||||
for (String interfaceName : interfaceNames) {
|
||||
List<Object> inSpeedSeries = new ArrayList<>();
|
||||
List<Object> outSpeedSeries = new ArrayList<>();
|
||||
|
||||
for (int i = 0; i < dataSize; i++) {
|
||||
if (i == 0) {
|
||||
// 第一个点补0
|
||||
inSpeedSeries.add(0);
|
||||
outSpeedSeries.add(0);
|
||||
} else {
|
||||
// 其他点补null
|
||||
inSpeedSeries.add(null);
|
||||
outSpeedSeries.add(null);
|
||||
}
|
||||
}
|
||||
|
||||
yData.put(interfaceName + "netInTraffic", inSpeedSeries);
|
||||
yData.put(interfaceName + "netOutTraffic", outSpeedSeries);
|
||||
}
|
||||
|
||||
// 创建汇总流量的空数据系列
|
||||
List<Object> totalInSpeedSeries = new ArrayList<>();
|
||||
List<Object> totalOutSpeedSeries = new ArrayList<>();
|
||||
for (int i = 0; i < dataSize; i++) {
|
||||
if (i == 0) {
|
||||
totalInSpeedSeries.add(0);
|
||||
totalOutSpeedSeries.add(0);
|
||||
} else {
|
||||
totalInSpeedSeries.add(null);
|
||||
totalOutSpeedSeries.add(null);
|
||||
}
|
||||
}
|
||||
yData.put("totalNetInTraffic", totalInSpeedSeries);
|
||||
yData.put("totalNetOutTraffic", totalOutSpeedSeries);
|
||||
|
||||
result.put("yData", yData);
|
||||
|
||||
} else {
|
||||
// 时间解析失败时返回空数据
|
||||
result.put("xData", new ArrayList<>());
|
||||
Map<String, Object> yData = new LinkedHashMap<>();
|
||||
for (String interfaceName : interfaceNames) {
|
||||
yData.put(interfaceName + "netInTraffic", new ArrayList<>());
|
||||
yData.put(interfaceName + "netOutTraffic", new ArrayList<>());
|
||||
}
|
||||
yData.put("totalNetInTraffic", new ArrayList<>());
|
||||
yData.put("totalNetOutTraffic", new ArrayList<>());
|
||||
result.put("yData", yData);
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
// 异常时返回空数据
|
||||
result.put("xData", new ArrayList<>());
|
||||
Map<String, Object> yData = new LinkedHashMap<>();
|
||||
for (String interfaceName : interfaceNames) {
|
||||
yData.put(interfaceName + "netInTraffic", new ArrayList<>());
|
||||
yData.put(interfaceName + "netOutTraffic", new ArrayList<>());
|
||||
}
|
||||
yData.put("totalNetInTraffic", new ArrayList<>());
|
||||
yData.put("totalNetOutTraffic", new ArrayList<>());
|
||||
result.put("yData", yData);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 字符串转日期
|
||||
*/
|
||||
private static Date parseStringToDate(String dateStr) {
|
||||
if (dateStr == null || dateStr.trim().isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
sdf.setLenient(false); // 严格模式
|
||||
return sdf.parse(dateStr);
|
||||
} catch (ParseException e) {
|
||||
System.err.println("日期解析失败: " + dateStr);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 日期转字符串
|
||||
*/
|
||||
private static String parseDateToStr(Date date) {
|
||||
if (date == null) {
|
||||
return "";
|
||||
}
|
||||
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
return sdf.format(date);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成完整的时间序列
|
||||
*/
|
||||
private static List<Date> generateTimeSeries(Date start, Date end, long interval) {
|
||||
List<Date> timeSeries = new ArrayList<>();
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
calendar.setTime(start);
|
||||
|
||||
// 确保开始时间对齐到时间间隔
|
||||
long startMillis = normalizeTime(start, interval);
|
||||
calendar.setTimeInMillis(startMillis);
|
||||
|
||||
while (!calendar.getTime().after(end)) {
|
||||
timeSeries.add(calendar.getTime());
|
||||
calendar.setTimeInMillis(calendar.getTimeInMillis() + interval);
|
||||
}
|
||||
return timeSeries;
|
||||
}
|
||||
|
||||
/**
|
||||
* 自动检测时间间隔
|
||||
*/
|
||||
private static <T> long detectTimeInterval(List<T> list, Function<T, Date> timeExtractor) {
|
||||
if (list.size() < 2) {
|
||||
return 300000; // 默认5分钟
|
||||
}
|
||||
|
||||
// 计算时间间隔的众数
|
||||
Map<Long, Integer> intervalCount = new HashMap<>();
|
||||
for (int i = 1; i < list.size(); i++) {
|
||||
long interval = timeExtractor.apply(list.get(i)).getTime() -
|
||||
timeExtractor.apply(list.get(i - 1)).getTime();
|
||||
if (interval > 0) {
|
||||
intervalCount.merge(interval, 1, Integer::sum);
|
||||
}
|
||||
}
|
||||
|
||||
// 如果没有有效间隔,使用默认值
|
||||
if (intervalCount.isEmpty()) {
|
||||
return 300000L;
|
||||
}
|
||||
|
||||
return intervalCount.entrySet().stream()
|
||||
.max(Map.Entry.comparingByValue())
|
||||
.map(Map.Entry::getKey)
|
||||
.orElse(300000L);
|
||||
}
|
||||
|
||||
/**
|
||||
* 时间标准化(对齐到时间间隔)
|
||||
*/
|
||||
private static long normalizeTime(Date time, long interval) {
|
||||
long timeMillis = time.getTime();
|
||||
return (timeMillis / interval) * interval;
|
||||
}
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
package com.tongran.common.core.utils;
|
||||
|
||||
public class NetworkNameUtil {
|
||||
|
||||
/**
|
||||
* 判断网卡是否为子网卡
|
||||
* @param interfaceName
|
||||
* @return
|
||||
*/
|
||||
public static boolean isSubInterface(String interfaceName) {
|
||||
// 非空检查
|
||||
if (interfaceName == null || interfaceName.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 匹配模式:冒号后跟数字 或 点后跟数字
|
||||
String pattern = ".*[:.]\\d+$";
|
||||
return interfaceName.matches(pattern);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user