diff --git a/common/pom.xml b/common/pom.xml index f2f5cdf5..4bce0d66 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -25,6 +25,19 @@ jakarta.servlet-api provided + + + + com.jcraft + jsch + 0.1.55 + + + + commons-net + commons-net + 3.11.0 + diff --git a/web-api/src/main/java/com/jeesite/modules/ApiApplication.java b/web-api/src/main/java/com/jeesite/modules/ApiApplication.java index d0cf5073..ae5b74f1 100644 --- a/web-api/src/main/java/com/jeesite/modules/ApiApplication.java +++ b/web-api/src/main/java/com/jeesite/modules/ApiApplication.java @@ -4,19 +4,18 @@ */ package com.jeesite.modules; -import com.jeesite.common.config.Global; -import com.jeesite.common.io.FileUtils; -import com.jeesite.modules.utils.IpUtils; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; +import org.springframework.scheduling.annotation.EnableScheduling; /** * Application * * @author ThinkGem */ +@EnableScheduling // 开启定时任务 @SpringBootApplication public class ApiApplication extends SpringBootServletInitializer { diff --git a/web-api/src/main/java/com/jeesite/modules/app/Job/hostJob.java b/web-api/src/main/java/com/jeesite/modules/app/Job/hostJob.java new file mode 100644 index 00000000..6d26f1f1 --- /dev/null +++ b/web-api/src/main/java/com/jeesite/modules/app/Job/hostJob.java @@ -0,0 +1,140 @@ +package com.jeesite.modules.app.Job; + +import com.jeesite.modules.app.dao.info.CpuInfo; +import com.jeesite.modules.app.dao.info.DiskInfo; +import com.jeesite.modules.app.dao.info.ServerInfo; +import com.jeesite.modules.app.utils.SystemUtil; +import com.jeesite.modules.app.utils.vo; +import com.jeesite.modules.biz.entity.BizDeviceInfo; +import com.jeesite.modules.biz.entity.BizMonitorAccount; +import com.jeesite.modules.biz.entity.BizMonitorHost; +import com.jeesite.modules.biz.entity.BizServerInfo; +import com.jeesite.modules.biz.service.BizDeviceInfoService; +import com.jeesite.modules.biz.service.BizMonitorAccountService; +import com.jeesite.modules.biz.service.BizMonitorHostService; +import com.jeesite.modules.app.utils.LoggerUtils; +import com.jeesite.modules.app.utils.NetworkUtils; +import com.jeesite.modules.biz.service.BizServerInfoService; +import jakarta.annotation.Resource; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; +import org.springframework.stereotype.Controller; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; + +@Controller +public class hostJob { + + @Resource + private BizMonitorHostService monitorHostService; + + @Resource + private BizMonitorAccountService accountService; + @Resource + private BizServerInfoService serverInfoService; + + @Resource + private BizDeviceInfoService deviceInfoService; + + @Resource(name = "hostMonitorExecutor") + private ThreadPoolTaskExecutor hostMonitorExecutor; + + + private static final LoggerUtils logger = LoggerUtils.getInstance(); + + /** + * 主机运行与磁盘监测 + */ + @Scheduled(cron = "0 0/15 * * * ?") + public void getHostStatus() { + List hosts = monitorHostService.findList(new BizMonitorHost()); + List> futures = new ArrayList<>(hosts.size()); + for (BizMonitorHost monitorHost : hosts) { + CompletableFuture future = CompletableFuture.runAsync(() -> { + boolean isReachable = NetworkUtils.isNetworkReachable(monitorHost.getIpAddress()); + monitorHost.setUstatus(isReachable ? "1" : "0"); + if (isReachable) { + monitorHost.setLastOnlineTime(new Date()); + syncServerInfo(monitorHost); + } + monitorHostService.update(monitorHost); + }, hostMonitorExecutor); // 指定使用配置的线程池 + futures.add(future); + } + try { + CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])) + .get(60, TimeUnit.SECONDS); // 超时时间可根据业务调整 + } catch (Exception e) { + logger.error(e.getMessage()); + } + } + + + private void syncServerInfo(BizMonitorHost host) { + try { + BizMonitorAccount monitorAccount = new BizMonitorAccount(); + monitorAccount.setHostId(host.getHostId()); + monitorAccount.setSshUsername("ogsapp"); + BizMonitorAccount account = accountService.get(monitorAccount); + CpuInfo cpuInfo = SystemUtil.getCpuMemUsage(host.getIpAddress(), account.getSshPort(), account.getSshUsername(), vo.getDecode(account.getSshPassword())); + ServerInfo info = SystemUtil.getServerBasicInfo(host.getIpAddress(), account.getSshPort(), account.getSshUsername(), vo.getDecode(account.getSshPassword()), host.getIpAddress()); + List diskInfos = SystemUtil.getDiskInfos(host.getIpAddress(), account.getSshPort(), account.getSshUsername(), vo.getDecode(account.getSshPassword())); + BizServerInfo bizServerInfo = new BizServerInfo(); + bizServerInfo.setHostId(host.getHostId()); + BizServerInfo serverInfo = serverInfoService.get(bizServerInfo); + if (serverInfo.getIsNewRecord()) { + getServerInfo(cpuInfo, info, bizServerInfo); + } else { + getServerInfo(cpuInfo, info, serverInfo); + } + syncDeviceInfo(host, diskInfos); + } catch (Exception e) { + logger.error(e.getMessage()); + } + } + + + private void syncDeviceInfo(BizMonitorHost host, List diskInfos) { + try { + for (DiskInfo diskInfo : diskInfos) { + BizDeviceInfo bizDeviceInfo = new BizDeviceInfo(); + bizDeviceInfo.setHostId(host.getHostId()); + bizDeviceInfo.setDevice(diskInfo.getDevice()); + bizDeviceInfo.setMountPoint(diskInfo.getMountPoint()); + BizDeviceInfo deviceInfo = deviceInfoService.get(bizDeviceInfo); + if (deviceInfo.getIsNewRecord()) { + bizDeviceInfo.setTotalSize(diskInfo.getTotalSize()); + bizDeviceInfo.setUsedSize(diskInfo.getUsedSize()); + bizDeviceInfo.setUsageRate(diskInfo.getUsageRate()); + bizDeviceInfo.setLastOnlineTime(new Date()); + deviceInfoService.save(bizDeviceInfo); + } else { + deviceInfo.setTotalSize(diskInfo.getTotalSize()); + deviceInfo.setUsedSize(diskInfo.getUsedSize()); + deviceInfo.setUsageRate(diskInfo.getUsageRate()); + deviceInfo.setLastOnlineTime(new Date()); + deviceInfoService.save(deviceInfo); + } + } + } catch (Exception e) { + logger.error(e.getMessage()); + } + } + + + private void getServerInfo(CpuInfo cpuInfo, ServerInfo info, BizServerInfo serverInfo) { + serverInfo.setUptime(info.getUptime()); + serverInfo.setOs(info.getOs()); + serverInfo.setKernelVersion(info.getKernelVersion()); + serverInfo.setHostname(info.getHostname()); + serverInfo.setIpAddress(info.getIpAddress()); + serverInfo.setCpuModel(info.getCpuModel()); + serverInfo.setCpuUsage(cpuInfo.getCpuUsage()); + serverInfo.setMemoryUsage(cpuInfo.getMemoryUsage()); + serverInfoService.save(serverInfo); + } +} diff --git a/web-api/src/main/java/com/jeesite/modules/app/config/MonitorExecutorConfig.java b/web-api/src/main/java/com/jeesite/modules/app/config/MonitorExecutorConfig.java new file mode 100644 index 00000000..f6e7eba4 --- /dev/null +++ b/web-api/src/main/java/com/jeesite/modules/app/config/MonitorExecutorConfig.java @@ -0,0 +1,30 @@ +package com.jeesite.modules.app.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.annotation.EnableAsync; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; + +import java.util.concurrent.ThreadPoolExecutor; + +@Configuration +@EnableAsync // 启用异步任务支持 +public class MonitorExecutorConfig { + + /** + * 配置主机监控专用线程池 + */ + @Bean(name = "hostMonitorExecutor") + public ThreadPoolTaskExecutor hostMonitorExecutor() { + ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); + int corePoolSize = Runtime.getRuntime().availableProcessors(); + executor.setCorePoolSize(corePoolSize); + executor.setMaxPoolSize(corePoolSize * 2); + executor.setQueueCapacity(200); + executor.setKeepAliveSeconds(60); + executor.setThreadNamePrefix("Host-Monitor-"); + executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); + executor.initialize(); + return executor; + } +} diff --git a/web-api/src/main/java/com/jeesite/modules/dao/TabItem.java b/web-api/src/main/java/com/jeesite/modules/app/dao/TabItem.java similarity index 94% rename from web-api/src/main/java/com/jeesite/modules/dao/TabItem.java rename to web-api/src/main/java/com/jeesite/modules/app/dao/TabItem.java index 82d18dbe..75ed9463 100644 --- a/web-api/src/main/java/com/jeesite/modules/dao/TabItem.java +++ b/web-api/src/main/java/com/jeesite/modules/app/dao/TabItem.java @@ -1,4 +1,4 @@ -package com.jeesite.modules.dao; +package com.jeesite.modules.app.dao; import com.jeesite.modules.biz.entity.BizListItem; import lombok.Data; diff --git a/web-api/src/main/java/com/jeesite/modules/app/dao/info/CpuInfo.java b/web-api/src/main/java/com/jeesite/modules/app/dao/info/CpuInfo.java new file mode 100644 index 00000000..047595eb --- /dev/null +++ b/web-api/src/main/java/com/jeesite/modules/app/dao/info/CpuInfo.java @@ -0,0 +1,22 @@ +package com.jeesite.modules.app.dao.info; + +import lombok.Data; + +import java.io.Serializable; + +@Data +public class CpuInfo implements Serializable { + + private double cpuUsage; // CPU使用率 + private double memoryUsage; // 内存使用率 + + + public CpuInfo() { + } + + public CpuInfo(double cpuUsage, double memoryUsage) { + this.cpuUsage = cpuUsage; + this.memoryUsage = memoryUsage; + } + +} diff --git a/web-api/src/main/java/com/jeesite/modules/app/dao/info/DiskInfo.java b/web-api/src/main/java/com/jeesite/modules/app/dao/info/DiskInfo.java new file mode 100644 index 00000000..2ed439b0 --- /dev/null +++ b/web-api/src/main/java/com/jeesite/modules/app/dao/info/DiskInfo.java @@ -0,0 +1,26 @@ +package com.jeesite.modules.app.dao.info; + +import lombok.Data; + +import java.io.Serializable; + +@Data +public class DiskInfo implements Serializable { + + private String device; // 设备 + private String mountPoint; // 挂载点 + private String totalSize; // 总容量 + private String usedSize; // 已使用 + private double usageRate; // 使用率 + + public DiskInfo() { + } + + public DiskInfo(String device, String mountPoint, String totalSize, String usedSize, double usageRate) { + this.device = device; + this.mountPoint = mountPoint; + this.totalSize = totalSize; + this.usedSize = usedSize; + this.usageRate = usageRate; + } +} diff --git a/web-api/src/main/java/com/jeesite/modules/app/dao/info/ServerInfo.java b/web-api/src/main/java/com/jeesite/modules/app/dao/info/ServerInfo.java new file mode 100644 index 00000000..d7c458a2 --- /dev/null +++ b/web-api/src/main/java/com/jeesite/modules/app/dao/info/ServerInfo.java @@ -0,0 +1,30 @@ +package com.jeesite.modules.app.dao.info; + +import lombok.Data; + +import java.io.Serializable; + +@Data +public class ServerInfo implements Serializable { + + private String uptime; // 主机运行时间 + private String os; // 操作系统 + private String kernelVersion; // 内核版本 + private String hostname; // 主机名 + private String ipAddress; // IP地址 + private String cpuModel; // CPU型号 + private String memoryTotal; // 内存总量 + + public ServerInfo() { + } + + public ServerInfo(String uptime, String os, String kernelVersion, String hostname, String ipAddress, String cpuModel, String memoryTotal) { + this.uptime = uptime; + this.os = os; + this.kernelVersion = kernelVersion; + this.hostname = hostname; + this.ipAddress = ipAddress; + this.cpuModel = cpuModel; + this.memoryTotal = memoryTotal; + } +} diff --git a/web-api/src/main/java/com/jeesite/modules/dict/NotifyType.java b/web-api/src/main/java/com/jeesite/modules/app/dict/NotifyType.java similarity index 92% rename from web-api/src/main/java/com/jeesite/modules/dict/NotifyType.java rename to web-api/src/main/java/com/jeesite/modules/app/dict/NotifyType.java index 93e44e74..f3877b81 100644 --- a/web-api/src/main/java/com/jeesite/modules/dict/NotifyType.java +++ b/web-api/src/main/java/com/jeesite/modules/app/dict/NotifyType.java @@ -1,4 +1,4 @@ -package com.jeesite.modules.dict; +package com.jeesite.modules.app.dict; public enum NotifyType { NOTIFICATION("1", "通知"), diff --git a/web-api/src/main/java/com/jeesite/modules/utils/BigDecimalUtils.java b/web-api/src/main/java/com/jeesite/modules/app/utils/BigDecimalUtils.java similarity index 98% rename from web-api/src/main/java/com/jeesite/modules/utils/BigDecimalUtils.java rename to web-api/src/main/java/com/jeesite/modules/app/utils/BigDecimalUtils.java index e6c3ebab..e29738e2 100644 --- a/web-api/src/main/java/com/jeesite/modules/utils/BigDecimalUtils.java +++ b/web-api/src/main/java/com/jeesite/modules/app/utils/BigDecimalUtils.java @@ -1,4 +1,4 @@ -package com.jeesite.modules.utils; +package com.jeesite.modules.app.utils; import java.math.BigDecimal; diff --git a/web-api/src/main/java/com/jeesite/modules/app/utils/DateUtils.java b/web-api/src/main/java/com/jeesite/modules/app/utils/DateUtils.java new file mode 100644 index 00000000..cf871e13 --- /dev/null +++ b/web-api/src/main/java/com/jeesite/modules/app/utils/DateUtils.java @@ -0,0 +1,78 @@ +package com.jeesite.modules.app.utils; + + +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.util.Date; + +public class DateUtils { + + // 日期格式化器(线程安全,复用提升性能) + private static final DateTimeFormatter DAY_FORMATTER = DateTimeFormatter.ISO_LOCAL_DATE; // yyyy-MM-dd + private static final DateTimeFormatter MONTH_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM"); // yyyy-MM + private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyyMMdd"); + + + public static String calculateStartCycleCode(String cycleType) { + // 默认使用当前日期计算,留参便于测试时指定日期 + return calculateStartCycleCode(cycleType, LocalDate.now()); + } + + /** + * 重载方法:支持指定基准日期计算起始cycleCode(便于测试) + * + * @param cycleType 周期类型(D:日, M:月, Q:季度, Y:年) + * @param baseDate 基准日期(以此日期为起点计算时间范围) + * @return 起始cycleCode(符合对应格式),未识别类型返回null + */ + public static String calculateStartCycleCode(String cycleType, LocalDate baseDate) { + if (baseDate == null) { + throw new IllegalArgumentException("基准日期不能为null"); + } + if (cycleType == null) { + return null; + } + + return switch (cycleType) { + case "D" -> + // 日:最近30天,格式 yyyy-MM-dd + baseDate.minusDays(30).format(DAY_FORMATTER); + case "M" -> + // 月:最近1年,格式 yyyy-MM + baseDate.minusYears(1).format(MONTH_FORMATTER); + case "Q" -> + // 季度:最近3年,格式 yyyy-Qx + getQuarterCycleCode(baseDate.minusYears(3)); + case "Y" -> + // 年:最近6年,格式 yyyy + String.valueOf(baseDate.minusYears(6).getYear()); + default -> + // 未识别的周期类型,返回null + null; + }; + } + + /** + * 计算指定日期对应的季度cycleCode(格式:yyyy-Qx) + * + * @param date 日期 + * @return 季度cycleCode(如2023-Q3) + */ + private static String getQuarterCycleCode(LocalDate date) { + int month = date.getMonthValue(); // 1-12月 + int quarter = (month - 1) / 3 + 1; + return String.format("%d-Q%d", date.getYear(), quarter); + } + + public static String dsValue() { + LocalDate currentDate = LocalDate.now(); + // 格式化日期为yyyymmdd + return currentDate.format(DATE_FORMATTER); + } + + + public static String dsValueDaysAgo(long days) { + LocalDate targetDate = LocalDate.now().minusDays(days); + return targetDate.format(DATE_FORMATTER); + } +} diff --git a/web-api/src/main/java/com/jeesite/modules/utils/IpUtils.java b/web-api/src/main/java/com/jeesite/modules/app/utils/IpUtils.java similarity index 99% rename from web-api/src/main/java/com/jeesite/modules/utils/IpUtils.java rename to web-api/src/main/java/com/jeesite/modules/app/utils/IpUtils.java index 58b4a142..436d7501 100644 --- a/web-api/src/main/java/com/jeesite/modules/utils/IpUtils.java +++ b/web-api/src/main/java/com/jeesite/modules/app/utils/IpUtils.java @@ -1,4 +1,4 @@ -package com.jeesite.modules.utils; +package com.jeesite.modules.app.utils; import com.jeesite.common.config.Global; import com.jeesite.common.io.FileUtils; diff --git a/web-api/src/main/java/com/jeesite/modules/app/utils/LoggerUtils.java b/web-api/src/main/java/com/jeesite/modules/app/utils/LoggerUtils.java new file mode 100644 index 00000000..45836918 --- /dev/null +++ b/web-api/src/main/java/com/jeesite/modules/app/utils/LoggerUtils.java @@ -0,0 +1,191 @@ +package com.jeesite.modules.app.utils; + +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.concurrent.locks.ReentrantLock; + + +public class LoggerUtils { + + // 日志级别 + public enum Level { + DEBUG, INFO, WARN, ERROR + } + + // 单例实例 + private static volatile LoggerUtils instance; + + private String baseLogPath; + + // 日期格式(文件名:yyyyMMdd,日志内容时间戳:yyyy-MM-dd HH:mm:ss.SSS) + private final SimpleDateFormat fileDateFormat = new SimpleDateFormat("yyyyMMdd"); + private final SimpleDateFormat logDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); + + // 线程安全锁 + private final ReentrantLock lock = new ReentrantLock(); + + // 禁止外部实例化 + private LoggerUtils(String baseLogPath) { + this.baseLogPath = baseLogPath; + initLogDir(); // 初始化日志目录 + } + + public static LoggerUtils getInstance() { + return getInstance("/ogsapp/logs"); + } + + /** + * 获取单例实例(自定义日志路径) + * + * @param baseLogPath 日志根目录(支持相对路径或绝对路径) + */ + public static LoggerUtils getInstance(String baseLogPath) { + if (instance == null) { + synchronized (LoggerUtils.class) { + if (instance == null) { + instance = new LoggerUtils(baseLogPath); + } + } + } + return instance; + } + + /** + * 初始化日志目录(若不存在则创建多级目录) + */ + private void initLogDir() { + try { + Files.createDirectories(Paths.get(baseLogPath)); + } catch (IOException e) { + throw new RuntimeException("初始化日志目录失败:" + baseLogPath, e); + } + } + + /** + * 获取当前日志文件路径(按日期拆分:baseLogPath/log_yyyyMMdd.txt) + */ + private String getCurrentLogFilePath(Level level) { + String fileName = level + "_" + fileDateFormat.format(new Date()) + ".log"; + return baseLogPath + File.separator + fileName; + } + + // ------------------------------ 日志方法(支持多类型可变参数) ------------------------------ + + /** + * 记录DEBUG级别日志(支持1到多个任意类型参数) + */ + public void debug(Object... messages) { + log(Level.DEBUG, joinMessages(messages), null); + } + + /** + * 记录INFO级别日志(支持1到多个任意类型参数) + */ + public void info(Object... messages) { + log(Level.INFO, joinMessages(messages), null); + } + + /** + * 记录WARN级别日志(支持1到多个任意类型参数) + */ + public void warn(Object... messages) { + log(Level.WARN, joinMessages(messages), null); + } + + /** + * 记录WARN级别日志(支持1到多个任意类型参数+异常) + */ + public void warn(Object[] messages, Throwable throwable) { + log(Level.WARN, joinMessages(messages), throwable); + } + + /** + * 记录ERROR级别日志(支持1到多个任意类型参数) + */ + public void error(Object... messages) { + log(Level.ERROR, joinMessages(messages), null); + } + + /** + * 记录ERROR级别日志(支持1到多个任意类型参数+异常) + */ + public void error(Object[] messages, Throwable throwable) { + log(Level.ERROR, joinMessages(messages), throwable); + } + + // ------------------------------ 核心方法 ------------------------------ + + /** + * 核心日志写入逻辑 + */ + private void log(Level level, String message, Throwable throwable) { + // 构建日志内容 + StringBuilder logContent = new StringBuilder(); + logContent.append("[").append(logDateFormat.format(new Date())).append("] "); // 时间戳 + logContent.append("[").append(level.name()).append("] "); // 日志级别 + logContent.append("[Thread-").append(Thread.currentThread().getId()).append("] "); // 线程ID + logContent.append(message); // 拼接后的消息 + + // 追加异常堆栈信息(如果有) + if (throwable != null) { + logContent.append("\n").append(getStackTrace(throwable)); + } + logContent.append("\n"); // 每条日志换行 + + // 加锁写入文件(保证线程安全) + lock.lock(); + try (BufferedWriter writer = new BufferedWriter( + new OutputStreamWriter( + new FileOutputStream(getCurrentLogFilePath(level), true), // 追加模式 + StandardCharsets.UTF_8 // 避免中文乱码 + ) + )) { + writer.write(logContent.toString()); + writer.flush(); + } catch (IOException e) { + System.err.println("日志写入失败:" + e.getMessage()); + } finally { + lock.unlock(); + } + } + + /** + * 拼接多类型可变参数为单个字符串(自动转换任意类型为字符串,处理null) + * + * @param messages 1到多个任意类型参数(不可为空数组) + * @return 拼接后的字符串 + */ + private String joinMessages(Object... messages) { + if (messages == null || messages.length == 0) { + throw new IllegalArgumentException("日志消息至少需要1个参数"); + } + StringBuilder sb = new StringBuilder(); + for (Object msg : messages) { + // 任意类型转换为字符串:null转为"null",其他类型调用String.valueOf() + sb.append(String.valueOf(msg)); + } + return sb.toString(); + } + + /** + * 异常堆栈信息转为字符串 + */ + private String getStackTrace(Throwable throwable) { + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + throwable.printStackTrace(pw); + return sw.toString(); + } + + /** + * 动态修改日志路径 + */ + public void setBaseLogPath(String baseLogPath) { + this.baseLogPath = baseLogPath; + initLogDir(); + } +} \ No newline at end of file diff --git a/web-api/src/main/java/com/jeesite/modules/app/utils/NetworkUtils.java b/web-api/src/main/java/com/jeesite/modules/app/utils/NetworkUtils.java new file mode 100644 index 00000000..92b819ce --- /dev/null +++ b/web-api/src/main/java/com/jeesite/modules/app/utils/NetworkUtils.java @@ -0,0 +1,58 @@ +package com.jeesite.modules.app.utils; + +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.Socket; + +/** + * 网络连通性检测工具类(支持IPv4和域名) + */ +public class NetworkUtils { + + + // 默认检查端口(保持80作为默认,兼容通用场景) + private static final int DEFAULT_PORT = 80; + // 默认超时时间(3秒,可根据需求调整) + private static final int DEFAULT_TIMEOUT_MS = 3000; + + private NetworkUtils() { + throw new UnsupportedOperationException("工具类不允许实例化"); + } + + /** + * 检查目标主机(IP/域名)的连通性(使用默认端口80) + * + * @param target 目标IP或域名(不可为null/空) + * @return true:可达;false:不可达(含参数无效/检查失败) + */ + public static boolean isNetworkReachable(String target) { + // 调用重载方法,传入默认端口 + return isNetworkReachable(target, DEFAULT_PORT); + } + /** + * 检查目标主机(IP/域名)的连通性(自定义端口) + */ + public static boolean isNetworkReachable(String target, int port) { + // 参数校验:目标为空或端口无效,直接返回不可达 + if (target == null || target.trim().isEmpty() || port < 1 || port > 65535) { + return false; + } + String normalizedTarget = target.trim(); + try { + // 先解析目标地址(只解析一次,避免重复操作) + InetAddress address = InetAddress.getByName(normalizedTarget); + // 优先尝试 Socket 连接指定端口 + try (Socket socket = new Socket()) { + socket.connect(new InetSocketAddress(address, port), DEFAULT_TIMEOUT_MS); + return true; // 端口连接成功,返回可达 + } catch (Exception e) { + // Socket 连接失败,不中断流程,继续执行主机可达性检查 + } + // Socket 失败后,降级检查主机基础可达性(ICMP) + return address.isReachable(DEFAULT_TIMEOUT_MS); + } catch (Exception e) { + // 任何异常(地址解析失败、ICMP 检查失败等)均返回不可达 + return false; + } + } +} \ No newline at end of file diff --git a/web-api/src/main/java/com/jeesite/modules/app/utils/SystemUtil.java b/web-api/src/main/java/com/jeesite/modules/app/utils/SystemUtil.java new file mode 100644 index 00000000..42173705 --- /dev/null +++ b/web-api/src/main/java/com/jeesite/modules/app/utils/SystemUtil.java @@ -0,0 +1,181 @@ +package com.jeesite.modules.app.utils; + + +import com.jcraft.jsch.ChannelExec; +import com.jcraft.jsch.JSch; +import com.jcraft.jsch.Session; +import com.jeesite.modules.app.dao.info.CpuInfo; +import com.jeesite.modules.app.dao.info.DiskInfo; +import com.jeesite.modules.app.dao.info.ServerInfo; +import org.springframework.util.StringUtils; + +import java.io.BufferedReader; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.util.*; + + +public class SystemUtil { + + + // SSH 连接超时时间(毫秒) + private static final int SSH_TIMEOUT = 5000; + + + /** + * 建立SSH连接 + */ + private static Session getSshSession(String host, int port, String username, String password) throws Exception { + JSch jsch = new JSch(); + Session session = jsch.getSession(username, host, port); + session.setPassword(password); + + Properties config = new Properties(); + config.put("StrictHostKeyChecking", "no"); + session.setConfig(config); + + session.setTimeout(SSH_TIMEOUT); + session.connect(); + return session; + } + + /** + * 执行SSH命令并返回结果 + */ + private static String executeCommand(Session session, String command) throws Exception { + ChannelExec channel = (ChannelExec) session.openChannel("exec"); + channel.setCommand(command); + + InputStream in = channel.getInputStream(); + channel.connect(); + + BufferedReader reader = new BufferedReader( + new InputStreamReader(in, StandardCharsets.UTF_8) + ); + StringBuilder result = new StringBuilder(); + String line; + while ((line = reader.readLine()) != null) { + result.append(line).append("\n"); + } + + reader.close(); + channel.disconnect(); + return result.toString().trim(); + } + + + /** + * 获取服务器基础信息 + */ + public static ServerInfo getServerBasicInfo(String host, int port, String username, String password, String ipAddress) throws Exception { + ServerInfo info = new ServerInfo(); + Session session = null; + try { + session = getSshSession(host, port, username, password); + + // 主机运行时间 + String uptimeResult = executeCommand(session, "uptime"); + if (StringUtils.hasText(uptimeResult)) { + String[] parts = uptimeResult.split("up "); + if (parts.length > 1) { + info.setUptime(parts[1].split(", ")[0]); + } + } + + // 操作系统 + String osResult = executeCommand(session, "cat /etc/os-release | grep PRETTY_NAME | cut -d'=' -f2 | tr -d '\"'"); + info.setOs(osResult.isEmpty() ? "Unknown" : osResult); + + // 内核版本 + info.setKernelVersion(executeCommand(session, "uname -r")); + + // 主机名 + info.setHostname(executeCommand(session, "hostname")); + + // IP地址(传入参数) + info.setIpAddress(ipAddress); + + // CPU型号 + String cpuResult = executeCommand(session, "cat /proc/cpuinfo | grep 'model name' | head -n 1 | cut -d: -f2 | sed -e 's/^ *//'"); + info.setCpuModel(cpuResult.isEmpty() ? "Unknown" : cpuResult); + + // 内存总量 + String memResult = executeCommand(session, "free -h | grep Mem | awk '{print $2}'"); + info.setMemoryTotal(memResult.isEmpty() ? "Unknown" : memResult); + + } finally { + if (session != null && session.isConnected()) { + session.disconnect(); + } + } + return info; + } + + /** + * 获取CPU和内存使用率 + */ + public static CpuInfo getCpuMemUsage(String host, int port, String username, String password) throws Exception { + CpuInfo usage = new CpuInfo(); + Session session = null; + try { + session = getSshSession(host, port, username, password); + + // CPU使用率 + String cpuCommand = "top -bn2 | grep '%Cpu' | tail -n1 | awk '{print 100 - $8}'"; + String cpuResult = executeCommand(session, cpuCommand); + if (StringUtils.hasText(cpuResult)) { + usage.setCpuUsage(Double.parseDouble(cpuResult)); + } + // 内存使用率 + String memCommand = "free | grep Mem | awk '{print $3/$2 * 100}'"; + String memResult = executeCommand(session, memCommand); + if (StringUtils.hasText(memResult)) { + usage.setMemoryUsage(Double.parseDouble(memResult)); + } + + } finally { + if (session != null && session.isConnected()) { + session.disconnect(); + } + } + return usage; + } + + /** + * 获取所有磁盘信息 + */ + public static List getDiskInfos(String host, int port, String username, String password) throws Exception { + List diskInfos = new ArrayList<>(); + Session session = null; + try { + session = getSshSession(host, port, username, password); + // 关键修改:强制英文输出 + 调整字段索引 + String diskCommand = "LANG=en_US.UTF-8 df -h | awk '{print $1, $6, $2, $3, $5}'"; + String diskResult = executeCommand(session, diskCommand); + + if (StringUtils.hasText(diskResult)) { + String[] lines = diskResult.split("\n"); + for (String line : lines) { + // 过滤空行和标题行 + if (line.trim().isEmpty() || line.contains("Filesystem")) { + continue; + } + // 按任意空格分割(兼容多个空格) + String[] parts = line.trim().split("\\s+"); + if (parts.length >= 5) { + String usageStr = parts[4].replace("%", "").trim(); + DiskInfo diskInfo = new DiskInfo(parts[0], parts[1], parts[2], parts[3], Double.parseDouble(usageStr)); + diskInfos.add(diskInfo); + } + } + } + } finally { + if (session != null && session.isConnected()) { + session.disconnect(); + } + } + return diskInfos; + } + +} diff --git a/web-api/src/main/java/com/jeesite/modules/app/utils/vo.java b/web-api/src/main/java/com/jeesite/modules/app/utils/vo.java new file mode 100644 index 00000000..1ab892ec --- /dev/null +++ b/web-api/src/main/java/com/jeesite/modules/app/utils/vo.java @@ -0,0 +1,54 @@ +package com.jeesite.modules.app.utils; + +import com.jeesite.common.codec.AesUtils; + +import java.util.Date; + +public class vo { + + private static final String AES_KEY = "010e06108bb801e85f8738e01c396dd2"; + + /** + * 获取加密Key + */ + public static String getKey() { + return AesUtils.genKeyString(); + } + + + /** + * 加密 + */ + public static String getEncode(String s) { + try { + return AesUtils.encode(s, AES_KEY); + } catch (Exception e) { + System.out.println(e.getMessage()); + return s; + } + } + + /** + * 解密 + */ + public static String getDecode(String s) { + try { + return AesUtils.decode(s, AES_KEY); + } catch (Exception e) { + System.out.println(e.getMessage()); + return s; + } + } + + + /** + * 获取更新时间 + */ + public static Date getUpdateTime(boolean isNew) { + if (isNew) { + return null; + } + return new Date(); + } + +} diff --git a/web-api/src/main/java/com/jeesite/modules/biz/dao/BizDeviceInfoDao.java b/web-api/src/main/java/com/jeesite/modules/biz/dao/BizDeviceInfoDao.java new file mode 100644 index 00000000..559d3309 --- /dev/null +++ b/web-api/src/main/java/com/jeesite/modules/biz/dao/BizDeviceInfoDao.java @@ -0,0 +1,15 @@ +package com.jeesite.modules.biz.dao; + +import com.jeesite.common.dao.CrudDao; +import com.jeesite.common.mybatis.annotation.MyBatisDao; +import com.jeesite.modules.biz.entity.BizDeviceInfo; + +/** + * 磁盘信息DAO接口 + * @author gaoxq + * @version 2025-11-30 + */ +@MyBatisDao(dataSourceName="work") +public interface BizDeviceInfoDao extends CrudDao { + +} \ No newline at end of file diff --git a/web-api/src/main/java/com/jeesite/modules/biz/dao/BizMonitorAccountDao.java b/web-api/src/main/java/com/jeesite/modules/biz/dao/BizMonitorAccountDao.java new file mode 100644 index 00000000..9372aae5 --- /dev/null +++ b/web-api/src/main/java/com/jeesite/modules/biz/dao/BizMonitorAccountDao.java @@ -0,0 +1,15 @@ +package com.jeesite.modules.biz.dao; + +import com.jeesite.common.dao.CrudDao; +import com.jeesite.common.mybatis.annotation.MyBatisDao; +import com.jeesite.modules.biz.entity.BizMonitorAccount; + +/** + * 账号信息DAO接口 + * @author gaoxq + * @version 2025-11-30 + */ +@MyBatisDao(dataSourceName="work") +public interface BizMonitorAccountDao extends CrudDao { + +} \ No newline at end of file diff --git a/web-api/src/main/java/com/jeesite/modules/biz/dao/BizMonitorHostDao.java b/web-api/src/main/java/com/jeesite/modules/biz/dao/BizMonitorHostDao.java new file mode 100644 index 00000000..40d2480c --- /dev/null +++ b/web-api/src/main/java/com/jeesite/modules/biz/dao/BizMonitorHostDao.java @@ -0,0 +1,15 @@ +package com.jeesite.modules.biz.dao; + +import com.jeesite.common.dao.CrudDao; +import com.jeesite.common.mybatis.annotation.MyBatisDao; +import com.jeesite.modules.biz.entity.BizMonitorHost; + +/** + * 主机信息DAO接口 + * @author gaoxq + * @version 2025-11-30 + */ +@MyBatisDao(dataSourceName="work") +public interface BizMonitorHostDao extends CrudDao { + +} \ No newline at end of file diff --git a/web-api/src/main/java/com/jeesite/modules/biz/dao/BizServerInfoDao.java b/web-api/src/main/java/com/jeesite/modules/biz/dao/BizServerInfoDao.java new file mode 100644 index 00000000..f4b8d34c --- /dev/null +++ b/web-api/src/main/java/com/jeesite/modules/biz/dao/BizServerInfoDao.java @@ -0,0 +1,15 @@ +package com.jeesite.modules.biz.dao; + +import com.jeesite.common.dao.CrudDao; +import com.jeesite.common.mybatis.annotation.MyBatisDao; +import com.jeesite.modules.biz.entity.BizServerInfo; + +/** + * 运行信息DAO接口 + * @author gaoxq + * @version 2025-11-30 + */ +@MyBatisDao(dataSourceName="work") +public interface BizServerInfoDao extends CrudDao { + +} \ No newline at end of file diff --git a/web-api/src/main/java/com/jeesite/modules/biz/entity/BizDeviceInfo.java b/web-api/src/main/java/com/jeesite/modules/biz/entity/BizDeviceInfo.java new file mode 100644 index 00000000..b86a656b --- /dev/null +++ b/web-api/src/main/java/com/jeesite/modules/biz/entity/BizDeviceInfo.java @@ -0,0 +1,107 @@ +package com.jeesite.modules.biz.entity; + +import java.io.Serializable; +import java.util.Date; + +import com.jeesite.common.mybatis.annotation.JoinTable; +import com.jeesite.common.mybatis.annotation.JoinTable.Type; +import com.fasterxml.jackson.annotation.JsonFormat; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; +import jakarta.validation.constraints.NotNull; + +import com.jeesite.common.entity.DataEntity; +import com.jeesite.common.mybatis.annotation.Column; +import com.jeesite.common.mybatis.annotation.Table; +import com.jeesite.common.mybatis.mapper.query.QueryType; +import com.jeesite.common.utils.excel.annotation.ExcelField; +import com.jeesite.common.utils.excel.annotation.ExcelField.Align; +import com.jeesite.common.utils.excel.annotation.ExcelFields; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serial; + +/** + * 磁盘信息Entity + * + * @author gaoxq + * @version 2025-11-30 + */ +@EqualsAndHashCode(callSuper = true) +@Table(name = "biz_device_info", alias = "a", label = "磁盘信息信息", columns = { + @Column(name = "create_time", attrName = "createTime", label = "记录时间", isUpdateForce = true), + @Column(name = "id", attrName = "id", label = "主键标识", isPK = true), + @Column(name = "device", attrName = "device", label = "设备名称", isQuery = false), + @Column(name = "mount_point", attrName = "mountPoint", label = "挂载点", isQuery = false), + @Column(name = "total_size", attrName = "totalSize", label = "总容量", isQuery = false), + @Column(name = "used_size", attrName = "usedSize", label = "已使用容量", isQuery = false), + @Column(name = "usage_rate", attrName = "usageRate", label = "使用率", comment = "使用率(%)", isQuery = false), + @Column(name = "last_online_time", attrName = "lastOnlineTime", label = "最后一次检测时间", isQuery = false), + @Column(name = "host_id", attrName = "hostId", label = "主机标识"), +}, joinTable = { + @JoinTable(type = Type.LEFT_JOIN, entity = BizMonitorHost.class, attrName = "this", alias = "b", + on = "a.host_id = b.host_id", + columns = { + @Column(name = "hostname", attrName = "hostname", label = "主机名称"), + @Column(name = "ip_address", attrName = "ipAddress", label = "IP地址"), + @Column(name = "ustatus", attrName = "ustatus", label = "主机状态"), + }), +}, orderBy = "a.create_time DESC" +) +@Data +public class BizDeviceInfo extends DataEntity implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + private Date createTime; // 记录时间 + private String device; // 设备名称 + private String mountPoint; // 挂载点 + private String totalSize; // 总容量 + private String usedSize; // 已使用容量 + private Double usageRate; // 使用率(%) + private Date lastOnlineTime; // 最后一次检测时间 + private String hostId; // 主机标识 + + private String hostname; + private String ipAddress; + private String ustatus; + + + @ExcelFields({ + @ExcelField(title = "记录时间", attrName = "createTime", align = Align.CENTER, sort = 10, dataFormat = "yyyy-MM-dd hh:mm"), + @ExcelField(title = "主键标识", attrName = "id", align = Align.CENTER, sort = 20), + @ExcelField(title = "设备名称", attrName = "device", align = Align.CENTER, sort = 30), + @ExcelField(title = "挂载点", attrName = "mountPoint", align = Align.CENTER, sort = 40), + @ExcelField(title = "总容量", attrName = "totalSize", align = Align.CENTER, sort = 50), + @ExcelField(title = "已使用容量", attrName = "usedSize", align = Align.CENTER, sort = 60), + @ExcelField(title = "使用率", attrName = "usageRate", align = Align.CENTER, sort = 70), + @ExcelField(title = "最后一次检测时间", attrName = "lastOnlineTime", align = Align.CENTER, sort = 80, dataFormat = "yyyy-MM-dd hh:mm"), + @ExcelField(title = "主机名称", attrName = "hostname", align = Align.CENTER, sort = 90), + @ExcelField(title = "主机IP", attrName = "ipAddress", align = Align.CENTER, sort = 90), + }) + public BizDeviceInfo() { + this(null); + } + + public BizDeviceInfo(String id) { + super(id); + } + + public Date getCreateTime_gte() { + return sqlMap.getWhere().getValue("create_time", QueryType.GTE); + } + + public void setCreateTime_gte(Date createTime) { + sqlMap.getWhere().and("create_time", QueryType.GTE, createTime); + } + + public Date getCreateTime_lte() { + return sqlMap.getWhere().getValue("create_time", QueryType.LTE); + } + + public void setCreateTime_lte(Date createTime) { + sqlMap.getWhere().and("create_time", QueryType.LTE, createTime); + } + +} \ No newline at end of file diff --git a/web-api/src/main/java/com/jeesite/modules/biz/entity/BizMonitorAccount.java b/web-api/src/main/java/com/jeesite/modules/biz/entity/BizMonitorAccount.java new file mode 100644 index 00000000..f9d47ee2 --- /dev/null +++ b/web-api/src/main/java/com/jeesite/modules/biz/entity/BizMonitorAccount.java @@ -0,0 +1,116 @@ +package com.jeesite.modules.biz.entity; + +import java.io.Serializable; +import java.util.Date; + +import com.jeesite.common.mybatis.annotation.JoinTable; +import com.jeesite.common.mybatis.annotation.JoinTable.Type; + +import com.jeesite.common.entity.DataEntity; +import com.jeesite.common.mybatis.annotation.Column; +import com.jeesite.common.mybatis.annotation.Table; +import com.jeesite.common.mybatis.mapper.query.QueryType; +import com.jeesite.common.utils.excel.annotation.ExcelField; +import com.jeesite.common.utils.excel.annotation.ExcelField.Align; +import com.jeesite.common.utils.excel.annotation.ExcelFields; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serial; + +/** + * 账号信息Entity + * + * @author gaoxq + * @version 2025-11-30 + */ +@EqualsAndHashCode(callSuper = true) +@Table(name = "biz_monitor_account", alias = "a", label = "账号信息信息", columns = { + @Column(name = "create_time", attrName = "createTime", label = "记录时间", isUpdate = false, isUpdateForce = true), + @Column(name = "account_id", attrName = "accountId", label = "唯一标识", isPK = true), + @Column(name = "host_id", attrName = "hostId", label = "主机标识"), + @Column(name = "ssh_username", attrName = "sshUsername", label = "登录账号"), + @Column(name = "ssh_password", attrName = "sshPassword", label = "登录密码", isQuery = false), + @Column(name = "ssh_port", attrName = "sshPort", label = "登录端口", isQuery = false), + @Column(name = "initial_path", attrName = "initialPath", label = "初始目录", queryType = QueryType.LIKE), + @Column(name = "timeout_seconds", attrName = "timeoutSeconds", label = "超时时间", isQuery = false), + @Column(name = "ustatus", attrName = "ustatus", label = "账号状态"), + @Column(name = "remark", attrName = "remark", label = "备注信息", queryType = QueryType.LIKE), + @Column(name = "update_time", attrName = "updateTime", label = "更新时间", isQuery = false, isUpdateForce = true), + @Column(name = "f_tenant_id", attrName = "ftenantId", label = "租户id", isUpdate = false, isQuery = false), + @Column(name = "f_flow_id", attrName = "fflowId", label = "流程id", isUpdate = false, isQuery = false), + @Column(name = "f_flow_task_id", attrName = "fflowTaskId", label = "流程任务主键", isUpdate = false, isQuery = false), + @Column(name = "f_flow_state", attrName = "fflowState", label = "流程任务状态", isUpdate = false, isQuery = false, isUpdateForce = true), +}, joinTable = { + @JoinTable(type = Type.LEFT_JOIN, entity = BizMonitorHost.class, attrName = "this", alias = "b", + on = "a.host_id = b.host_id", + columns = { + @Column(name = "hostname", attrName = "hostname", label = "主机名称"), + @Column(name = "ip_address", attrName = "ipAddress", label = "IP地址"), + }), +}, orderBy = "a.create_time DESC" +) +@Data +public class BizMonitorAccount extends DataEntity implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + private Date createTime; // 记录时间 + private String accountId; // 唯一标识 + private String hostId; // 主机标识 + private String sshUsername; // 登录账号 + private String sshPassword; // 登录密码 + private Integer sshPort; // 登录端口 + private String initialPath; // 初始目录 + private Integer timeoutSeconds; // 超时时间 + private String ustatus; // 账号状态 + private String remark; // 备注信息 + private Date updateTime; // 更新时间 + private String ftenantId; // 租户id + private String fflowId; // 流程id + private String fflowTaskId; // 流程任务主键 + private Integer fflowState; // 流程任务状态 + + private String hostname; + private String ipAddress; + + + @ExcelFields({ + @ExcelField(title = "记录时间", attrName = "createTime", align = Align.CENTER, sort = 10, dataFormat = "yyyy-MM-dd hh:mm"), + @ExcelField(title = "唯一标识", attrName = "accountId", align = Align.CENTER, sort = 20), + @ExcelField(title = "主机IP", attrName = "ipAddress", align = Align.CENTER, sort = 30), + @ExcelField(title = "主机名称", attrName = "hostname", align = Align.CENTER, sort = 30), + @ExcelField(title = "登录账号", attrName = "sshUsername", align = Align.CENTER, sort = 40), + @ExcelField(title = "登录密码", attrName = "sshPassword", align = Align.CENTER, sort = 50), + @ExcelField(title = "登录端口", attrName = "sshPort", align = Align.CENTER, sort = 60), + @ExcelField(title = "初始目录", attrName = "initialPath", align = Align.CENTER, sort = 70), + @ExcelField(title = "超时时间", attrName = "timeoutSeconds", align = Align.CENTER, sort = 80), + @ExcelField(title = "账号状态", attrName = "ustatus", align = Align.CENTER, sort = 90), + @ExcelField(title = "备注信息", attrName = "remark", align = Align.CENTER, sort = 100), + @ExcelField(title = "更新时间", attrName = "updateTime", align = Align.CENTER, sort = 110, dataFormat = "yyyy-MM-dd hh:mm"), + }) + public BizMonitorAccount() { + this(null); + } + + public BizMonitorAccount(String id) { + super(id); + } + + public Date getCreateTime_gte() { + return sqlMap.getWhere().getValue("create_time", QueryType.GTE); + } + + public void setCreateTime_gte(Date createTime) { + sqlMap.getWhere().and("create_time", QueryType.GTE, createTime); + } + + public Date getCreateTime_lte() { + return sqlMap.getWhere().getValue("create_time", QueryType.LTE); + } + + public void setCreateTime_lte(Date createTime) { + sqlMap.getWhere().and("create_time", QueryType.LTE, createTime); + } + +} \ No newline at end of file diff --git a/web-api/src/main/java/com/jeesite/modules/biz/entity/BizMonitorHost.java b/web-api/src/main/java/com/jeesite/modules/biz/entity/BizMonitorHost.java new file mode 100644 index 00000000..982d4de8 --- /dev/null +++ b/web-api/src/main/java/com/jeesite/modules/biz/entity/BizMonitorHost.java @@ -0,0 +1,130 @@ +package com.jeesite.modules.biz.entity; + +import java.io.Serializable; +import java.util.Date; + +import com.jeesite.common.mybatis.annotation.JoinTable; +import com.jeesite.common.mybatis.annotation.JoinTable.Type; +import com.fasterxml.jackson.annotation.JsonFormat; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; +import jakarta.validation.constraints.NotNull; + +import com.jeesite.common.entity.DataEntity; +import com.jeesite.common.mybatis.annotation.Column; +import com.jeesite.common.mybatis.annotation.Table; +import com.jeesite.common.mybatis.mapper.query.QueryType; +import com.jeesite.common.utils.excel.annotation.ExcelField; +import com.jeesite.common.utils.excel.annotation.ExcelField.Align; +import com.jeesite.common.utils.excel.annotation.ExcelFields; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serial; + +/** + * 主机信息Entity + * + * @author gaoxq + * @version 2025-11-30 + */ +@EqualsAndHashCode(callSuper = true) +@Table(name = "biz_monitor_host", alias = "a", label = "主机信息信息", columns = { + @Column(name = "create_time", attrName = "createTime", label = "记录时间", isUpdate = false, isUpdateForce = true), + @Column(name = "host_id", attrName = "hostId", label = "唯一标识", isPK = true), + @Column(name = "hostname", attrName = "hostname", label = "主机名称", queryType = QueryType.LIKE), + @Column(name = "ip_address", attrName = "ipAddress", label = "IP地址"), + @Column(name = "host_type", attrName = "hostType", label = "主机类型"), + @Column(name = "host_os", attrName = "hostOs", label = "操作系统"), + @Column(name = "ustatus", attrName = "ustatus", label = "主机状态"), + @Column(name = "last_online_time", attrName = "lastOnlineTime", label = "监测运行时间"), + @Column(name = "location_name", attrName = "locationName", label = "物理位置", queryType = QueryType.LIKE), + @Column(name = "location_type", attrName = "locationType", label = "地址类型"), + @Column(name = "admin_user", attrName = "adminUser", label = "管理人员"), + @Column(name = "other_contact", attrName = "otherContact", label = "联系方式", isQuery = false), + @Column(name = "remark", attrName = "remark", label = "备注信息", queryType = QueryType.LIKE), + @Column(name = "update_time", attrName = "updateTime", label = "更新时间", isQuery = false, isUpdateForce = true), + @Column(name = "expiry_date", attrName = "expiryDate", label = "失效日期", isQuery = false), + @Column(name = "f_tenant_id", attrName = "ftenantId", label = "租户id", isUpdate = false, isQuery = false), + @Column(name = "f_flow_id", attrName = "fflowId", label = "流程id", isUpdate = false, isQuery = false), + @Column(name = "f_flow_task_id", attrName = "fflowTaskId", label = "流程任务主键", isUpdate = false, isQuery = false), + @Column(name = "f_flow_state", attrName = "fflowState", label = "流程任务状态", isUpdate = false, isQuery = false, isUpdateForce = true), +}, joinTable = { + @JoinTable(type = Type.LEFT_JOIN, entity = BizResumeEmployee.class, attrName = "this", alias = "b", + on = "a.admin_user = b.employee_id", + columns = { + @Column(name = "employee_name", attrName = "employeeName", label = "员工姓名"), + @Column(name = "employee_code", attrName = "employeeCode", label = "员工编号"), + }), +}, orderBy = "a.create_time DESC" +) +@Data +public class BizMonitorHost extends DataEntity implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + private Date createTime; // 记录时间 + private String hostId; // 唯一标识 + private String hostname; // 主机名称 + private String ipAddress; // IP地址 + private String hostType; // 主机类型 + private String hostOs; // 操作系统 + private String ustatus; // 主机状态 + private Date lastOnlineTime; // 监测运行时间 + private String locationName; // 物理位置 + private String locationType; // 地址类型 + private String adminUser; // 管理人员 + private String otherContact; // 联系方式 + private String remark; // 备注信息 + private Date updateTime; // 更新时间 + private Date expiryDate; // 失效日期 + private String ftenantId; // 租户id + private String fflowId; // 流程id + private String fflowTaskId; // 流程任务主键 + private Integer fflowState; // 流程任务状态 + + private String employeeName; + private String employeeCode; + + @ExcelFields({ + @ExcelField(title = "记录时间", attrName = "createTime", align = Align.CENTER, sort = 10, dataFormat = "yyyy-MM-dd hh:mm"), + @ExcelField(title = "唯一标识", attrName = "hostId", align = Align.CENTER, sort = 20), + @ExcelField(title = "主机名称", attrName = "hostname", align = Align.CENTER, sort = 30), + @ExcelField(title = "IP地址", attrName = "ipAddress", align = Align.CENTER, sort = 40), + @ExcelField(title = "主机类型", attrName = "hostType", dictType = "host_type", align = Align.CENTER, sort = 50), + @ExcelField(title = "操作系统", attrName = "hostOs", dictType = "host_os", align = Align.CENTER, sort = 60), + @ExcelField(title = "主机状态", attrName = "ustatus", dictType = "host_status", align = Align.CENTER, sort = 70), + @ExcelField(title = "监测运行时间", attrName = "lastOnlineTime", align = Align.CENTER, sort = 80, dataFormat = "yyyy-MM-dd hh:mm"), + @ExcelField(title = "物理位置", attrName = "locationName", align = Align.CENTER, sort = 90), + @ExcelField(title = "地址类型", attrName = "locationType", dictType = "location_type", align = Align.CENTER, sort = 100), + @ExcelField(title = "管理人员", attrName = "employeeName", align = Align.CENTER, sort = 110), + @ExcelField(title = "联系方式", attrName = "otherContact", align = Align.CENTER, sort = 120), + @ExcelField(title = "备注信息", attrName = "remark", align = Align.CENTER, sort = 130), + @ExcelField(title = "更新时间", attrName = "updateTime", align = Align.CENTER, sort = 140, dataFormat = "yyyy-MM-dd hh:mm"), + @ExcelField(title = "失效日期", attrName = "expiryDate", align = Align.CENTER, sort = 150, dataFormat = "yyyy-MM-dd hh:mm"), + }) + public BizMonitorHost() { + this(null); + } + + public BizMonitorHost(String id) { + super(id); + } + + public Date getCreateTime_gte() { + return sqlMap.getWhere().getValue("create_time", QueryType.GTE); + } + + public void setCreateTime_gte(Date createTime) { + sqlMap.getWhere().and("create_time", QueryType.GTE, createTime); + } + + public Date getCreateTime_lte() { + return sqlMap.getWhere().getValue("create_time", QueryType.LTE); + } + + public void setCreateTime_lte(Date createTime) { + sqlMap.getWhere().and("create_time", QueryType.LTE, createTime); + } + +} \ No newline at end of file diff --git a/web-api/src/main/java/com/jeesite/modules/biz/entity/BizServerInfo.java b/web-api/src/main/java/com/jeesite/modules/biz/entity/BizServerInfo.java new file mode 100644 index 00000000..e70445da --- /dev/null +++ b/web-api/src/main/java/com/jeesite/modules/biz/entity/BizServerInfo.java @@ -0,0 +1,115 @@ +package com.jeesite.modules.biz.entity; + +import java.io.Serializable; +import java.util.Date; + +import com.jeesite.common.mybatis.annotation.JoinTable; +import com.jeesite.common.mybatis.annotation.JoinTable.Type; +import com.fasterxml.jackson.annotation.JsonFormat; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; +import jakarta.validation.constraints.NotNull; + +import com.jeesite.common.entity.DataEntity; +import com.jeesite.common.mybatis.annotation.Column; +import com.jeesite.common.mybatis.annotation.Table; +import com.jeesite.common.mybatis.mapper.query.QueryType; +import com.jeesite.common.utils.excel.annotation.ExcelField; +import com.jeesite.common.utils.excel.annotation.ExcelField.Align; +import com.jeesite.common.utils.excel.annotation.ExcelFields; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serial; + +/** + * 运行信息Entity + * + * @author gaoxq + * @version 2025-11-30 + */ +@EqualsAndHashCode(callSuper = true) +@Table(name = "biz_server_info", alias = "a", label = "运行信息信息", columns = { + @Column(name = "create_time", attrName = "createTime", label = "记录时间", isUpdate = false, isUpdateForce = true), + @Column(name = "id", attrName = "id", label = "唯一标识", isPK = true), + @Column(name = "uptime", attrName = "uptime", label = "主机运行时间", isQuery = false), + @Column(name = "os", attrName = "os", label = "操作系统", isQuery = false), + @Column(name = "kernel_version", attrName = "kernelVersion", label = "内核版本", queryType = QueryType.LIKE), + @Column(name = "hostname", attrName = "hostname", label = "主机名称", queryType = QueryType.LIKE), + @Column(name = "ip_address", attrName = "ipAddress", label = "主机地址", queryType = QueryType.LIKE), + @Column(name = "cpu_model", attrName = "cpuModel", label = "CPU型号", queryType = QueryType.LIKE), + @Column(name = "memory_total", attrName = "memoryTotal", label = "内存总量", isQuery = false), + @Column(name = "cpu_usage", attrName = "cpuUsage", label = "CPU使用率", comment = "CPU使用率(%)", isQuery = false), + @Column(name = "memory_usage", attrName = "memoryUsage", label = "内存使用率", comment = "内存使用率(%)", isQuery = false), + @Column(name = "last_online_time", attrName = "lastOnlineTime", label = "最后一次检测时间", isQuery = false), + @Column(name = "host_id", attrName = "hostId", label = "主机标识"), +}, joinTable = { + @JoinTable(type = Type.LEFT_JOIN, entity = BizMonitorHost.class, attrName = "this", alias = "b", + on = "a.host_id = b.host_id", + columns = { + @Column(name = "hostname", attrName = "sysHostname", label = "主机名称"), + @Column(name = "ustatus", attrName = "ustatus", label = "主机状态"), + }), +}, orderBy = "a.create_time DESC" +) +@Data +public class BizServerInfo extends DataEntity implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + private Date createTime; // 记录时间 + private String uptime; // 主机运行时间 + private String os; // 操作系统 + private String kernelVersion; // 内核版本 + private String hostname; // 主机名称 + private String ipAddress; // 主机地址 + private String cpuModel; // CPU型号 + private String memoryTotal; // 内存总量 + private Double cpuUsage; // CPU使用率(%) + private Double memoryUsage; // 内存使用率(%) + private Date lastOnlineTime; // 最后一次检测时间 + private String hostId; // 主机标识 + + private String sysHostname; + private String ustatus; + + @ExcelFields({ + @ExcelField(title = "记录时间", attrName = "createTime", align = Align.CENTER, sort = 10, dataFormat = "yyyy-MM-dd hh:mm"), + @ExcelField(title = "唯一标识", attrName = "id", align = Align.CENTER, sort = 20), + @ExcelField(title = "主机运行时间", attrName = "uptime", align = Align.CENTER, sort = 30), + @ExcelField(title = "操作系统", attrName = "os", align = Align.CENTER, sort = 40), + @ExcelField(title = "内核版本", attrName = "kernelVersion", align = Align.CENTER, sort = 50), + @ExcelField(title = "系统名称", attrName = "hostname", align = Align.CENTER, sort = 60), + @ExcelField(title = "主机地址", attrName = "ipAddress", align = Align.CENTER, sort = 70), + @ExcelField(title = "CPU型号", attrName = "cpuModel", align = Align.CENTER, sort = 80), + @ExcelField(title = "内存总量", attrName = "memoryTotal", align = Align.CENTER, sort = 90), + @ExcelField(title = "CPU使用率", attrName = "cpuUsage", align = Align.CENTER, sort = 100), + @ExcelField(title = "内存使用率", attrName = "memoryUsage", align = Align.CENTER, sort = 110), + @ExcelField(title = "最后一次检测时间", attrName = "lastOnlineTime", align = Align.CENTER, sort = 120, dataFormat = "yyyy-MM-dd hh:mm"), + @ExcelField(title = "主机名称", attrName = "sysHostname", align = Align.CENTER, sort = 130), + }) + public BizServerInfo() { + this(null); + } + + public BizServerInfo(String id) { + super(id); + } + + public Date getCreateTime_gte() { + return sqlMap.getWhere().getValue("create_time", QueryType.GTE); + } + + public void setCreateTime_gte(Date createTime) { + sqlMap.getWhere().and("create_time", QueryType.GTE, createTime); + } + + public Date getCreateTime_lte() { + return sqlMap.getWhere().getValue("create_time", QueryType.LTE); + } + + public void setCreateTime_lte(Date createTime) { + sqlMap.getWhere().and("create_time", QueryType.LTE, createTime); + } + +} \ No newline at end of file diff --git a/web-api/src/main/java/com/jeesite/modules/biz/service/BizDeviceInfoService.java b/web-api/src/main/java/com/jeesite/modules/biz/service/BizDeviceInfoService.java new file mode 100644 index 00000000..84267549 --- /dev/null +++ b/web-api/src/main/java/com/jeesite/modules/biz/service/BizDeviceInfoService.java @@ -0,0 +1,134 @@ +package com.jeesite.modules.biz.service; + +import java.util.List; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.jeesite.common.entity.Page; +import com.jeesite.common.service.CrudService; +import com.jeesite.modules.biz.entity.BizDeviceInfo; +import com.jeesite.modules.biz.dao.BizDeviceInfoDao; +import com.jeesite.common.service.ServiceException; +import com.jeesite.common.config.Global; +import com.jeesite.common.validator.ValidatorUtils; +import com.jeesite.common.utils.excel.ExcelImport; +import org.springframework.web.multipart.MultipartFile; +import jakarta.validation.ConstraintViolation; +import jakarta.validation.ConstraintViolationException; + +/** + * 磁盘信息Service + * @author gaoxq + * @version 2025-11-30 + */ +@Service +public class BizDeviceInfoService extends CrudService { + + /** + * 获取单条数据 + * @param bizDeviceInfo 主键 + */ + @Override + public BizDeviceInfo get(BizDeviceInfo bizDeviceInfo) { + return super.get(bizDeviceInfo); + } + + /** + * 查询分页数据 + * @param bizDeviceInfo 查询条件 + * @param bizDeviceInfo page 分页对象 + */ + @Override + public Page findPage(BizDeviceInfo bizDeviceInfo) { + return super.findPage(bizDeviceInfo); + } + + /** + * 查询列表数据 + * @param bizDeviceInfo 查询条件 + */ + @Override + public List findList(BizDeviceInfo bizDeviceInfo) { + return super.findList(bizDeviceInfo); + } + + /** + * 保存数据(插入或更新) + * @param bizDeviceInfo 数据对象 + */ + @Override + @Transactional + public void save(BizDeviceInfo bizDeviceInfo) { + super.save(bizDeviceInfo); + } + + /** + * 导入数据 + * @param file 导入的数据文件 + */ + @Transactional + public String importData(MultipartFile file) { + if (file == null){ + throw new ServiceException(text("请选择导入的数据文件!")); + } + int successNum = 0; int failureNum = 0; + StringBuilder successMsg = new StringBuilder(); + StringBuilder failureMsg = new StringBuilder(); + try(ExcelImport ei = new ExcelImport(file, 2, 0)){ + List list = ei.getDataList(BizDeviceInfo.class); + for (BizDeviceInfo bizDeviceInfo : list) { + try{ + ValidatorUtils.validateWithException(bizDeviceInfo); + this.save(bizDeviceInfo); + successNum++; + successMsg.append("
" + successNum + "、编号 " + bizDeviceInfo.getId() + " 导入成功"); + } catch (Exception e) { + failureNum++; + String msg = "
" + failureNum + "、编号 " + bizDeviceInfo.getId() + " 导入失败:"; + if (e instanceof ConstraintViolationException){ + ConstraintViolationException cve = (ConstraintViolationException)e; + for (ConstraintViolation violation : cve.getConstraintViolations()) { + msg += Global.getText(violation.getMessage()) + " ("+violation.getPropertyPath()+")"; + } + }else{ + msg += e.getMessage(); + } + failureMsg.append(msg); + logger.error(msg, e); + } + } + } catch (Exception e) { + logger.error(e.getMessage(), e); + failureMsg.append(e.getMessage()); + return failureMsg.toString(); + } + if (failureNum > 0) { + failureMsg.insert(0, "很抱歉,导入失败!共 " + failureNum + " 条数据格式不正确,错误如下:"); + throw new ServiceException(failureMsg.toString()); + }else{ + successMsg.insert(0, "恭喜您,数据已全部导入成功!共 " + successNum + " 条,数据如下:"); + } + return successMsg.toString(); + } + + /** + * 更新状态 + * @param bizDeviceInfo 数据对象 + */ + @Override + @Transactional + public void updateStatus(BizDeviceInfo bizDeviceInfo) { + super.updateStatus(bizDeviceInfo); + } + + /** + * 删除数据 + * @param bizDeviceInfo 数据对象 + */ + @Override + @Transactional + public void delete(BizDeviceInfo bizDeviceInfo) { + super.delete(bizDeviceInfo); + } + +} \ No newline at end of file diff --git a/web-api/src/main/java/com/jeesite/modules/biz/service/BizMonitorAccountService.java b/web-api/src/main/java/com/jeesite/modules/biz/service/BizMonitorAccountService.java new file mode 100644 index 00000000..431dfd2e --- /dev/null +++ b/web-api/src/main/java/com/jeesite/modules/biz/service/BizMonitorAccountService.java @@ -0,0 +1,134 @@ +package com.jeesite.modules.biz.service; + +import java.util.List; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.jeesite.common.entity.Page; +import com.jeesite.common.service.CrudService; +import com.jeesite.modules.biz.entity.BizMonitorAccount; +import com.jeesite.modules.biz.dao.BizMonitorAccountDao; +import com.jeesite.common.service.ServiceException; +import com.jeesite.common.config.Global; +import com.jeesite.common.validator.ValidatorUtils; +import com.jeesite.common.utils.excel.ExcelImport; +import org.springframework.web.multipart.MultipartFile; +import jakarta.validation.ConstraintViolation; +import jakarta.validation.ConstraintViolationException; + +/** + * 账号信息Service + * @author gaoxq + * @version 2025-11-30 + */ +@Service +public class BizMonitorAccountService extends CrudService { + + /** + * 获取单条数据 + * @param bizMonitorAccount 主键 + */ + @Override + public BizMonitorAccount get(BizMonitorAccount bizMonitorAccount) { + return super.get(bizMonitorAccount); + } + + /** + * 查询分页数据 + * @param bizMonitorAccount 查询条件 + * @param bizMonitorAccount page 分页对象 + */ + @Override + public Page findPage(BizMonitorAccount bizMonitorAccount) { + return super.findPage(bizMonitorAccount); + } + + /** + * 查询列表数据 + * @param bizMonitorAccount 查询条件 + */ + @Override + public List findList(BizMonitorAccount bizMonitorAccount) { + return super.findList(bizMonitorAccount); + } + + /** + * 保存数据(插入或更新) + * @param bizMonitorAccount 数据对象 + */ + @Override + @Transactional + public void save(BizMonitorAccount bizMonitorAccount) { + super.save(bizMonitorAccount); + } + + /** + * 导入数据 + * @param file 导入的数据文件 + */ + @Transactional + public String importData(MultipartFile file) { + if (file == null){ + throw new ServiceException(text("请选择导入的数据文件!")); + } + int successNum = 0; int failureNum = 0; + StringBuilder successMsg = new StringBuilder(); + StringBuilder failureMsg = new StringBuilder(); + try(ExcelImport ei = new ExcelImport(file, 2, 0)){ + List list = ei.getDataList(BizMonitorAccount.class); + for (BizMonitorAccount bizMonitorAccount : list) { + try{ + ValidatorUtils.validateWithException(bizMonitorAccount); + this.save(bizMonitorAccount); + successNum++; + successMsg.append("
" + successNum + "、编号 " + bizMonitorAccount.getId() + " 导入成功"); + } catch (Exception e) { + failureNum++; + String msg = "
" + failureNum + "、编号 " + bizMonitorAccount.getId() + " 导入失败:"; + if (e instanceof ConstraintViolationException){ + ConstraintViolationException cve = (ConstraintViolationException)e; + for (ConstraintViolation violation : cve.getConstraintViolations()) { + msg += Global.getText(violation.getMessage()) + " ("+violation.getPropertyPath()+")"; + } + }else{ + msg += e.getMessage(); + } + failureMsg.append(msg); + logger.error(msg, e); + } + } + } catch (Exception e) { + logger.error(e.getMessage(), e); + failureMsg.append(e.getMessage()); + return failureMsg.toString(); + } + if (failureNum > 0) { + failureMsg.insert(0, "很抱歉,导入失败!共 " + failureNum + " 条数据格式不正确,错误如下:"); + throw new ServiceException(failureMsg.toString()); + }else{ + successMsg.insert(0, "恭喜您,数据已全部导入成功!共 " + successNum + " 条,数据如下:"); + } + return successMsg.toString(); + } + + /** + * 更新状态 + * @param bizMonitorAccount 数据对象 + */ + @Override + @Transactional + public void updateStatus(BizMonitorAccount bizMonitorAccount) { + super.updateStatus(bizMonitorAccount); + } + + /** + * 删除数据 + * @param bizMonitorAccount 数据对象 + */ + @Override + @Transactional + public void delete(BizMonitorAccount bizMonitorAccount) { + super.delete(bizMonitorAccount); + } + +} \ No newline at end of file diff --git a/web-api/src/main/java/com/jeesite/modules/biz/service/BizMonitorHostService.java b/web-api/src/main/java/com/jeesite/modules/biz/service/BizMonitorHostService.java new file mode 100644 index 00000000..5eb5890e --- /dev/null +++ b/web-api/src/main/java/com/jeesite/modules/biz/service/BizMonitorHostService.java @@ -0,0 +1,134 @@ +package com.jeesite.modules.biz.service; + +import java.util.List; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.jeesite.common.entity.Page; +import com.jeesite.common.service.CrudService; +import com.jeesite.modules.biz.entity.BizMonitorHost; +import com.jeesite.modules.biz.dao.BizMonitorHostDao; +import com.jeesite.common.service.ServiceException; +import com.jeesite.common.config.Global; +import com.jeesite.common.validator.ValidatorUtils; +import com.jeesite.common.utils.excel.ExcelImport; +import org.springframework.web.multipart.MultipartFile; +import jakarta.validation.ConstraintViolation; +import jakarta.validation.ConstraintViolationException; + +/** + * 主机信息Service + * @author gaoxq + * @version 2025-11-30 + */ +@Service +public class BizMonitorHostService extends CrudService { + + /** + * 获取单条数据 + * @param bizMonitorHost 主键 + */ + @Override + public BizMonitorHost get(BizMonitorHost bizMonitorHost) { + return super.get(bizMonitorHost); + } + + /** + * 查询分页数据 + * @param bizMonitorHost 查询条件 + * @param bizMonitorHost page 分页对象 + */ + @Override + public Page findPage(BizMonitorHost bizMonitorHost) { + return super.findPage(bizMonitorHost); + } + + /** + * 查询列表数据 + * @param bizMonitorHost 查询条件 + */ + @Override + public List findList(BizMonitorHost bizMonitorHost) { + return super.findList(bizMonitorHost); + } + + /** + * 保存数据(插入或更新) + * @param bizMonitorHost 数据对象 + */ + @Override + @Transactional + public void save(BizMonitorHost bizMonitorHost) { + super.save(bizMonitorHost); + } + + /** + * 导入数据 + * @param file 导入的数据文件 + */ + @Transactional + public String importData(MultipartFile file) { + if (file == null){ + throw new ServiceException(text("请选择导入的数据文件!")); + } + int successNum = 0; int failureNum = 0; + StringBuilder successMsg = new StringBuilder(); + StringBuilder failureMsg = new StringBuilder(); + try(ExcelImport ei = new ExcelImport(file, 2, 0)){ + List list = ei.getDataList(BizMonitorHost.class); + for (BizMonitorHost bizMonitorHost : list) { + try{ + ValidatorUtils.validateWithException(bizMonitorHost); + this.save(bizMonitorHost); + successNum++; + successMsg.append("
" + successNum + "、编号 " + bizMonitorHost.getId() + " 导入成功"); + } catch (Exception e) { + failureNum++; + String msg = "
" + failureNum + "、编号 " + bizMonitorHost.getId() + " 导入失败:"; + if (e instanceof ConstraintViolationException){ + ConstraintViolationException cve = (ConstraintViolationException)e; + for (ConstraintViolation violation : cve.getConstraintViolations()) { + msg += Global.getText(violation.getMessage()) + " ("+violation.getPropertyPath()+")"; + } + }else{ + msg += e.getMessage(); + } + failureMsg.append(msg); + logger.error(msg, e); + } + } + } catch (Exception e) { + logger.error(e.getMessage(), e); + failureMsg.append(e.getMessage()); + return failureMsg.toString(); + } + if (failureNum > 0) { + failureMsg.insert(0, "很抱歉,导入失败!共 " + failureNum + " 条数据格式不正确,错误如下:"); + throw new ServiceException(failureMsg.toString()); + }else{ + successMsg.insert(0, "恭喜您,数据已全部导入成功!共 " + successNum + " 条,数据如下:"); + } + return successMsg.toString(); + } + + /** + * 更新状态 + * @param bizMonitorHost 数据对象 + */ + @Override + @Transactional + public void updateStatus(BizMonitorHost bizMonitorHost) { + super.updateStatus(bizMonitorHost); + } + + /** + * 删除数据 + * @param bizMonitorHost 数据对象 + */ + @Override + @Transactional + public void delete(BizMonitorHost bizMonitorHost) { + super.delete(bizMonitorHost); + } + +} \ No newline at end of file diff --git a/web-api/src/main/java/com/jeesite/modules/biz/service/BizServerInfoService.java b/web-api/src/main/java/com/jeesite/modules/biz/service/BizServerInfoService.java new file mode 100644 index 00000000..dbe5913b --- /dev/null +++ b/web-api/src/main/java/com/jeesite/modules/biz/service/BizServerInfoService.java @@ -0,0 +1,134 @@ +package com.jeesite.modules.biz.service; + +import java.util.List; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.jeesite.common.entity.Page; +import com.jeesite.common.service.CrudService; +import com.jeesite.modules.biz.entity.BizServerInfo; +import com.jeesite.modules.biz.dao.BizServerInfoDao; +import com.jeesite.common.service.ServiceException; +import com.jeesite.common.config.Global; +import com.jeesite.common.validator.ValidatorUtils; +import com.jeesite.common.utils.excel.ExcelImport; +import org.springframework.web.multipart.MultipartFile; +import jakarta.validation.ConstraintViolation; +import jakarta.validation.ConstraintViolationException; + +/** + * 运行信息Service + * @author gaoxq + * @version 2025-11-30 + */ +@Service +public class BizServerInfoService extends CrudService { + + /** + * 获取单条数据 + * @param bizServerInfo 主键 + */ + @Override + public BizServerInfo get(BizServerInfo bizServerInfo) { + return super.get(bizServerInfo); + } + + /** + * 查询分页数据 + * @param bizServerInfo 查询条件 + * @param bizServerInfo page 分页对象 + */ + @Override + public Page findPage(BizServerInfo bizServerInfo) { + return super.findPage(bizServerInfo); + } + + /** + * 查询列表数据 + * @param bizServerInfo 查询条件 + */ + @Override + public List findList(BizServerInfo bizServerInfo) { + return super.findList(bizServerInfo); + } + + /** + * 保存数据(插入或更新) + * @param bizServerInfo 数据对象 + */ + @Override + @Transactional + public void save(BizServerInfo bizServerInfo) { + super.save(bizServerInfo); + } + + /** + * 导入数据 + * @param file 导入的数据文件 + */ + @Transactional + public String importData(MultipartFile file) { + if (file == null){ + throw new ServiceException(text("请选择导入的数据文件!")); + } + int successNum = 0; int failureNum = 0; + StringBuilder successMsg = new StringBuilder(); + StringBuilder failureMsg = new StringBuilder(); + try(ExcelImport ei = new ExcelImport(file, 2, 0)){ + List list = ei.getDataList(BizServerInfo.class); + for (BizServerInfo bizServerInfo : list) { + try{ + ValidatorUtils.validateWithException(bizServerInfo); + this.save(bizServerInfo); + successNum++; + successMsg.append("
" + successNum + "、编号 " + bizServerInfo.getId() + " 导入成功"); + } catch (Exception e) { + failureNum++; + String msg = "
" + failureNum + "、编号 " + bizServerInfo.getId() + " 导入失败:"; + if (e instanceof ConstraintViolationException){ + ConstraintViolationException cve = (ConstraintViolationException)e; + for (ConstraintViolation violation : cve.getConstraintViolations()) { + msg += Global.getText(violation.getMessage()) + " ("+violation.getPropertyPath()+")"; + } + }else{ + msg += e.getMessage(); + } + failureMsg.append(msg); + logger.error(msg, e); + } + } + } catch (Exception e) { + logger.error(e.getMessage(), e); + failureMsg.append(e.getMessage()); + return failureMsg.toString(); + } + if (failureNum > 0) { + failureMsg.insert(0, "很抱歉,导入失败!共 " + failureNum + " 条数据格式不正确,错误如下:"); + throw new ServiceException(failureMsg.toString()); + }else{ + successMsg.insert(0, "恭喜您,数据已全部导入成功!共 " + successNum + " 条,数据如下:"); + } + return successMsg.toString(); + } + + /** + * 更新状态 + * @param bizServerInfo 数据对象 + */ + @Override + @Transactional + public void updateStatus(BizServerInfo bizServerInfo) { + super.updateStatus(bizServerInfo); + } + + /** + * 删除数据 + * @param bizServerInfo 数据对象 + */ + @Override + @Transactional + public void delete(BizServerInfo bizServerInfo) { + super.delete(bizServerInfo); + } + +} \ No newline at end of file diff --git a/web-api/src/main/java/com/jeesite/modules/biz/web/BizDeviceInfoController.java b/web-api/src/main/java/com/jeesite/modules/biz/web/BizDeviceInfoController.java new file mode 100644 index 00000000..e9fa574b --- /dev/null +++ b/web-api/src/main/java/com/jeesite/modules/biz/web/BizDeviceInfoController.java @@ -0,0 +1,146 @@ +package com.jeesite.modules.biz.web; + +import java.util.List; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +import org.apache.shiro.authz.annotation.RequiresPermissions; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseBody; + +import com.jeesite.common.config.Global; +import com.jeesite.common.collect.ListUtils; +import com.jeesite.common.entity.Page; +import com.jeesite.common.lang.DateUtils; +import com.jeesite.common.utils.excel.ExcelExport; +import com.jeesite.common.utils.excel.annotation.ExcelField.Type; +import org.springframework.web.multipart.MultipartFile; +import com.jeesite.common.web.BaseController; +import com.jeesite.modules.biz.entity.BizDeviceInfo; +import com.jeesite.modules.biz.service.BizDeviceInfoService; + +/** + * 磁盘信息Controller + * @author gaoxq + * @version 2025-11-30 + */ +@Controller +@RequestMapping(value = "${adminPath}/biz/deviceInfo") +public class BizDeviceInfoController extends BaseController { + + private final BizDeviceInfoService bizDeviceInfoService; + + public BizDeviceInfoController(BizDeviceInfoService bizDeviceInfoService) { + this.bizDeviceInfoService = bizDeviceInfoService; + } + + /** + * 获取数据 + */ + @ModelAttribute + public BizDeviceInfo get(String id, boolean isNewRecord) { + return bizDeviceInfoService.get(id, isNewRecord); + } + + /** + * 查询列表 + */ + @RequiresPermissions("biz:deviceInfo:view") + @RequestMapping(value = {"list", ""}) + public String list(BizDeviceInfo bizDeviceInfo, Model model) { + model.addAttribute("bizDeviceInfo", bizDeviceInfo); + return "modules/biz/bizDeviceInfoList"; + } + + /** + * 查询列表数据 + */ + @RequiresPermissions("biz:deviceInfo:view") + @RequestMapping(value = "listData") + @ResponseBody + public Page listData(BizDeviceInfo bizDeviceInfo, HttpServletRequest request, HttpServletResponse response) { + bizDeviceInfo.setPage(new Page<>(request, response)); + Page page = bizDeviceInfoService.findPage(bizDeviceInfo); + return page; + } + + /** + * 查看编辑表单 + */ + @RequiresPermissions("biz:deviceInfo:view") + @RequestMapping(value = "form") + public String form(BizDeviceInfo bizDeviceInfo, Model model) { + model.addAttribute("bizDeviceInfo", bizDeviceInfo); + return "modules/biz/bizDeviceInfoForm"; + } + + /** + * 保存数据 + */ + @RequiresPermissions("biz:deviceInfo:edit") + @PostMapping(value = "save") + @ResponseBody + public String save(@Validated BizDeviceInfo bizDeviceInfo) { + bizDeviceInfoService.save(bizDeviceInfo); + return renderResult(Global.TRUE, text("保存磁盘信息成功!")); + } + + /** + * 导出数据 + */ + @RequiresPermissions("biz:deviceInfo:view") + @RequestMapping(value = "exportData") + public void exportData(BizDeviceInfo bizDeviceInfo, HttpServletResponse response) { + List list = bizDeviceInfoService.findList(bizDeviceInfo); + String fileName = "磁盘信息" + DateUtils.getDate("yyyyMMddHHmmss") + ".xlsx"; + try(ExcelExport ee = new ExcelExport("磁盘信息", BizDeviceInfo.class)){ + ee.setDataList(list).write(response, fileName); + } + } + + /** + * 下载模板 + */ + @RequiresPermissions("biz:deviceInfo:view") + @RequestMapping(value = "importTemplate") + public void importTemplate(HttpServletResponse response) { + BizDeviceInfo bizDeviceInfo = new BizDeviceInfo(); + List list = ListUtils.newArrayList(bizDeviceInfo); + String fileName = "磁盘信息模板.xlsx"; + try(ExcelExport ee = new ExcelExport("磁盘信息", BizDeviceInfo.class, Type.IMPORT)){ + ee.setDataList(list).write(response, fileName); + } + } + + /** + * 导入数据 + */ + @ResponseBody + @RequiresPermissions("biz:deviceInfo:edit") + @PostMapping(value = "importData") + public String importData(MultipartFile file) { + try { + String message = bizDeviceInfoService.importData(file); + return renderResult(Global.TRUE, "posfull:"+message); + } catch (Exception ex) { + return renderResult(Global.FALSE, "posfull:"+ex.getMessage()); + } + } + + /** + * 删除数据 + */ + @RequiresPermissions("biz:deviceInfo:edit") + @RequestMapping(value = "delete") + @ResponseBody + public String delete(BizDeviceInfo bizDeviceInfo) { + bizDeviceInfoService.delete(bizDeviceInfo); + return renderResult(Global.TRUE, text("删除磁盘信息成功!")); + } + +} \ No newline at end of file diff --git a/web-api/src/main/java/com/jeesite/modules/biz/web/BizListItemController.java b/web-api/src/main/java/com/jeesite/modules/biz/web/BizListItemController.java index fdfc410c..9040d667 100644 --- a/web-api/src/main/java/com/jeesite/modules/biz/web/BizListItemController.java +++ b/web-api/src/main/java/com/jeesite/modules/biz/web/BizListItemController.java @@ -1,20 +1,18 @@ package com.jeesite.modules.biz.web; -import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Objects; import java.util.stream.Collectors; -import com.jeesite.modules.dao.TabItem; -import com.jeesite.modules.dict.NotifyType; +import com.jeesite.modules.app.dao.TabItem; +import com.jeesite.modules.app.dict.NotifyType; import com.jeesite.modules.sys.entity.User; import com.jeesite.modules.sys.utils.UserUtils; -import com.jeesite.modules.utils.IpUtils; +import com.jeesite.modules.app.utils.IpUtils; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; -import org.apache.poi.ss.formula.atp.Switch; import org.apache.shiro.authz.annotation.RequiresPermissions; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; diff --git a/web-api/src/main/java/com/jeesite/modules/biz/web/BizMonitorAccountController.java b/web-api/src/main/java/com/jeesite/modules/biz/web/BizMonitorAccountController.java new file mode 100644 index 00000000..46705dcc --- /dev/null +++ b/web-api/src/main/java/com/jeesite/modules/biz/web/BizMonitorAccountController.java @@ -0,0 +1,159 @@ +package com.jeesite.modules.biz.web; + +import java.util.Date; +import java.util.List; + +import com.jeesite.modules.app.utils.vo; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +import org.apache.shiro.authz.annotation.RequiresPermissions; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseBody; + +import com.jeesite.common.config.Global; +import com.jeesite.common.collect.ListUtils; +import com.jeesite.common.entity.Page; +import com.jeesite.common.lang.DateUtils; +import com.jeesite.common.utils.excel.ExcelExport; +import com.jeesite.common.utils.excel.annotation.ExcelField.Type; +import org.springframework.web.multipart.MultipartFile; +import com.jeesite.common.web.BaseController; +import com.jeesite.modules.biz.entity.BizMonitorAccount; +import com.jeesite.modules.biz.service.BizMonitorAccountService; + +/** + * 账号信息Controller + * + * @author gaoxq + * @version 2025-11-30 + */ +@Controller +@RequestMapping(value = "${adminPath}/biz/monitorAccount") +public class BizMonitorAccountController extends BaseController { + + private final BizMonitorAccountService bizMonitorAccountService; + + public BizMonitorAccountController(BizMonitorAccountService bizMonitorAccountService) { + this.bizMonitorAccountService = bizMonitorAccountService; + } + + /** + * 获取数据 + */ + @ModelAttribute + public BizMonitorAccount get(String accountId, boolean isNewRecord) { + return bizMonitorAccountService.get(accountId, isNewRecord); + } + + /** + * 查询列表 + */ + @RequiresPermissions("biz:monitorAccount:view") + @RequestMapping(value = {"list", ""}) + public String list(BizMonitorAccount bizMonitorAccount, Model model) { + model.addAttribute("bizMonitorAccount", bizMonitorAccount); + return "modules/biz/bizMonitorAccountList"; + } + + /** + * 查询列表数据 + */ + @RequiresPermissions("biz:monitorAccount:view") + @RequestMapping(value = "listData") + @ResponseBody + public Page listData(BizMonitorAccount bizMonitorAccount, HttpServletRequest request, HttpServletResponse response) { + bizMonitorAccount.setPage(new Page<>(request, response)); + Page page = bizMonitorAccountService.findPage(bizMonitorAccount); + return page; + } + + /** + * 查看编辑表单 + */ + @RequiresPermissions("biz:monitorAccount:view") + @RequestMapping(value = "form") + public String form(BizMonitorAccount bizMonitorAccount, Model model) { + model.addAttribute("bizMonitorAccount", bizMonitorAccount); + return "modules/biz/bizMonitorAccountForm"; + } + + /** + * 保存数据 + */ + @RequiresPermissions("biz:monitorAccount:edit") + @PostMapping(value = "save") + @ResponseBody + public String save(@Validated BizMonitorAccount bizMonitorAccount) { + if (bizMonitorAccount.getIsNewRecord()) { + bizMonitorAccount.setSshPassword(vo.getEncode(bizMonitorAccount.getSshPassword())); + } else { + bizMonitorAccount.setUpdateTime(new Date()); + BizMonitorAccount account = bizMonitorAccountService.get(bizMonitorAccount.getAccountId()); + if (!account.getSshPassword().equals(bizMonitorAccount.getSshPassword())) { + bizMonitorAccount.setSshPassword(vo.getEncode(bizMonitorAccount.getSshPassword())); + } + } + bizMonitorAccountService.save(bizMonitorAccount); + return renderResult(Global.TRUE, text("保存账号信息成功!")); + } + + /** + * 导出数据 + */ + @RequiresPermissions("biz:monitorAccount:view") + @RequestMapping(value = "exportData") + public void exportData(BizMonitorAccount bizMonitorAccount, HttpServletResponse response) { + List list = bizMonitorAccountService.findList(bizMonitorAccount); + String fileName = "账号信息" + DateUtils.getDate("yyyyMMddHHmmss") + ".xlsx"; + try (ExcelExport ee = new ExcelExport("账号信息", BizMonitorAccount.class)) { + ee.setDataList(list).write(response, fileName); + } + } + + /** + * 下载模板 + */ + @RequiresPermissions("biz:monitorAccount:view") + @RequestMapping(value = "importTemplate") + public void importTemplate(HttpServletResponse response) { + BizMonitorAccount bizMonitorAccount = new BizMonitorAccount(); + List list = ListUtils.newArrayList(bizMonitorAccount); + String fileName = "账号信息模板.xlsx"; + try (ExcelExport ee = new ExcelExport("账号信息", BizMonitorAccount.class, Type.IMPORT)) { + ee.setDataList(list).write(response, fileName); + } + } + + /** + * 导入数据 + */ + @ResponseBody + @RequiresPermissions("biz:monitorAccount:edit") + @PostMapping(value = "importData") + public String importData(MultipartFile file) { + try { + String message = bizMonitorAccountService.importData(file); + return renderResult(Global.TRUE, "posfull:" + message); + } catch (Exception ex) { + return renderResult(Global.FALSE, "posfull:" + ex.getMessage()); + } + } + + /** + * 删除数据 + */ + @RequiresPermissions("biz:monitorAccount:edit") + @RequestMapping(value = "delete") + @ResponseBody + public String delete(BizMonitorAccount bizMonitorAccount) { + bizMonitorAccountService.delete(bizMonitorAccount); + return renderResult(Global.TRUE, text("删除账号信息成功!")); + } + +} \ No newline at end of file diff --git a/web-api/src/main/java/com/jeesite/modules/biz/web/BizMonitorHostController.java b/web-api/src/main/java/com/jeesite/modules/biz/web/BizMonitorHostController.java new file mode 100644 index 00000000..a6cbbbb1 --- /dev/null +++ b/web-api/src/main/java/com/jeesite/modules/biz/web/BizMonitorHostController.java @@ -0,0 +1,150 @@ +package com.jeesite.modules.biz.web; + +import java.util.List; + +import com.jeesite.modules.app.utils.vo; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +import org.apache.shiro.authz.annotation.RequiresPermissions; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseBody; + +import com.jeesite.common.config.Global; +import com.jeesite.common.collect.ListUtils; +import com.jeesite.common.entity.Page; +import com.jeesite.common.lang.DateUtils; +import com.jeesite.common.utils.excel.ExcelExport; +import com.jeesite.common.utils.excel.annotation.ExcelField.Type; +import org.springframework.web.multipart.MultipartFile; +import com.jeesite.common.web.BaseController; +import com.jeesite.modules.biz.entity.BizMonitorHost; +import com.jeesite.modules.biz.service.BizMonitorHostService; + +/** + * 主机信息Controller + * + * @author gaoxq + * @version 2025-11-30 + */ +@Controller +@RequestMapping(value = "${adminPath}/biz/monitorHost") +public class BizMonitorHostController extends BaseController { + + private final BizMonitorHostService bizMonitorHostService; + + public BizMonitorHostController(BizMonitorHostService bizMonitorHostService) { + this.bizMonitorHostService = bizMonitorHostService; + } + + /** + * 获取数据 + */ + @ModelAttribute + public BizMonitorHost get(String hostId, boolean isNewRecord) { + return bizMonitorHostService.get(hostId, isNewRecord); + } + + /** + * 查询列表 + */ + @RequiresPermissions("biz:monitorHost:view") + @RequestMapping(value = {"list", ""}) + public String list(BizMonitorHost bizMonitorHost, Model model) { + model.addAttribute("bizMonitorHost", bizMonitorHost); + return "modules/biz/bizMonitorHostList"; + } + + /** + * 查询列表数据 + */ + @RequiresPermissions("biz:monitorHost:view") + @RequestMapping(value = "listData") + @ResponseBody + public Page listData(BizMonitorHost bizMonitorHost, HttpServletRequest request, HttpServletResponse response) { + bizMonitorHost.setPage(new Page<>(request, response)); + Page page = bizMonitorHostService.findPage(bizMonitorHost); + return page; + } + + /** + * 查看编辑表单 + */ + @RequiresPermissions("biz:monitorHost:view") + @RequestMapping(value = "form") + public String form(BizMonitorHost bizMonitorHost, Model model) { + model.addAttribute("bizMonitorHost", bizMonitorHost); + return "modules/biz/bizMonitorHostForm"; + } + + /** + * 保存数据 + */ + @RequiresPermissions("biz:monitorHost:edit") + @PostMapping(value = "save") + @ResponseBody + public String save(@Validated BizMonitorHost bizMonitorHost) { + bizMonitorHost.setUpdateTime(vo.getUpdateTime(bizMonitorHost.getIsNewRecord())); + bizMonitorHostService.save(bizMonitorHost); + return renderResult(Global.TRUE, text("保存主机信息成功!")); + } + + /** + * 导出数据 + */ + @RequiresPermissions("biz:monitorHost:view") + @RequestMapping(value = "exportData") + public void exportData(BizMonitorHost bizMonitorHost, HttpServletResponse response) { + List list = bizMonitorHostService.findList(bizMonitorHost); + String fileName = "主机信息" + DateUtils.getDate("yyyyMMddHHmmss") + ".xlsx"; + try (ExcelExport ee = new ExcelExport("主机信息", BizMonitorHost.class)) { + ee.setDataList(list).write(response, fileName); + } + } + + /** + * 下载模板 + */ + @RequiresPermissions("biz:monitorHost:view") + @RequestMapping(value = "importTemplate") + public void importTemplate(HttpServletResponse response) { + BizMonitorHost bizMonitorHost = new BizMonitorHost(); + List list = ListUtils.newArrayList(bizMonitorHost); + String fileName = "主机信息模板.xlsx"; + try (ExcelExport ee = new ExcelExport("主机信息", BizMonitorHost.class, Type.IMPORT)) { + ee.setDataList(list).write(response, fileName); + } + } + + /** + * 导入数据 + */ + @ResponseBody + @RequiresPermissions("biz:monitorHost:edit") + @PostMapping(value = "importData") + public String importData(MultipartFile file) { + try { + String message = bizMonitorHostService.importData(file); + return renderResult(Global.TRUE, "posfull:" + message); + } catch (Exception ex) { + return renderResult(Global.FALSE, "posfull:" + ex.getMessage()); + } + } + + /** + * 删除数据 + */ + @RequiresPermissions("biz:monitorHost:edit") + @RequestMapping(value = "delete") + @ResponseBody + public String delete(BizMonitorHost bizMonitorHost) { + bizMonitorHostService.delete(bizMonitorHost); + return renderResult(Global.TRUE, text("删除主机信息成功!")); + } + +} \ No newline at end of file diff --git a/web-api/src/main/java/com/jeesite/modules/biz/web/BizServerInfoController.java b/web-api/src/main/java/com/jeesite/modules/biz/web/BizServerInfoController.java new file mode 100644 index 00000000..2a91444f --- /dev/null +++ b/web-api/src/main/java/com/jeesite/modules/biz/web/BizServerInfoController.java @@ -0,0 +1,146 @@ +package com.jeesite.modules.biz.web; + +import java.util.List; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +import org.apache.shiro.authz.annotation.RequiresPermissions; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseBody; + +import com.jeesite.common.config.Global; +import com.jeesite.common.collect.ListUtils; +import com.jeesite.common.entity.Page; +import com.jeesite.common.lang.DateUtils; +import com.jeesite.common.utils.excel.ExcelExport; +import com.jeesite.common.utils.excel.annotation.ExcelField.Type; +import org.springframework.web.multipart.MultipartFile; +import com.jeesite.common.web.BaseController; +import com.jeesite.modules.biz.entity.BizServerInfo; +import com.jeesite.modules.biz.service.BizServerInfoService; + +/** + * 运行信息Controller + * @author gaoxq + * @version 2025-11-30 + */ +@Controller +@RequestMapping(value = "${adminPath}/biz/serverInfo") +public class BizServerInfoController extends BaseController { + + private final BizServerInfoService bizServerInfoService; + + public BizServerInfoController(BizServerInfoService bizServerInfoService) { + this.bizServerInfoService = bizServerInfoService; + } + + /** + * 获取数据 + */ + @ModelAttribute + public BizServerInfo get(String id, boolean isNewRecord) { + return bizServerInfoService.get(id, isNewRecord); + } + + /** + * 查询列表 + */ + @RequiresPermissions("biz:serverInfo:view") + @RequestMapping(value = {"list", ""}) + public String list(BizServerInfo bizServerInfo, Model model) { + model.addAttribute("bizServerInfo", bizServerInfo); + return "modules/biz/bizServerInfoList"; + } + + /** + * 查询列表数据 + */ + @RequiresPermissions("biz:serverInfo:view") + @RequestMapping(value = "listData") + @ResponseBody + public Page listData(BizServerInfo bizServerInfo, HttpServletRequest request, HttpServletResponse response) { + bizServerInfo.setPage(new Page<>(request, response)); + Page page = bizServerInfoService.findPage(bizServerInfo); + return page; + } + + /** + * 查看编辑表单 + */ + @RequiresPermissions("biz:serverInfo:view") + @RequestMapping(value = "form") + public String form(BizServerInfo bizServerInfo, Model model) { + model.addAttribute("bizServerInfo", bizServerInfo); + return "modules/biz/bizServerInfoForm"; + } + + /** + * 保存数据 + */ + @RequiresPermissions("biz:serverInfo:edit") + @PostMapping(value = "save") + @ResponseBody + public String save(@Validated BizServerInfo bizServerInfo) { + bizServerInfoService.save(bizServerInfo); + return renderResult(Global.TRUE, text("保存运行信息成功!")); + } + + /** + * 导出数据 + */ + @RequiresPermissions("biz:serverInfo:view") + @RequestMapping(value = "exportData") + public void exportData(BizServerInfo bizServerInfo, HttpServletResponse response) { + List list = bizServerInfoService.findList(bizServerInfo); + String fileName = "运行信息" + DateUtils.getDate("yyyyMMddHHmmss") + ".xlsx"; + try(ExcelExport ee = new ExcelExport("运行信息", BizServerInfo.class)){ + ee.setDataList(list).write(response, fileName); + } + } + + /** + * 下载模板 + */ + @RequiresPermissions("biz:serverInfo:view") + @RequestMapping(value = "importTemplate") + public void importTemplate(HttpServletResponse response) { + BizServerInfo bizServerInfo = new BizServerInfo(); + List list = ListUtils.newArrayList(bizServerInfo); + String fileName = "运行信息模板.xlsx"; + try(ExcelExport ee = new ExcelExport("运行信息", BizServerInfo.class, Type.IMPORT)){ + ee.setDataList(list).write(response, fileName); + } + } + + /** + * 导入数据 + */ + @ResponseBody + @RequiresPermissions("biz:serverInfo:edit") + @PostMapping(value = "importData") + public String importData(MultipartFile file) { + try { + String message = bizServerInfoService.importData(file); + return renderResult(Global.TRUE, "posfull:"+message); + } catch (Exception ex) { + return renderResult(Global.FALSE, "posfull:"+ex.getMessage()); + } + } + + /** + * 删除数据 + */ + @RequiresPermissions("biz:serverInfo:edit") + @RequestMapping(value = "delete") + @ResponseBody + public String delete(BizServerInfo bizServerInfo) { + bizServerInfoService.delete(bizServerInfo); + return renderResult(Global.TRUE, text("删除运行信息成功!")); + } + +} \ No newline at end of file diff --git a/web-api/src/main/java/com/jeesite/modules/erp/web/ErpTransactionFlowController.java b/web-api/src/main/java/com/jeesite/modules/erp/web/ErpTransactionFlowController.java index d81b2746..e0d94383 100644 --- a/web-api/src/main/java/com/jeesite/modules/erp/web/ErpTransactionFlowController.java +++ b/web-api/src/main/java/com/jeesite/modules/erp/web/ErpTransactionFlowController.java @@ -9,7 +9,7 @@ import com.jeesite.modules.erp.entity.ErpIncome; import com.jeesite.modules.erp.service.ErpAccountService; import com.jeesite.modules.erp.service.ErpExpenseService; import com.jeesite.modules.erp.service.ErpIncomeService; -import com.jeesite.modules.utils.BigDecimalUtils; +import com.jeesite.modules.app.utils.BigDecimalUtils; import jakarta.annotation.Resource; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; diff --git a/web-api/src/main/resources/mappings/modules/biz/BizDeviceInfoDao.xml b/web-api/src/main/resources/mappings/modules/biz/BizDeviceInfoDao.xml new file mode 100644 index 00000000..f848dafc --- /dev/null +++ b/web-api/src/main/resources/mappings/modules/biz/BizDeviceInfoDao.xml @@ -0,0 +1,15 @@ + + + + + + + \ No newline at end of file diff --git a/web-api/src/main/resources/mappings/modules/biz/BizMonitorAccountDao.xml b/web-api/src/main/resources/mappings/modules/biz/BizMonitorAccountDao.xml new file mode 100644 index 00000000..26c275e1 --- /dev/null +++ b/web-api/src/main/resources/mappings/modules/biz/BizMonitorAccountDao.xml @@ -0,0 +1,15 @@ + + + + + + + \ No newline at end of file diff --git a/web-api/src/main/resources/mappings/modules/biz/BizMonitorHostDao.xml b/web-api/src/main/resources/mappings/modules/biz/BizMonitorHostDao.xml new file mode 100644 index 00000000..b506fd91 --- /dev/null +++ b/web-api/src/main/resources/mappings/modules/biz/BizMonitorHostDao.xml @@ -0,0 +1,15 @@ + + + + + + + \ No newline at end of file diff --git a/web-api/src/main/resources/mappings/modules/biz/BizServerInfoDao.xml b/web-api/src/main/resources/mappings/modules/biz/BizServerInfoDao.xml new file mode 100644 index 00000000..1a9e289b --- /dev/null +++ b/web-api/src/main/resources/mappings/modules/biz/BizServerInfoDao.xml @@ -0,0 +1,15 @@ + + + + + + + \ No newline at end of file diff --git a/web-vue/packages/assets/images/login_logo.png b/web-vue/packages/assets/images/login_logo.png new file mode 100644 index 00000000..e165f7fb Binary files /dev/null and b/web-vue/packages/assets/images/login_logo.png differ diff --git a/web-vue/packages/assets/images/login_sutra_1.png b/web-vue/packages/assets/images/login_sutra_1.png new file mode 100644 index 00000000..35fe010a Binary files /dev/null and b/web-vue/packages/assets/images/login_sutra_1.png differ diff --git a/web-vue/packages/biz/api/biz/deviceInfo.ts b/web-vue/packages/biz/api/biz/deviceInfo.ts new file mode 100644 index 00000000..e095d18c --- /dev/null +++ b/web-vue/packages/biz/api/biz/deviceInfo.ts @@ -0,0 +1,51 @@ +/** + * Copyright (c) 2013-Now http://jeesite.com All rights reserved. + * No deletion without permission, or be held responsible to law. + * @author gaoxq + */ +import { defHttp } from '@jeesite/core/utils/http/axios'; +import { useGlobSetting } from '@jeesite/core/hooks/setting'; +import { BasicModel, Page } from '@jeesite/core/api/model/baseModel'; +import { UploadApiResult } from '@jeesite/core/api/sys/upload'; +import { UploadFileParams } from '@jeesite/types/axios'; +import { AxiosProgressEvent } from 'axios'; + +const { ctxPath, adminPath } = useGlobSetting(); + +export interface BizDeviceInfo extends BasicModel { + createTime?: string; // 记录时间 + device: string; // 设备名称 + mountPoint: string; // 挂载点 + totalSize: string; // 总容量 + usedSize: string; // 已使用容量 + usageRate: number; // 使用率(%) + lastOnlineTime: string; // 最后一次检测时间 + hostId: string; // 主机标识 +} + +export const bizDeviceInfoList = (params?: BizDeviceInfo | any) => + defHttp.get({ url: adminPath + '/biz/deviceInfo/list', params }); + +export const bizDeviceInfoListData = (params?: BizDeviceInfo | any) => + defHttp.post>({ url: adminPath + '/biz/deviceInfo/listData', params }); + +export const bizDeviceInfoForm = (params?: BizDeviceInfo | any) => + defHttp.get({ url: adminPath + '/biz/deviceInfo/form', params }); + +export const bizDeviceInfoSave = (params?: any, data?: BizDeviceInfo | any) => + defHttp.postJson({ url: adminPath + '/biz/deviceInfo/save', params, data }); + +export const bizDeviceInfoImportData = ( + params: UploadFileParams, + onUploadProgress: (progressEvent: AxiosProgressEvent) => void, +) => + defHttp.uploadFile( + { + url: ctxPath + adminPath + '/biz/deviceInfo/importData', + onUploadProgress, + }, + params, + ); + +export const bizDeviceInfoDelete = (params?: BizDeviceInfo | any) => + defHttp.get({ url: adminPath + '/biz/deviceInfo/delete', params }); diff --git a/web-vue/packages/biz/api/biz/monitorAccount.ts b/web-vue/packages/biz/api/biz/monitorAccount.ts new file mode 100644 index 00000000..92029ebc --- /dev/null +++ b/web-vue/packages/biz/api/biz/monitorAccount.ts @@ -0,0 +1,58 @@ +/** + * Copyright (c) 2013-Now http://jeesite.com All rights reserved. + * No deletion without permission, or be held responsible to law. + * @author gaoxq + */ +import { defHttp } from '@jeesite/core/utils/http/axios'; +import { useGlobSetting } from '@jeesite/core/hooks/setting'; +import { BasicModel, Page } from '@jeesite/core/api/model/baseModel'; +import { UploadApiResult } from '@jeesite/core/api/sys/upload'; +import { UploadFileParams } from '@jeesite/types/axios'; +import { AxiosProgressEvent } from 'axios'; + +const { ctxPath, adminPath } = useGlobSetting(); + +export interface BizMonitorAccount extends BasicModel { + createTime?: string; // 记录时间 + accountId?: string; // 唯一标识 + hostId: string; // 主机标识 + sshUsername: string; // 登录账号 + sshPassword: string; // 登录密码 + sshPort: number; // 登录端口 + initialPath: string; // 初始目录 + timeoutSeconds: number; // 超时时间 + ustatus: string; // 账号状态 + remark?: string; // 备注信息 + updateTime?: string; // 更新时间 + ftenantId?: string; // 租户id + fflowId?: string; // 流程id + fflowTaskId?: string; // 流程任务主键 + fflowState?: number; // 流程任务状态 +} + +export const bizMonitorAccountList = (params?: BizMonitorAccount | any) => + defHttp.get({ url: adminPath + '/biz/monitorAccount/list', params }); + +export const bizMonitorAccountListData = (params?: BizMonitorAccount | any) => + defHttp.post>({ url: adminPath + '/biz/monitorAccount/listData', params }); + +export const bizMonitorAccountForm = (params?: BizMonitorAccount | any) => + defHttp.get({ url: adminPath + '/biz/monitorAccount/form', params }); + +export const bizMonitorAccountSave = (params?: any, data?: BizMonitorAccount | any) => + defHttp.postJson({ url: adminPath + '/biz/monitorAccount/save', params, data }); + +export const bizMonitorAccountImportData = ( + params: UploadFileParams, + onUploadProgress: (progressEvent: AxiosProgressEvent) => void, +) => + defHttp.uploadFile( + { + url: ctxPath + adminPath + '/biz/monitorAccount/importData', + onUploadProgress, + }, + params, + ); + +export const bizMonitorAccountDelete = (params?: BizMonitorAccount | any) => + defHttp.get({ url: adminPath + '/biz/monitorAccount/delete', params }); diff --git a/web-vue/packages/biz/api/biz/monitorHost.ts b/web-vue/packages/biz/api/biz/monitorHost.ts new file mode 100644 index 00000000..c04b55a9 --- /dev/null +++ b/web-vue/packages/biz/api/biz/monitorHost.ts @@ -0,0 +1,62 @@ +/** + * Copyright (c) 2013-Now http://jeesite.com All rights reserved. + * No deletion without permission, or be held responsible to law. + * @author gaoxq + */ +import { defHttp } from '@jeesite/core/utils/http/axios'; +import { useGlobSetting } from '@jeesite/core/hooks/setting'; +import { BasicModel, Page } from '@jeesite/core/api/model/baseModel'; +import { UploadApiResult } from '@jeesite/core/api/sys/upload'; +import { UploadFileParams } from '@jeesite/types/axios'; +import { AxiosProgressEvent } from 'axios'; + +const { ctxPath, adminPath } = useGlobSetting(); + +export interface BizMonitorHost extends BasicModel { + createTime?: string; // 记录时间 + hostId?: string; // 唯一标识 + hostname: string; // 主机名称 + ipAddress: string; // IP地址 + hostType: string; // 主机类型 + hostOs: string; // 操作系统 + ustatus: string; // 主机状态 + lastOnlineTime?: string; // 监测运行时间 + locationName: string; // 物理位置 + locationType: string; // 地址类型 + adminUser: string; // 管理人员 + otherContact: string; // 联系方式 + remark?: string; // 备注信息 + updateTime?: string; // 更新时间 + expiryDate: string; // 失效日期 + ftenantId?: string; // 租户id + fflowId?: string; // 流程id + fflowTaskId?: string; // 流程任务主键 + fflowState?: number; // 流程任务状态 +} + +export const bizMonitorHostList = (params?: BizMonitorHost | any) => + defHttp.get({ url: adminPath + '/biz/monitorHost/list', params }); + +export const bizMonitorHostListData = (params?: BizMonitorHost | any) => + defHttp.post>({ url: adminPath + '/biz/monitorHost/listData', params }); + +export const bizMonitorHostForm = (params?: BizMonitorHost | any) => + defHttp.get({ url: adminPath + '/biz/monitorHost/form', params }); + +export const bizMonitorHostSave = (params?: any, data?: BizMonitorHost | any) => + defHttp.postJson({ url: adminPath + '/biz/monitorHost/save', params, data }); + +export const bizMonitorHostImportData = ( + params: UploadFileParams, + onUploadProgress: (progressEvent: AxiosProgressEvent) => void, +) => + defHttp.uploadFile( + { + url: ctxPath + adminPath + '/biz/monitorHost/importData', + onUploadProgress, + }, + params, + ); + +export const bizMonitorHostDelete = (params?: BizMonitorHost | any) => + defHttp.get({ url: adminPath + '/biz/monitorHost/delete', params }); diff --git a/web-vue/packages/biz/api/biz/serverInfo.ts b/web-vue/packages/biz/api/biz/serverInfo.ts new file mode 100644 index 00000000..067d62c2 --- /dev/null +++ b/web-vue/packages/biz/api/biz/serverInfo.ts @@ -0,0 +1,55 @@ +/** + * Copyright (c) 2013-Now http://jeesite.com All rights reserved. + * No deletion without permission, or be held responsible to law. + * @author gaoxq + */ +import { defHttp } from '@jeesite/core/utils/http/axios'; +import { useGlobSetting } from '@jeesite/core/hooks/setting'; +import { BasicModel, Page } from '@jeesite/core/api/model/baseModel'; +import { UploadApiResult } from '@jeesite/core/api/sys/upload'; +import { UploadFileParams } from '@jeesite/types/axios'; +import { AxiosProgressEvent } from 'axios'; + +const { ctxPath, adminPath } = useGlobSetting(); + +export interface BizServerInfo extends BasicModel { + createTime?: string; // 记录时间 + uptime: string; // 主机运行时间 + os: string; // 操作系统 + kernelVersion: string; // 内核版本 + hostname: string; // 主机名称 + ipAddress: string; // 主机地址 + cpuModel: string; // CPU型号 + memoryTotal: string; // 内存总量 + cpuUsage: number; // CPU使用率(%) + memoryUsage: number; // 内存使用率(%) + lastOnlineTime: string; // 最后一次检测时间 + hostId: string; // 主机标识 +} + +export const bizServerInfoList = (params?: BizServerInfo | any) => + defHttp.get({ url: adminPath + '/biz/serverInfo/list', params }); + +export const bizServerInfoListData = (params?: BizServerInfo | any) => + defHttp.post>({ url: adminPath + '/biz/serverInfo/listData', params }); + +export const bizServerInfoForm = (params?: BizServerInfo | any) => + defHttp.get({ url: adminPath + '/biz/serverInfo/form', params }); + +export const bizServerInfoSave = (params?: any, data?: BizServerInfo | any) => + defHttp.postJson({ url: adminPath + '/biz/serverInfo/save', params, data }); + +export const bizServerInfoImportData = ( + params: UploadFileParams, + onUploadProgress: (progressEvent: AxiosProgressEvent) => void, +) => + defHttp.uploadFile( + { + url: ctxPath + adminPath + '/biz/serverInfo/importData', + onUploadProgress, + }, + params, + ); + +export const bizServerInfoDelete = (params?: BizServerInfo | any) => + defHttp.get({ url: adminPath + '/biz/serverInfo/delete', params }); diff --git a/web-vue/packages/biz/views/biz/deviceInfo/formImport.vue b/web-vue/packages/biz/views/biz/deviceInfo/formImport.vue new file mode 100644 index 00000000..6e24049c --- /dev/null +++ b/web-vue/packages/biz/views/biz/deviceInfo/formImport.vue @@ -0,0 +1,103 @@ + + + diff --git a/web-vue/packages/biz/views/biz/deviceInfo/list.vue b/web-vue/packages/biz/views/biz/deviceInfo/list.vue new file mode 100644 index 00000000..e4fc39ee --- /dev/null +++ b/web-vue/packages/biz/views/biz/deviceInfo/list.vue @@ -0,0 +1,228 @@ + + + diff --git a/web-vue/packages/biz/views/biz/deviceInfo/select.ts b/web-vue/packages/biz/views/biz/deviceInfo/select.ts new file mode 100644 index 00000000..95d3e1f3 --- /dev/null +++ b/web-vue/packages/biz/views/biz/deviceInfo/select.ts @@ -0,0 +1,126 @@ +import { useI18n } from '@jeesite/core/hooks/web/useI18n'; +import { BasicColumn, BasicTableProps, FormProps } from '@jeesite/core/components/Table'; +import { bizDeviceInfoListData } from '@jeesite/biz/api/biz/deviceInfo'; + +const { t } = useI18n('biz.deviceInfo'); + +const modalProps = { + title: t('磁盘信息选择'), +}; + +const searchForm: FormProps = { + baseColProps: { md: 8, lg: 6 }, + labelWidth: 90, + schemas: [ + { + label: t('记录时间起'), + field: 'createTime_gte', + component: 'DatePicker', + componentProps: { + format: 'YYYY-MM-DD HH:mm', + showTime: { format: 'HH:mm' }, + }, + }, + { + label: t('记录时间止'), + field: 'createTime_lte', + component: 'DatePicker', + componentProps: { + format: 'YYYY-MM-DD HH:mm', + showTime: { format: 'HH:mm' }, + }, + }, + { + label: t('主机标识'), + field: 'hostId', + component: 'Input', + }, + ], +}; + +const tableColumns: BasicColumn[] = [ + { + title: t('记录时间'), + dataIndex: 'createTime', + key: 'a.create_time', + sorter: true, + width: 230, + align: 'left', + slot: 'firstColumn', + }, + { + title: t('设备名称'), + dataIndex: 'device', + key: 'a.device', + sorter: true, + width: 130, + align: 'left', + }, + { + title: t('挂载点'), + dataIndex: 'mountPoint', + key: 'a.mount_point', + sorter: true, + width: 130, + align: 'left', + }, + { + title: t('总容量'), + dataIndex: 'totalSize', + key: 'a.total_size', + sorter: true, + width: 130, + align: 'left', + }, + { + title: t('已使用容量'), + dataIndex: 'usedSize', + key: 'a.used_size', + sorter: true, + width: 130, + align: 'left', + }, + { + title: t('使用率'), + dataIndex: 'usageRate', + key: 'a.usage_rate', + sorter: true, + width: 130, + align: 'right', + }, + { + title: t('最后一次检测时间'), + dataIndex: 'lastOnlineTime', + key: 'a.last_online_time', + sorter: true, + width: 130, + align: 'center', + }, + { + title: t('主机标识'), + dataIndex: 'hostId', + key: 'a.host_id', + sorter: true, + width: 130, + align: 'left', + }, +]; + +const tableProps: BasicTableProps = { + api: bizDeviceInfoListData, + beforeFetch: (params) => { + params['isAll'] = true; + return params; + }, + columns: tableColumns, + formConfig: searchForm, + rowKey: 'id', +}; + +export default { + modalProps, + tableProps, + itemCode: 'id', + itemName: 'id', + isShowCode: false, +}; diff --git a/web-vue/packages/biz/views/biz/monitorAccount/form.vue b/web-vue/packages/biz/views/biz/monitorAccount/form.vue new file mode 100644 index 00000000..9667ba32 --- /dev/null +++ b/web-vue/packages/biz/views/biz/monitorAccount/form.vue @@ -0,0 +1,157 @@ + + + diff --git a/web-vue/packages/biz/views/biz/monitorAccount/formImport.vue b/web-vue/packages/biz/views/biz/monitorAccount/formImport.vue new file mode 100644 index 00000000..2f4711ff --- /dev/null +++ b/web-vue/packages/biz/views/biz/monitorAccount/formImport.vue @@ -0,0 +1,103 @@ + + + diff --git a/web-vue/packages/biz/views/biz/monitorAccount/list.vue b/web-vue/packages/biz/views/biz/monitorAccount/list.vue new file mode 100644 index 00000000..b057744e --- /dev/null +++ b/web-vue/packages/biz/views/biz/monitorAccount/list.vue @@ -0,0 +1,277 @@ + + + diff --git a/web-vue/packages/biz/views/biz/monitorAccount/select.ts b/web-vue/packages/biz/views/biz/monitorAccount/select.ts new file mode 100644 index 00000000..4884d4c6 --- /dev/null +++ b/web-vue/packages/biz/views/biz/monitorAccount/select.ts @@ -0,0 +1,162 @@ +import { useI18n } from '@jeesite/core/hooks/web/useI18n'; +import { BasicColumn, BasicTableProps, FormProps } from '@jeesite/core/components/Table'; +import { bizMonitorAccountListData } from '@jeesite/biz/api/biz/monitorAccount'; + +const { t } = useI18n('biz.monitorAccount'); + +const modalProps = { + title: t('账号信息选择'), +}; + +const searchForm: FormProps = { + baseColProps: { md: 8, lg: 6 }, + labelWidth: 90, + schemas: [ + { + label: t('记录时间起'), + field: 'createTime_gte', + component: 'DatePicker', + componentProps: { + format: 'YYYY-MM-DD HH:mm', + showTime: { format: 'HH:mm' }, + }, + }, + { + label: t('记录时间止'), + field: 'createTime_lte', + component: 'DatePicker', + componentProps: { + format: 'YYYY-MM-DD HH:mm', + showTime: { format: 'HH:mm' }, + }, + }, + { + label: t('主机标识'), + field: 'hostId', + component: 'Input', + }, + { + label: t('登录账号'), + field: 'sshUsername', + component: 'Input', + }, + { + label: t('初始目录'), + field: 'initialPath', + component: 'Input', + }, + { + label: t('账号状态'), + field: 'ustatus', + component: 'Input', + }, + { + label: t('备注信息'), + field: 'remark', + component: 'Input', + }, + ], +}; + +const tableColumns: BasicColumn[] = [ + { + title: t('记录时间'), + dataIndex: 'createTime', + key: 'a.create_time', + sorter: true, + width: 230, + align: 'left', + slot: 'firstColumn', + }, + { + title: t('主机标识'), + dataIndex: 'hostId', + key: 'a.host_id', + sorter: true, + width: 130, + align: 'left', + }, + { + title: t('登录账号'), + dataIndex: 'sshUsername', + key: 'a.ssh_username', + sorter: true, + width: 130, + align: 'left', + }, + { + title: t('登录密码'), + dataIndex: 'sshPassword', + key: 'a.ssh_password', + sorter: true, + width: 130, + align: 'left', + }, + { + title: t('登录端口'), + dataIndex: 'sshPort', + key: 'a.ssh_port', + sorter: true, + width: 130, + align: 'center', + }, + { + title: t('初始目录'), + dataIndex: 'initialPath', + key: 'a.initial_path', + sorter: true, + width: 130, + align: 'left', + }, + { + title: t('超时时间'), + dataIndex: 'timeoutSeconds', + key: 'a.timeout_seconds', + sorter: true, + width: 130, + align: 'center', + }, + { + title: t('账号状态'), + dataIndex: 'ustatus', + key: 'a.ustatus', + sorter: true, + width: 130, + align: 'left', + }, + { + title: t('备注信息'), + dataIndex: 'remark', + key: 'a.remark', + sorter: true, + width: 130, + align: 'left', + }, + { + title: t('更新时间'), + dataIndex: 'updateTime', + key: 'a.update_time', + sorter: true, + width: 130, + align: 'center', + }, +]; + +const tableProps: BasicTableProps = { + api: bizMonitorAccountListData, + beforeFetch: (params) => { + params['isAll'] = true; + return params; + }, + columns: tableColumns, + formConfig: searchForm, + rowKey: 'accountId', +}; + +export default { + modalProps, + tableProps, + itemCode: 'accountId', + itemName: 'accountId', + isShowCode: false, +}; diff --git a/web-vue/packages/biz/views/biz/monitorHost/form.vue b/web-vue/packages/biz/views/biz/monitorHost/form.vue new file mode 100644 index 00000000..7970bdeb --- /dev/null +++ b/web-vue/packages/biz/views/biz/monitorHost/form.vue @@ -0,0 +1,187 @@ + + + diff --git a/web-vue/packages/biz/views/biz/monitorHost/formImport.vue b/web-vue/packages/biz/views/biz/monitorHost/formImport.vue new file mode 100644 index 00000000..29cc768b --- /dev/null +++ b/web-vue/packages/biz/views/biz/monitorHost/formImport.vue @@ -0,0 +1,103 @@ + + + diff --git a/web-vue/packages/biz/views/biz/monitorHost/list.vue b/web-vue/packages/biz/views/biz/monitorHost/list.vue new file mode 100644 index 00000000..8eecf34b --- /dev/null +++ b/web-vue/packages/biz/views/biz/monitorHost/list.vue @@ -0,0 +1,345 @@ + + + diff --git a/web-vue/packages/biz/views/biz/monitorHost/select.ts b/web-vue/packages/biz/views/biz/monitorHost/select.ts new file mode 100644 index 00000000..69e3c6ff --- /dev/null +++ b/web-vue/packages/biz/views/biz/monitorHost/select.ts @@ -0,0 +1,197 @@ +import { useI18n } from '@jeesite/core/hooks/web/useI18n'; +import { BasicColumn, BasicTableProps, FormProps } from '@jeesite/core/components/Table'; +import { BizMonitorHost, bizMonitorHostListData } from '@jeesite/biz/api/biz/monitorHost'; + +const { t } = useI18n('biz.monitorHost'); + +const modalProps = { + title: t('主机信息选择'), +}; + +const searchForm: FormProps = { + baseColProps: { md: 8, lg: 6 }, + labelWidth: 90, + schemas: [ + { + label: t('主机名称'), + field: 'hostname', + component: 'Input', + }, + { + label: t('IP地址'), + field: 'ipAddress', + component: 'Input', + }, + { + label: t('主机类型'), + field: 'hostType', + component: 'Select', + componentProps: { + dictType: 'host_type', + allowClear: true, + }, + }, + { + label: t('操作系统'), + field: 'hostOs', + component: 'Select', + componentProps: { + dictType: 'host_os', + allowClear: true, + }, + }, + { + label: t('主机状态'), + field: 'ustatus', + component: 'Select', + componentProps: { + dictType: 'host_status', + allowClear: true, + }, + }, + { + label: t('物理位置'), + field: 'locationName', + component: 'Input', + }, + { + label: t('地址类型'), + field: 'locationType', + component: 'Select', + componentProps: { + dictType: 'location_type', + allowClear: true, + }, + }, + ], +}; + +const tableColumns: BasicColumn[] = [ + { + title: t('记录时间'), + dataIndex: 'createTime', + key: 'a.create_time', + sorter: true, + width: 180, + align: 'left', + }, + { + title: t('主机名称'), + dataIndex: 'hostname', + key: 'a.hostname', + sorter: true, + width: 130, + align: 'left', + }, + { + title: t('IP地址'), + dataIndex: 'ipAddress', + key: 'a.ip_address', + sorter: true, + width: 130, + align: 'left', + }, + { + title: t('主机类型'), + dataIndex: 'hostType', + key: 'a.host_type', + sorter: true, + width: 130, + align: 'left', + dictType: 'host_type', + }, + { + title: t('操作系统'), + dataIndex: 'hostOs', + key: 'a.host_os', + sorter: true, + width: 130, + align: 'left', + dictType: 'host_os', + }, + { + title: t('主机状态'), + dataIndex: 'ustatus', + key: 'a.ustatus', + sorter: true, + width: 130, + align: 'left', + dictType: 'host_status', + }, + { + title: t('监测运行时间'), + dataIndex: 'lastOnlineTime', + key: 'a.last_online_time', + sorter: true, + width: 180, + align: 'center', + }, + { + title: t('物理位置'), + dataIndex: 'locationName', + key: 'a.location_name', + sorter: true, + width: 130, + align: 'left', + }, + { + title: t('地址类型'), + dataIndex: 'locationType', + key: 'a.location_type', + sorter: true, + width: 130, + align: 'left', + dictType: 'location_type', + }, + { + title: t('联系方式'), + dataIndex: 'otherContact', + key: 'a.other_contact', + sorter: true, + width: 130, + align: 'left', + }, + { + title: t('备注信息'), + dataIndex: 'remark', + key: 'a.remark', + sorter: true, + width: 130, + align: 'left', + }, + { + title: t('更新时间'), + dataIndex: 'updateTime', + key: 'a.update_time', + sorter: true, + width: 180, + align: 'center', + }, + { + title: t('失效日期'), + dataIndex: 'expiryDate', + key: 'a.expiry_date', + sorter: true, + width: 180, + align: 'center', + }, +]; + +const tableProps: BasicTableProps = { + api: bizMonitorHostListData, + beforeFetch: (params) => { + params['isAll'] = true; + return params; + }, + columns: tableColumns, + formConfig: searchForm, + rowKey: 'hostId', +}; + +export default { + modalProps, + tableProps, + itemCode: 'hostId', + itemName: 'hostId', + isShowCode: false, +}; diff --git a/web-vue/packages/biz/views/biz/serverInfo/formImport.vue b/web-vue/packages/biz/views/biz/serverInfo/formImport.vue new file mode 100644 index 00000000..d52194ee --- /dev/null +++ b/web-vue/packages/biz/views/biz/serverInfo/formImport.vue @@ -0,0 +1,103 @@ + + + diff --git a/web-vue/packages/biz/views/biz/serverInfo/list.vue b/web-vue/packages/biz/views/biz/serverInfo/list.vue new file mode 100644 index 00000000..94ea5683 --- /dev/null +++ b/web-vue/packages/biz/views/biz/serverInfo/list.vue @@ -0,0 +1,272 @@ + + + diff --git a/web-vue/packages/biz/views/biz/serverInfo/select.ts b/web-vue/packages/biz/views/biz/serverInfo/select.ts new file mode 100644 index 00000000..83fe387f --- /dev/null +++ b/web-vue/packages/biz/views/biz/serverInfo/select.ts @@ -0,0 +1,178 @@ +import { useI18n } from '@jeesite/core/hooks/web/useI18n'; +import { BasicColumn, BasicTableProps, FormProps } from '@jeesite/core/components/Table'; +import { bizServerInfoListData } from '@jeesite/biz/api/biz/serverInfo'; + +const { t } = useI18n('biz.serverInfo'); + +const modalProps = { + title: t('运行信息选择'), +}; + +const searchForm: FormProps = { + baseColProps: { md: 8, lg: 6 }, + labelWidth: 90, + schemas: [ + { + label: t('记录时间起'), + field: 'createTime_gte', + component: 'DatePicker', + componentProps: { + format: 'YYYY-MM-DD HH:mm', + showTime: { format: 'HH:mm' }, + }, + }, + { + label: t('记录时间止'), + field: 'createTime_lte', + component: 'DatePicker', + componentProps: { + format: 'YYYY-MM-DD HH:mm', + showTime: { format: 'HH:mm' }, + }, + }, + { + label: t('内核版本'), + field: 'kernelVersion', + component: 'Input', + }, + { + label: t('主机名称'), + field: 'hostname', + component: 'Input', + }, + { + label: t('主机地址'), + field: 'ipAddress', + component: 'Input', + }, + { + label: t('CPU型号'), + field: 'cpuModel', + component: 'Input', + }, + { + label: t('主机标识'), + field: 'hostId', + component: 'Input', + }, + ], +}; + +const tableColumns: BasicColumn[] = [ + { + title: t('记录时间'), + dataIndex: 'createTime', + key: 'a.create_time', + sorter: true, + width: 230, + align: 'left', + slot: 'firstColumn', + }, + { + title: t('主机运行时间'), + dataIndex: 'uptime', + key: 'a.uptime', + sorter: true, + width: 130, + align: 'left', + }, + { + title: t('操作系统'), + dataIndex: 'os', + key: 'a.os', + sorter: true, + width: 130, + align: 'left', + }, + { + title: t('内核版本'), + dataIndex: 'kernelVersion', + key: 'a.kernel_version', + sorter: true, + width: 130, + align: 'left', + }, + { + title: t('主机名称'), + dataIndex: 'hostname', + key: 'a.hostname', + sorter: true, + width: 130, + align: 'left', + }, + { + title: t('主机地址'), + dataIndex: 'ipAddress', + key: 'a.ip_address', + sorter: true, + width: 130, + align: 'left', + }, + { + title: t('CPU型号'), + dataIndex: 'cpuModel', + key: 'a.cpu_model', + sorter: true, + width: 130, + align: 'left', + }, + { + title: t('内存总量'), + dataIndex: 'memoryTotal', + key: 'a.memory_total', + sorter: true, + width: 130, + align: 'left', + }, + { + title: t('CPU使用率'), + dataIndex: 'cpuUsage', + key: 'a.cpu_usage', + sorter: true, + width: 130, + align: 'right', + }, + { + title: t('内存使用率'), + dataIndex: 'memoryUsage', + key: 'a.memory_usage', + sorter: true, + width: 130, + align: 'right', + }, + { + title: t('最后一次检测时间'), + dataIndex: 'lastOnlineTime', + key: 'a.last_online_time', + sorter: true, + width: 130, + align: 'center', + }, + { + title: t('主机标识'), + dataIndex: 'hostId', + key: 'a.host_id', + sorter: true, + width: 130, + align: 'left', + }, +]; + +const tableProps: BasicTableProps = { + api: bizServerInfoListData, + beforeFetch: (params) => { + params['isAll'] = true; + return params; + }, + columns: tableColumns, + formConfig: searchForm, + rowKey: 'id', +}; + +export default { + modalProps, + tableProps, + itemCode: 'id', + itemName: 'id', + isShowCode: false, +}; diff --git a/web-vue/packages/core/components/ListSelect/src/selectType/bizHostSelect.ts b/web-vue/packages/core/components/ListSelect/src/selectType/bizHostSelect.ts new file mode 100644 index 00000000..34c52a5a --- /dev/null +++ b/web-vue/packages/core/components/ListSelect/src/selectType/bizHostSelect.ts @@ -0,0 +1,197 @@ +import { useI18n } from '@jeesite/core/hooks/web/useI18n'; +import { BasicColumn, BasicTableProps, FormProps } from '@jeesite/core/components/Table'; +import { BizMonitorHost, bizMonitorHostListData } from '@jeesite/biz/api/biz/monitorHost'; + +const { t } = useI18n('biz.monitorHost'); + +const modalProps = { + title: t('主机信息选择'), +}; + +const searchForm: FormProps = { + baseColProps: { md: 8, lg: 6 }, + labelWidth: 90, + schemas: [ + { + label: t('主机名称'), + field: 'hostname', + component: 'Input', + }, + { + label: t('IP地址'), + field: 'ipAddress', + component: 'Input', + }, + { + label: t('主机类型'), + field: 'hostType', + component: 'Select', + componentProps: { + dictType: 'host_type', + allowClear: true, + }, + }, + { + label: t('操作系统'), + field: 'hostOs', + component: 'Select', + componentProps: { + dictType: 'host_os', + allowClear: true, + }, + }, + { + label: t('主机状态'), + field: 'ustatus', + component: 'Select', + componentProps: { + dictType: 'host_status', + allowClear: true, + }, + }, + { + label: t('物理位置'), + field: 'locationName', + component: 'Input', + }, + { + label: t('地址类型'), + field: 'locationType', + component: 'Select', + componentProps: { + dictType: 'location_type', + allowClear: true, + }, + }, + ], +}; + +const tableColumns: BasicColumn[] = [ + { + title: t('记录时间'), + dataIndex: 'createTime', + key: 'a.create_time', + sorter: true, + width: 180, + align: 'left', + }, + { + title: t('主机名称'), + dataIndex: 'hostname', + key: 'a.hostname', + sorter: true, + width: 130, + align: 'left', + }, + { + title: t('IP地址'), + dataIndex: 'ipAddress', + key: 'a.ip_address', + sorter: true, + width: 130, + align: 'left', + }, + { + title: t('主机类型'), + dataIndex: 'hostType', + key: 'a.host_type', + sorter: true, + width: 130, + align: 'left', + dictType: 'host_type', + }, + { + title: t('操作系统'), + dataIndex: 'hostOs', + key: 'a.host_os', + sorter: true, + width: 130, + align: 'left', + dictType: 'host_os', + }, + { + title: t('主机状态'), + dataIndex: 'ustatus', + key: 'a.ustatus', + sorter: true, + width: 130, + align: 'left', + dictType: 'host_status', + }, + { + title: t('监测运行时间'), + dataIndex: 'lastOnlineTime', + key: 'a.last_online_time', + sorter: true, + width: 180, + align: 'center', + }, + { + title: t('物理位置'), + dataIndex: 'locationName', + key: 'a.location_name', + sorter: true, + width: 130, + align: 'left', + }, + { + title: t('地址类型'), + dataIndex: 'locationType', + key: 'a.location_type', + sorter: true, + width: 130, + align: 'left', + dictType: 'location_type', + }, + { + title: t('联系方式'), + dataIndex: 'otherContact', + key: 'a.other_contact', + sorter: true, + width: 130, + align: 'left', + }, + { + title: t('备注信息'), + dataIndex: 'remark', + key: 'a.remark', + sorter: true, + width: 130, + align: 'left', + }, + { + title: t('更新时间'), + dataIndex: 'updateTime', + key: 'a.update_time', + sorter: true, + width: 180, + align: 'center', + }, + { + title: t('失效日期'), + dataIndex: 'expiryDate', + key: 'a.expiry_date', + sorter: true, + width: 180, + align: 'center', + }, +]; + +const tableProps: BasicTableProps = { + api: bizMonitorHostListData, + beforeFetch: (params) => { + params['isAll'] = true; + return params; + }, + columns: tableColumns, + formConfig: searchForm, + rowKey: 'hostId', +}; + +export default { + modalProps, + tableProps, + itemCode: 'hostId', + itemName: 'hostname', + isShowCode: true, +}; diff --git a/web-vue/packages/core/layouts/views/login/Login.vue b/web-vue/packages/core/layouts/views/login/Login.vue index cd7fd150..74466561 100644 --- a/web-vue/packages/core/layouts/views/login/Login.vue +++ b/web-vue/packages/core/layouts/views/login/Login.vue @@ -25,7 +25,7 @@
@@ -63,6 +63,10 @@ @logo-prefix-cls: ~'jeesite-app-logo'; @countdown-prefix-cls: ~'jeesite-countdown-input'; @dark-bg: #293146; + // 淡白色主色(柔和不刺眼,带轻微蓝调的白) + @light-white-bg: #f8fafc; + // 深色模式下表单背景(适配深色主题的暗白色) + @dark-light-white-bg: rgba(41, 49, 70, 0.95); html[data-theme='dark'] { .@{prefix-cls} { @@ -82,8 +86,8 @@ } &-form { - background: transparent !important; - box-shadow: none; + background: @dark-light-white-bg !important; + box-shadow: 0 0 12px rgba(0, 0, 0, 0.2); } .@{logo-prefix-cls} { @@ -95,6 +99,10 @@ .jeesite-icon { color: #fff; } + + .form-container-bg { + background: @dark-light-white-bg !important; + } } input.fix-auto-fill, @@ -107,11 +115,8 @@ .@{prefix-cls} { min-height: 100%; overflow: hidden; - //background-color: #f2fafd; @media (max-width: @screen-lg) { - //background-color: #3f60b5; - .@{prefix-cls}-form { box-shadow: none; } @@ -120,14 +125,24 @@ &-form { top: -20px; margin: auto; - background-color: #fff; - box-shadow: 0 0 8px #ddd; + // 淡白色背景(核心修改) + background-color: @light-white-bg; + box-shadow: 0 0 8px rgba(248, 250, 252, 0.8); + background-clip: padding-box; .ant-form-item { margin-bottom: 15px; } } + // 表单容器专属样式 + .form-container-bg { + background-color: @light-white-bg; + // 轻量阴影增强层次 + box-shadow: 0 4px 12px rgba(240, 248, 255, 0.5); + border-radius: 12px; + } + &::before { position: absolute; top: 0; @@ -147,10 +162,6 @@ } .@{logo-prefix-cls} { - // position: absolute; - // top: 12px; - // height: 30px; - &.logo { margin-top: -110px; padding-bottom: 80px; @@ -230,4 +241,4 @@ color: @text-color-secondary; } } - + \ No newline at end of file diff --git a/web-vue/packages/core/layouts/views/login/LoginForm.vue b/web-vue/packages/core/layouts/views/login/LoginForm.vue index 15a95d78..bbb3464f 100644 --- a/web-vue/packages/core/layouts/views/login/LoginForm.vue +++ b/web-vue/packages/core/layouts/views/login/LoginForm.vue @@ -30,19 +30,6 @@ -
- 💡提示:当前您连接的后端服务,可能是 - vue.jeesite.com
-     的演示服务器,请进入文档:《 - - 配置服务端接口 - - 》 -
- @@ -108,7 +95,7 @@ const emit = defineEmits(['demoMode']); const formData = reactive({ - account: 'system', + account: '', password: '', validCode: '', }); @@ -185,11 +172,6 @@ loading.value = false; } } - - function handleOauth2(event: Event) { - window.location.href = 'https://vue.jeesite.com/js/oauth2/login/gitee?state=vue'; - event.preventDefault(); - }