最近新增的工作

This commit is contained in:
2026-04-23 11:06:04 +08:00
parent c9461f8b8a
commit 03df9d3a10
140 changed files with 5591 additions and 2767 deletions

29
systemmonitor/README.md Normal file
View File

@@ -0,0 +1,29 @@
# System Monitor 模块说明
## 当前状态
`systemmonitor` 当前作为系统监控能力聚合模块使用。
当前真实保留的子模块有:
- `disk-monitor`
## 当前结构
```text
systemmonitor/
└── disk-monitor/
```
## disk-monitor 的职责
`disk-monitor` 预留用于承载磁盘监控相关能力,后续可在该模块内继续补充:
- 磁盘监控配置管理
- 磁盘巡检任务
- 磁盘容量计算与阈值判定
- 磁盘预警与告警留痕
## 模块定位
当前将磁盘监控拆分到 `systemmonitor/disk-monitor`,目的是避免将系统监控扩展逻辑直接混入 `system` 公共基础模块,便于后续继续扩展 CPU、内存或进程级监控能力。

View File

@@ -0,0 +1,11 @@
# disk-monitor
`disk-monitor` 模块用于承载磁盘监控相关代码。
当前这一版只完成模块骨架接入,后续可以在该模块内继续补充:
- Controller
- Service
- 定时巡检任务
- 磁盘容量采集与阈值判断
- 日志留痕与 SQL 脚本

View File

@@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.njcn.gather</groupId>
<artifactId>systemmonitor</artifactId>
<version>1.0.0</version>
</parent>
<artifactId>disk-monitor</artifactId>
<packaging>jar</packaging>
<name>disk-monitor</name>
<dependencies>
<dependency>
<groupId>com.njcn</groupId>
<artifactId>njcn-common</artifactId>
<version>0.0.1</version>
</dependency>
<dependency>
<groupId>com.njcn</groupId>
<artifactId>mybatis-plus</artifactId>
<version>0.0.1</version>
</dependency>
<dependency>
<groupId>com.njcn</groupId>
<artifactId>spingboot2.3.12</artifactId>
<version>2.3.12</version>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,250 @@
package com.njcn.gather.systemmonitor.disk.component;
import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.njcn.gather.systemmonitor.disk.constant.DiskMonitorConstant;
import com.njcn.gather.systemmonitor.disk.mapper.DiskMonitorNotifyLogMapper;
import com.njcn.gather.systemmonitor.disk.pojo.dto.DiskMonitorNotifyHttpItem;
import com.njcn.gather.systemmonitor.disk.pojo.dto.DiskMonitorNotifyPathItem;
import com.njcn.gather.systemmonitor.disk.pojo.po.DiskMonitorJob;
import com.njcn.gather.systemmonitor.disk.pojo.po.DiskMonitorNotifyLog;
import com.njcn.gather.systemmonitor.disk.pojo.po.DiskMonitorResult;
import com.njcn.gather.systemmonitor.disk.pojo.po.DiskMonitorTarget;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.io.OutputStream;
import java.math.BigDecimal;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
/**
* 磁盘监控通知发送组件。
*/
@Slf4j
@Component
@RequiredArgsConstructor
public class DiskMonitorNotificationComponent {
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
private static final TypeReference<List<DiskMonitorNotifyPathItem>> PATH_LIST_TYPE = new TypeReference<List<DiskMonitorNotifyPathItem>>() {
};
private static final TypeReference<List<DiskMonitorNotifyHttpItem>> HTTP_LIST_TYPE = new TypeReference<List<DiskMonitorNotifyHttpItem>>() {
};
private final DiskMonitorNotifyLogMapper notifyLogMapper;
private final ObjectMapper objectMapper;
public void sendNotifications(DiskMonitorJob job, DiskMonitorTarget target, DiskMonitorResult result,
BigDecimal usedPercent, String currentStatus, String notifyReason,
String notifyLevel, LocalDateTime scanTime, String message) {
List<DiskMonitorNotifyPathItem> pathItems;
List<DiskMonitorNotifyHttpItem> httpItems;
try {
pathItems = parsePathItems(target.getNotifyPathListJson());
httpItems = parseHttpItems(target.getNotifyHttpListJson());
} catch (Exception exception) {
insertFailedNotifyLog(job, result, target, notifyLevel, "N/A", "通知配置解析失败: " + exception.getMessage());
return;
}
boolean pathConfigured = isEnabled(target.getNotifyPathEnabled()) && pathItems.stream().anyMatch(item -> Boolean.TRUE.equals(item.getEnabled()));
boolean httpConfigured = isEnabled(target.getNotifyHttpEnabled()) && httpItems.stream().anyMatch(item -> Boolean.TRUE.equals(item.getEnabled()));
if (!pathConfigured && !httpConfigured) {
insertFailedNotifyLog(job, result, target, notifyLevel, "N/A", "当前盘符未配置可用通知通道");
return;
}
String notifyTitle = buildNotifyTitle(target.getDriveLetter(), notifyLevel, usedPercent);
String notifyContent = buildNotifyContent(job, target, usedPercent, currentStatus, notifyLevel, scanTime, message);
if (pathConfigured) {
for (DiskMonitorNotifyPathItem item : pathItems) {
if (!Boolean.TRUE.equals(item.getEnabled())) {
continue;
}
sendPathNotification(job, target, result, notifyReason, notifyLevel, notifyTitle, notifyContent, item,
usedPercent, currentStatus, scanTime, message);
}
}
if (httpConfigured) {
for (DiskMonitorNotifyHttpItem item : httpItems) {
if (!Boolean.TRUE.equals(item.getEnabled())) {
continue;
}
sendHttpNotification(job, target, result, notifyReason, notifyLevel, notifyTitle, notifyContent, item,
usedPercent, currentStatus, scanTime, message);
}
}
}
private void sendPathNotification(DiskMonitorJob job, DiskMonitorTarget target, DiskMonitorResult result,
String notifyReason, String notifyLevel, String notifyTitle, String notifyContent,
DiskMonitorNotifyPathItem item, BigDecimal usedPercent, String currentStatus,
LocalDateTime scanTime, String message) {
DiskMonitorNotifyLog notifyLog = createNotifyLog(job, result, target, notifyLevel, notifyTitle, notifyContent,
DiskMonitorConstant.CHANNEL_TYPE_PATH, item.getPath());
try {
Path directoryPath = Paths.get(item.getPath());
Files.createDirectories(directoryPath);
String fileName = String.format("disk-monitor-%s-%s-%s.json",
job.getJobNo(), target.getDriveLetter().replace(":", ""),
LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")));
Path filePath = directoryPath.resolve(fileName);
Map<String, Object> payload = buildNotifyPayload(job, target, usedPercent, currentStatus, notifyReason, notifyLevel, scanTime, message);
Files.write(filePath, objectMapper.writerWithDefaultPrettyPrinter().writeValueAsBytes(payload));
notifyLog.setSendStatus(DiskMonitorConstant.SEND_STATUS_SUCCESS);
notifyLog.setResponseMessage("写入成功:" + filePath.toString());
} catch (Exception exception) {
log.error("磁盘监控路径通知发送失败driveLetter={}, path={}", target.getDriveLetter(), item.getPath(), exception);
notifyLog.setSendStatus(DiskMonitorConstant.SEND_STATUS_FAILED);
notifyLog.setResponseMessage(exception.getMessage());
}
notifyLogMapper.insert(notifyLog);
}
private void sendHttpNotification(DiskMonitorJob job, DiskMonitorTarget target, DiskMonitorResult result,
String notifyReason, String notifyLevel, String notifyTitle, String notifyContent,
DiskMonitorNotifyHttpItem item, BigDecimal usedPercent, String currentStatus,
LocalDateTime scanTime, String message) {
DiskMonitorNotifyLog notifyLog = createNotifyLog(job, result, target, notifyLevel, notifyTitle, notifyContent,
DiskMonitorConstant.CHANNEL_TYPE_HTTP, item.getUrl());
HttpURLConnection connection = null;
try {
Map<String, Object> payload = buildNotifyPayload(job, target, usedPercent, currentStatus, notifyReason, notifyLevel, scanTime, message);
byte[] body = objectMapper.writeValueAsBytes(payload);
URL url = new URL(item.getUrl());
connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod(StrUtil.blankToDefault(item.getMethod(), DiskMonitorConstant.HTTP_METHOD_POST).trim().toUpperCase(Locale.ROOT));
connection.setConnectTimeout(resolveTimeout(item.getTimeoutMs()));
connection.setReadTimeout(resolveTimeout(item.getTimeoutMs()));
connection.setRequestProperty("Content-Type", "application/json;charset=UTF-8");
connection.setDoInput(true);
if (!"GET".equalsIgnoreCase(connection.getRequestMethod())) {
connection.setDoOutput(true);
try (OutputStream outputStream = connection.getOutputStream()) {
outputStream.write(body);
}
}
int statusCode = connection.getResponseCode();
notifyLog.setSendStatus(statusCode >= 200 && statusCode < 300
? DiskMonitorConstant.SEND_STATUS_SUCCESS
: DiskMonitorConstant.SEND_STATUS_FAILED);
notifyLog.setResponseMessage(statusCode + " " + connection.getResponseMessage());
} catch (Exception exception) {
log.error("磁盘监控HTTP通知发送失败driveLetter={}, url={}", target.getDriveLetter(), item.getUrl(), exception);
notifyLog.setSendStatus(DiskMonitorConstant.SEND_STATUS_FAILED);
notifyLog.setResponseMessage(exception.getMessage());
} finally {
if (connection != null) {
connection.disconnect();
}
}
notifyLogMapper.insert(notifyLog);
}
private Map<String, Object> buildNotifyPayload(DiskMonitorJob job, DiskMonitorTarget target, BigDecimal usedPercent,
String currentStatus, String notifyReason, String notifyLevel,
LocalDateTime scanTime, String message) {
Map<String, Object> payload = new LinkedHashMap<>();
payload.put("jobNo", job.getJobNo());
payload.put("driveLetter", target.getDriveLetter());
payload.put("currentStatus", currentStatus);
payload.put("notifyReason", notifyReason);
payload.put("notifyLevel", notifyLevel);
payload.put("usedPercent", usedPercent);
payload.put("warningUsagePercent", target.getWarningUsagePercent());
payload.put("alarmUsagePercent", target.getAlarmUsagePercent());
payload.put("scanTime", formatDateTime(scanTime));
payload.put("message", message);
return payload;
}
private String buildNotifyTitle(String driveLetter, String notifyLevel, BigDecimal usedPercent) {
return String.format("磁盘监控通知[%s] %s 使用率%s%%",
notifyLevel, driveLetter, usedPercent == null ? "0.00" : usedPercent.toPlainString());
}
private String buildNotifyContent(DiskMonitorJob job, DiskMonitorTarget target, BigDecimal usedPercent,
String currentStatus, String notifyLevel, LocalDateTime scanTime, String message) {
StringBuilder builder = new StringBuilder();
builder.append("任务编号:").append(job.getJobNo())
.append(";盘符:").append(target.getDriveLetter())
.append(";当前状态:").append(currentStatus)
.append(";通知级别:").append(notifyLevel)
.append(";使用率:").append(usedPercent == null ? "0.00" : usedPercent.toPlainString()).append("%")
.append(";预警阈值:").append(target.getWarningUsagePercent()).append("%")
.append(";告警阈值:").append(target.getAlarmUsagePercent()).append("%")
.append(";扫描时间:").append(formatDateTime(scanTime));
if (StrUtil.isNotBlank(message)) {
builder.append(";说明:").append(message);
}
return builder.toString();
}
private DiskMonitorNotifyLog createNotifyLog(DiskMonitorJob job, DiskMonitorResult result, DiskMonitorTarget target,
String notifyLevel, String notifyTitle, String notifyContent,
String channelType, String channelTarget) {
DiskMonitorNotifyLog notifyLog = new DiskMonitorNotifyLog();
notifyLog.setJobId(job.getId());
notifyLog.setResultId(result.getId());
notifyLog.setTargetId(target.getId());
notifyLog.setDriveLetter(target.getDriveLetter());
notifyLog.setNotifyLevel(notifyLevel);
notifyLog.setChannelType(channelType);
notifyLog.setChannelTarget(channelTarget);
notifyLog.setNotifyTitle(notifyTitle);
notifyLog.setNotifyContent(notifyContent);
notifyLog.setSentAt(LocalDateTime.now());
return notifyLog;
}
private void insertFailedNotifyLog(DiskMonitorJob job, DiskMonitorResult result, DiskMonitorTarget target,
String notifyLevel, String channelTarget, String responseMessage) {
DiskMonitorNotifyLog notifyLog = createNotifyLog(job, result, target, notifyLevel,
"磁盘监控通知失败", responseMessage, DiskMonitorConstant.CHANNEL_TYPE_HTTP, channelTarget);
notifyLog.setSendStatus(DiskMonitorConstant.SEND_STATUS_FAILED);
notifyLog.setResponseMessage(responseMessage);
notifyLogMapper.insert(notifyLog);
}
private List<DiskMonitorNotifyPathItem> parsePathItems(String json) throws Exception {
return readJsonList(json, PATH_LIST_TYPE);
}
private List<DiskMonitorNotifyHttpItem> parseHttpItems(String json) throws Exception {
return readJsonList(json, HTTP_LIST_TYPE);
}
private <T> List<T> readJsonList(String json, TypeReference<List<T>> typeReference) throws Exception {
if (StrUtil.isBlank(json)) {
return new ArrayList<>();
}
List<T> result = objectMapper.readValue(json, typeReference);
return result == null ? new ArrayList<>() : result;
}
private boolean isEnabled(Integer value) {
return value != null && value == 1;
}
private int resolveTimeout(Integer timeoutMs) {
return timeoutMs == null || timeoutMs <= 0 ? DiskMonitorConstant.DEFAULT_HTTP_TIMEOUT_MS : timeoutMs;
}
private String formatDateTime(LocalDateTime dateTime) {
return dateTime == null ? null : dateTime.format(DATE_TIME_FORMATTER);
}
}

View File

@@ -0,0 +1,10 @@
package com.njcn.gather.systemmonitor.disk.config;
import org.springframework.context.annotation.Configuration;
/**
* 磁盘监控模块基础配置入口,后续磁盘监控相关 Bean 统一放在该模块下扩展。
*/
@Configuration
public class DiskMonitorModuleConfig {
}

View File

@@ -0,0 +1,43 @@
package com.njcn.gather.systemmonitor.disk.constant;
/**
* 磁盘监控模块常量。
*/
public interface DiskMonitorConstant {
String DEFAULT_POLICY_NAME = "默认磁盘监控策略";
String DEFAULT_DAILY_RUN_TIME = "08:30:00";
String WARNING_NOTIFY_MODE_STATUS_CHANGE = "STATUS_CHANGE";
String ALARM_NOTIFY_MODE_EVERY_TIME = "EVERY_TIME";
String STATUS_UNKNOWN = "UNKNOWN";
String STATUS_NORMAL = "NORMAL";
String STATUS_WARNING = "WARNING";
String STATUS_ALARM = "ALARM";
String JOB_SOURCE_APP_START = "APP_START";
String JOB_SOURCE_DAILY_SCHEDULE = "DAILY_SCHEDULE";
String JOB_SOURCE_MANUAL = "MANUAL";
String JOB_STATUS_RUNNING = "RUNNING";
String JOB_STATUS_SUCCESS = "SUCCESS";
String JOB_STATUS_PARTIAL_SUCCESS = "PARTIAL_SUCCESS";
String JOB_STATUS_FAILED = "FAILED";
String NOTIFY_REASON_ALARM_EVERY_TIME = "ALARM_EVERY_TIME";
String NOTIFY_REASON_STATUS_CHANGED = "STATUS_CHANGED";
String NOTIFY_REASON_NO_NOTIFY = "NO_NOTIFY";
String NOTIFY_LEVEL_WARNING = "WARNING";
String NOTIFY_LEVEL_ALARM = "ALARM";
String NOTIFY_LEVEL_RECOVER = "RECOVER";
String CHANNEL_TYPE_PATH = "PATH";
String CHANNEL_TYPE_HTTP = "HTTP";
String SEND_STATUS_SUCCESS = "SUCCESS";
String SEND_STATUS_FAILED = "FAILED";
String HTTP_METHOD_POST = "POST";
int DEFAULT_HTTP_TIMEOUT_MS = 5000;
}

View File

@@ -0,0 +1,19 @@
package com.njcn.gather.systemmonitor.disk.constant;
/**
* 磁盘监控参数校验提示。
*/
public interface DiskMonitorValidMessage {
String POLICY_NOT_NULL = "policy不能为空请检查policy参数";
String TARGETS_NOT_NULL = "targets不能为空请检查targets参数";
String DRIVE_LETTER_NOT_BLANK = "盘符不能为空请检查driveLetter参数";
String DAILY_RUN_TIME_NOT_BLANK = "每日统一执行时间不能为空请检查dailyRunTime参数";
String DAILY_RUN_TIME_FORMAT_ERROR = "每日统一执行时间格式错误请使用HH:mm:ss";
String WARNING_USAGE_PERCENT_NOT_NULL = "预警使用率不能为空请检查warningUsagePercent参数";
String ALARM_USAGE_PERCENT_NOT_NULL = "告警使用率不能为空请检查alarmUsagePercent参数";
String USAGE_PERCENT_FORMAT_ERROR = "阈值范围必须在1-100之间请检查阈值参数";
String JOB_SOURCE_NOT_BLANK = "任务来源不能为空请检查jobSource参数";
String JOB_ID_NOT_NULL = "jobId不能为空请检查jobId参数";
String POLICY_NAME_NOT_BLANK = "策略名称不能为空请检查policyName参数";
}

View File

@@ -0,0 +1,72 @@
package com.njcn.gather.systemmonitor.disk.controller;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.njcn.common.pojo.annotation.OperateInfo;
import com.njcn.common.pojo.constant.OperateType;
import com.njcn.common.pojo.enums.common.LogEnum;
import com.njcn.common.pojo.enums.response.CommonResponseEnum;
import com.njcn.common.pojo.response.HttpResult;
import com.njcn.common.utils.LogUtil;
import com.njcn.gather.systemmonitor.disk.pojo.param.DiskMonitorParam;
import com.njcn.gather.systemmonitor.disk.pojo.vo.DiskMonitorVO;
import com.njcn.gather.systemmonitor.disk.service.IDiskMonitorJobService;
import com.njcn.web.controller.BaseController;
import com.njcn.web.utils.HttpResultUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.Valid;
/**
* 磁盘监控任务接口。
*/
@Validated
@Slf4j
@Api(tags = "磁盘监控任务")
@RestController
@RequestMapping("/disk-monitor")
@RequiredArgsConstructor
public class DiskMonitorJobController extends BaseController {
private final IDiskMonitorJobService diskMonitorJobService;
@OperateInfo(info = LogEnum.SYSTEM_COMMON, operateType = OperateType.UPDATE)
@PostMapping("/job/run")
@ApiOperation("手动执行磁盘监控")
@ApiImplicitParam(name = "param", value = "手动执行参数", required = true)
public HttpResult<DiskMonitorVO.JobRunVO> run(@RequestBody @Valid DiskMonitorParam.JobRunParam param) {
String methodDescribe = getMethodDescribe("run");
LogUtil.njcnDebug(log, "{}执行磁盘监控任务source={}", methodDescribe, param.getJobSource());
return HttpResultUtil.assembleResult(CommonResponseEnum.SUCCESS.getCode(), diskMonitorJobService.runJob(param), "任务已启动");
}
@OperateInfo(info = LogEnum.SYSTEM_COMMON)
@PostMapping("/job/list")
@ApiOperation("分页查询磁盘监控任务列表")
@ApiImplicitParam(name = "param", value = "任务分页查询参数", required = true)
public HttpResult<Page<DiskMonitorVO.JobListVO>> list(@RequestBody @Valid DiskMonitorParam.JobListParam param) {
String methodDescribe = getMethodDescribe("list");
LogUtil.njcnDebug(log, "{},分页查询磁盘监控任务列表", methodDescribe);
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, diskMonitorJobService.listJobs(param), methodDescribe);
}
@OperateInfo(info = LogEnum.SYSTEM_COMMON)
@GetMapping("/job/{jobId}/detail")
@ApiOperation("查询磁盘监控任务详情")
@ApiImplicitParam(name = "jobId", value = "任务ID", required = true)
public HttpResult<DiskMonitorVO.JobDetailVO> jobDetail(@PathVariable("jobId") Long jobId) {
String methodDescribe = getMethodDescribe("jobDetail");
LogUtil.njcnDebug(log, "{}查询磁盘监控任务详情jobId={}", methodDescribe, jobId);
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, diskMonitorJobService.getJobDetail(jobId), methodDescribe);
}
}

