增加存储子网卡信息、修复子网卡流量递增问题

子网卡流量图合并
This commit is contained in:
gaoyutao
2025-12-15 19:50:44 +08:00
parent 9a81408ada
commit 6d3b5201e0
20 changed files with 1192 additions and 29 deletions
@@ -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;
}
}
@@ -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);
}
}