From 5ab04189570f2be87131b3bbca3f1a0ca2b6544f Mon Sep 17 00:00:00 2001 From: luyya Date: Mon, 16 Mar 2026 15:42:46 +0800 Subject: [PATCH] =?UTF-8?q?=E8=9E=8D=E5=90=88=E9=80=9A=E4=BF=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dromara/system/config/DruidConfig.java | 81 ++++ .../monitor/DruidMonitorController.java | 75 +++ .../monitor/SystemMonitorController.java | 32 ++ .../dromara/system/domain/SysSqlFirewall.java | 93 ++++ .../dromara/system/domain/SysSqlMonitor.java | 103 +++++ .../dromara/system/domain/SysUriMonitor.java | 109 +++++ .../system/domain/vo/DataSourceMonitorVo.java | 99 ++++ .../system/domain/vo/SqlFirewallVo.java | 84 ++++ .../system/domain/vo/SqlMonitorVo.java | 89 ++++ .../system/domain/vo/SystemMonitorVo.java | 234 ++++++++++ .../system/domain/vo/UriMonitorVo.java | 104 +++++ .../system/mapper/SysSqlFirewallMapper.java | 14 + .../system/service/IDruidMonitorService.java | 52 +++ .../system/service/ISystemMonitorService.java | 18 + .../service/impl/DruidMonitorServiceImpl.java | 244 ++++++++++ .../impl/SystemMonitorServiceImpl.java | 436 ++++++++++++++++++ 16 files changed, 1867 insertions(+) create mode 100644 stwzhj-modules/wzhj-system/src/main/java/org/dromara/system/config/DruidConfig.java create mode 100644 stwzhj-modules/wzhj-system/src/main/java/org/dromara/system/controller/monitor/DruidMonitorController.java create mode 100644 stwzhj-modules/wzhj-system/src/main/java/org/dromara/system/controller/monitor/SystemMonitorController.java create mode 100644 stwzhj-modules/wzhj-system/src/main/java/org/dromara/system/domain/SysSqlFirewall.java create mode 100644 stwzhj-modules/wzhj-system/src/main/java/org/dromara/system/domain/SysSqlMonitor.java create mode 100644 stwzhj-modules/wzhj-system/src/main/java/org/dromara/system/domain/SysUriMonitor.java create mode 100644 stwzhj-modules/wzhj-system/src/main/java/org/dromara/system/domain/vo/DataSourceMonitorVo.java create mode 100644 stwzhj-modules/wzhj-system/src/main/java/org/dromara/system/domain/vo/SqlFirewallVo.java create mode 100644 stwzhj-modules/wzhj-system/src/main/java/org/dromara/system/domain/vo/SqlMonitorVo.java create mode 100644 stwzhj-modules/wzhj-system/src/main/java/org/dromara/system/domain/vo/SystemMonitorVo.java create mode 100644 stwzhj-modules/wzhj-system/src/main/java/org/dromara/system/domain/vo/UriMonitorVo.java create mode 100644 stwzhj-modules/wzhj-system/src/main/java/org/dromara/system/mapper/SysSqlFirewallMapper.java create mode 100644 stwzhj-modules/wzhj-system/src/main/java/org/dromara/system/service/IDruidMonitorService.java create mode 100644 stwzhj-modules/wzhj-system/src/main/java/org/dromara/system/service/ISystemMonitorService.java create mode 100644 stwzhj-modules/wzhj-system/src/main/java/org/dromara/system/service/impl/DruidMonitorServiceImpl.java create mode 100644 stwzhj-modules/wzhj-system/src/main/java/org/dromara/system/service/impl/SystemMonitorServiceImpl.java diff --git a/stwzhj-modules/wzhj-system/src/main/java/org/dromara/system/config/DruidConfig.java b/stwzhj-modules/wzhj-system/src/main/java/org/dromara/system/config/DruidConfig.java new file mode 100644 index 00000000..d3c2cbc4 --- /dev/null +++ b/stwzhj-modules/wzhj-system/src/main/java/org/dromara/system/config/DruidConfig.java @@ -0,0 +1,81 @@ + +package org.dromara.system.config; + +import com.alibaba.druid.pool.DruidDataSource; +import com.alibaba.druid.support.jakarta.StatViewServlet; +import com.alibaba.druid.support.jakarta.WebStatFilter; +import lombok.RequiredArgsConstructor; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.web.servlet.FilterRegistrationBean; +import org.springframework.boot.web.servlet.ServletRegistrationBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import javax.sql.DataSource; +import java.sql.SQLException; +import java.util.HashMap; +import java.util.Map; + +/** + * Druid配置类 + * + * @author CodeGeeX + */ +@RequiredArgsConstructor +@Configuration +public class DruidConfig { + + private final DataSource dataSource; + + /** + * 配置Druid监控页面 + */ + @Bean + @ConditionalOnProperty(name = "spring.datasource.druid.stat-view-servlet.enabled", havingValue = "true", matchIfMissing = true) + public ServletRegistrationBean druidStatViewServlet() { + ServletRegistrationBean registrationBean = new ServletRegistrationBean<>(new StatViewServlet(), "/druid/*"); + + // 配置监控页面访问控制 + Map initParams = new HashMap<>(); + initParams.put("loginUsername", "admin"); + initParams.put("loginPassword", "admin"); + initParams.put("allow", ""); // 默认允许所有IP访问 + // initParams.put("deny", ""); // 拒绝访问的IP + + registrationBean.setInitParameters(initParams); + return registrationBean; + } + + /** + * 配置Web应用监控Filter + */ + @Bean + @ConditionalOnProperty(name = "spring.datasource.druid.web-stat-filter.enabled", havingValue = "true", matchIfMissing = true) + public FilterRegistrationBean druidWebStatFilter() { + FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean<>(new WebStatFilter()); + + // 配置拦截规则 + Map initParams = new HashMap<>(); + initParams.put("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*"); + initParams.put("profileEnable", "true"); + initParams.put("sessionStatEnable", "true"); + + filterRegistrationBean.setInitParameters(initParams); + filterRegistrationBean.addUrlPatterns("/*"); + return filterRegistrationBean; + } + + /** + * 配置数据源监控统计 + */ + @ConditionalOnProperty(name = "spring.datasource.druid.filter.stat.enabled", havingValue = "true", matchIfMissing = true) + public void druidStatFilter() throws SQLException { + if (dataSource instanceof DruidDataSource) { + DruidDataSource druidDataSource = (DruidDataSource) dataSource; + // 开启监控统计功能 + druidDataSource.setFilters("stat,wall,slf4j"); + // 慢SQL记录 + druidDataSource.setConnectionProperties("druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000"); + } + } +} diff --git a/stwzhj-modules/wzhj-system/src/main/java/org/dromara/system/controller/monitor/DruidMonitorController.java b/stwzhj-modules/wzhj-system/src/main/java/org/dromara/system/controller/monitor/DruidMonitorController.java new file mode 100644 index 00000000..c1b1e0f7 --- /dev/null +++ b/stwzhj-modules/wzhj-system/src/main/java/org/dromara/system/controller/monitor/DruidMonitorController.java @@ -0,0 +1,75 @@ + +package org.dromara.system.controller.monitor; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.domain.R; +import org.dromara.system.service.IDruidMonitorService; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.Map; + +/** + * Druid监控控制器 + * + * @author CodeGeeX + */ +@Slf4j +@RequiredArgsConstructor +@RestController +@RequestMapping("/monitor/druid") +public class DruidMonitorController { + + private final IDruidMonitorService druidMonitorService; + + /** + * 获取数据源监控信息 + */ + @GetMapping("/datasource") + public R> getDataSourceInfo() { + return R.ok(druidMonitorService.getDataSourceInfo()); + } + + /** + * 获取SQL监控信息 + */ + @GetMapping("/sql") + public R> getSqlInfo() { + return R.ok(druidMonitorService.getSqlInfo()); + } + + /** + * 获取SQL防火墙信息 + */ + @GetMapping("/firewall") + public R> getSqlFirewallInfo() { + return R.ok(druidMonitorService.getSqlFirewallInfo()); + } + + /** + * 获取URI监控信息 + */ + @GetMapping("/uri") + public R> getUriInfo() { + return R.ok(druidMonitorService.getUriInfo()); + } + + /** + * 重置SQL监控统计信息 + */ + @PostMapping("/sql/reset") + public R resetSqlStat() { + druidMonitorService.resetSqlStat(); + return R.ok(); + } + + /** + * 获取活跃连接信息 + */ + @GetMapping("/activeConnections") + public R> getActiveConnections() { + return R.ok(druidMonitorService.getActiveConnections()); + } +} diff --git a/stwzhj-modules/wzhj-system/src/main/java/org/dromara/system/controller/monitor/SystemMonitorController.java b/stwzhj-modules/wzhj-system/src/main/java/org/dromara/system/controller/monitor/SystemMonitorController.java new file mode 100644 index 00000000..e2c9ef3a --- /dev/null +++ b/stwzhj-modules/wzhj-system/src/main/java/org/dromara/system/controller/monitor/SystemMonitorController.java @@ -0,0 +1,32 @@ + +package org.dromara.system.controller.monitor; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.system.domain.vo.SystemMonitorVo; +import org.dromara.system.service.ISystemMonitorService; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * 系统监控 + * + * @author CodeGeeX + */ +@RequiredArgsConstructor +@RestController +@RequestMapping("/monitor") +public class SystemMonitorController { + + private final ISystemMonitorService systemMonitorService; + + /** + * 获取系统监控信息 + */ + @GetMapping("/sysInfo") + public R getInfo() { + return R.ok(systemMonitorService.getAllSystemInfo()); + } +} diff --git a/stwzhj-modules/wzhj-system/src/main/java/org/dromara/system/domain/SysSqlFirewall.java b/stwzhj-modules/wzhj-system/src/main/java/org/dromara/system/domain/SysSqlFirewall.java new file mode 100644 index 00000000..db35c06e --- /dev/null +++ b/stwzhj-modules/wzhj-system/src/main/java/org/dromara/system/domain/SysSqlFirewall.java @@ -0,0 +1,93 @@ + +package org.dromara.system.domain; + +import com.baomidou.mybatisplus.annotation.*; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.time.LocalDateTime; + +/** + * SQL防火墙记录实体 + * + * @author CodeGeeX + */ +@Data +@TableName("sys_sql_firewall") +public class SysSqlFirewall implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键ID + */ + @TableId(value = "id", type = IdType.AUTO) + private Long id; + + /** + * SQL语句 + */ + private String sql; + + /** + * SQL类型 + */ + private String sqlType; + + /** + * 表名 + */ + private String tableName; + + /** + * 是否被拦截 + */ + private Boolean blocked; + + /** + * 拦截原因 + */ + private String blockReason; + + /** + * 来源IP + */ + private String sourceIp; + + /** + * 执行用户 + */ + private String executeUser; + + /** + * 执行时间 + */ + private LocalDateTime executeTime; + + /** + * 风险等级 + */ + private String riskLevel; + + /** + * 是否包含敏感字段 + */ + private Boolean hasSensitiveFields; + + /** + * 影响行数 + */ + private Integer affectRows; + + /** + * 数据源名称 + */ + private String dataSourceName; + + /** + * 创建时间 + */ + private LocalDateTime createTime; +} diff --git a/stwzhj-modules/wzhj-system/src/main/java/org/dromara/system/domain/SysSqlMonitor.java b/stwzhj-modules/wzhj-system/src/main/java/org/dromara/system/domain/SysSqlMonitor.java new file mode 100644 index 00000000..75174abe --- /dev/null +++ b/stwzhj-modules/wzhj-system/src/main/java/org/dromara/system/domain/SysSqlMonitor.java @@ -0,0 +1,103 @@ + +package org.dromara.system.domain; + +import com.baomidou.mybatisplus.annotation.*; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.time.LocalDateTime; + +/** + * SQL监控历史记录实体 + * + * @author CodeGeeX + */ +@Data +@TableName("sys_sql_monitor") +public class SysSqlMonitor implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键ID + */ + @TableId(value = "id", type = IdType.AUTO) + private Long id; + + /** + * SQL语句 + */ + private String sql; + + /** + * 执行次数 + */ + private Long executeCount; + + /** + * 执行时间(毫秒) + */ + private Long executeTimeMillis; + + /** + * 最大执行时间(毫秒) + */ + private Long maxTimespan; + + /** + * 最小执行时间(毫秒) + */ + private Long minTimespan; + + /** + * 平均执行时间(毫秒) + */ + private Long avgTimespan; + + /** + * 最后执行时间 + */ + private LocalDateTime lastExecuteTime; + + /** + * 出错次数 + */ + private Long errorCount; + + /** + * 更新行数 + */ + private Long updateCount; + + /** + * 读取行数 + */ + private Long fetchRowCount; + + /** + * 运行中数量 + */ + private Integer runningCount; + + /** + * 并发数量 + */ + private Integer concurrentMax; + + /** + * 数据源名称 + */ + private String dataSourceName; + + /** + * 创建时间 + */ + private LocalDateTime createTime; + + /** + * 更新时间 + */ + private LocalDateTime updateTime; +} diff --git a/stwzhj-modules/wzhj-system/src/main/java/org/dromara/system/domain/SysUriMonitor.java b/stwzhj-modules/wzhj-system/src/main/java/org/dromara/system/domain/SysUriMonitor.java new file mode 100644 index 00000000..07aa259f --- /dev/null +++ b/stwzhj-modules/wzhj-system/src/main/java/org/dromara/system/domain/SysUriMonitor.java @@ -0,0 +1,109 @@ + +package org.dromara.system.domain; + +import com.baomidou.mybatisplus.annotation.*; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.math.BigDecimal; +import java.time.LocalDateTime; + +/** + * URI监控记录实体 + * + * @author CodeGeeX + */ +@Data +@TableName("sys_uri_monitor") +public class SysUriMonitor implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键ID + */ + @TableId(value = "id", type = IdType.AUTO) + private Long id; + + /** + * 请求URI + */ + private String uri; + + /** + * 请求方法 + */ + private String method; + + /** + * 请求次数 + */ + private Long requestCount; + + /** + * 成功次数 + */ + private Long successCount; + + /** + * 失败次数 + */ + private Long failureCount; + + /** + * 总响应时间(毫秒) + */ + private Long totalResponseTime; + + /** + * 最大响应时间(毫秒) + */ + private Long maxResponseTime; + + /** + * 最小响应时间(毫秒) + */ + private Long minResponseTime; + + /** + * 平均响应时间(毫秒) + */ + private Long avgResponseTime; + + /** + * 最后访问时间 + */ + private LocalDateTime lastAccessTime; + + /** + * 并发数 + */ + private Integer concurrentCount; + + /** + * 最大并发数 + */ + private Integer maxConcurrentCount; + + /** + * QPS(每秒查询率) + */ + private BigDecimal qps; + + /** + * 错误率 + */ + private BigDecimal errorRate; + + /** + * 创建时间 + */ + private LocalDateTime createTime; + + /** + * 更新时间 + */ + private LocalDateTime updateTime; +} diff --git a/stwzhj-modules/wzhj-system/src/main/java/org/dromara/system/domain/vo/DataSourceMonitorVo.java b/stwzhj-modules/wzhj-system/src/main/java/org/dromara/system/domain/vo/DataSourceMonitorVo.java new file mode 100644 index 00000000..69415367 --- /dev/null +++ b/stwzhj-modules/wzhj-system/src/main/java/org/dromara/system/domain/vo/DataSourceMonitorVo.java @@ -0,0 +1,99 @@ + +package org.dromara.system.domain.vo; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 数据源监控信息 + * + * @author CodeGeeX + */ +@Data +public class DataSourceMonitorVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 数据源名称 + */ + private String name; + + /** + * 数据库类型 + */ + private String dbType; + + /** + * 连接URL + */ + private String url; + + /** + * 用户名 + */ + private String username; + + /** + * 驱动类名 + */ + private String driverClassName; + + /** + * 初始化连接数 + */ + private int initialSize; + + /** + * 最小空闲连接数 + */ + private int minIdle; + + /** + * 最大活跃连接数 + */ + private int maxActive; + + /** + * 当前活跃连接数 + */ + private int activeCount; + + /** + * 空闲连接数 + */ + private int idleCount; + + /** + * 总连接数 + */ + private int totalCount; + + /** + * 最大等待时间(毫秒) + */ + private long maxWait; + + /** + * 连接池使用率 + */ + private double usageRate; + + /** + * 是否有效 + */ + private boolean valid; + + /** + * 创建时间 + */ + private String createTime; + + /** + * 最后一次使用时间 + */ + private String lastUseTime; +} diff --git a/stwzhj-modules/wzhj-system/src/main/java/org/dromara/system/domain/vo/SqlFirewallVo.java b/stwzhj-modules/wzhj-system/src/main/java/org/dromara/system/domain/vo/SqlFirewallVo.java new file mode 100644 index 00000000..f71e1772 --- /dev/null +++ b/stwzhj-modules/wzhj-system/src/main/java/org/dromara/system/domain/vo/SqlFirewallVo.java @@ -0,0 +1,84 @@ + +package org.dromara.system.domain.vo; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * SQL防火墙监控信息 + * + * @author CodeGeeX + */ +@Data +public class SqlFirewallVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * ID + */ + private Long id; + + /** + * SQL语句 + */ + private String sql; + + /** + * SQL类型(SELECT/INSERT/UPDATE/DELETE等) + */ + private String sqlType; + + /** + * 表名 + */ + private String tableName; + + /** + * 是否被拦截 + */ + private boolean blocked; + + /** + * 拦截原因 + */ + private String blockReason; + + /** + * 来源IP + */ + private String sourceIp; + + /** + * 执行用户 + */ + private String executeUser; + + /** + * 执行时间 + */ + private String executeTime; + + /** + * 风险等级(低/中/高) + */ + private String riskLevel; + + /** + * 是否包含敏感字段 + */ + private boolean hasSensitiveFields; + + /** + * 影响行数 + */ + private long affectRows; + + /** + * 数据源名称 + */ + private String dataSourceName; +} diff --git a/stwzhj-modules/wzhj-system/src/main/java/org/dromara/system/domain/vo/SqlMonitorVo.java b/stwzhj-modules/wzhj-system/src/main/java/org/dromara/system/domain/vo/SqlMonitorVo.java new file mode 100644 index 00000000..3dd1183d --- /dev/null +++ b/stwzhj-modules/wzhj-system/src/main/java/org/dromara/system/domain/vo/SqlMonitorVo.java @@ -0,0 +1,89 @@ + +package org.dromara.system.domain.vo; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * SQL监控信息 + * + * @author CodeGeeX + */ +@Data +public class SqlMonitorVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * SQL ID + */ + private Long id; + + /** + * SQL语句 + */ + private String sql; + + /** + * 执行次数 + */ + private long executeCount; + + /** + * 执行时间(毫秒) + */ + private long executeTimeMillis; + + /** + * 最大执行时间(毫秒) + */ + private long maxTimespan; + + /** + * 最小执行时间(毫秒) + */ + private long minTimespan; + + /** + * 平均执行时间(毫秒) + */ + private long avgTimespan; + + /** + * 最后执行时间 + */ + private String lastExecuteTime; + + /** + * 出错次数 + */ + private long errorCount; + + /** + * 更新行数 + */ + private long updateCount; + + /** + * 读取行数 + */ + private long fetchRowCount; + + /** + * 运行中数量 + */ + private Long runningCount; + + /** + * 并发数量 + */ + private Long concurrentMax; + + /** + * 数据源名称 + */ + private String dataSourceName; +} diff --git a/stwzhj-modules/wzhj-system/src/main/java/org/dromara/system/domain/vo/SystemMonitorVo.java b/stwzhj-modules/wzhj-system/src/main/java/org/dromara/system/domain/vo/SystemMonitorVo.java new file mode 100644 index 00000000..58f0a6ab --- /dev/null +++ b/stwzhj-modules/wzhj-system/src/main/java/org/dromara/system/domain/vo/SystemMonitorVo.java @@ -0,0 +1,234 @@ + +package org.dromara.system.domain.vo; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 系统监控信息 + * + * @author CodeGeeX + */ +@Data +public class SystemMonitorVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * CPU信息 + */ + private CpuInfo cpu; + + /** + * 内存信息 + */ + private MemoryInfo memory; + + /** + * JVM信息 + */ + private JvmInfo jvm; + + /** + * 系统信息 + */ + private SystemInfo sys; + + /** + * 磁盘信息 + */ + private DiskInfo disk; + + /** + * CPU信息 + */ + @Data + public static class CpuInfo implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + /** + * 核心数 + */ + private int cpuNum; + + /** + * CPU总的使用率 + */ + private double total; + + /** + * CPU系统使用率 + */ + private double sys; + + /** + * CPU用户使用率 + */ + private double used; + + /** + * CPU当前等待率 + */ + private double wait; + + /** + * CPU当前空闲率 + */ + private double free; + } + + /** + * 内存信息 + */ + @Data + public static class MemoryInfo implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + /** + * 总内存 + */ + private double total; + + /** + * 已用内存 + */ + private double used; + + /** + * 剩余内存 + */ + private double free; + + /** + * 使用率 + */ + private double usage; + } + + /** + * JVM信息 + */ + @Data + public static class JvmInfo implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + /** + * 当前JVM占用的内存总数(M) + */ + private double total; + + /** + * JVM最大可用内存总数(M) + */ + private double max; + + /** + * JVM空闲内存(M) + */ + private double free; + + /** + * JDK版本 + */ + private String version; + + /** + * JDK路径 + */ + private String home; + + /** + * 启动时间 + */ + private String startTime; + + /** + * 运行时间 + */ + private String runTime; + + /** + * 运行时间(秒) + */ + private long runTimeSeconds; + } + + /** + * 系统信息 + */ + @Data + public static class SystemInfo implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + /** + * 服务器名称 + */ + private String computerName; + + /** + * 服务器IP + */ + private String computerIp; + + /** + * 操作系统 + */ + private String osName; + + /** + * 系统架构 + */ + private String osArch; + } + + /** + * 磁盘信息 + */ + @Data + public static class DiskInfo implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + /** + * 盘符路径 + */ + private String dirName; + + /** + * 盘符类型 + */ + private String sysTypeName; + + /** + * 文件类型 + */ + private String typeName; + + /** + * 总大小 + */ + private String total; + + /** + * 剩余大小 + */ + private String free; + + /** + * 已经使用量 + */ + private String used; + + /** + * 资源的使用率 + */ + private double usage; + } +} diff --git a/stwzhj-modules/wzhj-system/src/main/java/org/dromara/system/domain/vo/UriMonitorVo.java b/stwzhj-modules/wzhj-system/src/main/java/org/dromara/system/domain/vo/UriMonitorVo.java new file mode 100644 index 00000000..ba93e451 --- /dev/null +++ b/stwzhj-modules/wzhj-system/src/main/java/org/dromara/system/domain/vo/UriMonitorVo.java @@ -0,0 +1,104 @@ + +package org.dromara.system.domain.vo; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * URI监控信息 + * + * @author CodeGeeX + */ +@Data +public class UriMonitorVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * URI ID + */ + private Long id; + + /** + * 请求URI + */ + private String uri; + + /** + * 请求方法(GET/POST/PUT/DELETE等) + */ + private String method; + + /** + * 请求次数 + */ + private long requestCount; + + /** + * 成功次数 + */ + private long successCount; + + /** + * 失败次数 + */ + private long failureCount; + + /** + * 总响应时间(毫秒) + */ + private long totalResponseTime; + + /** + * 最大响应时间(毫秒) + */ + private long maxResponseTime; + + /** + * 最小响应时间(毫秒) + */ + private long minResponseTime; + + /** + * 平均响应时间(毫秒) + */ + private long avgResponseTime; + + /** + * 最后访问时间 + */ + private String lastAccessTime; + + /** + * 并发数 + */ + private int concurrentCount; + + /** + * 最大并发数 + */ + private int maxConcurrentCount; + + /** + * QPS(每秒查询率) + */ + private double qps; + + /** + * 错误率 + */ + private double errorRate; + + /** + * 来源IP + */ + private String sourceIp; + + /** + * 用户代理 + */ + private String userAgent; +} diff --git a/stwzhj-modules/wzhj-system/src/main/java/org/dromara/system/mapper/SysSqlFirewallMapper.java b/stwzhj-modules/wzhj-system/src/main/java/org/dromara/system/mapper/SysSqlFirewallMapper.java new file mode 100644 index 00000000..11e39211 --- /dev/null +++ b/stwzhj-modules/wzhj-system/src/main/java/org/dromara/system/mapper/SysSqlFirewallMapper.java @@ -0,0 +1,14 @@ + +package org.dromara.system.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.dromara.system.domain.SysSqlFirewall; + +/** + * SQL防火墙记录Mapper接口 + * + * @author CodeGeeX + */ +public interface SysSqlFirewallMapper extends BaseMapper { + +} diff --git a/stwzhj-modules/wzhj-system/src/main/java/org/dromara/system/service/IDruidMonitorService.java b/stwzhj-modules/wzhj-system/src/main/java/org/dromara/system/service/IDruidMonitorService.java new file mode 100644 index 00000000..38b3f0ec --- /dev/null +++ b/stwzhj-modules/wzhj-system/src/main/java/org/dromara/system/service/IDruidMonitorService.java @@ -0,0 +1,52 @@ + +package org.dromara.system.service; + +import java.util.List; + +/** + * Druid监控服务接口 + * + * @author CodeGeeX + */ +public interface IDruidMonitorService { + + /** + * 获取数据源监控信息 + * + * @return 数据源监控信息列表 + */ + List getDataSourceInfo(); + + /** + * 获取SQL监控信息 + * + * @return SQL监控信息列表 + */ + List getSqlInfo(); + + /** + * 获取SQL防火墙信息 + * + * @return SQL防火墙信息列表 + */ + List getSqlFirewallInfo(); + + /** + * 获取URI监控信息 + * + * @return URI监控信息列表 + */ + List getUriInfo(); + + /** + * 重置SQL监控统计信息 + */ + void resetSqlStat(); + + /** + * 获取活跃连接信息 + * + * @return 活跃连接信息列表 + */ + List getActiveConnections(); +} diff --git a/stwzhj-modules/wzhj-system/src/main/java/org/dromara/system/service/ISystemMonitorService.java b/stwzhj-modules/wzhj-system/src/main/java/org/dromara/system/service/ISystemMonitorService.java new file mode 100644 index 00000000..b21e5b43 --- /dev/null +++ b/stwzhj-modules/wzhj-system/src/main/java/org/dromara/system/service/ISystemMonitorService.java @@ -0,0 +1,18 @@ +package org.dromara.system.service; + +import org.dromara.system.domain.vo.SystemMonitorVo; + +/** + * 系统监控服务 + * + * @author CodeGeeX + */ +public interface ISystemMonitorService { + + /** + * 获取系统监控信息 + * + * @return 系统监控信息 + */ + SystemMonitorVo getAllSystemInfo(); +} diff --git a/stwzhj-modules/wzhj-system/src/main/java/org/dromara/system/service/impl/DruidMonitorServiceImpl.java b/stwzhj-modules/wzhj-system/src/main/java/org/dromara/system/service/impl/DruidMonitorServiceImpl.java new file mode 100644 index 00000000..5c1c04ff --- /dev/null +++ b/stwzhj-modules/wzhj-system/src/main/java/org/dromara/system/service/impl/DruidMonitorServiceImpl.java @@ -0,0 +1,244 @@ +package org.dromara.system.service.impl; + +import com.alibaba.druid.pool.DruidDataSource; +import com.baomidou.dynamic.datasource.DynamicRoutingDataSource; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.system.domain.vo.SqlMonitorVo; +import org.dromara.system.service.IDruidMonitorService; +import org.springframework.stereotype.Service; + +import javax.sql.DataSource; +import java.text.SimpleDateFormat; +import java.util.*; + +/** + * Druid监控服务实现 + * + * @author CodeGeeX + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class DruidMonitorServiceImpl implements IDruidMonitorService { + + private final DataSource dataSource; + + /** + * 获取所有Druid数据源 + */ + private Map getDruidDataSources() { + Map druidDataSources = new HashMap<>(); + + if (dataSource instanceof DynamicRoutingDataSource) { + DynamicRoutingDataSource dynamicDataSource = (DynamicRoutingDataSource) dataSource; + Map dataSources = dynamicDataSource.getDataSources(); + + for (Map.Entry entry : dataSources.entrySet()) { + DataSource ds = entry.getValue(); + // 尝试获取底层的Druid数据源 + if (ds instanceof DruidDataSource) { + druidDataSources.put(entry.getKey(), (DruidDataSource) ds); + } + // 如果数据源被代理,尝试解包 + else { + try { + // 对于p6spy等代理,可能需要解包 + if (ds instanceof com.p6spy.engine.spy.P6DataSource) { + java.lang.reflect.Field realDataSourceField = ds.getClass().getDeclaredField("realDataSource"); + realDataSourceField.setAccessible(true); + Object realDs = realDataSourceField.get(ds); + if (realDs instanceof DruidDataSource) { + druidDataSources.put(entry.getKey(), (DruidDataSource) realDs); + } + } + } catch (Exception e) { + log.warn("无法获取数据源 {} 的底层Druid数据源: {}", entry.getKey(), e.getMessage()); + } + } + } + } else if (dataSource instanceof DruidDataSource) { + // 如果是直接使用Druid数据源 + druidDataSources.put("default", (DruidDataSource) dataSource); + } + + return druidDataSources; + } + + /** + * 获取数据源监控信息 + */ + @Override + public List getDataSourceInfo() { + List dataSourceList = new ArrayList<>(); + Map druidDataSources = getDruidDataSources(); + + for (Map.Entry entry : druidDataSources.entrySet()) { + DruidDataSource druidDataSource = entry.getValue(); + Map dsInfo = new LinkedHashMap<>(); + dsInfo.put("name", entry.getKey() + "(" + druidDataSource.getName() + ")"); + dsInfo.put("url", druidDataSource.getUrl()); + dsInfo.put("username", druidDataSource.getUsername()); + dsInfo.put("dbType", druidDataSource.getDbType()); + dsInfo.put("driverClassName", druidDataSource.getDriverClassName()); + dsInfo.put("initialSize", druidDataSource.getInitialSize()); + dsInfo.put("minIdle", druidDataSource.getMinIdle()); + dsInfo.put("maxActive", druidDataSource.getMaxActive()); + dsInfo.put("activeCount", druidDataSource.getActiveCount()); + dsInfo.put("poolingCount", druidDataSource.getPoolingCount()); + dsInfo.put("maxWait", druidDataSource.getMaxWait()); + dsInfo.put("validationQuery", druidDataSource.getValidationQuery()); + dsInfo.put("testOnBorrow", druidDataSource.isTestOnBorrow()); + dsInfo.put("testOnReturn", druidDataSource.isTestOnReturn()); + dsInfo.put("testWhileIdle", druidDataSource.isTestWhileIdle()); + + // 计算连接池使用率 + int maxActive = druidDataSource.getMaxActive(); + int activeCount = druidDataSource.getActiveCount(); + double usageRate = maxActive > 0 ? (activeCount * 100.0 / maxActive) : 0; + dsInfo.put("usageRate", String.format("%.2f", usageRate) + "%"); + + dsInfo.put("createTime", formatDate(druidDataSource.getCreatedTime().getTime())); + dsInfo.put("lastUseTime", formatDate(System.currentTimeMillis())); + + dataSourceList.add(dsInfo); + } + + if (dataSourceList.isEmpty()) { + log.warn("未找到Druid数据源,当前数据源类型: {}", dataSource.getClass().getName()); + } + + return dataSourceList; + } + + /** + * 获取SQL监控信息 + */ + @Override + public List getSqlInfo() { + List sqlList = new ArrayList<>(); + Map druidDataSources = getDruidDataSources(); + + for (Map.Entry entry : druidDataSources.entrySet()) { + DruidDataSource druidDataSource = entry.getValue(); + Map sqlStatMap = druidDataSource.getSqlStatMap(); + + if (sqlStatMap != null && !sqlStatMap.isEmpty()) { + for (Map.Entry statEntry : sqlStatMap.entrySet()) { + SqlMonitorVo sqlMonitor = new SqlMonitorVo(); + com.alibaba.druid.stat.JdbcSqlStat sqlStat = statEntry.getValue(); + sqlMonitor.setId(sqlStat.getId()); + sqlMonitor.setSql(sqlStat.getSql()); + sqlMonitor.setExecuteCount(sqlStat.getExecuteCount()); + sqlMonitor.setExecuteTimeMillis(sqlStat.getExecuteMillisTotal()); + sqlMonitor.setMaxTimespan(sqlStat.getExecuteMillisMax()); + sqlMonitor.setMinTimespan(0L); + + long executeCount = sqlMonitor.getExecuteCount(); + long executeTimeMillis = sqlMonitor.getExecuteTimeMillis(); + sqlMonitor.setAvgTimespan(executeCount > 0 ? executeTimeMillis / executeCount : 0); + + sqlMonitor.setLastExecuteTime(formatDate(System.currentTimeMillis())); + sqlMonitor.setErrorCount(sqlStat.getErrorCount()); + sqlMonitor.setUpdateCount(sqlStat.getUpdateCount()); + sqlMonitor.setFetchRowCount(sqlStat.getFetchRowCount()); + sqlMonitor.setRunningCount(sqlStat.getRunningCount()); + sqlMonitor.setConcurrentMax(sqlStat.getConcurrentMax()); + sqlMonitor.setDataSourceName(entry.getKey()); + + sqlList.add(sqlMonitor); + } + } + } + return sqlList; + } + + /** + * 获取SQL防火墙信息 + */ + @Override + public List getSqlFirewallInfo() { + List firewallList = new ArrayList<>(); + Map druidDataSources = getDruidDataSources(); + + for (Map.Entry entry : druidDataSources.entrySet()) { + DruidDataSource druidDataSource = entry.getValue(); + Map wallStatMap = druidDataSource.getWallStatMap(); + if (wallStatMap != null && !wallStatMap.isEmpty()) { + Map firewallInfo = new LinkedHashMap<>(); + firewallInfo.put("dataSource", entry.getKey()); + firewallInfo.put("checkCount", wallStatMap.getOrDefault("checkCount", 0)); + firewallInfo.put("violationCount", wallStatMap.getOrDefault("violationCount", 0)); + firewallInfo.put("blackListHitCount", wallStatMap.getOrDefault("blackListHitCount", 0)); + firewallInfo.put("whiteListHitCount", wallStatMap.getOrDefault("whiteListHitCount", 0)); + firewallInfo.put("syntaxErrorCount", wallStatMap.getOrDefault("syntaxErrorCount", 0)); + firewallInfo.put("tableCount", wallStatMap.getOrDefault("tableCount", 0)); + firewallInfo.put("selectCount", wallStatMap.getOrDefault("selectCount", 0)); + firewallInfo.put("updateCount", wallStatMap.getOrDefault("updateCount", 0)); + firewallInfo.put("deleteCount", wallStatMap.getOrDefault("deleteCount", 0)); + firewallInfo.put("insertCount", wallStatMap.getOrDefault("insertCount", 0)); + + firewallList.add(firewallInfo); + } + } + return firewallList; + } + + /** + * 获取URI监控信息 + */ + @Override + public List getUriInfo() { + // URI监控功能在新版Druid中已移除 + return new ArrayList<>(); + } + + /** + * 重置SQL监控统计信息 + */ + @Override + public void resetSqlStat() { + Map druidDataSources = getDruidDataSources(); + for (Map.Entry entry : druidDataSources.entrySet()) { + entry.getValue().resetStat(); + log.info("数据源 {} 的SQL监控统计信息已重置", entry.getKey()); + } + } + + /** + * 获取活跃连接信息 + */ + @Override + public List getActiveConnections() { + List activeConnections = new ArrayList<>(); + Map druidDataSources = getDruidDataSources(); + + for (Map.Entry entry : druidDataSources.entrySet()) { + DruidDataSource druidDataSource = entry.getValue(); + Set activeSet = druidDataSource.getActiveConnections(); + + for (com.alibaba.druid.pool.DruidPooledConnection activeConn : activeSet) { + Map connInfo = new LinkedHashMap<>(); + connInfo.put("dataSource", entry.getKey()); + connInfo.put("connectTime", formatDate(System.currentTimeMillis())); + connInfo.put("connectHoldTime", 0L); + connInfo.put("lastSql", "-"); + connInfo.put("lastExecuteTime", "-"); + + activeConnections.add(connInfo); + } + } + return activeConnections; + } + + /** + * 格式化日期 + */ + private String formatDate(long timestamp) { + if (timestamp <= 0) { + return "-"; + } + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + return sdf.format(new Date(timestamp)); + } +} diff --git a/stwzhj-modules/wzhj-system/src/main/java/org/dromara/system/service/impl/SystemMonitorServiceImpl.java b/stwzhj-modules/wzhj-system/src/main/java/org/dromara/system/service/impl/SystemMonitorServiceImpl.java new file mode 100644 index 00000000..00dc02ba --- /dev/null +++ b/stwzhj-modules/wzhj-system/src/main/java/org/dromara/system/service/impl/SystemMonitorServiceImpl.java @@ -0,0 +1,436 @@ + +package org.dromara.system.service.impl; + +import lombok.RequiredArgsConstructor; +import org.dromara.system.utils.IPUtils; +import oshi.SystemInfo; +import oshi.hardware.CentralProcessor; +import oshi.hardware.GlobalMemory; +import oshi.hardware.HardwareAbstractionLayer; +import oshi.software.os.FileSystem; +import oshi.software.os.OSFileStore; +import oshi.util.Util; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.system.domain.vo.SystemMonitorVo; +import org.dromara.system.service.ISystemMonitorService; +import org.springframework.stereotype.Service; + +import java.lang.management.ManagementFactory; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.text.DecimalFormat; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +/** + * 系统监控服务实现 + * + * @author CodeGeeX + */ +@RequiredArgsConstructor +@Service +public class SystemMonitorServiceImpl implements ISystemMonitorService { + + private static final int OSHI_WAIT_SECONDS = 1000; + + /** + * 获取系统监控信息 + * + * @return 系统监控信息 + */ + @Override + public SystemMonitorVo getAllSystemInfo() { + SystemMonitorVo monitorVo = new SystemMonitorVo(); + + // 获取系统信息 + monitorVo.setSys(getSystemInfo()); + + // 获取CPU信息 + monitorVo.setCpu(getCpuInfo()); + + // 获取内存信息 + monitorVo.setMemory(getMemoryInfo()); + + // 获取JVM信息 + monitorVo.setJvm(getJvmInfo()); + + // 获取磁盘信息 + monitorVo.setDisk(getDiskInfo()); + + return monitorVo; + } + + /** + * 获取系统信息 + */ + private SystemMonitorVo.SystemInfo getSystemInfo() { + SystemMonitorVo.SystemInfo sysInfo = new SystemMonitorVo.SystemInfo(); + + try { + InetAddress inetAddress = InetAddress.getLocalHost(); + sysInfo.setComputerName(inetAddress.getHostName()); + sysInfo.setComputerIp(inetAddress.getHostAddress()); + } catch (UnknownHostException e) { + sysInfo.setComputerName("未知"); + sysInfo.setComputerIp("未知"); + } + + SystemInfo systemInfo = new SystemInfo(); + sysInfo.setOsName(systemInfo.getOperatingSystem().getFamily() + " " + systemInfo.getOperatingSystem().getVersionInfo()); + sysInfo.setOsArch(systemInfo.getHardware().getProcessor().getPhysicalProcessorCount() + "核 " + systemInfo.getHardware().getProcessor().getLogicalProcessorCount() + "线程"); + + return sysInfo; + } + + /** + * 获取CPU信息 + */ + private SystemMonitorVo.CpuInfo getCpuInfo() { + SystemMonitorVo.CpuInfo cpuInfo = new SystemMonitorVo.CpuInfo(); + + SystemInfo systemInfo = new SystemInfo(); + CentralProcessor processor = systemInfo.getHardware().getProcessor(); + + // CPU核心数 + cpuInfo.setCpuNum(processor.getLogicalProcessorCount()); + + // CPU信息 + long[] prevTicks = processor.getSystemCpuLoadTicks(); + // 等待1秒 + Util.sleep(OSHI_WAIT_SECONDS); + long[] ticks = processor.getSystemCpuLoadTicks(); + + long nice = ticks[CentralProcessor.TickType.NICE.getIndex()] - prevTicks[CentralProcessor.TickType.NICE.getIndex()]; + long irq = ticks[CentralProcessor.TickType.IRQ.getIndex()] - prevTicks[CentralProcessor.TickType.IRQ.getIndex()]; + long softirq = ticks[CentralProcessor.TickType.SOFTIRQ.getIndex()] - prevTicks[CentralProcessor.TickType.SOFTIRQ.getIndex()]; + long steal = ticks[CentralProcessor.TickType.STEAL.getIndex()] - prevTicks[CentralProcessor.TickType.STEAL.getIndex()]; + long cSys = ticks[CentralProcessor.TickType.SYSTEM.getIndex()] - prevTicks[CentralProcessor.TickType.SYSTEM.getIndex()]; + long user = ticks[CentralProcessor.TickType.USER.getIndex()] - prevTicks[CentralProcessor.TickType.USER.getIndex()]; + long iowait = ticks[CentralProcessor.TickType.IOWAIT.getIndex()] - prevTicks[CentralProcessor.TickType.IOWAIT.getIndex()]; + long idle = ticks[CentralProcessor.TickType.IDLE.getIndex()] - prevTicks[CentralProcessor.TickType.IDLE.getIndex()]; + long total = user + nice + cSys + idle + iowait + irq + softirq + steal; + + cpuInfo.setTotal(Arith.div(total - idle, total, 4) * 100); + cpuInfo.setSys(Arith.div(cSys + irq + softirq, total, 4) * 100); + cpuInfo.setUsed(Arith.div(user + nice, total, 4) * 100); + cpuInfo.setWait(Arith.div(iowait, total, 4) * 100); + cpuInfo.setFree(Arith.div(idle, total, 4) * 100); + + return cpuInfo; + } + + /** + * 获取内存信息 + */ + private SystemMonitorVo.MemoryInfo getMemoryInfo() { + SystemMonitorVo.MemoryInfo memoryInfo = new SystemMonitorVo.MemoryInfo(); + + SystemInfo systemInfo = new SystemInfo(); + GlobalMemory memory = systemInfo.getHardware().getMemory(); + + // 总内存 + long total = memory.getTotal(); + // 已用内存 + long used = total - memory.getAvailable(); + // 剩余内存 + long free = memory.getAvailable(); + + memoryInfo.setTotal(Arith.div(total, (1024 * 1024 * 1024), 2)); + memoryInfo.setUsed(Arith.div(used, (1024 * 1024 * 1024), 2)); + memoryInfo.setFree(Arith.div(free, (1024 * 1024 * 1024), 2)); + memoryInfo.setUsage(Arith.mul(Arith.div(used, total, 4), 100)); + + return memoryInfo; + } + + /** + * 获取JVM信息 + */ + private SystemMonitorVo.JvmInfo getJvmInfo() { + SystemMonitorVo.JvmInfo jvmInfo = new SystemMonitorVo.JvmInfo(); + + // JVM运行时间 + long startTime = ManagementFactory.getRuntimeMXBean().getStartTime(); + Date date = new Date(startTime); + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + jvmInfo.setStartTime(sdf.format(date)); + + // 运行时间 + long runTime = System.currentTimeMillis() - startTime; + jvmInfo.setRunTimeSeconds(runTime / 1000); + jvmInfo.setRunTime(formatTime(runTime)); + + // JVM内存 + Runtime runtime = Runtime.getRuntime(); + // JVM总内存 + jvmInfo.setTotal(Arith.div(runtime.totalMemory(), (1024 * 1024), 2)); + // JVM最大可用内存 + jvmInfo.setMax(Arith.div(runtime.maxMemory(), (1024 * 1024), 2)); + // JVM空闲内存 + jvmInfo.setFree(Arith.div(runtime.freeMemory(), (1024 * 1024), 2)); + + // JDK版本 + jvmInfo.setVersion(System.getProperty("java.version")); + + // JDK路径 + jvmInfo.setHome(System.getProperty("java.home")); + + return jvmInfo; + } + + /** + * 获取磁盘信息 + */ + private SystemMonitorVo.DiskInfo getDiskInfo() { + SystemMonitorVo.DiskInfo diskInfo = new SystemMonitorVo.DiskInfo(); + + try { + SystemInfo systemInfo = new SystemInfo(); + FileSystem fileSystem = systemInfo.getOperatingSystem().getFileSystem(); + List fileStores = fileSystem.getFileStores(); + + // 判断操作系统类型 + boolean isWindows = System.getProperty("os.name").toLowerCase().contains("windows"); + + // 用于Linux系统的后备选项 + OSFileStore fallbackFileStore = null; + + for (OSFileStore fileStore : fileStores) { + String mount = fileStore.getMount(); + String name = fileStore.getName(); + + if (StringUtils.isEmpty(mount)) { + continue; + } + + // Windows系统处理 + if (isWindows) { + // 修正:正确的Windows盘符匹配模式 + if (mount.matches("^[A-Za-z]:\\\\$") || mount.matches("^[A-Za-z]:/$") || mount.equals("C:\\") || mount.equals("C:/")) { + setDiskInfo(diskInfo, fileStore, mount); + + // 添加日志便于调试 + System.out.println("Found Windows disk: " + mount); + break; + } + } + // Linux/Unix系统处理 + else { + // 跳过系统特殊目录 + if (mount.startsWith("/dev") || mount.startsWith("/sys") || + mount.startsWith("/proc") || mount.startsWith("/run") || + mount.startsWith("/boot") || mount.equals("/tmp")) { + continue; + } + + // 优先获取根目录(/) + if (mount.equals("/")) { + setDiskInfo(diskInfo, fileStore, mount); + System.out.println("Found root directory: " + mount); + break; + } + + // 如果还没找到根目录,保存第一个有效的挂载点作为后备 + if (fallbackFileStore == null && isValidMountPoint(mount)) { + fallbackFileStore = fileStore; + } + } + } + + // Linux系统:如果没找到根目录但找到了其他有效挂载点 + if (!isWindows && StringUtils.isEmpty(diskInfo.getDirName()) && fallbackFileStore != null) { + setDiskInfo(diskInfo, fallbackFileStore, fallbackFileStore.getMount()); + System.out.println("Using fallback mount point: " + fallbackFileStore.getMount()); + } + + // 如果仍然没有获取到磁盘信息,尝试获取第一个有效磁盘 + if (StringUtils.isEmpty(diskInfo.getDirName()) && !fileStores.isEmpty()) { + for (OSFileStore fileStore : fileStores) { + String mount = fileStore.getMount(); + if (!StringUtils.isEmpty(mount)) { + setDiskInfo(diskInfo, fileStore, mount); + System.out.println("Using first available disk: " + mount); + break; + } + } + } + + } catch (Exception e) { + e.printStackTrace(); + // 处理异常情况,设置默认值 + diskInfo.setDirName("Unknown"); + diskInfo.setTotal("0G"); + diskInfo.setFree("0G"); + diskInfo.setUsed("0G"); + diskInfo.setUsage(0); + } + + return diskInfo; + } + + // 提取公共方法设置磁盘信息 + private void setDiskInfo(SystemMonitorVo.DiskInfo diskInfo, OSFileStore fileStore, String mount) { + try { + diskInfo.setDirName(mount); + diskInfo.setSysTypeName(fileStore.getType()); + diskInfo.setTypeName(fileStore.getVolume()); + + long total = fileStore.getTotalSpace(); + long free = fileStore.getUsableSpace(); + long used = total - free; + + // 防止除零错误 + if (total > 0) { + diskInfo.setTotal(Arith.div(total, (1024 * 1024 * 1024), 2) + "G"); + diskInfo.setFree(Arith.div(free, (1024 * 1024 * 1024), 2) + "G"); + diskInfo.setUsed(Arith.div(used, (1024 * 1024 * 1024), 2) + "G"); + diskInfo.setUsage(Arith.mul(Arith.div(used, total, 4), 100)); + } else { + diskInfo.setTotal("0G"); + diskInfo.setFree("0G"); + diskInfo.setUsed("0G"); + diskInfo.setUsage(0); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + // 检查是否是有效的挂载点 + private boolean isValidMountPoint(String mount) { + return !StringUtils.isEmpty(mount) && + !mount.startsWith("/dev") && + !mount.startsWith("/sys") && + !mount.startsWith("/proc"); + } + + /** + * 格式化时间 + * + * @param time 毫秒 + * @return 格式化后的时间字符串 + */ + private String formatTime(long time) { + long days = time / (1000 * 60 * 60 * 24); + long hours = (time % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60); + long minutes = (time % (1000 * 60 * 60)) / (1000 * 60); + long seconds = (time % (1000 * 60)) / 1000; + + StringBuilder sb = new StringBuilder(); + if (days > 0) { + sb.append(days).append("天"); + } + if (hours > 0) { + sb.append(hours).append("小时"); + } + if (minutes > 0) { + sb.append(minutes).append("分钟"); + } + if (seconds > 0) { + sb.append(seconds).append("秒"); + } + return sb.toString(); + } + + /** + * 精确计算 + */ + private static class Arith { + /** + * 默认除法运算精度 + */ + private static final int DEF_DIV_SCALE = 10; + + private Arith() { + } + + /** + * 提供精确的加法运算 + * + * @param v1 被加数 + * @param v2 加数 + * @return 两个参数的和 + */ + public static double add(double v1, double v2) { + java.math.BigDecimal b1 = new java.math.BigDecimal(Double.toString(v1)); + java.math.BigDecimal b2 = new java.math.BigDecimal(Double.toString(v2)); + return b1.add(b2).doubleValue(); + } + + /** + * 提供精确的减法运算 + * + * @param v1 被减数 + * @param v2 减数 + * @return 两个参数的差 + */ + public static double sub(double v1, double v2) { + java.math.BigDecimal b1 = new java.math.BigDecimal(Double.toString(v1)); + java.math.BigDecimal b2 = new java.math.BigDecimal(Double.toString(v2)); + return b1.subtract(b2).doubleValue(); + } + + /** + * 提供精确的乘法运算 + * + * @param v1 被乘数 + * @param v2 乘数 + * @return 两个参数的积 + */ + public static double mul(double v1, double v2) { + java.math.BigDecimal b1 = new java.math.BigDecimal(Double.toString(v1)); + java.math.BigDecimal b2 = new java.math.BigDecimal(Double.toString(v2)); + return b1.multiply(b2).doubleValue(); + } + + /** + * 提供(相对)精确的除法运算,当发生除不尽的情况时,精确到 + * 小数点以后10位,以后的数字四舍五入 + * + * @param v1 被除数 + * @param v2 除数 + * @return 两个参数的商 + */ + public static double div(double v1, double v2) { + return div(v1, v2, DEF_DIV_SCALE); + } + + /** + * 提供(相对)精确的除法运算。当发生除不尽的情况时,由scale参数指 + * 定精度,以后的数字四舍五入 + * + * @param v1 被除数 + * @param v2 除数 + * @param scale 表示表示需要精确到小数点以后几位。 + * @return 两个参数的商 + */ + public static double div(double v1, double v2, int scale) { + if (scale < 0) { + throw new IllegalArgumentException( + "The scale must be a positive integer or zero"); + } + java.math.BigDecimal b1 = new java.math.BigDecimal(Double.toString(v1)); + java.math.BigDecimal b2 = new java.math.BigDecimal(Double.toString(v2)); + return b1.divide(b2, scale, java.math.BigDecimal.ROUND_HALF_UP).doubleValue(); + } + + /** + * 提供精确的小数位四舍五入处理 + * + * @param v 需要四舍五入的数字 + * @param scale 小数点后保留几位 + * @return 四舍五入后的结果 + */ + public static double round(double v, int scale) { + if (scale < 0) { + throw new IllegalArgumentException( + "The scale must be a positive integer or zero"); + } + java.math.BigDecimal b = new java.math.BigDecimal(Double.toString(v)); + java.math.BigDecimal one = new java.math.BigDecimal("1"); + return b.divide(one, scale, java.math.BigDecimal.ROUND_HALF_UP).doubleValue(); + } + } +}