View File

@@ -0,0 +1,49 @@
package com.njcn.gather.systemmonitor.disk.controller;
import com.njcn.common.pojo.annotation.OperateInfo;
import com.njcn.common.pojo.constant.OperateType;
import com.njcn.common.pojo.enums.common.LogEnum;
import com.njcn.common.pojo.enums.response.CommonResponseEnum;
import com.njcn.common.pojo.response.HttpResult;
import com.njcn.common.utils.LogUtil;
import com.njcn.gather.systemmonitor.disk.pojo.param.DiskMonitorParam;
import com.njcn.gather.systemmonitor.disk.service.IDiskMonitorNotifyService;
import com.njcn.web.controller.BaseController;
import com.njcn.web.utils.HttpResultUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.Valid;
/**
* 磁盘监控通知接口。
*/
@Validated
@Slf4j
@Api(tags = "磁盘监控通知")
@RestController
@RequestMapping("/disk-monitor")
@RequiredArgsConstructor
public class DiskMonitorNotifyController extends BaseController {
private final IDiskMonitorNotifyService diskMonitorNotifyService;
@OperateInfo(info = LogEnum.SYSTEM_COMMON, operateType = OperateType.UPDATE)
@PostMapping("/notify/test")
@ApiOperation("测试磁盘监控通知")
@ApiImplicitParam(name = "param", value = "通知测试参数", required = true)
public HttpResult<Boolean> testNotify(@RequestBody @Valid DiskMonitorParam.NotifyTestParam param) {
String methodDescribe = getMethodDescribe("testNotify");
LogUtil.njcnDebug(log, "{}测试磁盘监控通知driveLetter={}", methodDescribe, param.getDriveLetter());
boolean result = diskMonitorNotifyService.testNotify(param);
return HttpResultUtil.assembleCommonResponseResult(result ? CommonResponseEnum.SUCCESS : CommonResponseEnum.FAIL, result, methodDescribe);
}
}

View File

@@ -0,0 +1,60 @@
package com.njcn.gather.systemmonitor.disk.controller;
import com.njcn.common.pojo.annotation.OperateInfo;
import com.njcn.common.pojo.constant.OperateType;
import com.njcn.common.pojo.enums.common.LogEnum;
import com.njcn.common.pojo.enums.response.CommonResponseEnum;
import com.njcn.common.pojo.response.HttpResult;
import com.njcn.common.utils.LogUtil;
import com.njcn.gather.systemmonitor.disk.pojo.param.DiskMonitorParam;
import com.njcn.gather.systemmonitor.disk.pojo.vo.DiskMonitorVO;
import com.njcn.gather.systemmonitor.disk.service.IDiskMonitorPolicyService;
import com.njcn.web.controller.BaseController;
import com.njcn.web.utils.HttpResultUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.Valid;
/**
* 磁盘监控配置接口。
*/
@Validated
@Slf4j
@Api(tags = "磁盘监控配置")
@RestController
@RequestMapping("/disk-monitor")
@RequiredArgsConstructor
public class DiskMonitorPolicyController extends BaseController {
private final IDiskMonitorPolicyService diskMonitorPolicyService;
@OperateInfo(info = LogEnum.SYSTEM_COMMON)
@GetMapping("/policy/detail")
@ApiOperation("查询磁盘监控配置详情")
public HttpResult<DiskMonitorVO.PolicyDetailVO> detail() {
String methodDescribe = getMethodDescribe("detail");
LogUtil.njcnDebug(log, "{},查询磁盘监控配置详情", methodDescribe);
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, diskMonitorPolicyService.getPolicyDetail(), methodDescribe);
}
@OperateInfo(info = LogEnum.SYSTEM_COMMON, operateType = OperateType.UPDATE)
@PostMapping("/policy/save")
@ApiOperation("保存磁盘监控配置")
@ApiImplicitParam(name = "param", value = "磁盘监控配置", required = true)
public HttpResult<Boolean> save(@RequestBody @Valid DiskMonitorParam.PolicySaveParam param) {
String methodDescribe = getMethodDescribe("save");
LogUtil.njcnDebug(log, "{},保存磁盘监控配置", methodDescribe);
boolean result = diskMonitorPolicyService.savePolicy(param);
return HttpResultUtil.assembleCommonResponseResult(result ? CommonResponseEnum.SUCCESS : CommonResponseEnum.FAIL, result, methodDescribe);
}
}

