feat(dbms): 增加数据库备份任务停止重启功能和MySQL支持
- 添加了备份任务停止和重启接口及实现 - 实现了对MySQL数据库的支持,包括数据库名配置 - 重构了数据库连接和备份操作的SPI架构 - 优化了备份文件删除逻辑,支持目录递归删除 - 增加了连接名称唯一性校验 - 完善了备份任务状态管理和错误处理机制 - 更新了数据库连接参数验证逻辑
This commit is contained in:
@@ -4,12 +4,14 @@ import com.fasterxml.jackson.databind.JsonNode;
|
||||
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.steady.checksquare.pojo.bo.SteadyChecksquareValuePointBO;
|
||||
import com.njcn.gather.steady.datavie.config.SteadyInfluxDbProperties;
|
||||
import com.njcn.gather.steady.datavie.pojo.bo.SteadyTrendResolvedFieldBO;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
@@ -22,7 +24,9 @@ import java.time.LocalDateTime;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.time.ZoneOffset;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
@@ -56,7 +60,29 @@ public class SteadyChecksquareInfluxQueryComponent {
|
||||
}
|
||||
}
|
||||
|
||||
public List<SteadyChecksquareValuePointBO> queryValuePoints(SteadyTrendResolvedFieldBO field, LocalDateTime startTime,
|
||||
LocalDateTime endTime, int intervalMinutes) {
|
||||
validateConfig();
|
||||
String query = buildValuePointQuery(field, startTime, endTime);
|
||||
long startMillis = System.currentTimeMillis();
|
||||
log.info("数据校验指标值 InfluxDB 查询开始,measurement={},field={},lineId={},phase={},statType={},query={}",
|
||||
field.getMeasurement(), field.getField(), field.getLineId(), field.getPhase(), field.getStatType(), query);
|
||||
try {
|
||||
String body = executeQuery(query);
|
||||
List<SteadyChecksquareValuePointBO> points = parseValuePoints(body, intervalMinutes);
|
||||
log.info("数据校验指标值 InfluxDB 查询结束,pointCount={},costMs={}", points.size(), System.currentTimeMillis() - startMillis);
|
||||
return points;
|
||||
} catch (RuntimeException ex) {
|
||||
log.warn("数据校验指标值 InfluxDB 查询异常,costMs={},error={}", System.currentTimeMillis() - startMillis, ex.getMessage());
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
public String buildChecksquareQuery(SteadyTrendResolvedFieldBO field, LocalDateTime startTime, LocalDateTime endTime) {
|
||||
return buildValuePointQuery(field, startTime, endTime);
|
||||
}
|
||||
|
||||
public String buildValuePointQuery(SteadyTrendResolvedFieldBO field, LocalDateTime startTime, LocalDateTime endTime) {
|
||||
StringBuilder sql = new StringBuilder();
|
||||
sql.append("SELECT \"").append(field.getField()).append("\" AS \"value\"");
|
||||
sql.append(" FROM \"").append(field.getMeasurement()).append("\"");
|
||||
@@ -94,6 +120,35 @@ public class SteadyChecksquareInfluxQueryComponent {
|
||||
}
|
||||
}
|
||||
|
||||
private List<SteadyChecksquareValuePointBO> parseValuePoints(String body, int intervalMinutes) {
|
||||
try {
|
||||
JsonNode root = OBJECT_MAPPER.readTree(body);
|
||||
JsonNode values = root.path("results").path(0).path("series").path(0).path("values");
|
||||
List<SteadyChecksquareValuePointBO> result = new ArrayList<SteadyChecksquareValuePointBO>();
|
||||
if (!values.isArray()) {
|
||||
return result;
|
||||
}
|
||||
for (JsonNode value : values) {
|
||||
if (value.size() < 2 || value.get(1).isNull()) {
|
||||
continue;
|
||||
}
|
||||
LocalDateTime time = parseInfluxTime(value.get(0).asText());
|
||||
if (time == null) {
|
||||
continue;
|
||||
}
|
||||
SteadyChecksquareValuePointBO point = new SteadyChecksquareValuePointBO();
|
||||
point.setTime(alignToPreviousSlot(time, intervalMinutes));
|
||||
point.setValue(new BigDecimal(value.get(1).asText()));
|
||||
result.add(point);
|
||||
}
|
||||
return result;
|
||||
} catch (IOException ex) {
|
||||
throw fail("InfluxDB 返回结果解析失败:" + ex.getMessage());
|
||||
} catch (NumberFormatException ex) {
|
||||
throw fail("InfluxDB 返回指标值格式不正确:" + ex.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private LocalDateTime alignToPreviousSlot(LocalDateTime time, int intervalMinutes) {
|
||||
LocalDateTime minuteFloor = time.withSecond(0).withNano(0);
|
||||
int minuteOfDay = minuteFloor.getHour() * 60 + minuteFloor.getMinute();
|
||||
|
||||
@@ -0,0 +1,143 @@
|
||||
package com.njcn.gather.steady.checksquare.component;
|
||||
|
||||
import com.njcn.gather.steady.checksquare.pojo.bo.SteadyChecksquareValuePointBO;
|
||||
import com.njcn.gather.steady.checksquare.pojo.vo.SteadyChecksquareValueOrderDetailVO;
|
||||
import com.njcn.gather.steady.checksquare.pojo.vo.SteadyChecksquareValueOrderRuleVO;
|
||||
import com.njcn.gather.steady.datavie.pojo.bo.SteadyTrendIndicatorDefinitionBO;
|
||||
import com.njcn.gather.steady.datavie.pojo.bo.SteadyTrendResolvedFieldBO;
|
||||
import com.njcn.gather.steady.datavie.pojo.bo.SteadyTrendSeriesFieldBO;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 数据校验指标值大小关系规则。
|
||||
*/
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class SteadyChecksquareValueOrderRuleComponent {
|
||||
|
||||
private static final DateTimeFormatter OUTPUT_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||
private static final List<String> REQUIRED_STATS = Collections.unmodifiableList(Arrays.asList("MAX", "CP95", "AVG", "MIN"));
|
||||
private static final int ABNORMAL_THRESHOLD = 1;
|
||||
|
||||
private final SteadyChecksquareInfluxQueryComponent influxQueryComponent;
|
||||
|
||||
public SteadyChecksquareValueOrderRuleVO check(String lineId, SteadyTrendIndicatorDefinitionBO indicator,
|
||||
Integer harmonicOrder, LocalDateTime startTime,
|
||||
LocalDateTime endTime, int intervalMinutes) {
|
||||
SteadyChecksquareValueOrderRuleVO result = new SteadyChecksquareValueOrderRuleVO();
|
||||
if (!supportValueOrderRule(indicator)) {
|
||||
return result;
|
||||
}
|
||||
for (String phase : indicator.getPhaseCodes()) {
|
||||
Map<String, Map<LocalDateTime, BigDecimal>> statValueMap = queryStatValueMap(lineId, indicator,
|
||||
harmonicOrder, phase, startTime, endTime, intervalMinutes);
|
||||
appendAbnormalDetails(result, phase, statValueMap);
|
||||
}
|
||||
result.setAbnormalPointCount(result.getAbnormalDetails().size());
|
||||
result.setAbnormal(result.getAbnormalPointCount() > ABNORMAL_THRESHOLD);
|
||||
return result;
|
||||
}
|
||||
|
||||
private boolean supportValueOrderRule(SteadyTrendIndicatorDefinitionBO indicator) {
|
||||
return indicator != null && indicator.getSupportStats() != null && indicator.getSupportStats().containsAll(REQUIRED_STATS);
|
||||
}
|
||||
|
||||
private Map<String, Map<LocalDateTime, BigDecimal>> queryStatValueMap(String lineId,
|
||||
SteadyTrendIndicatorDefinitionBO indicator,
|
||||
Integer harmonicOrder, String phase,
|
||||
LocalDateTime startTime, LocalDateTime endTime,
|
||||
int intervalMinutes) {
|
||||
Map<String, Map<LocalDateTime, BigDecimal>> result = new LinkedHashMap<String, Map<LocalDateTime, BigDecimal>>();
|
||||
for (String statType : REQUIRED_STATS) {
|
||||
SteadyTrendResolvedFieldBO field = buildResolvedField(lineId, indicator, harmonicOrder, phase, statType);
|
||||
result.put(statType, toValueMap(influxQueryComponent.queryValuePoints(field, startTime, endTime, intervalMinutes)));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private void appendAbnormalDetails(SteadyChecksquareValueOrderRuleVO result, String phase,
|
||||
Map<String, Map<LocalDateTime, BigDecimal>> statValueMap) {
|
||||
Map<LocalDateTime, BigDecimal> maxValues = statValueMap.get("MAX");
|
||||
Map<LocalDateTime, BigDecimal> cp95Values = statValueMap.get("CP95");
|
||||
Map<LocalDateTime, BigDecimal> avgValues = statValueMap.get("AVG");
|
||||
Map<LocalDateTime, BigDecimal> minValues = statValueMap.get("MIN");
|
||||
if (maxValues == null || cp95Values == null || avgValues == null || minValues == null) {
|
||||
return;
|
||||
}
|
||||
for (Map.Entry<LocalDateTime, BigDecimal> entry : maxValues.entrySet()) {
|
||||
LocalDateTime time = entry.getKey();
|
||||
BigDecimal maxValue = entry.getValue();
|
||||
BigDecimal cp95Value = cp95Values.get(time);
|
||||
BigDecimal avgValue = avgValues.get(time);
|
||||
BigDecimal minValue = minValues.get(time);
|
||||
// 缺少任一统计值时由缺数校验负责,不重复计入大小关系异常。
|
||||
if (maxValue == null || cp95Value == null || avgValue == null || minValue == null) {
|
||||
continue;
|
||||
}
|
||||
if (maxValue.compareTo(cp95Value) > 0 && cp95Value.compareTo(avgValue) > 0 && avgValue.compareTo(minValue) > 0) {
|
||||
continue;
|
||||
}
|
||||
result.getAbnormalDetails().add(buildDetail(time, phase, maxValue, minValue, avgValue, cp95Value));
|
||||
}
|
||||
}
|
||||
|
||||
private SteadyChecksquareValueOrderDetailVO buildDetail(LocalDateTime time, String phase, BigDecimal maxValue,
|
||||
BigDecimal minValue, BigDecimal avgValue, BigDecimal cp95Value) {
|
||||
SteadyChecksquareValueOrderDetailVO detail = new SteadyChecksquareValueOrderDetailVO();
|
||||
detail.setTime(OUTPUT_TIME_FORMATTER.format(time));
|
||||
detail.setPhase(phase);
|
||||
detail.setMaxValue(maxValue);
|
||||
detail.setMinValue(minValue);
|
||||
detail.setAvgValue(avgValue);
|
||||
detail.setCp95Value(cp95Value);
|
||||
return detail;
|
||||
}
|
||||
|
||||
private Map<LocalDateTime, BigDecimal> toValueMap(List<SteadyChecksquareValuePointBO> points) {
|
||||
Map<LocalDateTime, BigDecimal> result = new LinkedHashMap<LocalDateTime, BigDecimal>();
|
||||
if (points == null || points.isEmpty()) {
|
||||
return result;
|
||||
}
|
||||
for (SteadyChecksquareValuePointBO point : points) {
|
||||
if (point != null && point.getTime() != null && point.getValue() != null) {
|
||||
result.put(point.getTime(), point.getValue());
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private SteadyTrendResolvedFieldBO buildResolvedField(String lineId, SteadyTrendIndicatorDefinitionBO indicator,
|
||||
Integer harmonicOrder, String phase, String statType) {
|
||||
SteadyTrendResolvedFieldBO field = new SteadyTrendResolvedFieldBO();
|
||||
field.setMeasurement(indicator.getTableName());
|
||||
field.setField(resolveField(indicator, harmonicOrder));
|
||||
field.setLineId(lineId);
|
||||
field.setIndicatorCode(indicator.getIndicatorCode());
|
||||
field.setIndicatorName(indicator.getName());
|
||||
field.setPhase(phase);
|
||||
field.setStatType(statType);
|
||||
field.setUnit(indicator.getUnit());
|
||||
return field;
|
||||
}
|
||||
|
||||
private String resolveField(SteadyTrendIndicatorDefinitionBO indicator, Integer harmonicOrder) {
|
||||
if (Boolean.TRUE.equals(indicator.getHarmonic())) {
|
||||
return indicator.getHarmonicFieldPrefix() + "_" + harmonicOrder;
|
||||
}
|
||||
List<SteadyTrendSeriesFieldBO> fields = indicator.getSeriesFields();
|
||||
if (fields == null || fields.isEmpty()) {
|
||||
return "";
|
||||
}
|
||||
return fields.get(0).getField();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.njcn.gather.steady.checksquare.pojo.bo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 数据校验指标值时间点。
|
||||
*/
|
||||
@Data
|
||||
public class SteadyChecksquareValuePointBO implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** 对齐后的统计时间。 */
|
||||
private LocalDateTime time;
|
||||
|
||||
/** 指标值。 */
|
||||
private BigDecimal value;
|
||||
}
|
||||
@@ -54,6 +54,15 @@ public class SteadyChecksquareItemVO implements Serializable {
|
||||
@ApiModelProperty("最大连续缺失时长,单位分钟")
|
||||
private Integer maxContinuousMissingMinutes;
|
||||
|
||||
@ApiModelProperty("指标值大小关系是否异常")
|
||||
private Boolean abnormal;
|
||||
|
||||
@ApiModelProperty("指标值大小关系异常累计值")
|
||||
private Integer abnormalPointCount;
|
||||
|
||||
@ApiModelProperty("指标值大小关系异常明细")
|
||||
private List<SteadyChecksquareValueOrderDetailVO> abnormalDetails = new ArrayList<SteadyChecksquareValueOrderDetailVO>();
|
||||
|
||||
@ApiModelProperty("统计类型摘要")
|
||||
private List<SteadyChecksquareStatSummaryVO> statSummaries = new ArrayList<SteadyChecksquareStatSummaryVO>();
|
||||
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.njcn.gather.steady.checksquare.pojo.vo;
|
||||
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* 数据校验指标值大小关系异常明细。
|
||||
*/
|
||||
@Data
|
||||
@ApiModel("数据校验指标值大小关系异常明细")
|
||||
public class SteadyChecksquareValueOrderDetailVO implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@ApiModelProperty("时间")
|
||||
private String time;
|
||||
|
||||
@ApiModelProperty("相别")
|
||||
private String phase;
|
||||
|
||||
@ApiModelProperty("最大值")
|
||||
private BigDecimal maxValue;
|
||||
|
||||
@ApiModelProperty("最小值")
|
||||
private BigDecimal minValue;
|
||||
|
||||
@ApiModelProperty("平均值")
|
||||
private BigDecimal avgValue;
|
||||
|
||||
@ApiModelProperty("CP95 值")
|
||||
private BigDecimal cp95Value;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.njcn.gather.steady.checksquare.pojo.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 数据校验指标值大小关系规则结果。
|
||||
*/
|
||||
@Data
|
||||
public class SteadyChecksquareValueOrderRuleVO implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private Boolean abnormal = false;
|
||||
|
||||
private Integer abnormalPointCount = 0;
|
||||
|
||||
private List<SteadyChecksquareValueOrderDetailVO> abnormalDetails = new ArrayList<SteadyChecksquareValueOrderDetailVO>();
|
||||
}
|
||||
@@ -4,12 +4,14 @@ import com.njcn.common.pojo.enums.response.CommonResponseEnum;
|
||||
import com.njcn.common.pojo.exception.BusinessException;
|
||||
import com.njcn.gather.steady.checksquare.component.SteadyChecksquareCalculator;
|
||||
import com.njcn.gather.steady.checksquare.component.SteadyChecksquareInfluxQueryComponent;
|
||||
import com.njcn.gather.steady.checksquare.component.SteadyChecksquareValueOrderRuleComponent;
|
||||
import com.njcn.gather.steady.checksquare.pojo.param.SteadyChecksquareQueryParam;
|
||||
import com.njcn.gather.steady.checksquare.pojo.vo.SteadyChecksquareItemVO;
|
||||
import com.njcn.gather.steady.checksquare.pojo.vo.SteadyChecksquareQueryVO;
|
||||
import com.njcn.gather.steady.checksquare.pojo.vo.SteadyChecksquareSegmentVO;
|
||||
import com.njcn.gather.steady.checksquare.pojo.vo.SteadyChecksquareStatDetailVO;
|
||||
import com.njcn.gather.steady.checksquare.pojo.vo.SteadyChecksquareStatSummaryVO;
|
||||
import com.njcn.gather.steady.checksquare.pojo.vo.SteadyChecksquareValueOrderRuleVO;
|
||||
import com.njcn.gather.steady.checksquare.service.SteadyChecksquareService;
|
||||
import com.njcn.gather.steady.datavie.component.SteadyTrendIndicatorCatalog;
|
||||
import com.njcn.gather.steady.datavie.pojo.bo.SteadyTrendIndicatorDefinitionBO;
|
||||
@@ -52,6 +54,7 @@ public class SteadyChecksquareServiceImpl implements SteadyChecksquareService {
|
||||
private final SteadyTrendIndicatorCatalog indicatorCatalog;
|
||||
private final SteadyChecksquareInfluxQueryComponent influxQueryComponent;
|
||||
private final SteadyChecksquareCalculator calculator;
|
||||
private final SteadyChecksquareValueOrderRuleComponent valueOrderRuleComponent;
|
||||
private final AddDataTimeSlotCalculator timeSlotCalculator;
|
||||
private final AddLedgerService addLedgerService;
|
||||
|
||||
@@ -138,9 +141,20 @@ public class SteadyChecksquareServiceImpl implements SteadyChecksquareService {
|
||||
item.setMissingRate(calculateRate(item.getMissingPointCount(), totalExpected));
|
||||
item.setMissingRateText(formatRateText(item.getMissingRate()));
|
||||
item.setMaxContinuousMissingMinutes(maxContinuousMissingMinutes);
|
||||
fillValueOrderRuleResult(item, lineId, indicator, harmonicOrder, startTime, endTime, intervalMinutes);
|
||||
return item;
|
||||
}
|
||||
|
||||
private void fillValueOrderRuleResult(SteadyChecksquareItemVO item, String lineId, SteadyTrendIndicatorDefinitionBO indicator,
|
||||
Integer harmonicOrder, LocalDateTime startTime, LocalDateTime endTime,
|
||||
int intervalMinutes) {
|
||||
SteadyChecksquareValueOrderRuleVO ruleResult = valueOrderRuleComponent.check(lineId, indicator, harmonicOrder,
|
||||
startTime, endTime, intervalMinutes);
|
||||
item.setAbnormal(ruleResult.getAbnormal());
|
||||
item.setAbnormalPointCount(ruleResult.getAbnormalPointCount());
|
||||
item.setAbnormalDetails(ruleResult.getAbnormalDetails());
|
||||
}
|
||||
|
||||
private Set<LocalDateTime> queryMergedActualSlots(String lineId, SteadyTrendIndicatorDefinitionBO indicator, Integer harmonicOrder,
|
||||
String statType, LocalDateTime startTime, LocalDateTime endTime,
|
||||
int intervalMinutes) {
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
-- 稳态数据查看建议索引。
|
||||
-- 本脚本不自动执行,请按数据库现状审阅后单独执行。
|
||||
|
||||
CREATE INDEX idx_data_v_time_line_phase
|
||||
ON data_v (TIMEID, LINEID, PHASIC_TYPE);
|
||||
|
||||
CREATE INDEX idx_data_i_time_line_phase
|
||||
ON data_i (TIMEID, LINEID, PHASIC_TYPE);
|
||||
|
||||
CREATE INDEX idx_data_flicker_time_line_phase
|
||||
ON data_flicker (TIMEID, LINEID, PHASIC_TYPE);
|
||||
|
||||
CREATE INDEX idx_data_fluc_time_line_phase
|
||||
ON data_fluc (TIMEID, LINEID, PHASIC_TYPE);
|
||||
|
||||
CREATE INDEX idx_data_harmphasic_i_time_line_phase
|
||||
ON data_harmphasic_i (TIMEID, LINEID, PHASIC_TYPE);
|
||||
|
||||
CREATE INDEX idx_data_harmphasic_v_time_line_phase
|
||||
ON data_harmphasic_v (TIMEID, LINEID, PHASIC_TYPE);
|
||||
|
||||
CREATE INDEX idx_data_harmpower_p_time_line_phase
|
||||
ON data_harmpower_p (TIMEID, LINEID, PHASIC_TYPE);
|
||||
|
||||
CREATE INDEX idx_data_harmpower_q_time_line_phase
|
||||
ON data_harmpower_q (TIMEID, LINEID, PHASIC_TYPE);
|
||||
|
||||
CREATE INDEX idx_data_harmpower_s_time_line_phase
|
||||
ON data_harmpower_s (TIMEID, LINEID, PHASIC_TYPE);
|
||||
|
||||
CREATE INDEX idx_data_harmrate_i_time_line_phase
|
||||
ON data_harmrate_i (TIMEID, LINEID, PHASIC_TYPE);
|
||||
|
||||
CREATE INDEX idx_data_harmrate_v_time_line_phase
|
||||
ON data_harmrate_v (TIMEID, LINEID, PHASIC_TYPE);
|
||||
|
||||
CREATE INDEX idx_data_inharm_i_time_line_phase
|
||||
ON data_inharm_i (TIMEID, LINEID, PHASIC_TYPE);
|
||||
|
||||
CREATE INDEX idx_data_plt_time_line_phase
|
||||
ON data_plt (TIMEID, LINEID, PHASIC_TYPE);
|
||||
@@ -0,0 +1,42 @@
|
||||
-- 稳态模块菜单图标修正脚本。
|
||||
-- 本脚本不自动执行,请按数据库现状审阅后单独执行。
|
||||
|
||||
UPDATE sys_function
|
||||
SET Icon = 'DataAnalysis'
|
||||
WHERE State = 1
|
||||
AND Type = 0
|
||||
AND (
|
||||
Name = '稳态模块'
|
||||
OR Code IN ('steady', 'steadyModule', 'steadyDataView')
|
||||
OR Path IN ('/steady', '/steadyDataView', '/steady/data-view')
|
||||
);
|
||||
|
||||
UPDATE sys_function
|
||||
SET Icon = 'DataBoard'
|
||||
WHERE State = 1
|
||||
AND Type = 0
|
||||
AND (
|
||||
Name = '稳态数据'
|
||||
OR Code IN ('steadyData', 'steadyDataDetail')
|
||||
OR Path IN ('/steady/data', '/steady/data-view/detail', '/steadyDataView/index')
|
||||
);
|
||||
|
||||
UPDATE sys_function
|
||||
SET Icon = 'TrendCharts'
|
||||
WHERE State = 1
|
||||
AND Type = 0
|
||||
AND (
|
||||
Name = '稳态趋势'
|
||||
OR Code IN ('steadyTrend', 'steadyDataTrend')
|
||||
OR Path IN ('/steady/trend', '/steady/data-view/trend', '/steadyTrend/index')
|
||||
);
|
||||
|
||||
UPDATE sys_function
|
||||
SET Icon = 'CircleCheck'
|
||||
WHERE State = 1
|
||||
AND Type = 0
|
||||
AND (
|
||||
Name = '数据验证'
|
||||
OR Code IN ('dataValidation', 'steadyDataValidation')
|
||||
OR Path IN ('/steady/data-validation', '/steady/data-view/validation', '/dataValidation/index')
|
||||
);
|
||||
@@ -33,4 +33,25 @@ class SteadyChecksquareInfluxQueryComponentTest {
|
||||
Assertions.assertFalse(query.contains("quality_flag"));
|
||||
Assertions.assertFalse(query.contains("GROUP BY time"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldBuildValuePointQueryWithStatTypeFilter() {
|
||||
SteadyChecksquareInfluxQueryComponent component = new SteadyChecksquareInfluxQueryComponent(new SteadyInfluxDbProperties());
|
||||
SteadyTrendResolvedFieldBO field = new SteadyTrendResolvedFieldBO();
|
||||
field.setMeasurement("data_v");
|
||||
field.setField("rms");
|
||||
field.setLineId("line-001");
|
||||
field.setPhase("A");
|
||||
field.setStatType("CP95");
|
||||
|
||||
String query = component.buildValuePointQuery(field,
|
||||
LocalDateTime.of(2026, 5, 1, 0, 0, 0),
|
||||
LocalDateTime.of(2026, 5, 1, 23, 59, 59));
|
||||
|
||||
Assertions.assertTrue(query.contains("SELECT \"rms\" AS \"value\""));
|
||||
Assertions.assertTrue(query.contains("\"line_id\" = 'line-001'"));
|
||||
Assertions.assertTrue(query.contains("\"phasic_type\" = 'A'"));
|
||||
Assertions.assertTrue(query.contains("\"value_type\" = 'CP95'"));
|
||||
Assertions.assertTrue(query.endsWith("ORDER BY time ASC"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,155 @@
|
||||
package com.njcn.gather.steady.checksquare.component;
|
||||
|
||||
import com.njcn.gather.steady.checksquare.pojo.bo.SteadyChecksquareValuePointBO;
|
||||
import com.njcn.gather.steady.checksquare.pojo.vo.SteadyChecksquareValueOrderRuleVO;
|
||||
import com.njcn.gather.steady.datavie.pojo.bo.SteadyTrendIndicatorDefinitionBO;
|
||||
import com.njcn.gather.steady.datavie.pojo.bo.SteadyTrendSeriesFieldBO;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* 数据校验指标值大小关系规则测试。
|
||||
*/
|
||||
class SteadyChecksquareValueOrderRuleComponentTest {
|
||||
|
||||
@Test
|
||||
void shouldMarkIndicatorAbnormalWhenInvalidPointCountGreaterThanOne() {
|
||||
SteadyChecksquareInfluxQueryComponent influxQueryComponent = mock(SteadyChecksquareInfluxQueryComponent.class);
|
||||
SteadyChecksquareValueOrderRuleComponent component = new SteadyChecksquareValueOrderRuleComponent(influxQueryComponent);
|
||||
LocalDateTime firstTime = LocalDateTime.of(2026, 5, 1, 0, 0);
|
||||
LocalDateTime secondTime = LocalDateTime.of(2026, 5, 1, 0, 1);
|
||||
when(influxQueryComponent.queryValuePoints(any(), any(LocalDateTime.class), any(LocalDateTime.class), eq(1)))
|
||||
.thenAnswer(invocation -> {
|
||||
String statType = invocation.getArgument(0, com.njcn.gather.steady.datavie.pojo.bo.SteadyTrendResolvedFieldBO.class).getStatType();
|
||||
if ("MAX".equals(statType)) {
|
||||
return Arrays.asList(point(firstTime, "8"), point(secondTime, "9"));
|
||||
}
|
||||
if ("CP95".equals(statType)) {
|
||||
return Arrays.asList(point(firstTime, "8"), point(secondTime, "10"));
|
||||
}
|
||||
if ("AVG".equals(statType)) {
|
||||
return Arrays.asList(point(firstTime, "7"), point(secondTime, "8"));
|
||||
}
|
||||
if ("MIN".equals(statType)) {
|
||||
return Arrays.asList(point(firstTime, "1"), point(secondTime, "8"));
|
||||
}
|
||||
return Collections.emptyList();
|
||||
});
|
||||
|
||||
SteadyChecksquareValueOrderRuleVO result = component.check("line-001", indicator(), null,
|
||||
LocalDateTime.of(2026, 5, 1, 0, 0), LocalDateTime.of(2026, 5, 1, 0, 2), 1);
|
||||
|
||||
Assertions.assertEquals(Integer.valueOf(2), result.getAbnormalPointCount());
|
||||
Assertions.assertEquals(Boolean.TRUE, result.getAbnormal());
|
||||
Assertions.assertEquals(2, result.getAbnormalDetails().size());
|
||||
Assertions.assertEquals("2026-05-01 00:00:00", result.getAbnormalDetails().get(0).getTime());
|
||||
Assertions.assertEquals("A", result.getAbnormalDetails().get(0).getPhase());
|
||||
Assertions.assertEquals(new BigDecimal("8"), result.getAbnormalDetails().get(0).getMaxValue());
|
||||
Assertions.assertEquals(new BigDecimal("1"), result.getAbnormalDetails().get(0).getMinValue());
|
||||
Assertions.assertEquals(new BigDecimal("7"), result.getAbnormalDetails().get(0).getAvgValue());
|
||||
Assertions.assertEquals(new BigDecimal("8"), result.getAbnormalDetails().get(0).getCp95Value());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldNotMarkIndicatorAbnormalWhenOnlyOneInvalidPointExists() {
|
||||
SteadyChecksquareInfluxQueryComponent influxQueryComponent = mock(SteadyChecksquareInfluxQueryComponent.class);
|
||||
SteadyChecksquareValueOrderRuleComponent component = new SteadyChecksquareValueOrderRuleComponent(influxQueryComponent);
|
||||
LocalDateTime time = LocalDateTime.of(2026, 5, 1, 0, 0);
|
||||
when(influxQueryComponent.queryValuePoints(any(), any(LocalDateTime.class), any(LocalDateTime.class), eq(1)))
|
||||
.thenAnswer(invocation -> {
|
||||
String statType = invocation.getArgument(0, com.njcn.gather.steady.datavie.pojo.bo.SteadyTrendResolvedFieldBO.class).getStatType();
|
||||
if ("MAX".equals(statType)) {
|
||||
return Collections.singletonList(point(time, "10"));
|
||||
}
|
||||
if ("CP95".equals(statType)) {
|
||||
return Collections.singletonList(point(time, "8"));
|
||||
}
|
||||
if ("AVG".equals(statType)) {
|
||||
return Collections.singletonList(point(time, "8"));
|
||||
}
|
||||
if ("MIN".equals(statType)) {
|
||||
return Collections.singletonList(point(time, "1"));
|
||||
}
|
||||
return Collections.emptyList();
|
||||
});
|
||||
|
||||
SteadyChecksquareValueOrderRuleVO result = component.check("line-001", indicator(), null,
|
||||
LocalDateTime.of(2026, 5, 1, 0, 0), LocalDateTime.of(2026, 5, 1, 0, 1), 1);
|
||||
|
||||
Assertions.assertEquals(Integer.valueOf(1), result.getAbnormalPointCount());
|
||||
Assertions.assertEquals(Boolean.FALSE, result.getAbnormal());
|
||||
Assertions.assertEquals(1, result.getAbnormalDetails().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldSkipPointWhenAnyRequiredStatValueMissing() {
|
||||
SteadyChecksquareInfluxQueryComponent influxQueryComponent = mock(SteadyChecksquareInfluxQueryComponent.class);
|
||||
SteadyChecksquareValueOrderRuleComponent component = new SteadyChecksquareValueOrderRuleComponent(influxQueryComponent);
|
||||
LocalDateTime time = LocalDateTime.of(2026, 5, 1, 0, 0);
|
||||
when(influxQueryComponent.queryValuePoints(any(), any(LocalDateTime.class), any(LocalDateTime.class), eq(1)))
|
||||
.thenAnswer(invocation -> {
|
||||
String statType = invocation.getArgument(0, com.njcn.gather.steady.datavie.pojo.bo.SteadyTrendResolvedFieldBO.class).getStatType();
|
||||
if ("MAX".equals(statType)) {
|
||||
return Collections.singletonList(point(time, "10"));
|
||||
}
|
||||
if ("CP95".equals(statType)) {
|
||||
return Collections.singletonList(point(time, "11"));
|
||||
}
|
||||
if ("MIN".equals(statType)) {
|
||||
return Collections.singletonList(point(time, "1"));
|
||||
}
|
||||
return Collections.emptyList();
|
||||
});
|
||||
|
||||
SteadyChecksquareValueOrderRuleVO result = component.check("line-001", indicator(), null,
|
||||
LocalDateTime.of(2026, 5, 1, 0, 0), LocalDateTime.of(2026, 5, 1, 0, 1), 1);
|
||||
|
||||
Assertions.assertEquals(Integer.valueOf(0), result.getAbnormalPointCount());
|
||||
Assertions.assertEquals(Boolean.FALSE, result.getAbnormal());
|
||||
Assertions.assertTrue(result.getAbnormalDetails().isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldSkipIndicatorWhenNotAllFourStatsSupported() {
|
||||
SteadyChecksquareInfluxQueryComponent influxQueryComponent = mock(SteadyChecksquareInfluxQueryComponent.class);
|
||||
SteadyChecksquareValueOrderRuleComponent component = new SteadyChecksquareValueOrderRuleComponent(influxQueryComponent);
|
||||
SteadyTrendIndicatorDefinitionBO indicator = indicator();
|
||||
indicator.setSupportStats(Collections.singletonList("AVG"));
|
||||
|
||||
SteadyChecksquareValueOrderRuleVO result = component.check("line-001", indicator, null,
|
||||
LocalDateTime.of(2026, 5, 1, 0, 0), LocalDateTime.of(2026, 5, 1, 0, 1), 1);
|
||||
|
||||
Assertions.assertEquals(Integer.valueOf(0), result.getAbnormalPointCount());
|
||||
Assertions.assertEquals(Boolean.FALSE, result.getAbnormal());
|
||||
Assertions.assertTrue(result.getAbnormalDetails().isEmpty());
|
||||
}
|
||||
|
||||
private SteadyTrendIndicatorDefinitionBO indicator() {
|
||||
SteadyTrendIndicatorDefinitionBO indicator = new SteadyTrendIndicatorDefinitionBO();
|
||||
indicator.setIndicatorCode("V_RMS");
|
||||
indicator.setName("相电压有效值");
|
||||
indicator.setTableName("data_v");
|
||||
indicator.setPhaseCodes(Collections.singletonList("A"));
|
||||
indicator.setSeriesFields(Collections.singletonList(new SteadyTrendSeriesFieldBO("rms", "相电压有效值")));
|
||||
indicator.setSupportStats(Arrays.asList("AVG", "MAX", "MIN", "CP95"));
|
||||
indicator.setUnit("V");
|
||||
return indicator;
|
||||
}
|
||||
|
||||
private SteadyChecksquareValuePointBO point(LocalDateTime time, String value) {
|
||||
SteadyChecksquareValuePointBO point = new SteadyChecksquareValuePointBO();
|
||||
point.setTime(time);
|
||||
point.setValue(new BigDecimal(value));
|
||||
return point;
|
||||
}
|
||||
}
|
||||
@@ -2,9 +2,12 @@ package com.njcn.gather.steady.checksquare.service.impl;
|
||||
|
||||
import com.njcn.gather.steady.checksquare.component.SteadyChecksquareCalculator;
|
||||
import com.njcn.gather.steady.checksquare.component.SteadyChecksquareInfluxQueryComponent;
|
||||
import com.njcn.gather.steady.checksquare.component.SteadyChecksquareValueOrderRuleComponent;
|
||||
import com.njcn.gather.steady.checksquare.pojo.param.SteadyChecksquareQueryParam;
|
||||
import com.njcn.gather.steady.checksquare.pojo.vo.SteadyChecksquareItemVO;
|
||||
import com.njcn.gather.steady.checksquare.pojo.vo.SteadyChecksquareQueryVO;
|
||||
import com.njcn.gather.steady.checksquare.pojo.vo.SteadyChecksquareValueOrderDetailVO;
|
||||
import com.njcn.gather.steady.checksquare.pojo.vo.SteadyChecksquareValueOrderRuleVO;
|
||||
import com.njcn.gather.steady.datavie.component.SteadyTrendIndicatorCatalog;
|
||||
import com.njcn.gather.tool.adddata.component.AddDataTimeSlotCalculator;
|
||||
import com.njcn.gather.tool.addledger.pojo.vo.AddLedgerLinePathVO;
|
||||
@@ -19,6 +22,7 @@ import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
@@ -31,9 +35,12 @@ class SteadyChecksquareServiceImplTest {
|
||||
@Test
|
||||
void shouldUseFixedFlickerIntervalsPerIndicator() {
|
||||
SteadyChecksquareInfluxQueryComponent influxQueryComponent = mock(SteadyChecksquareInfluxQueryComponent.class);
|
||||
SteadyChecksquareValueOrderRuleComponent valueOrderRuleComponent = mock(SteadyChecksquareValueOrderRuleComponent.class);
|
||||
AddLedgerService addLedgerService = mock(AddLedgerService.class);
|
||||
SteadyChecksquareServiceImpl service = new SteadyChecksquareServiceImpl(new SteadyTrendIndicatorCatalog(),
|
||||
influxQueryComponent, new SteadyChecksquareCalculator(), new AddDataTimeSlotCalculator(), addLedgerService);
|
||||
influxQueryComponent, new SteadyChecksquareCalculator(), valueOrderRuleComponent, new AddDataTimeSlotCalculator(), addLedgerService);
|
||||
when(valueOrderRuleComponent.check(any(), any(), any(), any(LocalDateTime.class), any(LocalDateTime.class), anyInt()))
|
||||
.thenReturn(emptyRuleResult());
|
||||
AddLedgerLinePathVO linePath = new AddLedgerLinePathVO();
|
||||
linePath.setLineId("line-001");
|
||||
linePath.setLineName("进线一");
|
||||
@@ -66,9 +73,12 @@ class SteadyChecksquareServiceImplTest {
|
||||
@Test
|
||||
void shouldOnlyQueryRequestedHarmonicOrders() {
|
||||
SteadyChecksquareInfluxQueryComponent influxQueryComponent = mock(SteadyChecksquareInfluxQueryComponent.class);
|
||||
SteadyChecksquareValueOrderRuleComponent valueOrderRuleComponent = mock(SteadyChecksquareValueOrderRuleComponent.class);
|
||||
AddLedgerService addLedgerService = mock(AddLedgerService.class);
|
||||
SteadyChecksquareServiceImpl service = new SteadyChecksquareServiceImpl(new SteadyTrendIndicatorCatalog(),
|
||||
influxQueryComponent, new SteadyChecksquareCalculator(), new AddDataTimeSlotCalculator(), addLedgerService);
|
||||
influxQueryComponent, new SteadyChecksquareCalculator(), valueOrderRuleComponent, new AddDataTimeSlotCalculator(), addLedgerService);
|
||||
when(valueOrderRuleComponent.check(any(), any(), any(), any(LocalDateTime.class), any(LocalDateTime.class), anyInt()))
|
||||
.thenReturn(emptyRuleResult());
|
||||
AddLedgerLinePathVO linePath = new AddLedgerLinePathVO();
|
||||
linePath.setLineId("line-001");
|
||||
linePath.setLineName("进线一");
|
||||
@@ -95,9 +105,12 @@ class SteadyChecksquareServiceImplTest {
|
||||
@Test
|
||||
void shouldKeepRequestedHarmonicOrdersDistinctAndOrdered() {
|
||||
SteadyChecksquareInfluxQueryComponent influxQueryComponent = mock(SteadyChecksquareInfluxQueryComponent.class);
|
||||
SteadyChecksquareValueOrderRuleComponent valueOrderRuleComponent = mock(SteadyChecksquareValueOrderRuleComponent.class);
|
||||
AddLedgerService addLedgerService = mock(AddLedgerService.class);
|
||||
SteadyChecksquareServiceImpl service = new SteadyChecksquareServiceImpl(new SteadyTrendIndicatorCatalog(),
|
||||
influxQueryComponent, new SteadyChecksquareCalculator(), new AddDataTimeSlotCalculator(), addLedgerService);
|
||||
influxQueryComponent, new SteadyChecksquareCalculator(), valueOrderRuleComponent, new AddDataTimeSlotCalculator(), addLedgerService);
|
||||
when(valueOrderRuleComponent.check(any(), any(), any(), any(LocalDateTime.class), any(LocalDateTime.class), anyInt()))
|
||||
.thenReturn(emptyRuleResult());
|
||||
AddLedgerLinePathVO linePath = new AddLedgerLinePathVO();
|
||||
linePath.setLineId("line-001");
|
||||
linePath.setLineName("进线一");
|
||||
@@ -122,9 +135,54 @@ class SteadyChecksquareServiceImplTest {
|
||||
Assertions.assertEquals(Integer.valueOf(5), items.get(1).getHarmonicOrder());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldAssembleValueOrderRuleResultIntoItem() {
|
||||
SteadyChecksquareInfluxQueryComponent influxQueryComponent = mock(SteadyChecksquareInfluxQueryComponent.class);
|
||||
SteadyChecksquareValueOrderRuleComponent valueOrderRuleComponent = mock(SteadyChecksquareValueOrderRuleComponent.class);
|
||||
AddLedgerService addLedgerService = mock(AddLedgerService.class);
|
||||
SteadyChecksquareServiceImpl service = new SteadyChecksquareServiceImpl(new SteadyTrendIndicatorCatalog(),
|
||||
influxQueryComponent, new SteadyChecksquareCalculator(), valueOrderRuleComponent, new AddDataTimeSlotCalculator(), addLedgerService);
|
||||
AddLedgerLinePathVO linePath = new AddLedgerLinePathVO();
|
||||
linePath.setLineId("line-001");
|
||||
linePath.setLineName("进线一");
|
||||
linePath.setLineInterval(1);
|
||||
when(addLedgerService.listLinePathByLineIds(eq(Collections.singletonList("line-001"))))
|
||||
.thenReturn(Collections.singletonMap("line-001", linePath));
|
||||
when(influxQueryComponent.queryExistingSlots(any(), any(LocalDateTime.class), any(LocalDateTime.class), eq(1)))
|
||||
.thenReturn(new HashSet<LocalDateTime>(Collections.singletonList(
|
||||
LocalDateTime.of(2026, 5, 1, 0, 0))));
|
||||
SteadyChecksquareValueOrderRuleVO ruleResult = new SteadyChecksquareValueOrderRuleVO();
|
||||
SteadyChecksquareValueOrderDetailVO detail = new SteadyChecksquareValueOrderDetailVO();
|
||||
detail.setTime("2026-05-01 00:00:00");
|
||||
detail.setPhase("A");
|
||||
ruleResult.setAbnormalPointCount(2);
|
||||
ruleResult.setAbnormal(true);
|
||||
ruleResult.setAbnormalDetails(Collections.singletonList(detail));
|
||||
when(valueOrderRuleComponent.check(any(), any(), any(), any(LocalDateTime.class), any(LocalDateTime.class), eq(1)))
|
||||
.thenReturn(ruleResult);
|
||||
|
||||
SteadyChecksquareQueryParam param = new SteadyChecksquareQueryParam();
|
||||
param.setLineId("line-001");
|
||||
param.setIndicatorCodes(Collections.singletonList("V_RMS"));
|
||||
param.setTimeStart("2026-05-01 00:00:00");
|
||||
param.setTimeEnd("2026-05-01 00:01:00");
|
||||
|
||||
SteadyChecksquareQueryVO result = service.query(param);
|
||||
|
||||
SteadyChecksquareItemVO item = result.getItems().get(0);
|
||||
Assertions.assertEquals(Boolean.TRUE, item.getAbnormal());
|
||||
Assertions.assertEquals(Integer.valueOf(2), item.getAbnormalPointCount());
|
||||
Assertions.assertEquals(1, item.getAbnormalDetails().size());
|
||||
Assertions.assertEquals("A", item.getAbnormalDetails().get(0).getPhase());
|
||||
}
|
||||
|
||||
private void assertItemInterval(SteadyChecksquareItemVO item, String indicatorCode, int intervalMinutes, int expectedPointCount) {
|
||||
Assertions.assertEquals(indicatorCode, item.getIndicatorCode());
|
||||
Assertions.assertEquals(Integer.valueOf(intervalMinutes), item.getIntervalMinutes());
|
||||
Assertions.assertEquals(Integer.valueOf(expectedPointCount), item.getExpectedPointCount());
|
||||
}
|
||||
|
||||
private SteadyChecksquareValueOrderRuleVO emptyRuleResult() {
|
||||
return new SteadyChecksquareValueOrderRuleVO();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user