View File

@@ -0,0 +1,37 @@
package com.njcn.gather.systemmonitor.disk.enums;
import lombok.Getter;
/**
* 磁盘监控模块业务响应码。
*/
@Getter
public enum DiskMonitorResponseEnum {
POLICY_NOT_FOUND("A01060", "磁盘监控策略不存在"),
TARGET_NOT_FOUND("A01061", "磁盘监控盘符配置不存在"),
DRIVE_LETTER_REPEAT("A01062", "盘符重复请检查driveLetter参数"),
DRIVE_LETTER_FORMAT_ERROR("A01063", "盘符格式错误,请使用类似 C: 的格式"),
DAILY_RUN_TIME_FORMAT_ERROR("A01064", "每日统一执行时间格式错误请使用HH:mm:ss"),
USAGE_PERCENT_INVALID("A01065", "告警使用率必须大于等于预警使用率"),
NOTIFY_PATH_EMPTY("A01066", "路径通知目标不能为空"),
NOTIFY_HTTP_EMPTY("A01067", "HTTP通知目标不能为空"),
NOTIFY_PATH_VALUE_EMPTY("A01068", "通知路径不能为空"),
NOTIFY_HTTP_URL_INVALID("A01069", "HTTP通知目标格式错误请检查url参数"),
NO_ENABLED_TARGET("A01070", "暂无启用的磁盘监控盘符配置"),
JOB_NOT_FOUND("A01071", "监控任务不存在"),
POLICY_SAVE_FAILED("A01072", "磁盘监控配置保存失败"),
DRIVE_SCAN_FAILED("A01073", "磁盘扫描失败"),
NOTIFY_TARGET_NOT_FOUND("A01074", "通知测试盘符不存在"),
NOTIFY_CHANNEL_MISSING("A01075", "当前盘符未配置可用通知通道"),
POLICY_MODE_INVALID("A01076", "通知模式配置非法"),
JOB_SOURCE_INVALID("A01077", "任务来源非法");
private final String code;
private final String message;
DiskMonitorResponseEnum(String code, String message) {
this.code = code;
this.message = message;
}
}

View File

@@ -0,0 +1,7 @@
package com.njcn.gather.systemmonitor.disk.event;
/**
* 磁盘监控策略变更事件。
*/
public class DiskMonitorPolicyChangedEvent {
}

View File

@@ -0,0 +1,10 @@
package com.njcn.gather.systemmonitor.disk.mapper;
import com.github.yulichang.base.MPJBaseMapper;
import com.njcn.gather.systemmonitor.disk.pojo.po.DiskMonitorJob;
/**
* 磁盘监控任务 Mapper。
*/
public interface DiskMonitorJobMapper extends MPJBaseMapper<DiskMonitorJob> {
}

View File

@@ -0,0 +1,10 @@
package com.njcn.gather.systemmonitor.disk.mapper;
import com.github.yulichang.base.MPJBaseMapper;
import com.njcn.gather.systemmonitor.disk.pojo.po.DiskMonitorNotifyLog;
/**
* 磁盘监控通知日志 Mapper。
*/
public interface DiskMonitorNotifyLogMapper extends MPJBaseMapper<DiskMonitorNotifyLog> {
}

View File

@@ -0,0 +1,10 @@
package com.njcn.gather.systemmonitor.disk.mapper;
import com.github.yulichang.base.MPJBaseMapper;
import com.njcn.gather.systemmonitor.disk.pojo.po.DiskMonitorPolicy;
/**
* 磁盘监控策略 Mapper。
*/
public interface DiskMonitorPolicyMapper extends MPJBaseMapper<DiskMonitorPolicy> {
}

View File

@@ -0,0 +1,10 @@
package com.njcn.gather.systemmonitor.disk.mapper;
import com.github.yulichang.base.MPJBaseMapper;
import com.njcn.gather.systemmonitor.disk.pojo.po.DiskMonitorResult;
/**
* 磁盘监控结果 Mapper。
*/
public interface DiskMonitorResultMapper extends MPJBaseMapper<DiskMonitorResult> {
}

View File

@@ -0,0 +1,10 @@
package com.njcn.gather.systemmonitor.disk.mapper;
import com.github.yulichang.base.MPJBaseMapper;
import com.njcn.gather.systemmonitor.disk.pojo.po.DiskMonitorTarget;
/**
* 磁盘监控盘符配置 Mapper。
*/
public interface DiskMonitorTargetMapper extends MPJBaseMapper<DiskMonitorTarget> {
}

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.njcn.gather.systemmonitor.disk.mapper.DiskMonitorJobMapper">
</mapper>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.njcn.gather.systemmonitor.disk.mapper.DiskMonitorNotifyLogMapper">
</mapper>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.njcn.gather.systemmonitor.disk.mapper.DiskMonitorPolicyMapper">
</mapper>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.njcn.gather.systemmonitor.disk.mapper.DiskMonitorResultMapper">
</mapper>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.njcn.gather.systemmonitor.disk.mapper.DiskMonitorTargetMapper">
</mapper>

View File

@@ -0,0 +1,31 @@
package com.njcn.gather.systemmonitor.disk.pojo.dto;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotBlank;
/**
* HTTP 通知项。
*/
@Data
public class DiskMonitorNotifyHttpItem {
@ApiModelProperty("回调地址")
@NotBlank(message = "HTTP通知目标不能为空请检查url参数")
private String url;
@ApiModelProperty("名称")
private String name;
@ApiModelProperty("请求方法")
private String method;
@ApiModelProperty("超时时间")
@Min(value = 1, message = "timeoutMs必须大于0")
private Integer timeoutMs;
@ApiModelProperty("是否启用")
private Boolean enabled;
}

View File

@@ -0,0 +1,23 @@
package com.njcn.gather.systemmonitor.disk.pojo.dto;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotBlank;
/**
* 路径通知项。
*/
@Data
public class DiskMonitorNotifyPathItem {
@ApiModelProperty("路径")
@NotBlank(message = "通知路径不能为空请检查path参数")
private String path;
@ApiModelProperty("名称")
private String name;
@ApiModelProperty("是否启用")
private Boolean enabled;
}

View File

@@ -0,0 +1,131 @@
package com.njcn.gather.systemmonitor.disk.pojo.param;
import com.njcn.gather.systemmonitor.disk.constant.DiskMonitorValidMessage;
import com.njcn.gather.systemmonitor.disk.pojo.dto.DiskMonitorNotifyHttpItem;
import com.njcn.gather.systemmonitor.disk.pojo.dto.DiskMonitorNotifyPathItem;
import com.njcn.web.pojo.param.BaseParam;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import javax.validation.Valid;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.util.ArrayList;
import java.util.List;
/**
* 磁盘监控接口参数。
*/
public class DiskMonitorParam {
@Data
public static class PolicySaveParam {
@ApiModelProperty("全局策略")
@NotNull(message = DiskMonitorValidMessage.POLICY_NOT_NULL)
@Valid
private PolicyParam policy;
@ApiModelProperty("盘符配置")
@NotNull(message = DiskMonitorValidMessage.TARGETS_NOT_NULL)
@Valid
private List<TargetParam> targets = new ArrayList<>();
}
@Data
public static class PolicyParam {
@ApiModelProperty("策略ID")
private Long id;
@ApiModelProperty("策略名称")
@NotBlank(message = DiskMonitorValidMessage.POLICY_NAME_NOT_BLANK)
private String policyName;
@ApiModelProperty("是否启用监控")
private Boolean monitorEnabled;
@ApiModelProperty("应用启动后立即执行一次")
private Boolean runOnAppStart;
@ApiModelProperty("每日统一执行时间")
@NotBlank(message = DiskMonitorValidMessage.DAILY_RUN_TIME_NOT_BLANK)
private String dailyRunTime;
@ApiModelProperty("预警通知模式")
private String warningNotifyMode;
@ApiModelProperty("告警通知模式")
private String alarmNotifyMode;
@ApiModelProperty("备注")
private String remark;
}
@Data
public static class TargetParam {
@ApiModelProperty("盘符配置ID")
private Long id;
@ApiModelProperty("盘符")
@NotBlank(message = DiskMonitorValidMessage.DRIVE_LETTER_NOT_BLANK)
private String driveLetter;
@ApiModelProperty("是否监控")
private Boolean monitorEnabled;
@ApiModelProperty("预警使用率")
@NotNull(message = DiskMonitorValidMessage.WARNING_USAGE_PERCENT_NOT_NULL)
@Min(value = 1, message = DiskMonitorValidMessage.USAGE_PERCENT_FORMAT_ERROR)
@Max(value = 100, message = DiskMonitorValidMessage.USAGE_PERCENT_FORMAT_ERROR)
private Integer warningUsagePercent;
@ApiModelProperty("告警使用率")
@NotNull(message = DiskMonitorValidMessage.ALARM_USAGE_PERCENT_NOT_NULL)
@Min(value = 1, message = DiskMonitorValidMessage.USAGE_PERCENT_FORMAT_ERROR)
@Max(value = 100, message = DiskMonitorValidMessage.USAGE_PERCENT_FORMAT_ERROR)
private Integer alarmUsagePercent;
@ApiModelProperty("是否启用路径通知")
private Boolean notifyPathEnabled;
@ApiModelProperty("路径通知列表")
@Valid
private List<DiskMonitorNotifyPathItem> notifyPathList = new ArrayList<>();
@ApiModelProperty("是否启用HTTP通知")
private Boolean notifyHttpEnabled;
@ApiModelProperty("HTTP通知列表")
@Valid
private List<DiskMonitorNotifyHttpItem> notifyHttpList = new ArrayList<>();
@ApiModelProperty("备注")
private String remark;
}
@Data
public static class JobRunParam {
@ApiModelProperty("任务来源")
@NotBlank(message = DiskMonitorValidMessage.JOB_SOURCE_NOT_BLANK)
private String jobSource;
}
@Data
@EqualsAndHashCode(callSuper = true)
public static class JobListParam extends BaseParam {
}
@Data
public static class NotifyTestParam {
@ApiModelProperty("盘符")
@NotBlank(message = DiskMonitorValidMessage.DRIVE_LETTER_NOT_BLANK)
private String driveLetter;
}
}

View File

@@ -0,0 +1,58 @@
package com.njcn.gather.systemmonitor.disk.pojo.po;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.io.Serializable;
import java.time.LocalDateTime;
/**
* 磁盘监控任务批次表。
*/
@Data
@TableName("disk_monitor_job")
public class DiskMonitorJob implements Serializable {
private static final long serialVersionUID = 2956749403195165256L;
@TableId(value = "id", type = IdType.AUTO)
private Long id;
@TableField("job_no")
private String jobNo;
@TableField("job_source")
private String jobSource;
@TableField("planned_time")
private LocalDateTime plannedTime;
@TableField("started_at")
private LocalDateTime startedAt;
@TableField("finished_at")
private LocalDateTime finishedAt;
@TableField("job_status")
private String jobStatus;
@TableField("target_count")
private Integer targetCount;
@TableField("success_count")
private Integer successCount;
@TableField("warning_count")
private Integer warningCount;
@TableField("alarm_count")
private Integer alarmCount;
@TableField("message")
private String message;
@TableField("created_at")
private LocalDateTime createdAt;
}

View File

@@ -0,0 +1,58 @@
package com.njcn.gather.systemmonitor.disk.pojo.po;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.io.Serializable;
import java.time.LocalDateTime;
/**
* 磁盘监控通知日志表。
*/
@Data
@TableName("disk_monitor_notify_log")
public class DiskMonitorNotifyLog implements Serializable {
private static final long serialVersionUID = -8873612319853902451L;
@TableId(value = "id", type = IdType.AUTO)
private Long id;
@TableField("job_id")
private Long jobId;
@TableField("result_id")
private Long resultId;
@TableField("target_id")
private Long targetId;
@TableField("drive_letter")
private String driveLetter;
@TableField("notify_level")
private String notifyLevel;
@TableField("channel_type")
private String channelType;
@TableField("channel_target")
private String channelTarget;
@TableField("notify_title")
private String notifyTitle;
@TableField("notify_content")
private String notifyContent;
@TableField("send_status")
private String sendStatus;
@TableField("response_message")
private String responseMessage;
@TableField("sent_at")
private LocalDateTime sentAt;
}

View File

@@ -0,0 +1,59 @@
package com.njcn.gather.systemmonitor.disk.pojo.po;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.io.Serializable;
import java.time.LocalDateTime;
import java.time.LocalTime;
/**
* 磁盘监控全局策略表。
*/
@Data
@TableName("disk_monitor_policy")
public class DiskMonitorPolicy implements Serializable {
private static final long serialVersionUID = -2789228200584651740L;
@TableId(value = "id", type = IdType.AUTO)
private Long id;
@TableField("policy_name")
private String policyName;
@TableField("monitor_enabled")
private Integer monitorEnabled;
@TableField("run_on_app_start")
private Integer runOnAppStart;
@TableField("daily_run_time")
private LocalTime dailyRunTime;
@TableField("warning_notify_mode")
private String warningNotifyMode;
@TableField("alarm_notify_mode")
private String alarmNotifyMode;
@TableField("last_job_id")
private Long lastJobId;
@TableField("remark")
private String remark;
@TableField("created_by")
private String createdBy;
@TableField("created_at")
private LocalDateTime createdAt;
@TableField("updated_by")
private String updatedBy;
@TableField("updated_at")
private LocalDateTime updatedAt;
}

View File

@@ -0,0 +1,65 @@
package com.njcn.gather.systemmonitor.disk.pojo.po;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDateTime;
/**
* 磁盘监控结果表。
*/
@Data
@TableName("disk_monitor_result")
public class DiskMonitorResult implements Serializable {
private static final long serialVersionUID = 5557836396879348480L;
@TableId(value = "id", type = IdType.AUTO)
private Long id;
@TableField("job_id")
private Long jobId;
@TableField("target_id")
private Long targetId;
@TableField("drive_letter")
private String driveLetter;
@TableField("total_bytes")
private Long totalBytes;
@TableField("used_bytes")
private Long usedBytes;
@TableField("free_bytes")
private Long freeBytes;
@TableField("used_percent")
private BigDecimal usedPercent;
@TableField("current_status")
private String currentStatus;
@TableField("previous_status")
private String previousStatus;
@TableField("status_changed")
private Integer statusChanged;
@TableField("should_notify")
private Integer shouldNotify;
@TableField("notify_reason")
private String notifyReason;
@TableField("scan_time")
private LocalDateTime scanTime;
@TableField("message")
private String message;
}

View File

@@ -0,0 +1,74 @@
package com.njcn.gather.systemmonitor.disk.pojo.po;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDateTime;
/**
* 磁盘监控盘符配置表。
*/
@Data
@TableName("disk_monitor_target")
public class DiskMonitorTarget implements Serializable {
private static final long serialVersionUID = -3567440027709476481L;
@TableId(value = "id", type = IdType.AUTO)
private Long id;
@TableField("policy_id")
private Long policyId;
@TableField("drive_letter")
private String driveLetter;
@TableField("monitor_enabled")
private Integer monitorEnabled;
@TableField("warning_usage_percent")
private Integer warningUsagePercent;
@TableField("alarm_usage_percent")
private Integer alarmUsagePercent;
@TableField("notify_path_enabled")
private Integer notifyPathEnabled;
@TableField("notify_path_list_json")
private String notifyPathListJson;
@TableField("notify_http_enabled")
private Integer notifyHttpEnabled;
@TableField("notify_http_list_json")
private String notifyHttpListJson;
@TableField("last_status")
private String lastStatus;
@TableField("last_scan_time")
private LocalDateTime lastScanTime;
@TableField("last_used_percent")
private BigDecimal lastUsedPercent;
@TableField("remark")
private String remark;
@TableField("created_by")
private String createdBy;
@TableField("created_at")
private LocalDateTime createdAt;
@TableField("updated_by")
private String updatedBy;
@TableField("updated_at")
private LocalDateTime updatedAt;
}

View File

@@ -0,0 +1,124 @@
package com.njcn.gather.systemmonitor.disk.pojo.vo;
import com.njcn.gather.systemmonitor.disk.pojo.dto.DiskMonitorNotifyHttpItem;
import com.njcn.gather.systemmonitor.disk.pojo.dto.DiskMonitorNotifyPathItem;
import lombok.Data;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
/**
* 磁盘监控接口返回对象。
*/
public class DiskMonitorVO {
@Data
public static class PolicyDetailVO {
private PolicyVO policy;
private List<TargetVO> targets = new ArrayList<>();
}
@Data
public static class PolicyVO {
private Long id;
private String policyName;
private Boolean monitorEnabled;
private Boolean runOnAppStart;
private String dailyRunTime;
private String warningNotifyMode;
private String alarmNotifyMode;
private Long lastJobId;
private String remark;
}
@Data
public static class TargetVO {
private Long id;
private String driveLetter;
private Boolean monitorEnabled;
private Integer warningUsagePercent;
private Integer alarmUsagePercent;
private Boolean notifyPathEnabled;
private List<DiskMonitorNotifyPathItem> notifyPathList = new ArrayList<>();
private Boolean notifyHttpEnabled;
private List<DiskMonitorNotifyHttpItem> notifyHttpList = new ArrayList<>();
private String lastStatus;
private String lastScanTime;
private BigDecimal lastUsedPercent;
private String remark;
}
@Data
public static class JobRunVO {
private Long jobId;
private String jobNo;
}
@Data
public static class JobListVO {
private Long jobId;
private String jobNo;
private String jobSource;
private String startedAt;
private String finishedAt;
private String jobStatus;
private Integer targetCount;
private Integer warningCount;
private Integer alarmCount;
private String message;
}
@Data
public static class JobDetailVO {
private JobInfoVO job;
private List<ResultVO> results = new ArrayList<>();
private List<NotifyLogVO> notifyLogs = new ArrayList<>();
}
@Data
public static class JobInfoVO {
private Long id;
private String jobNo;
private String jobSource;
private String startedAt;
private String finishedAt;
private String jobStatus;
private Integer targetCount;
private Integer successCount;
private Integer warningCount;
private Integer alarmCount;
private String message;
}
@Data
public static class ResultVO {
private Long resultId;
private Long targetId;
private String driveLetter;
private Long totalBytes;
private Long usedBytes;
private Long freeBytes;
private BigDecimal usedPercent;
private String currentStatus;
private String previousStatus;
private Boolean statusChanged;
private Boolean shouldNotify;
private String notifyReason;
private String scanTime;
private String message;
}
@Data
public static class NotifyLogVO {
private Long id;
private Long resultId;
private String driveLetter;
private String notifyLevel;
private String channelType;
private String channelTarget;
private String sendStatus;
private String responseMessage;
private String sentAt;
}
}

View File

@@ -0,0 +1,94 @@
package com.njcn.gather.systemmonitor.disk.schedule;
import com.njcn.gather.systemmonitor.disk.event.DiskMonitorPolicyChangedEvent;
import com.njcn.gather.systemmonitor.disk.service.IDiskMonitorJobService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
import javax.annotation.PreDestroy;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
/**
* 磁盘监控启动与定时调度管理。
*/
@Slf4j
@Component
@RequiredArgsConstructor
public class DiskMonitorScheduleManager implements ApplicationRunner {
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
private final IDiskMonitorJobService diskMonitorJobService;
private final AtomicInteger threadIndex = new AtomicInteger(1);
private final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(createThreadFactory());
private ScheduledFuture<?> scheduledFuture;
@Override
public void run(ApplicationArguments args) {
try {
diskMonitorJobService.executeAppStartMonitor();
} catch (Exception exception) {
log.error("应用启动后执行磁盘监控失败", exception);
}
refreshSchedule();
}
@EventListener(DiskMonitorPolicyChangedEvent.class)
public void onPolicyChanged() {
refreshSchedule();
}
public synchronized void refreshSchedule() {
if (scheduledFuture != null) {
scheduledFuture.cancel(false);
scheduledFuture = null;
}
LocalDateTime nextRunTime = diskMonitorJobService.getNextDailyRunTime();
if (nextRunTime == null) {
log.info("磁盘监控每日调度未启用或未配置执行时间,跳过注册");
return;
}
long delayMs = Math.max(Duration.between(LocalDateTime.now(), nextRunTime).toMillis(), 0L);
scheduledFuture = scheduler.schedule(this::executeDailyTask, delayMs, TimeUnit.MILLISECONDS);
log.info("磁盘监控每日调度已注册,下次执行时间:{}", nextRunTime.format(DATE_TIME_FORMATTER));
}
private void executeDailyTask() {
try {
diskMonitorJobService.executeDailyScheduleMonitor();
} catch (Exception exception) {
log.error("每日磁盘监控执行失败", exception);
} finally {
refreshSchedule();
}
}
private ThreadFactory createThreadFactory() {
return runnable -> {
Thread thread = new Thread(runnable);
thread.setName("disk-monitor-schedule-" + threadIndex.getAndIncrement());
thread.setDaemon(true);
return thread;
};
}
@PreDestroy
public synchronized void destroy() {
if (scheduledFuture != null) {
scheduledFuture.cancel(false);
}
scheduler.shutdown();
}
}

View File

@@ -0,0 +1,27 @@
package com.njcn.gather.systemmonitor.disk.service;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.njcn.gather.systemmonitor.disk.pojo.param.DiskMonitorParam;
import com.njcn.gather.systemmonitor.disk.pojo.vo.DiskMonitorVO;
import java.time.LocalDateTime;
/**
* 磁盘监控任务服务。
*/
public interface IDiskMonitorJobService {
DiskMonitorVO.JobRunVO runJob(DiskMonitorParam.JobRunParam param);
Page<DiskMonitorVO.JobListVO> listJobs(DiskMonitorParam.JobListParam param);
DiskMonitorVO.JobDetailVO getJobDetail(Long jobId);
void executeAppStartMonitor();
void executeDailyScheduleMonitor();
LocalDateTime getNextDailyRunTime();
void executeNotifyTest(String driveLetter);
}

View File

@@ -0,0 +1,11 @@
package com.njcn.gather.systemmonitor.disk.service;
import com.njcn.gather.systemmonitor.disk.pojo.param.DiskMonitorParam;
/**
* 磁盘监控通知服务。
*/
public interface IDiskMonitorNotifyService {
boolean testNotify(DiskMonitorParam.NotifyTestParam param);
}

View File

@@ -0,0 +1,26 @@
package com.njcn.gather.systemmonitor.disk.service;
import com.njcn.gather.systemmonitor.disk.pojo.param.DiskMonitorParam;
import com.njcn.gather.systemmonitor.disk.pojo.po.DiskMonitorPolicy;
import com.njcn.gather.systemmonitor.disk.pojo.po.DiskMonitorTarget;
import com.njcn.gather.systemmonitor.disk.pojo.vo.DiskMonitorVO;
import java.util.List;
/**
* 磁盘监控配置服务。
*/
public interface IDiskMonitorPolicyService {
DiskMonitorVO.PolicyDetailVO getPolicyDetail();
boolean savePolicy(DiskMonitorParam.PolicySaveParam param);
DiskMonitorPolicy getCurrentPolicy();
DiskMonitorPolicy getOrCreatePolicy();
List<DiskMonitorTarget> listEnabledTargets();
DiskMonitorTarget getTargetByDriveLetter(String driveLetter);
}

View File

@@ -0,0 +1,517 @@
package com.njcn.gather.systemmonitor.disk.service.impl;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.njcn.common.pojo.exception.BusinessException;
import com.njcn.gather.systemmonitor.disk.component.DiskMonitorNotificationComponent;
import com.njcn.gather.systemmonitor.disk.constant.DiskMonitorConstant;
import com.njcn.gather.systemmonitor.disk.enums.DiskMonitorResponseEnum;
import com.njcn.gather.systemmonitor.disk.mapper.DiskMonitorJobMapper;
import com.njcn.gather.systemmonitor.disk.mapper.DiskMonitorNotifyLogMapper;
import com.njcn.gather.systemmonitor.disk.mapper.DiskMonitorPolicyMapper;
import com.njcn.gather.systemmonitor.disk.mapper.DiskMonitorResultMapper;
import com.njcn.gather.systemmonitor.disk.mapper.DiskMonitorTargetMapper;
import com.njcn.gather.systemmonitor.disk.pojo.param.DiskMonitorParam;
import com.njcn.gather.systemmonitor.disk.pojo.po.DiskMonitorJob;
import com.njcn.gather.systemmonitor.disk.pojo.po.DiskMonitorNotifyLog;
import com.njcn.gather.systemmonitor.disk.pojo.po.DiskMonitorPolicy;
import com.njcn.gather.systemmonitor.disk.pojo.po.DiskMonitorResult;
import com.njcn.gather.systemmonitor.disk.pojo.po.DiskMonitorTarget;
import com.njcn.gather.systemmonitor.disk.pojo.vo.DiskMonitorVO;
import com.njcn.gather.systemmonitor.disk.service.IDiskMonitorJobService;
import com.njcn.gather.systemmonitor.disk.service.IDiskMonitorPolicyService;
import com.njcn.web.factory.PageFactory;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.io.File;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
/**
* 磁盘监控任务服务实现。
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class DiskMonitorJobServiceImpl implements IDiskMonitorJobService {
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
private final IDiskMonitorPolicyService diskMonitorPolicyService;
private final DiskMonitorNotificationComponent diskMonitorNotificationComponent;
private final DiskMonitorJobMapper jobMapper;
private final DiskMonitorResultMapper resultMapper;
private final DiskMonitorNotifyLogMapper notifyLogMapper;
private final DiskMonitorTargetMapper targetMapper;
private final DiskMonitorPolicyMapper policyMapper;
@Override
@Transactional(rollbackFor = Exception.class)
public DiskMonitorVO.JobRunVO runJob(DiskMonitorParam.JobRunParam param) {
String jobSource = StrUtil.blankToDefault(param.getJobSource(), DiskMonitorConstant.JOB_SOURCE_MANUAL).trim().toUpperCase();
if (!DiskMonitorConstant.JOB_SOURCE_MANUAL.equals(jobSource)) {
throw new BusinessException(DiskMonitorResponseEnum.JOB_SOURCE_INVALID);
}
return executeMonitorJob(jobSource, null, false, true);
}
@Override
public Page<DiskMonitorVO.JobListVO> listJobs(DiskMonitorParam.JobListParam param) {
Page<DiskMonitorJob> page = jobMapper.selectPage(
new Page<>(PageFactory.getPageNum(param), PageFactory.getPageSize(param)),
new QueryWrapper<DiskMonitorJob>().orderByDesc("started_at", "id")
);
Page<DiskMonitorVO.JobListVO> resultPage = new Page<>(page.getCurrent(), page.getSize(), page.getTotal());
resultPage.setRecords(page.getRecords().stream().map(this::toJobListVO).collect(Collectors.toList()));
return resultPage;
}
@Override
public DiskMonitorVO.JobDetailVO getJobDetail(Long jobId) {
DiskMonitorJob job = jobMapper.selectById(jobId);
if (job == null) {
throw new BusinessException(DiskMonitorResponseEnum.JOB_NOT_FOUND);
}
DiskMonitorVO.JobDetailVO detailVO = new DiskMonitorVO.JobDetailVO();
detailVO.setJob(toJobInfoVO(job));
List<DiskMonitorResult> results = resultMapper.selectList(new QueryWrapper<DiskMonitorResult>()
.eq("job_id", jobId)
.orderByAsc("scan_time", "id"));
detailVO.setResults(results.stream().map(this::toResultVO).collect(Collectors.toList()));
List<DiskMonitorNotifyLog> notifyLogs = notifyLogMapper.selectList(new QueryWrapper<DiskMonitorNotifyLog>()
.eq("job_id", jobId)
.orderByAsc("sent_at", "id"));
detailVO.setNotifyLogs(notifyLogs.stream().map(this::toNotifyLogVO).collect(Collectors.toList()));
return detailVO;
}
@Override
@Transactional(rollbackFor = Exception.class)
public void executeAppStartMonitor() {
DiskMonitorPolicy policy = diskMonitorPolicyService.getOrCreatePolicy();
if (!isEnabled(policy.getMonitorEnabled()) || !isEnabled(policy.getRunOnAppStart())) {
return;
}
executeMonitorJob(DiskMonitorConstant.JOB_SOURCE_APP_START, null, false, false);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void executeDailyScheduleMonitor() {
DiskMonitorPolicy policy = diskMonitorPolicyService.getOrCreatePolicy();
if (!isEnabled(policy.getMonitorEnabled()) || policy.getDailyRunTime() == null) {
return;
}
executeMonitorJob(DiskMonitorConstant.JOB_SOURCE_DAILY_SCHEDULE, null, false, false);
}
@Override
public LocalDateTime getNextDailyRunTime() {
DiskMonitorPolicy policy = diskMonitorPolicyService.getOrCreatePolicy();
if (!isEnabled(policy.getMonitorEnabled()) || policy.getDailyRunTime() == null) {
return null;
}
LocalDateTime now = LocalDateTime.now();
LocalDateTime candidate = LocalDate.now().atTime(policy.getDailyRunTime());
if (!candidate.isAfter(now)) {
candidate = candidate.plusDays(1);
}
return candidate;
}
@Override
@Transactional(rollbackFor = Exception.class)
public void executeNotifyTest(String driveLetter) {
executeMonitorJob(DiskMonitorConstant.JOB_SOURCE_MANUAL, driveLetter, true, true);
}
private DiskMonitorVO.JobRunVO executeMonitorJob(String jobSource, String driveLetter, boolean forceNotify, boolean failWhenNoTarget) {
validateJobSource(jobSource);
List<DiskMonitorTarget> targets = resolveExecutionTargets(driveLetter);
if (targets.isEmpty()) {
logNoExecutionTargets(jobSource, driveLetter);
if (failWhenNoTarget) {
throw new BusinessException(DiskMonitorResponseEnum.NO_ENABLED_TARGET);
}
return null;
}
LocalDateTime now = LocalDateTime.now();
DiskMonitorJob job = new DiskMonitorJob();
job.setJobNo(generateJobNo());
job.setJobSource(jobSource);
job.setPlannedTime(now);
job.setStartedAt(now);
job.setJobStatus(DiskMonitorConstant.JOB_STATUS_RUNNING);
job.setTargetCount(targets.size());
job.setSuccessCount(0);
job.setWarningCount(0);
job.setAlarmCount(0);
job.setMessage(forceNotify ? "通知测试任务" : null);
jobMapper.insert(job);
int successCount = 0;
int warningCount = 0;
int alarmCount = 0;
List<String> errorMessages = new ArrayList<>();
DiskMonitorPolicy policy = diskMonitorPolicyService.getOrCreatePolicy();
for (DiskMonitorTarget target : targets) {
try {
ScanSnapshot snapshot = resolveSnapshotStatus(target, scanDrive(target.getDriveLetter()));
if (snapshot.isSuccess()) {
successCount++;
}
if (DiskMonitorConstant.STATUS_WARNING.equals(snapshot.getCurrentStatus())) {
warningCount++;
}
if (DiskMonitorConstant.STATUS_ALARM.equals(snapshot.getCurrentStatus())) {
alarmCount++;
}
NotifyDecision notifyDecision = resolveNotifyDecision(target, snapshot.getCurrentStatus(), forceNotify);
DiskMonitorResult result = buildResult(job, target, snapshot, notifyDecision);
resultMapper.insert(result);
if (notifyDecision.isShouldNotify()) {
diskMonitorNotificationComponent.sendNotifications(job, target, result,
snapshot.getUsedPercent(), snapshot.getCurrentStatus(),
notifyDecision.getNotifyReason(), notifyDecision.getNotifyLevel(),
snapshot.getScanTime(), snapshot.getMessage());
}
updateTargetLastState(target, snapshot);
} catch (Exception exception) {
log.error("磁盘监控执行异常driveLetter={}", target.getDriveLetter(), exception);
errorMessages.add(target.getDriveLetter() + ":" + exception.getMessage());
ScanSnapshot failedSnapshot = ScanSnapshot.failed(exception.getMessage());
DiskMonitorResult result = buildResult(job, target, failedSnapshot,
new NotifyDecision(false, DiskMonitorConstant.NOTIFY_REASON_NO_NOTIFY, resolveNotifyLevel(failedSnapshot.getCurrentStatus())));
resultMapper.insert(result);
updateTargetLastState(target, failedSnapshot);
}
}
finishJob(job, policy, successCount, warningCount, alarmCount, errorMessages);
DiskMonitorVO.JobRunVO runVO = new DiskMonitorVO.JobRunVO();
runVO.setJobId(job.getId());
runVO.setJobNo(job.getJobNo());
return runVO;
}
private void finishJob(DiskMonitorJob job, DiskMonitorPolicy policy, int successCount, int warningCount,
int alarmCount, List<String> errorMessages) {
job.setSuccessCount(successCount);
job.setWarningCount(warningCount);
job.setAlarmCount(alarmCount);
job.setFinishedAt(LocalDateTime.now());
if (successCount == job.getTargetCount()) {
job.setJobStatus(DiskMonitorConstant.JOB_STATUS_SUCCESS);
} else if (successCount == 0) {
job.setJobStatus(DiskMonitorConstant.JOB_STATUS_FAILED);
} else {
job.setJobStatus(DiskMonitorConstant.JOB_STATUS_PARTIAL_SUCCESS);
}
if (!errorMessages.isEmpty()) {
job.setMessage(String.join("", errorMessages));
}
jobMapper.updateById(job);
policy.setLastJobId(job.getId());
policyMapper.updateById(policy);
}
private DiskMonitorResult buildResult(DiskMonitorJob job, DiskMonitorTarget target, ScanSnapshot snapshot, NotifyDecision notifyDecision) {
DiskMonitorResult result = new DiskMonitorResult();
result.setJobId(job.getId());
result.setTargetId(target.getId());
result.setDriveLetter(target.getDriveLetter());
result.setTotalBytes(snapshot.getTotalBytes());
result.setUsedBytes(snapshot.getUsedBytes());
result.setFreeBytes(snapshot.getFreeBytes());
result.setUsedPercent(snapshot.getUsedPercent());
result.setCurrentStatus(snapshot.getCurrentStatus());
result.setPreviousStatus(resolvePreviousStatus(target.getLastStatus()));
result.setStatusChanged(snapshot.getCurrentStatus().equals(resolvePreviousStatus(target.getLastStatus())) ? 0 : 1);
result.setShouldNotify(notifyDecision.isShouldNotify() ? 1 : 0);
result.setNotifyReason(notifyDecision.getNotifyReason());
result.setScanTime(snapshot.getScanTime());
result.setMessage(snapshot.getMessage());
return result;
}
private void updateTargetLastState(DiskMonitorTarget target, ScanSnapshot snapshot) {
target.setLastStatus(snapshot.getCurrentStatus());
target.setLastScanTime(snapshot.getScanTime());
target.setLastUsedPercent(snapshot.getUsedPercent());
targetMapper.updateById(target);
}
private NotifyDecision resolveNotifyDecision(DiskMonitorTarget target, String currentStatus, boolean forceNotify) {
String previousStatus = resolvePreviousStatus(target.getLastStatus());
if (forceNotify) {
return new NotifyDecision(true, DiskMonitorConstant.NOTIFY_REASON_STATUS_CHANGED, resolveNotifyLevel(currentStatus));
}
if (DiskMonitorConstant.STATUS_ALARM.equals(currentStatus)) {
return new NotifyDecision(true, DiskMonitorConstant.NOTIFY_REASON_ALARM_EVERY_TIME, DiskMonitorConstant.NOTIFY_LEVEL_ALARM);
}
if (DiskMonitorConstant.STATUS_WARNING.equals(currentStatus) && !DiskMonitorConstant.STATUS_WARNING.equals(previousStatus)) {
return new NotifyDecision(true, DiskMonitorConstant.NOTIFY_REASON_STATUS_CHANGED, DiskMonitorConstant.NOTIFY_LEVEL_WARNING);
}
if (DiskMonitorConstant.STATUS_NORMAL.equals(currentStatus)
&& (DiskMonitorConstant.STATUS_WARNING.equals(previousStatus) || DiskMonitorConstant.STATUS_ALARM.equals(previousStatus))) {
return new NotifyDecision(true, DiskMonitorConstant.NOTIFY_REASON_STATUS_CHANGED, DiskMonitorConstant.NOTIFY_LEVEL_RECOVER);
}
return new NotifyDecision(false, DiskMonitorConstant.NOTIFY_REASON_NO_NOTIFY, resolveNotifyLevel(currentStatus));
}
private String resolveNotifyLevel(String currentStatus) {
if (DiskMonitorConstant.STATUS_ALARM.equals(currentStatus)) {
return DiskMonitorConstant.NOTIFY_LEVEL_ALARM;
}
if (DiskMonitorConstant.STATUS_WARNING.equals(currentStatus)) {
return DiskMonitorConstant.NOTIFY_LEVEL_WARNING;
}
return DiskMonitorConstant.NOTIFY_LEVEL_RECOVER;
}
private ScanSnapshot scanDrive(String driveLetter) {
File driveRoot = new File(driveLetter + File.separator);
if (!driveRoot.exists()) {
log.warn("磁盘监控扫描跳过监控盘符不存在或当前运行账号无访问权限driveLetter={}", driveLetter);
return ScanSnapshot.failed("盘符不存在或当前运行账户无访问权限");
}
long totalBytes = driveRoot.getTotalSpace();
if (totalBytes <= 0) {
log.warn("磁盘监控扫描失败未读取到有效磁盘容量driveLetter={}", driveLetter);
return ScanSnapshot.failed("未读取到有效磁盘容量");
}
long freeBytes = driveRoot.getUsableSpace();
long usedBytes = totalBytes - freeBytes;
BigDecimal usedPercent = BigDecimal.valueOf(usedBytes)
.multiply(BigDecimal.valueOf(100))
.divide(BigDecimal.valueOf(totalBytes), 2, RoundingMode.HALF_UP);
return new ScanSnapshot(true, totalBytes, usedBytes, freeBytes, usedPercent, LocalDateTime.now(),
DiskMonitorConstant.STATUS_UNKNOWN, null);
}
private ScanSnapshot resolveSnapshotStatus(DiskMonitorTarget target, ScanSnapshot snapshot) {
if (!snapshot.isSuccess()) {
return snapshot;
}
String currentStatus;
if (snapshot.getUsedPercent().compareTo(BigDecimal.valueOf(target.getAlarmUsagePercent())) >= 0) {
currentStatus = DiskMonitorConstant.STATUS_ALARM;
} else if (snapshot.getUsedPercent().compareTo(BigDecimal.valueOf(target.getWarningUsagePercent())) >= 0) {
currentStatus = DiskMonitorConstant.STATUS_WARNING;
} else {
currentStatus = DiskMonitorConstant.STATUS_NORMAL;
}
return new ScanSnapshot(true, snapshot.getTotalBytes(), snapshot.getUsedBytes(), snapshot.getFreeBytes(),
snapshot.getUsedPercent(), snapshot.getScanTime(), currentStatus, snapshot.getMessage());
}
private List<DiskMonitorTarget> resolveExecutionTargets(String driveLetter) {
if (StrUtil.isNotBlank(driveLetter)) {
DiskMonitorTarget target = diskMonitorPolicyService.getTargetByDriveLetter(driveLetter);
return target == null ? Collections.emptyList() : Collections.singletonList(target);
}
return diskMonitorPolicyService.listEnabledTargets();
}
private void logNoExecutionTargets(String jobSource, String driveLetter) {
if (StrUtil.isNotBlank(driveLetter)) {
log.warn("磁盘监控执行跳过未找到可执行的盘符配置jobSource={}, driveLetter={}", jobSource, driveLetter);
return;
}
log.warn("磁盘监控执行跳过暂无启用的磁盘监控盘符配置jobSource={}", jobSource);
}
private void validateJobSource(String jobSource) {
if (!DiskMonitorConstant.JOB_SOURCE_APP_START.equals(jobSource)
&& !DiskMonitorConstant.JOB_SOURCE_DAILY_SCHEDULE.equals(jobSource)
&& !DiskMonitorConstant.JOB_SOURCE_MANUAL.equals(jobSource)) {
throw new BusinessException(DiskMonitorResponseEnum.JOB_SOURCE_INVALID);
}
}
private boolean isEnabled(Integer value) {
return value != null && value == 1;
}
private String resolvePreviousStatus(String previousStatus) {
return StrUtil.isBlank(previousStatus) ? DiskMonitorConstant.STATUS_UNKNOWN : previousStatus;
}
private String formatDateTime(LocalDateTime dateTime) {
return dateTime == null ? null : dateTime.format(DATE_TIME_FORMATTER);
}
private String generateJobNo() {
return "DM" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS"));
}
private DiskMonitorVO.JobListVO toJobListVO(DiskMonitorJob job) {
DiskMonitorVO.JobListVO jobListVO = new DiskMonitorVO.JobListVO();
jobListVO.setJobId(job.getId());
jobListVO.setJobNo(job.getJobNo());
jobListVO.setJobSource(job.getJobSource());
jobListVO.setStartedAt(formatDateTime(job.getStartedAt()));
jobListVO.setFinishedAt(formatDateTime(job.getFinishedAt()));
jobListVO.setJobStatus(job.getJobStatus());
jobListVO.setTargetCount(job.getTargetCount());
jobListVO.setWarningCount(job.getWarningCount());
jobListVO.setAlarmCount(job.getAlarmCount());
jobListVO.setMessage(job.getMessage());
return jobListVO;
}
private DiskMonitorVO.JobInfoVO toJobInfoVO(DiskMonitorJob job) {
DiskMonitorVO.JobInfoVO jobInfoVO = new DiskMonitorVO.JobInfoVO();
jobInfoVO.setId(job.getId());
jobInfoVO.setJobNo(job.getJobNo());
jobInfoVO.setJobSource(job.getJobSource());
jobInfoVO.setStartedAt(formatDateTime(job.getStartedAt()));
jobInfoVO.setFinishedAt(formatDateTime(job.getFinishedAt()));
jobInfoVO.setJobStatus(job.getJobStatus());
jobInfoVO.setTargetCount(job.getTargetCount());
jobInfoVO.setSuccessCount(job.getSuccessCount());
jobInfoVO.setWarningCount(job.getWarningCount());
jobInfoVO.setAlarmCount(job.getAlarmCount());
jobInfoVO.setMessage(job.getMessage());
return jobInfoVO;
}
private DiskMonitorVO.ResultVO toResultVO(DiskMonitorResult result) {
DiskMonitorVO.ResultVO resultVO = new DiskMonitorVO.ResultVO();
resultVO.setResultId(result.getId());
resultVO.setTargetId(result.getTargetId());
resultVO.setDriveLetter(result.getDriveLetter());
resultVO.setTotalBytes(result.getTotalBytes());
resultVO.setUsedBytes(result.getUsedBytes());
resultVO.setFreeBytes(result.getFreeBytes());
resultVO.setUsedPercent(result.getUsedPercent());
resultVO.setCurrentStatus(result.getCurrentStatus());
resultVO.setPreviousStatus(result.getPreviousStatus());
resultVO.setStatusChanged(Objects.equals(result.getStatusChanged(), 1));
resultVO.setShouldNotify(Objects.equals(result.getShouldNotify(), 1));
resultVO.setNotifyReason(result.getNotifyReason());
resultVO.setScanTime(formatDateTime(result.getScanTime()));
resultVO.setMessage(result.getMessage());
return resultVO;
}
private DiskMonitorVO.NotifyLogVO toNotifyLogVO(DiskMonitorNotifyLog notifyLog) {
DiskMonitorVO.NotifyLogVO notifyLogVO = new DiskMonitorVO.NotifyLogVO();
notifyLogVO.setId(notifyLog.getId());
notifyLogVO.setResultId(notifyLog.getResultId());
notifyLogVO.setDriveLetter(notifyLog.getDriveLetter());
notifyLogVO.setNotifyLevel(notifyLog.getNotifyLevel());
notifyLogVO.setChannelType(notifyLog.getChannelType());
notifyLogVO.setChannelTarget(notifyLog.getChannelTarget());
notifyLogVO.setSendStatus(notifyLog.getSendStatus());
notifyLogVO.setResponseMessage(notifyLog.getResponseMessage());
notifyLogVO.setSentAt(formatDateTime(notifyLog.getSentAt()));
return notifyLogVO;
}
private static class NotifyDecision {
private final boolean shouldNotify;
private final String notifyReason;
private final String notifyLevel;
private NotifyDecision(boolean shouldNotify, String notifyReason, String notifyLevel) {
this.shouldNotify = shouldNotify;
this.notifyReason = notifyReason;
this.notifyLevel = notifyLevel;
}
public boolean isShouldNotify() {
return shouldNotify;
}
public String getNotifyReason() {
return notifyReason;
}
public String getNotifyLevel() {
return notifyLevel;
}
}
private static class ScanSnapshot {
private final boolean success;
private final Long totalBytes;
private final Long usedBytes;
private final Long freeBytes;
private final BigDecimal usedPercent;
private final LocalDateTime scanTime;
private final String currentStatus;
private final String message;
private ScanSnapshot(boolean success, Long totalBytes, Long usedBytes, Long freeBytes,
BigDecimal usedPercent, LocalDateTime scanTime, String currentStatus, String message) {
this.success = success;
this.totalBytes = totalBytes;
this.usedBytes = usedBytes;
this.freeBytes = freeBytes;
this.usedPercent = usedPercent == null ? BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP) : usedPercent;
this.scanTime = scanTime == null ? LocalDateTime.now() : scanTime;
this.currentStatus = StrUtil.blankToDefault(currentStatus, DiskMonitorConstant.STATUS_UNKNOWN);
this.message = message;
}
public static ScanSnapshot failed(String message) {
return new ScanSnapshot(false, 0L, 0L, 0L, BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP),
LocalDateTime.now(), DiskMonitorConstant.STATUS_UNKNOWN, message);
}
public boolean isSuccess() {
return success;
}
public Long getTotalBytes() {
return totalBytes;
}
public Long getUsedBytes() {
return usedBytes;
}
public Long getFreeBytes() {
return freeBytes;
}
public BigDecimal getUsedPercent() {
return usedPercent;
}
public LocalDateTime getScanTime() {
return scanTime;
}
public String getCurrentStatus() {
if (!success || totalBytes == null || totalBytes <= 0) {
return DiskMonitorConstant.STATUS_UNKNOWN;
}
return currentStatus == null ? DiskMonitorConstant.STATUS_NORMAL : currentStatus;
}
public String getMessage() {
return message;
}
}
}

View File

@@ -0,0 +1,34 @@
package com.njcn.gather.systemmonitor.disk.service.impl;
import com.njcn.common.pojo.exception.BusinessException;
import com.njcn.gather.systemmonitor.disk.enums.DiskMonitorResponseEnum;
import com.njcn.gather.systemmonitor.disk.pojo.param.DiskMonitorParam;
import com.njcn.gather.systemmonitor.disk.pojo.po.DiskMonitorTarget;
import com.njcn.gather.systemmonitor.disk.service.IDiskMonitorJobService;
import com.njcn.gather.systemmonitor.disk.service.IDiskMonitorNotifyService;
import com.njcn.gather.systemmonitor.disk.service.IDiskMonitorPolicyService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
/**
* 磁盘监控通知服务实现。
*/
@Service
@RequiredArgsConstructor
public class DiskMonitorNotifyServiceImpl implements IDiskMonitorNotifyService {
private final IDiskMonitorPolicyService diskMonitorPolicyService;
private final IDiskMonitorJobService diskMonitorJobService;
@Override
@Transactional(rollbackFor = Exception.class)
public boolean testNotify(DiskMonitorParam.NotifyTestParam param) {
DiskMonitorTarget target = diskMonitorPolicyService.getTargetByDriveLetter(param.getDriveLetter());
if (target == null) {
throw new BusinessException(DiskMonitorResponseEnum.NOTIFY_TARGET_NOT_FOUND);
}
diskMonitorJobService.executeNotifyTest(target.getDriveLetter());
return true;
}
}

View File

@@ -0,0 +1,341 @@
package com.njcn.gather.systemmonitor.disk.service.impl;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.njcn.common.pojo.enums.response.CommonResponseEnum;
import com.njcn.common.pojo.exception.BusinessException;
import com.njcn.gather.systemmonitor.disk.constant.DiskMonitorConstant;
import com.njcn.gather.systemmonitor.disk.enums.DiskMonitorResponseEnum;
import com.njcn.gather.systemmonitor.disk.event.DiskMonitorPolicyChangedEvent;
import com.njcn.gather.systemmonitor.disk.mapper.DiskMonitorPolicyMapper;
import com.njcn.gather.systemmonitor.disk.mapper.DiskMonitorTargetMapper;
import com.njcn.gather.systemmonitor.disk.pojo.dto.DiskMonitorNotifyHttpItem;
import com.njcn.gather.systemmonitor.disk.pojo.dto.DiskMonitorNotifyPathItem;
import com.njcn.gather.systemmonitor.disk.pojo.param.DiskMonitorParam;
import com.njcn.gather.systemmonitor.disk.pojo.po.DiskMonitorPolicy;
import com.njcn.gather.systemmonitor.disk.pojo.po.DiskMonitorTarget;
import com.njcn.gather.systemmonitor.disk.pojo.vo.DiskMonitorVO;
import com.njcn.gather.systemmonitor.disk.service.IDiskMonitorPolicyService;
import lombok.RequiredArgsConstructor;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.net.URL;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.stream.Collectors;
/**
* 磁盘监控配置服务实现。
*/
@Service
@RequiredArgsConstructor
public class DiskMonitorPolicyServiceImpl implements IDiskMonitorPolicyService {
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
private static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern("HH:mm:ss");
private static final TypeReference<List<DiskMonitorNotifyPathItem>> PATH_LIST_TYPE = new TypeReference<List<DiskMonitorNotifyPathItem>>() {
};
private static final TypeReference<List<DiskMonitorNotifyHttpItem>> HTTP_LIST_TYPE = new TypeReference<List<DiskMonitorNotifyHttpItem>>() {
};
private final DiskMonitorPolicyMapper policyMapper;
private final DiskMonitorTargetMapper targetMapper;
private final ObjectMapper objectMapper;
private final ApplicationEventPublisher applicationEventPublisher;
@Override
@Transactional(rollbackFor = Exception.class)
public DiskMonitorVO.PolicyDetailVO getPolicyDetail() {
DiskMonitorPolicy policy = getOrCreatePolicy();
DiskMonitorVO.PolicyDetailVO detailVO = new DiskMonitorVO.PolicyDetailVO();
detailVO.setPolicy(toPolicyVO(policy));
detailVO.setTargets(listTargetsByPolicyId(policy.getId()).stream().map(this::toTargetVO).collect(Collectors.toList()));
return detailVO;
}
@Override
@Transactional(rollbackFor = Exception.class)
public boolean savePolicy(DiskMonitorParam.PolicySaveParam param) {
validateSaveParam(param);
DiskMonitorPolicy currentPolicy = getOrCreatePolicy();
DiskMonitorParam.PolicyParam policyParam = param.getPolicy();
currentPolicy.setPolicyName(policyParam.getPolicyName().trim());
currentPolicy.setMonitorEnabled(boolToInt(policyParam.getMonitorEnabled(), true));
currentPolicy.setRunOnAppStart(boolToInt(policyParam.getRunOnAppStart(), true));
currentPolicy.setDailyRunTime(parseTime(policyParam.getDailyRunTime()));
currentPolicy.setWarningNotifyMode(resolveWarningNotifyMode(policyParam.getWarningNotifyMode()));
currentPolicy.setAlarmNotifyMode(resolveAlarmNotifyMode(policyParam.getAlarmNotifyMode()));
currentPolicy.setRemark(policyParam.getRemark());
if (policyMapper.updateById(currentPolicy) <= 0) {
throw new BusinessException(DiskMonitorResponseEnum.POLICY_SAVE_FAILED);
}
List<DiskMonitorTarget> existingTargets = listTargetsByPolicyId(currentPolicy.getId());
Map<Long, DiskMonitorTarget> existingById = existingTargets.stream()
.collect(Collectors.toMap(DiskMonitorTarget::getId, item -> item, (left, right) -> left, LinkedHashMap::new));
Map<String, DiskMonitorTarget> existingByDrive = existingTargets.stream()
.collect(Collectors.toMap(item -> normalizeDriveLetter(item.getDriveLetter()), item -> item, (left, right) -> left, LinkedHashMap::new));
List<Long> keepIds = new ArrayList<>();
for (DiskMonitorParam.TargetParam targetParam : param.getTargets()) {
String driveLetter = normalizeDriveLetter(targetParam.getDriveLetter());
DiskMonitorTarget target = targetParam.getId() == null ? null : existingById.get(targetParam.getId());
if (target == null) {
target = existingByDrive.get(driveLetter);
}
if (target == null) {
target = new DiskMonitorTarget();
target.setPolicyId(currentPolicy.getId());
target.setDriveLetter(driveLetter);
target.setLastStatus(DiskMonitorConstant.STATUS_UNKNOWN);
}
fillTarget(target, targetParam, driveLetter);
if (target.getId() == null) {
targetMapper.insert(target);
} else {
targetMapper.updateById(target);
}
keepIds.add(target.getId());
}
List<Long> deleteIds = existingTargets.stream()
.map(DiskMonitorTarget::getId)
.filter(id -> !keepIds.contains(id))
.collect(Collectors.toList());
if (!deleteIds.isEmpty()) {
targetMapper.deleteBatchIds(deleteIds);
}
applicationEventPublisher.publishEvent(new DiskMonitorPolicyChangedEvent());
return true;
}
@Override
public DiskMonitorPolicy getCurrentPolicy() {
return policyMapper.selectOne(new QueryWrapper<DiskMonitorPolicy>().orderByAsc("id").last("LIMIT 1"));
}
@Override
@Transactional(rollbackFor = Exception.class)
public DiskMonitorPolicy getOrCreatePolicy() {
DiskMonitorPolicy policy = getCurrentPolicy();
if (policy != null) {
return policy;
}
policy = new DiskMonitorPolicy();
policy.setPolicyName(DiskMonitorConstant.DEFAULT_POLICY_NAME);
policy.setMonitorEnabled(1);
policy.setRunOnAppStart(1);
policy.setDailyRunTime(parseTime(DiskMonitorConstant.DEFAULT_DAILY_RUN_TIME));
policy.setWarningNotifyMode(DiskMonitorConstant.WARNING_NOTIFY_MODE_STATUS_CHANGE);
policy.setAlarmNotifyMode(DiskMonitorConstant.ALARM_NOTIFY_MODE_EVERY_TIME);
policyMapper.insert(policy);
return policy;
}
@Override
public List<DiskMonitorTarget> listEnabledTargets() {
return targetMapper.selectList(new QueryWrapper<DiskMonitorTarget>()
.eq("monitor_enabled", 1)
.orderByAsc("drive_letter", "id"));
}
@Override
public DiskMonitorTarget getTargetByDriveLetter(String driveLetter) {
return targetMapper.selectOne(new QueryWrapper<DiskMonitorTarget>()
.eq("drive_letter", normalizeDriveLetter(driveLetter))
.last("LIMIT 1"));
}
private List<DiskMonitorTarget> listTargetsByPolicyId(Long policyId) {
return targetMapper.selectList(new QueryWrapper<DiskMonitorTarget>()
.eq("policy_id", policyId)
.orderByAsc("drive_letter", "id"));
}
private void fillTarget(DiskMonitorTarget target, DiskMonitorParam.TargetParam targetParam, String driveLetter) {
target.setDriveLetter(driveLetter);
target.setMonitorEnabled(boolToInt(targetParam.getMonitorEnabled(), true));
target.setWarningUsagePercent(targetParam.getWarningUsagePercent());
target.setAlarmUsagePercent(targetParam.getAlarmUsagePercent());
target.setNotifyPathEnabled(boolToInt(targetParam.getNotifyPathEnabled(), false));
target.setNotifyPathListJson(writeValueAsJson(targetParam.getNotifyPathList()));
target.setNotifyHttpEnabled(boolToInt(targetParam.getNotifyHttpEnabled(), false));
target.setNotifyHttpListJson(writeValueAsJson(targetParam.getNotifyHttpList()));
target.setRemark(targetParam.getRemark());
}
private void validateSaveParam(DiskMonitorParam.PolicySaveParam param) {
DiskMonitorParam.PolicyParam policyParam = param.getPolicy();
parseTime(policyParam.getDailyRunTime());
resolveWarningNotifyMode(policyParam.getWarningNotifyMode());
resolveAlarmNotifyMode(policyParam.getAlarmNotifyMode());
Map<String, Boolean> driveMap = new LinkedHashMap<>();
for (DiskMonitorParam.TargetParam targetParam : param.getTargets()) {
String driveLetter = normalizeDriveLetter(targetParam.getDriveLetter());
if (driveMap.put(driveLetter, Boolean.TRUE) != null) {
throw new BusinessException(DiskMonitorResponseEnum.DRIVE_LETTER_REPEAT);
}
if (targetParam.getAlarmUsagePercent() < targetParam.getWarningUsagePercent()) {
throw new BusinessException(DiskMonitorResponseEnum.USAGE_PERCENT_INVALID);
}
if (Boolean.TRUE.equals(targetParam.getNotifyPathEnabled())
&& (targetParam.getNotifyPathList() == null || targetParam.getNotifyPathList().isEmpty())) {
throw new BusinessException(DiskMonitorResponseEnum.NOTIFY_PATH_EMPTY);
}
if (Boolean.TRUE.equals(targetParam.getNotifyHttpEnabled())
&& (targetParam.getNotifyHttpList() == null || targetParam.getNotifyHttpList().isEmpty())) {
throw new BusinessException(DiskMonitorResponseEnum.NOTIFY_HTTP_EMPTY);
}
if (targetParam.getNotifyPathList() != null) {
for (DiskMonitorNotifyPathItem item : targetParam.getNotifyPathList()) {
if (StrUtil.isBlank(item.getPath())) {
throw new BusinessException(DiskMonitorResponseEnum.NOTIFY_PATH_VALUE_EMPTY);
}
}
}
if (targetParam.getNotifyHttpList() != null) {
for (DiskMonitorNotifyHttpItem item : targetParam.getNotifyHttpList()) {
validateHttpUrl(item.getUrl());
}
}
}
}
private String resolveWarningNotifyMode(String mode) {
String value = StrUtil.blankToDefault(mode, DiskMonitorConstant.WARNING_NOTIFY_MODE_STATUS_CHANGE).trim().toUpperCase(Locale.ROOT);
if (!DiskMonitorConstant.WARNING_NOTIFY_MODE_STATUS_CHANGE.equals(value)) {
throw new BusinessException(DiskMonitorResponseEnum.POLICY_MODE_INVALID);
}
return value;
}
private String resolveAlarmNotifyMode(String mode) {
String value = StrUtil.blankToDefault(mode, DiskMonitorConstant.ALARM_NOTIFY_MODE_EVERY_TIME).trim().toUpperCase(Locale.ROOT);
if (!DiskMonitorConstant.ALARM_NOTIFY_MODE_EVERY_TIME.equals(value)) {
throw new BusinessException(DiskMonitorResponseEnum.POLICY_MODE_INVALID);
}
return value;
}
private void validateHttpUrl(String url) {
try {
URL httpUrl = new URL(url);
httpUrl.toURI();
String protocol = httpUrl.getProtocol();
if (!"http".equalsIgnoreCase(protocol) && !"https".equalsIgnoreCase(protocol)) {
throw new BusinessException(DiskMonitorResponseEnum.NOTIFY_HTTP_URL_INVALID);
}
} catch (Exception exception) {
throw new BusinessException(DiskMonitorResponseEnum.NOTIFY_HTTP_URL_INVALID);
}
}
private String normalizeDriveLetter(String driveLetter) {
if (StrUtil.isBlank(driveLetter)) {
throw new BusinessException(DiskMonitorResponseEnum.DRIVE_LETTER_FORMAT_ERROR);
}
String normalized = driveLetter.trim().toUpperCase(Locale.ROOT);
if (!normalized.matches("^[A-Z]:$")) {
throw new BusinessException(DiskMonitorResponseEnum.DRIVE_LETTER_FORMAT_ERROR);
}
return normalized;
}
private LocalTime parseTime(String time) {
try {
return LocalTime.parse(time, TIME_FORMATTER);
} catch (DateTimeParseException exception) {
throw new BusinessException(DiskMonitorResponseEnum.DAILY_RUN_TIME_FORMAT_ERROR);
}
}
private int boolToInt(Boolean value, boolean defaultValue) {
return Boolean.TRUE.equals(value == null ? defaultValue : value) ? 1 : 0;
}
private boolean isEnabled(Integer value) {
return value != null && value == 1;
}
private String writeValueAsJson(Object value) {
if (value == null) {
return null;
}
try {
return objectMapper.writeValueAsString(value);
} catch (JsonProcessingException exception) {
throw new BusinessException(CommonResponseEnum.JSON_CONVERT_EXCEPTION, exception.getMessage());
}
}
private List<DiskMonitorNotifyPathItem> parsePathItems(String json) {
return readJsonList(json, PATH_LIST_TYPE);
}
private List<DiskMonitorNotifyHttpItem> parseHttpItems(String json) {
return readJsonList(json, HTTP_LIST_TYPE);
}
private <T> List<T> readJsonList(String json, TypeReference<List<T>> typeReference) {
if (StrUtil.isBlank(json)) {
return new ArrayList<>();
}
try {
List<T> result = objectMapper.readValue(json, typeReference);
return result == null ? new ArrayList<>() : result;
} catch (Exception exception) {
throw new BusinessException(CommonResponseEnum.JSON_CONVERT_EXCEPTION, exception.getMessage());
}
}
private DiskMonitorVO.PolicyVO toPolicyVO(DiskMonitorPolicy policy) {
DiskMonitorVO.PolicyVO policyVO = new DiskMonitorVO.PolicyVO();
policyVO.setId(policy.getId());
policyVO.setPolicyName(policy.getPolicyName());
policyVO.setMonitorEnabled(isEnabled(policy.getMonitorEnabled()));
policyVO.setRunOnAppStart(isEnabled(policy.getRunOnAppStart()));
policyVO.setDailyRunTime(policy.getDailyRunTime() == null ? null : policy.getDailyRunTime().format(TIME_FORMATTER));
policyVO.setWarningNotifyMode(policy.getWarningNotifyMode());
policyVO.setAlarmNotifyMode(policy.getAlarmNotifyMode());
policyVO.setLastJobId(policy.getLastJobId());
policyVO.setRemark(policy.getRemark());
return policyVO;
}
private DiskMonitorVO.TargetVO toTargetVO(DiskMonitorTarget target) {
DiskMonitorVO.TargetVO targetVO = new DiskMonitorVO.TargetVO();
targetVO.setId(target.getId());
targetVO.setDriveLetter(target.getDriveLetter());
targetVO.setMonitorEnabled(isEnabled(target.getMonitorEnabled()));
targetVO.setWarningUsagePercent(target.getWarningUsagePercent());
targetVO.setAlarmUsagePercent(target.getAlarmUsagePercent());
targetVO.setNotifyPathEnabled(isEnabled(target.getNotifyPathEnabled()));
targetVO.setNotifyPathList(parsePathItems(target.getNotifyPathListJson()));
targetVO.setNotifyHttpEnabled(isEnabled(target.getNotifyHttpEnabled()));
targetVO.setNotifyHttpList(parseHttpItems(target.getNotifyHttpListJson()));
targetVO.setLastStatus(target.getLastStatus());
targetVO.setLastScanTime(formatDateTime(target.getLastScanTime()));
targetVO.setLastUsedPercent(target.getLastUsedPercent());
targetVO.setRemark(target.getRemark());
return targetVO;
}
private String formatDateTime(LocalDateTime dateTime) {
return dateTime == null ? null : dateTime.format(DATE_TIME_FORMATTER);
}
}

View File

@@ -0,0 +1,104 @@
CREATE TABLE IF NOT EXISTS `disk_monitor_policy` (
`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键',
`policy_name` VARCHAR(100) NOT NULL DEFAULT '默认磁盘监控策略' COMMENT '策略名称',
`monitor_enabled` TINYINT(1) NOT NULL DEFAULT 1 COMMENT '是否启用监控0否 1是',
`run_on_app_start` TINYINT(1) NOT NULL DEFAULT 1 COMMENT '应用启动后是否执行一次0否 1是',
`daily_run_time` TIME NOT NULL COMMENT '每日统一执行时间',
`warning_notify_mode` VARCHAR(32) NOT NULL DEFAULT 'STATUS_CHANGE' COMMENT '预警通知模式',
`alarm_notify_mode` VARCHAR(32) NOT NULL DEFAULT 'EVERY_TIME' COMMENT '告警通知模式',
`last_job_id` BIGINT NULL COMMENT '最近一次任务ID',
`remark` VARCHAR(500) NULL COMMENT '备注',
`created_by` VARCHAR(64) NULL COMMENT '创建人',
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updated_by` VARCHAR(64) NULL COMMENT '更新人',
`updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='磁盘监控全局策略表';
CREATE TABLE IF NOT EXISTS `disk_monitor_target` (
`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键',
`policy_id` BIGINT NOT NULL COMMENT '策略ID',
`drive_letter` VARCHAR(10) NOT NULL COMMENT '盘符,例如 C:',
`monitor_enabled` TINYINT(1) NOT NULL DEFAULT 1 COMMENT '是否启用监控0否 1是',
`warning_usage_percent` TINYINT UNSIGNED NOT NULL COMMENT '预警使用率阈值',
`alarm_usage_percent` TINYINT UNSIGNED NOT NULL COMMENT '告警使用率阈值',
`notify_path_enabled` TINYINT(1) NOT NULL DEFAULT 0 COMMENT '是否启用路径通知0否 1是',
`notify_path_list_json` JSON NULL COMMENT '路径通知目标列表JSON',
`notify_http_enabled` TINYINT(1) NOT NULL DEFAULT 0 COMMENT '是否启用HTTP通知0否 1是',
`notify_http_list_json` JSON NULL COMMENT 'HTTP通知目标列表JSON',
`last_status` VARCHAR(32) NOT NULL DEFAULT 'UNKNOWN' COMMENT '最近一次状态',
`last_scan_time` DATETIME NULL COMMENT '最近扫描时间',
`last_used_percent` DECIMAL(5,2) NULL COMMENT '最近一次使用率',
`remark` VARCHAR(500) NULL COMMENT '备注',
`created_by` VARCHAR(64) NULL COMMENT '创建人',
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updated_by` VARCHAR(64) NULL COMMENT '更新人',
`updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_drive_letter` (`drive_letter`),
KEY `idx_policy_id` (`policy_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='磁盘监控盘符配置表';
CREATE TABLE IF NOT EXISTS `disk_monitor_job` (
`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键',
`job_no` VARCHAR(64) NOT NULL COMMENT '任务编号',
`job_source` VARCHAR(32) NOT NULL COMMENT '任务来源',
`planned_time` DATETIME NULL COMMENT '计划执行时间',
`started_at` DATETIME NOT NULL COMMENT '开始时间',
`finished_at` DATETIME NULL COMMENT '结束时间',
`job_status` VARCHAR(32) NOT NULL COMMENT '任务状态',
`target_count` INT NOT NULL DEFAULT 0 COMMENT '计划扫描盘符数量',
`success_count` INT NOT NULL DEFAULT 0 COMMENT '成功扫描数量',
`warning_count` INT NOT NULL DEFAULT 0 COMMENT '预警数量',
`alarm_count` INT NOT NULL DEFAULT 0 COMMENT '告警数量',
`message` VARCHAR(1000) NULL COMMENT '结果说明',
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_job_no` (`job_no`),
KEY `idx_job_source` (`job_source`),
KEY `idx_started_at` (`started_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='磁盘监控任务批次表';
CREATE TABLE IF NOT EXISTS `disk_monitor_result` (
`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键',
`job_id` BIGINT NOT NULL COMMENT '任务ID',
`target_id` BIGINT NOT NULL COMMENT '盘符配置ID',
`drive_letter` VARCHAR(10) NOT NULL COMMENT '盘符',
`total_bytes` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '总容量字节数',
`used_bytes` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '已使用字节数',
`free_bytes` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '剩余字节数',
`used_percent` DECIMAL(5,2) NOT NULL DEFAULT 0.00 COMMENT '使用率',
`current_status` VARCHAR(32) NOT NULL COMMENT '当前状态',
`previous_status` VARCHAR(32) NOT NULL DEFAULT 'UNKNOWN' COMMENT '上一次状态',
`status_changed` TINYINT(1) NOT NULL DEFAULT 0 COMMENT '状态是否变化0否 1是',
`should_notify` TINYINT(1) NOT NULL DEFAULT 0 COMMENT '本次是否通知0否 1是',
`notify_reason` VARCHAR(32) NOT NULL DEFAULT 'NO_NOTIFY' COMMENT '通知原因',
`scan_time` DATETIME NOT NULL COMMENT '扫描时间',
`message` VARCHAR(1000) NULL COMMENT '扫描说明',
PRIMARY KEY (`id`),
KEY `idx_job_id` (`job_id`),
KEY `idx_target_id` (`target_id`),
KEY `idx_drive_letter` (`drive_letter`),
KEY `idx_scan_time` (`scan_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='磁盘监控结果表';
CREATE TABLE IF NOT EXISTS `disk_monitor_notify_log` (
`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键',
`job_id` BIGINT NOT NULL COMMENT '任务ID',
`result_id` BIGINT NOT NULL COMMENT '结果ID',
`target_id` BIGINT NOT NULL COMMENT '盘符配置ID',
`drive_letter` VARCHAR(10) NOT NULL COMMENT '盘符',
`notify_level` VARCHAR(32) NOT NULL COMMENT '通知级别',
`channel_type` VARCHAR(32) NOT NULL COMMENT '通知通道类型',
`channel_target` VARCHAR(1000) NOT NULL COMMENT '通知目标',
`notify_title` VARCHAR(255) NOT NULL COMMENT '通知标题',
`notify_content` TEXT NOT NULL COMMENT '通知内容',
`send_status` VARCHAR(32) NOT NULL COMMENT '发送状态',
`response_message` VARCHAR(2000) NULL COMMENT '响应结果或异常信息',
`sent_at` DATETIME NOT NULL COMMENT '发送时间',
PRIMARY KEY (`id`),
KEY `idx_job_id` (`job_id`),
KEY `idx_result_id` (`result_id`),
KEY `idx_target_id` (`target_id`),
KEY `idx_sent_at` (`sent_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='磁盘监控通知日志表';

22
systemmonitor/pom.xml Normal file
View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.njcn.gather</groupId>
<artifactId>CN_Tool</artifactId>
<version>1.0.0</version>
</parent>
<artifactId>systemmonitor</artifactId>
<packaging>pom</packaging>
<name>systemmonitor</name>
<description>System monitor capability aggregator.</description>
<modules>
<module>disk-monitor</module>
</modules>
</project>