feat(mms-mapping): 添加ICD一致性校验功能并重构设备类型管理
- 在MappingController中新增ICD一致性校验接口checkIcdJsonConsistency - 添加IcdConsistencyCheckService服务实现ICD映射JSON一致性校验逻辑 - 添加IcdConsistencyCheckRequest和IcdConsistencyCheckResponse相关数据传输对象 - 在CsIcdPathPO中新增icdContent字段存储ICD内容字节数组 - 在CsIcdPathMapper中新增selectIcdPathList方法支持关键词搜索 - 移除设备类型相关的控制器、服务接口及实现类(MmsDeviceTypeController等) - 更新.gitignore文件排除特定jar包路径 - 在pom.xml中添加device-types模块依赖和JNA库依赖 - 更新README.md文档添加device-types模块说明 - 重命名steady-DataView为steady-dataView模块名统一格式
This commit is contained in:
@@ -9,7 +9,7 @@
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>steady-DataView</artifactId>
|
||||
<artifactId>steady-dataView</artifactId>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
package com.njcn.gather.steady.checksquare.component;
|
||||
|
||||
import com.njcn.gather.steady.checksquare.pojo.vo.SteadyChecksquareSegmentVO;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 数据校验连续性计算组件。
|
||||
*/
|
||||
@Component
|
||||
public class SteadyChecksquareCalculator {
|
||||
|
||||
public static final String STATUS_NORMAL = "NORMAL";
|
||||
public static final String STATUS_MISSING = "MISSING";
|
||||
private static final DateTimeFormatter OUTPUT_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||
|
||||
public List<SteadyChecksquareSegmentVO> buildSegments(List<LocalDateTime> slots, Set<LocalDateTime> actualSlots,
|
||||
int intervalMinutes) {
|
||||
List<SteadyChecksquareSegmentVO> result = new ArrayList<SteadyChecksquareSegmentVO>();
|
||||
if (slots == null || slots.isEmpty()) {
|
||||
return result;
|
||||
}
|
||||
String currentStatus = resolveStatus(slots.get(0), actualSlots);
|
||||
LocalDateTime segmentStart = slots.get(0);
|
||||
LocalDateTime previousSlot = slots.get(0);
|
||||
int pointCount = 1;
|
||||
for (int i = 1; i < slots.size(); i++) {
|
||||
LocalDateTime slot = slots.get(i);
|
||||
String status = resolveStatus(slot, actualSlots);
|
||||
if (!currentStatus.equals(status)) {
|
||||
result.add(buildSegment(segmentStart, previousSlot, currentStatus, pointCount, intervalMinutes));
|
||||
segmentStart = slot;
|
||||
pointCount = 0;
|
||||
currentStatus = status;
|
||||
}
|
||||
previousSlot = slot;
|
||||
pointCount++;
|
||||
}
|
||||
result.add(buildSegment(segmentStart, previousSlot, currentStatus, pointCount, intervalMinutes));
|
||||
return result;
|
||||
}
|
||||
|
||||
private SteadyChecksquareSegmentVO buildSegment(LocalDateTime startTime, LocalDateTime endTime, String status,
|
||||
int pointCount, int intervalMinutes) {
|
||||
SteadyChecksquareSegmentVO segment = new SteadyChecksquareSegmentVO();
|
||||
segment.setStartTime(OUTPUT_TIME_FORMATTER.format(startTime));
|
||||
segment.setEndTime(OUTPUT_TIME_FORMATTER.format(endTime));
|
||||
segment.setStatus(status);
|
||||
segment.setMissingPointCount(STATUS_MISSING.equals(status) ? pointCount : 0);
|
||||
segment.setDurationMinutes(pointCount * intervalMinutes);
|
||||
return segment;
|
||||
}
|
||||
|
||||
private String resolveStatus(LocalDateTime slot, Set<LocalDateTime> actualSlots) {
|
||||
return actualSlots != null && actualSlots.contains(slot) ? STATUS_NORMAL : STATUS_MISSING;
|
||||
}
|
||||
}
|
||||
@@ -1,193 +0,0 @@
|
||||
package com.njcn.gather.steady.checksquare.component;
|
||||
|
||||
import com.njcn.gather.steady.checksquare.pojo.bo.SteadyChecksquareValuePointBO;
|
||||
import com.njcn.gather.steady.checksquare.pojo.vo.SteadyChecksquareHarmonicParityDetailVO;
|
||||
import com.njcn.gather.steady.checksquare.pojo.vo.SteadyChecksquareHarmonicParityRuleVO;
|
||||
import com.njcn.gather.steady.datavie.pojo.bo.SteadyTrendIndicatorDefinitionBO;
|
||||
import com.njcn.gather.steady.datavie.pojo.bo.SteadyTrendResolvedFieldBO;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 谐波偶次与局部奇次基线关系规则。
|
||||
*/
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class SteadyChecksquareHarmonicParityRuleComponent {
|
||||
|
||||
private static final DateTimeFormatter OUTPUT_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||
private static final BigDecimal THRESHOLD_MULTIPLIER = new BigDecimal("2");
|
||||
private static final BigDecimal EVEN_HARMONIC_DEADBAND_VALUE = new BigDecimal("0.1");
|
||||
private static final int MIN_ODD_REFERENCE_COUNT = 2;
|
||||
|
||||
private final SteadyChecksquareInfluxQueryComponent influxQueryComponent;
|
||||
|
||||
public SteadyChecksquareHarmonicParityRuleVO check(String lineId, SteadyTrendIndicatorDefinitionBO indicator,
|
||||
LocalDateTime startTime, LocalDateTime endTime,
|
||||
int intervalMinutes) {
|
||||
SteadyChecksquareHarmonicParityRuleVO result = new SteadyChecksquareHarmonicParityRuleVO();
|
||||
if (!supportHarmonicParityRule(indicator)) {
|
||||
return result;
|
||||
}
|
||||
for (String statType : indicator.getSupportStats()) {
|
||||
for (String phase : indicator.getPhaseCodes()) {
|
||||
Map<Integer, Map<LocalDateTime, BigDecimal>> valueMap = queryOrderValueMap(lineId, indicator, phase,
|
||||
statType, startTime, endTime, intervalMinutes);
|
||||
appendAbnormalDetails(result, phase, statType, indicator, valueMap);
|
||||
}
|
||||
}
|
||||
result.setAbnormalPointCount(result.getAbnormalDetails().size());
|
||||
result.setAbnormal(result.getAbnormalPointCount() > 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
private boolean supportHarmonicParityRule(SteadyTrendIndicatorDefinitionBO indicator) {
|
||||
return indicator != null && Boolean.TRUE.equals(indicator.getHarmonic())
|
||||
&& indicator.getHarmonicOrderStart() != null && indicator.getHarmonicOrderEnd() != null;
|
||||
}
|
||||
|
||||
private Map<Integer, Map<LocalDateTime, BigDecimal>> queryOrderValueMap(String lineId,
|
||||
SteadyTrendIndicatorDefinitionBO indicator,
|
||||
String phase, String statType,
|
||||
LocalDateTime startTime,
|
||||
LocalDateTime endTime,
|
||||
int intervalMinutes) {
|
||||
Map<Integer, Map<LocalDateTime, BigDecimal>> result = new LinkedHashMap<Integer, Map<LocalDateTime, BigDecimal>>();
|
||||
List<SteadyTrendResolvedFieldBO> fields = new ArrayList<SteadyTrendResolvedFieldBO>();
|
||||
for (int order = indicator.getHarmonicOrderStart(); order <= indicator.getHarmonicOrderEnd(); order++) {
|
||||
fields.add(buildResolvedField(lineId, indicator, order, phase, statType));
|
||||
}
|
||||
Map<String, List<SteadyChecksquareValuePointBO>> fieldValueMap =
|
||||
influxQueryComponent.queryValuePointMap(fields, startTime, endTime, intervalMinutes);
|
||||
if (fieldValueMap == null) {
|
||||
fieldValueMap = Collections.emptyMap();
|
||||
}
|
||||
for (int order = indicator.getHarmonicOrderStart(); order <= indicator.getHarmonicOrderEnd(); order++) {
|
||||
result.put(order, toValueMap(fieldValueMap.get(indicator.getHarmonicFieldPrefix() + "_" + order)));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private void appendAbnormalDetails(SteadyChecksquareHarmonicParityRuleVO result, String phase, String statType,
|
||||
SteadyTrendIndicatorDefinitionBO indicator,
|
||||
Map<Integer, Map<LocalDateTime, BigDecimal>> valueMap) {
|
||||
for (int order = firstEvenOrder(indicator.getHarmonicOrderStart()); order <= indicator.getHarmonicOrderEnd(); order += 2) {
|
||||
Map<LocalDateTime, BigDecimal> evenValues = valueMap.get(order);
|
||||
if (evenValues == null || evenValues.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
for (Map.Entry<LocalDateTime, BigDecimal> entry : evenValues.entrySet()) {
|
||||
appendAbnormalDetailIfNecessary(result, phase, statType, order, entry.getKey(), entry.getValue(), valueMap);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void appendAbnormalDetailIfNecessary(SteadyChecksquareHarmonicParityRuleVO result, String phase,
|
||||
String statType, int evenOrder, LocalDateTime time,
|
||||
BigDecimal evenValue,
|
||||
Map<Integer, Map<LocalDateTime, BigDecimal>> valueMap) {
|
||||
if (evenValue == null || evenValue.compareTo(EVEN_HARMONIC_DEADBAND_VALUE) <= 0) {
|
||||
return;
|
||||
}
|
||||
List<Integer> oddOrders = buildOddReferenceOrders(evenOrder);
|
||||
List<BigDecimal> oddValues = new ArrayList<BigDecimal>();
|
||||
List<Integer> effectiveOddOrders = new ArrayList<Integer>();
|
||||
for (Integer oddOrder : oddOrders) {
|
||||
Map<LocalDateTime, BigDecimal> values = valueMap.get(oddOrder);
|
||||
BigDecimal oddValue = values == null ? null : values.get(time);
|
||||
if (oddValue != null) {
|
||||
effectiveOddOrders.add(oddOrder);
|
||||
oddValues.add(oddValue);
|
||||
}
|
||||
}
|
||||
if (oddValues.size() < MIN_ODD_REFERENCE_COUNT) {
|
||||
return;
|
||||
}
|
||||
BigDecimal median = calculateMedian(oddValues);
|
||||
if (median == null || evenValue.compareTo(median.multiply(THRESHOLD_MULTIPLIER)) <= 0) {
|
||||
return;
|
||||
}
|
||||
result.getAbnormalDetails().add(buildDetail(time, phase, statType, evenOrder, evenValue,
|
||||
effectiveOddOrders, oddValues, median));
|
||||
}
|
||||
|
||||
private SteadyChecksquareHarmonicParityDetailVO buildDetail(LocalDateTime time, String phase, String statType,
|
||||
Integer evenOrder, BigDecimal evenValue,
|
||||
List<Integer> oddOrders, List<BigDecimal> oddValues,
|
||||
BigDecimal median) {
|
||||
SteadyChecksquareHarmonicParityDetailVO detail = new SteadyChecksquareHarmonicParityDetailVO();
|
||||
detail.setTime(OUTPUT_TIME_FORMATTER.format(time));
|
||||
detail.setPhase(phase);
|
||||
detail.setStatType(statType);
|
||||
detail.setEvenHarmonicOrder(evenOrder);
|
||||
detail.setEvenValue(evenValue);
|
||||
detail.setOddHarmonicOrders(new ArrayList<Integer>(oddOrders));
|
||||
detail.setOddValues(new ArrayList<BigDecimal>(oddValues));
|
||||
detail.setOddMedianValue(median);
|
||||
detail.setThresholdMultiplier(THRESHOLD_MULTIPLIER);
|
||||
return detail;
|
||||
}
|
||||
|
||||
private List<Integer> buildOddReferenceOrders(int evenOrder) {
|
||||
List<Integer> result = new ArrayList<Integer>();
|
||||
result.add(evenOrder - 3);
|
||||
result.add(evenOrder - 1);
|
||||
result.add(evenOrder + 1);
|
||||
result.add(evenOrder + 3);
|
||||
return result;
|
||||
}
|
||||
|
||||
private BigDecimal calculateMedian(List<BigDecimal> values) {
|
||||
if (values == null || values.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
List<BigDecimal> sorted = new ArrayList<BigDecimal>(values);
|
||||
Collections.sort(sorted, Comparator.naturalOrder());
|
||||
int middleIndex = sorted.size() / 2;
|
||||
if (sorted.size() % 2 == 1) {
|
||||
return sorted.get(middleIndex);
|
||||
}
|
||||
return sorted.get(middleIndex - 1).add(sorted.get(middleIndex)).divide(new BigDecimal("2"));
|
||||
}
|
||||
|
||||
private int firstEvenOrder(int startOrder) {
|
||||
return startOrder % 2 == 0 ? startOrder : startOrder + 1;
|
||||
}
|
||||
|
||||
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(indicator.getHarmonicFieldPrefix() + "_" + harmonicOrder);
|
||||
field.setLineId(lineId);
|
||||
field.setIndicatorCode(indicator.getIndicatorCode());
|
||||
field.setIndicatorName(indicator.getName());
|
||||
field.setPhase(phase);
|
||||
field.setStatType(statType);
|
||||
field.setUnit(indicator.getUnit());
|
||||
return field;
|
||||
}
|
||||
}
|
||||
@@ -1,373 +0,0 @@
|
||||
package com.njcn.gather.steady.checksquare.component;
|
||||
|
||||
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.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.math.BigDecimal;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URLEncoder;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
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.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 数据校验 InfluxDB 查询组件。
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class SteadyChecksquareInfluxQueryComponent {
|
||||
|
||||
private static final DateTimeFormatter INFLUX_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss'Z'");
|
||||
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
|
||||
private static final ThreadLocal<Map<String, List<SteadyChecksquareValuePointBO>>> REQUEST_VALUE_CACHE =
|
||||
new ThreadLocal<Map<String, List<SteadyChecksquareValuePointBO>>>();
|
||||
|
||||
private final SteadyInfluxDbProperties properties;
|
||||
|
||||
public void enableRequestCache() {
|
||||
REQUEST_VALUE_CACHE.set(new LinkedHashMap<String, List<SteadyChecksquareValuePointBO>>());
|
||||
}
|
||||
|
||||
public void clearRequestCache() {
|
||||
REQUEST_VALUE_CACHE.remove();
|
||||
}
|
||||
|
||||
public Set<LocalDateTime> queryExistingSlots(SteadyTrendResolvedFieldBO field, LocalDateTime startTime,
|
||||
LocalDateTime endTime, int intervalMinutes) {
|
||||
List<SteadyChecksquareValuePointBO> points = queryValuePoints(field, startTime, endTime, intervalMinutes);
|
||||
Set<LocalDateTime> result = new HashSet<LocalDateTime>();
|
||||
for (SteadyChecksquareValuePointBO point : points) {
|
||||
if (point != null && point.getTime() != null) {
|
||||
result.add(point.getTime());
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public List<SteadyChecksquareValuePointBO> queryValuePoints(SteadyTrendResolvedFieldBO field, LocalDateTime startTime,
|
||||
LocalDateTime endTime, int intervalMinutes) {
|
||||
validateConfig();
|
||||
String query = buildValuePointQuery(field, startTime, endTime);
|
||||
String cacheKey = buildCacheKey(query, intervalMinutes);
|
||||
Map<String, List<SteadyChecksquareValuePointBO>> cache = REQUEST_VALUE_CACHE.get();
|
||||
if (cache != null && cache.containsKey(cacheKey)) {
|
||||
return new ArrayList<SteadyChecksquareValuePointBO>(cache.get(cacheKey));
|
||||
}
|
||||
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);
|
||||
if (cache != null) {
|
||||
cache.put(cacheKey, new ArrayList<SteadyChecksquareValuePointBO>(points));
|
||||
}
|
||||
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 Map<String, List<SteadyChecksquareValuePointBO>> queryValuePointMap(List<SteadyTrendResolvedFieldBO> fields,
|
||||
LocalDateTime startTime,
|
||||
LocalDateTime endTime,
|
||||
int intervalMinutes) {
|
||||
Map<String, List<SteadyChecksquareValuePointBO>> result =
|
||||
new LinkedHashMap<String, List<SteadyChecksquareValuePointBO>>();
|
||||
if (fields == null || fields.isEmpty()) {
|
||||
return result;
|
||||
}
|
||||
if (fields.size() == 1) {
|
||||
SteadyTrendResolvedFieldBO field = fields.get(0);
|
||||
result.put(field.getField(), queryValuePoints(field, startTime, endTime, intervalMinutes));
|
||||
return result;
|
||||
}
|
||||
validateConfig();
|
||||
Map<String, List<SteadyChecksquareValuePointBO>> cache = REQUEST_VALUE_CACHE.get();
|
||||
List<SteadyTrendResolvedFieldBO> missingFields = new ArrayList<SteadyTrendResolvedFieldBO>();
|
||||
for (SteadyTrendResolvedFieldBO field : fields) {
|
||||
String cacheKey = buildCacheKey(buildValuePointQuery(field, startTime, endTime), intervalMinutes);
|
||||
if (cache != null && cache.containsKey(cacheKey)) {
|
||||
result.put(field.getField(), new ArrayList<SteadyChecksquareValuePointBO>(cache.get(cacheKey)));
|
||||
} else {
|
||||
missingFields.add(field);
|
||||
}
|
||||
}
|
||||
if (!missingFields.isEmpty()) {
|
||||
String query = buildBatchValuePointQuery(missingFields, startTime, endTime);
|
||||
long startMillis = System.currentTimeMillis();
|
||||
SteadyTrendResolvedFieldBO first = missingFields.get(0);
|
||||
log.info("数据校验指标值 InfluxDB 批量查询开始,measurement={},fieldCount={},lineId={},phase={},statType={},query={}",
|
||||
first.getMeasurement(), missingFields.size(), first.getLineId(), first.getPhase(), first.getStatType(), query);
|
||||
try {
|
||||
Map<String, List<SteadyChecksquareValuePointBO>> queried = parseBatchValuePoints(executeQuery(query), intervalMinutes);
|
||||
for (SteadyTrendResolvedFieldBO field : missingFields) {
|
||||
List<SteadyChecksquareValuePointBO> points = queried.get(field.getField());
|
||||
if (points == null) {
|
||||
points = new ArrayList<SteadyChecksquareValuePointBO>();
|
||||
}
|
||||
result.put(field.getField(), points);
|
||||
if (cache != null) {
|
||||
String cacheKey = buildCacheKey(buildValuePointQuery(field, startTime, endTime), intervalMinutes);
|
||||
cache.put(cacheKey, new ArrayList<SteadyChecksquareValuePointBO>(points));
|
||||
}
|
||||
}
|
||||
log.info("数据校验指标值 InfluxDB 批量查询结束,fieldCount={},costMs={}",
|
||||
missingFields.size(), System.currentTimeMillis() - startMillis);
|
||||
} catch (RuntimeException ex) {
|
||||
log.warn("数据校验指标值 InfluxDB 批量查询异常,fieldCount={},costMs={},error={}",
|
||||
missingFields.size(), System.currentTimeMillis() - startMillis, ex.getMessage());
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public String buildChecksquareQuery(SteadyTrendResolvedFieldBO field, LocalDateTime startTime, LocalDateTime endTime) {
|
||||
return buildValuePointQuery(field, startTime, endTime);
|
||||
}
|
||||
|
||||
private String buildCacheKey(String query, int intervalMinutes) {
|
||||
return query + "|intervalMinutes=" + intervalMinutes;
|
||||
}
|
||||
|
||||
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("\"");
|
||||
sql.append(" WHERE time >= '").append(INFLUX_TIME_FORMATTER.format(startTime)).append("'");
|
||||
sql.append(" AND time <= '").append(INFLUX_TIME_FORMATTER.format(endTime)).append("'");
|
||||
sql.append(" AND \"line_id\" = '").append(escapeTagValue(field.getLineId())).append("'");
|
||||
sql.append(" AND \"phasic_type\" = '").append(escapeTagValue(field.getPhase())).append("'");
|
||||
if (hasValueTypeTag(field.getMeasurement())) {
|
||||
sql.append(" AND \"value_type\" = '").append(resolveValueType(field.getStatType())).append("'");
|
||||
}
|
||||
sql.append(" ORDER BY time ASC");
|
||||
return sql.toString();
|
||||
}
|
||||
|
||||
public String buildBatchValuePointQuery(List<SteadyTrendResolvedFieldBO> fields, LocalDateTime startTime, LocalDateTime endTime) {
|
||||
SteadyTrendResolvedFieldBO first = fields.get(0);
|
||||
StringBuilder sql = new StringBuilder("SELECT ");
|
||||
for (int i = 0; i < fields.size(); i++) {
|
||||
SteadyTrendResolvedFieldBO field = fields.get(i);
|
||||
if (i > 0) {
|
||||
sql.append(", ");
|
||||
}
|
||||
sql.append("\"").append(field.getField()).append("\" AS \"").append(field.getField()).append("\"");
|
||||
}
|
||||
sql.append(" FROM \"").append(first.getMeasurement()).append("\"");
|
||||
sql.append(" WHERE time >= '").append(INFLUX_TIME_FORMATTER.format(startTime)).append("'");
|
||||
sql.append(" AND time <= '").append(INFLUX_TIME_FORMATTER.format(endTime)).append("'");
|
||||
sql.append(" AND \"line_id\" = '").append(escapeTagValue(first.getLineId())).append("'");
|
||||
sql.append(" AND \"phasic_type\" = '").append(escapeTagValue(first.getPhase())).append("'");
|
||||
if (hasValueTypeTag(first.getMeasurement())) {
|
||||
sql.append(" AND \"value_type\" = '").append(resolveValueType(first.getStatType())).append("'");
|
||||
}
|
||||
sql.append(" ORDER BY time ASC");
|
||||
return sql.toString();
|
||||
}
|
||||
|
||||
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 Map<String, List<SteadyChecksquareValuePointBO>> parseBatchValuePoints(String body, int intervalMinutes) {
|
||||
try {
|
||||
JsonNode root = OBJECT_MAPPER.readTree(body);
|
||||
JsonNode series = root.path("results").path(0).path("series").path(0);
|
||||
JsonNode columns = series.path("columns");
|
||||
JsonNode values = series.path("values");
|
||||
Map<Integer, String> columnMap = new LinkedHashMap<Integer, String>();
|
||||
Map<String, List<SteadyChecksquareValuePointBO>> result =
|
||||
new LinkedHashMap<String, List<SteadyChecksquareValuePointBO>>();
|
||||
if (!columns.isArray() || !values.isArray()) {
|
||||
return result;
|
||||
}
|
||||
for (int i = 1; i < columns.size(); i++) {
|
||||
String fieldName = columns.get(i).asText();
|
||||
columnMap.put(i, fieldName);
|
||||
result.put(fieldName, new ArrayList<SteadyChecksquareValuePointBO>());
|
||||
}
|
||||
for (JsonNode row : values) {
|
||||
if (row.size() < 2) {
|
||||
continue;
|
||||
}
|
||||
LocalDateTime time = parseInfluxTime(row.get(0).asText());
|
||||
if (time == null) {
|
||||
continue;
|
||||
}
|
||||
LocalDateTime slot = alignToPreviousSlot(time, intervalMinutes);
|
||||
for (Map.Entry<Integer, String> entry : columnMap.entrySet()) {
|
||||
JsonNode value = row.get(entry.getKey());
|
||||
if (value == null || value.isNull()) {
|
||||
continue;
|
||||
}
|
||||
SteadyChecksquareValuePointBO point = new SteadyChecksquareValuePointBO();
|
||||
point.setTime(slot);
|
||||
point.setValue(new BigDecimal(value.asText()));
|
||||
result.get(entry.getValue()).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();
|
||||
int remainder = minuteOfDay % intervalMinutes;
|
||||
return minuteFloor.minusMinutes(remainder);
|
||||
}
|
||||
|
||||
private LocalDateTime parseInfluxTime(String value) {
|
||||
try {
|
||||
return OffsetDateTime.parse(value).withOffsetSameInstant(ZoneOffset.UTC).toLocalDateTime();
|
||||
} catch (RuntimeException ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private String executeQuery(String query) {
|
||||
HttpURLConnection connection = null;
|
||||
try {
|
||||
URL url = new URL(buildQueryUrl(query));
|
||||
connection = (HttpURLConnection) url.openConnection();
|
||||
connection.setRequestMethod("GET");
|
||||
connection.setConnectTimeout(properties.getConnectTimeoutMs());
|
||||
connection.setReadTimeout(properties.getReadTimeoutMs());
|
||||
int status = connection.getResponseCode();
|
||||
InputStream stream = status >= 200 && status < 300 ? connection.getInputStream() : connection.getErrorStream();
|
||||
String body = readBody(stream);
|
||||
if (status < 200 || status >= 300) {
|
||||
throw fail("InfluxDB 查询失败:" + body);
|
||||
}
|
||||
return body;
|
||||
} catch (IOException ex) {
|
||||
throw fail("InfluxDB 查询异常:" + ex.getMessage());
|
||||
} finally {
|
||||
if (connection != null) {
|
||||
connection.disconnect();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String buildQueryUrl(String query) throws IOException {
|
||||
StringBuilder url = new StringBuilder(trimRightSlash(properties.getUrl())).append("/query?");
|
||||
url.append("db=").append(encode(properties.getDatabase()));
|
||||
if (properties.getUsername() != null && !properties.getUsername().trim().isEmpty()) {
|
||||
url.append("&u=").append(encode(properties.getUsername().trim()));
|
||||
}
|
||||
if (properties.getPassword() != null && !properties.getPassword().trim().isEmpty()) {
|
||||
url.append("&p=").append(encode(properties.getPassword()));
|
||||
}
|
||||
url.append("&q=").append(encode(query));
|
||||
return url.toString();
|
||||
}
|
||||
|
||||
private void validateConfig() {
|
||||
if (properties.getUrl() == null || properties.getUrl().trim().isEmpty()) {
|
||||
throw fail("InfluxDB 地址未配置");
|
||||
}
|
||||
if (properties.getDatabase() == null || properties.getDatabase().trim().isEmpty()) {
|
||||
throw fail("InfluxDB database 未配置");
|
||||
}
|
||||
}
|
||||
|
||||
private String readBody(InputStream stream) throws IOException {
|
||||
if (stream == null) {
|
||||
return "";
|
||||
}
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(stream, StandardCharsets.UTF_8));
|
||||
StringBuilder body = new StringBuilder();
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
body.append(line);
|
||||
}
|
||||
return body.toString();
|
||||
}
|
||||
|
||||
private String escapeTagValue(String value) {
|
||||
return value == null ? "" : value.replace("\\", "\\\\").replace("'", "\\'");
|
||||
}
|
||||
|
||||
private String resolveValueType(String statType) {
|
||||
if (statType == null || statType.trim().isEmpty()) {
|
||||
return "AVG";
|
||||
}
|
||||
return statType.trim().toUpperCase();
|
||||
}
|
||||
|
||||
private boolean hasValueTypeTag(String measurement) {
|
||||
return !"data_flicker".equals(measurement) && !"data_fluc".equals(measurement) && !"data_plt".equals(measurement);
|
||||
}
|
||||
|
||||
private String trimRightSlash(String value) {
|
||||
String text = value.trim();
|
||||
while (text.endsWith("/")) {
|
||||
text = text.substring(0, text.length() - 1);
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
private String encode(String value) throws IOException {
|
||||
return URLEncoder.encode(value, StandardCharsets.UTF_8.name());
|
||||
}
|
||||
|
||||
private BusinessException fail(String message) {
|
||||
return new BusinessException(CommonResponseEnum.FAIL, message);
|
||||
}
|
||||
}
|
||||
@@ -1,145 +0,0 @@
|
||||
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, harmonicOrder, 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, Integer harmonicOrder,
|
||||
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, harmonicOrder, maxValue, minValue, avgValue, cp95Value));
|
||||
}
|
||||
}
|
||||
|
||||
private SteadyChecksquareValueOrderDetailVO buildDetail(LocalDateTime time, String phase, Integer harmonicOrder,
|
||||
BigDecimal maxValue, BigDecimal minValue,
|
||||
BigDecimal avgValue, BigDecimal cp95Value) {
|
||||
SteadyChecksquareValueOrderDetailVO detail = new SteadyChecksquareValueOrderDetailVO();
|
||||
detail.setTime(OUTPUT_TIME_FORMATTER.format(time));
|
||||
detail.setPhase(phase);
|
||||
detail.setHarmonicOrder(harmonicOrder);
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -1,95 +0,0 @@
|
||||
package com.njcn.gather.steady.checksquare.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.steady.checksquare.pojo.param.SteadyChecksquareHistoryQueryParam;
|
||||
import com.njcn.gather.steady.checksquare.pojo.param.SteadyChecksquareQueryParam;
|
||||
import com.njcn.gather.steady.checksquare.pojo.vo.SteadyChecksquareCreateVO;
|
||||
import com.njcn.gather.steady.checksquare.pojo.vo.SteadyChecksquareItemDetailVO;
|
||||
import com.njcn.gather.steady.checksquare.pojo.vo.SteadyChecksquareQueryVO;
|
||||
import com.njcn.gather.steady.checksquare.pojo.vo.SteadyChecksquareTaskVO;
|
||||
import com.njcn.gather.steady.checksquare.service.SteadyChecksquareService;
|
||||
import com.njcn.web.controller.BaseController;
|
||||
import com.njcn.web.utils.HttpResultUtil;
|
||||
import io.swagger.annotations.Api;
|
||||
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.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 数据校验接口。
|
||||
*/
|
||||
@Slf4j
|
||||
@Api(tags = "数据校验")
|
||||
@RestController
|
||||
@RequestMapping("/steady/data-view/checksquare")
|
||||
@RequiredArgsConstructor
|
||||
public class SteadyChecksquareController extends BaseController {
|
||||
|
||||
private final SteadyChecksquareService checksquareService;
|
||||
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
|
||||
@ApiOperation("查询数据校验历史记录")
|
||||
@PostMapping("/query")
|
||||
public HttpResult<Page<SteadyChecksquareTaskVO>> query(@RequestBody @Validated SteadyChecksquareHistoryQueryParam param) {
|
||||
String methodDescribe = getMethodDescribe("query");
|
||||
LogUtil.njcnDebug(log, "{},开始查询数据校验历史记录,param={}", methodDescribe, param);
|
||||
Page<SteadyChecksquareTaskVO> result = checksquareService.query(param);
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe);
|
||||
}
|
||||
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON, operateType = OperateType.ADD)
|
||||
@ApiOperation("新增数据校验记录")
|
||||
@PostMapping("/create")
|
||||
public HttpResult<SteadyChecksquareCreateVO> create(@RequestBody @Validated SteadyChecksquareQueryParam param) {
|
||||
String methodDescribe = getMethodDescribe("create");
|
||||
LogUtil.njcnDebug(log, "{},开始新增数据校验记录,param={}", methodDescribe, param);
|
||||
SteadyChecksquareCreateVO result = checksquareService.create(param);
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe);
|
||||
}
|
||||
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON, operateType = OperateType.DELETE)
|
||||
@ApiOperation("删除数据校验任务")
|
||||
@PostMapping("/delete")
|
||||
public HttpResult<Boolean> delete(@RequestBody List<String> taskIds) {
|
||||
String methodDescribe = getMethodDescribe("delete");
|
||||
LogUtil.njcnDebug(log, "{},开始删除数据校验任务,taskIds={}", methodDescribe, taskIds);
|
||||
boolean result = checksquareService.delete(taskIds);
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe);
|
||||
}
|
||||
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
|
||||
@ApiOperation("查询数据校验任务详情")
|
||||
@GetMapping("/detail")
|
||||
public HttpResult<SteadyChecksquareQueryVO> detail(@RequestParam("taskId") String taskId) {
|
||||
String methodDescribe = getMethodDescribe("detail");
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, checksquareService.detail(taskId), methodDescribe);
|
||||
}
|
||||
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
|
||||
@ApiOperation("查询数据校验检测项明细")
|
||||
@GetMapping("/item-detail")
|
||||
public HttpResult<SteadyChecksquareItemDetailVO> itemDetail(@RequestParam("itemId") String itemId,
|
||||
@RequestParam("detailType") String detailType,
|
||||
@RequestParam(value = "statType", required = false) String statType,
|
||||
@RequestParam(value = "pageNum", required = false) Integer pageNum,
|
||||
@RequestParam(value = "pageSize", required = false) Integer pageSize) {
|
||||
String methodDescribe = getMethodDescribe("itemDetail");
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS,
|
||||
checksquareService.itemDetail(itemId, detailType, statType, pageNum, pageSize), methodDescribe);
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
package com.njcn.gather.steady.checksquare.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.njcn.gather.steady.checksquare.pojo.po.SteadyChecksquareDetailPO;
|
||||
|
||||
/**
|
||||
* 数据校验明细 Mapper。
|
||||
*/
|
||||
public interface SteadyChecksquareDetailMapper extends BaseMapper<SteadyChecksquareDetailPO> {
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
package com.njcn.gather.steady.checksquare.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.njcn.gather.steady.checksquare.pojo.po.SteadyChecksquareItemPO;
|
||||
|
||||
/**
|
||||
* 数据校验检测项 Mapper。
|
||||
*/
|
||||
public interface SteadyChecksquareItemMapper extends BaseMapper<SteadyChecksquareItemPO> {
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
package com.njcn.gather.steady.checksquare.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.njcn.gather.steady.checksquare.pojo.po.SteadyChecksquareStatSummaryPO;
|
||||
|
||||
/**
|
||||
* 数据校验统计摘要 Mapper。
|
||||
*/
|
||||
public interface SteadyChecksquareStatSummaryMapper extends BaseMapper<SteadyChecksquareStatSummaryPO> {
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
package com.njcn.gather.steady.checksquare.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.njcn.gather.steady.checksquare.pojo.po.SteadyChecksquareTaskPO;
|
||||
|
||||
/**
|
||||
* 数据校验任务 Mapper。
|
||||
*/
|
||||
public interface SteadyChecksquareTaskMapper extends BaseMapper<SteadyChecksquareTaskPO> {
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
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;
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
package com.njcn.gather.steady.checksquare.pojo.constant;
|
||||
|
||||
/**
|
||||
* 数据校验常量。
|
||||
*/
|
||||
public final class SteadyChecksquareConst {
|
||||
|
||||
public static final int STATE_DELETED = 0;
|
||||
public static final int STATE_ENABLED = 1;
|
||||
|
||||
public static final String TASK_STATUS_SUCCESS = "SUCCESS";
|
||||
public static final String DETAIL_TYPE_SEGMENT = "SEGMENT";
|
||||
public static final String DETAIL_TYPE_VALUE_ORDER = "VALUE_ORDER";
|
||||
public static final String DETAIL_TYPE_HARMONIC_PARITY = "HARMONIC_PARITY";
|
||||
|
||||
private SteadyChecksquareConst() {
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
package com.njcn.gather.steady.checksquare.pojo.param;
|
||||
|
||||
import com.njcn.web.pojo.param.BaseParam;
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 数据校验历史查询参数。
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ApiModel("数据校验历史查询参数")
|
||||
public class SteadyChecksquareHistoryQueryParam extends BaseParam {
|
||||
|
||||
@ApiModelProperty("监测点 ID")
|
||||
private String lineId;
|
||||
|
||||
@ApiModelProperty("指标编码")
|
||||
private String indicatorCode;
|
||||
|
||||
@ApiModelProperty("检测开始时间,格式 yyyy-MM-dd HH:mm:ss")
|
||||
private String timeStart;
|
||||
|
||||
@ApiModelProperty("检测结束时间,格式 yyyy-MM-dd HH:mm:ss")
|
||||
private String timeEnd;
|
||||
|
||||
@ApiModelProperty("是否存在异常")
|
||||
private Boolean hasAbnormal;
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
package com.njcn.gather.steady.checksquare.pojo.param;
|
||||
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 数据校验新增检测参数。
|
||||
*/
|
||||
@Data
|
||||
@ApiModel("数据校验新增检测参数")
|
||||
public class SteadyChecksquareQueryParam implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@ApiModelProperty("监测点 ID")
|
||||
private String lineId;
|
||||
|
||||
@ApiModelProperty("指标编码")
|
||||
private List<String> indicatorCodes;
|
||||
|
||||
@ApiModelProperty("开始时间,格式 yyyy-MM-dd HH:mm:ss")
|
||||
private String timeStart;
|
||||
|
||||
@ApiModelProperty("结束时间,格式 yyyy-MM-dd HH:mm:ss")
|
||||
private String timeEnd;
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
package com.njcn.gather.steady.checksquare.pojo.po;
|
||||
|
||||
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("steady_checksquare_detail")
|
||||
public class SteadyChecksquareDetailPO implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@TableId("id")
|
||||
private String id;
|
||||
@TableField("item_id")
|
||||
private String itemId;
|
||||
@TableField("detail_type")
|
||||
private String detailType;
|
||||
@TableField("stat_type")
|
||||
private String statType;
|
||||
@TableField("start_time")
|
||||
private LocalDateTime startTime;
|
||||
@TableField("end_time")
|
||||
private LocalDateTime endTime;
|
||||
@TableField("point_time")
|
||||
private LocalDateTime pointTime;
|
||||
@TableField("segment_status")
|
||||
private String segmentStatus;
|
||||
@TableField("missing_point_count")
|
||||
private Integer missingPointCount;
|
||||
@TableField("duration_minutes")
|
||||
private Integer durationMinutes;
|
||||
@TableField("phase")
|
||||
private String phase;
|
||||
@TableField("harmonic_order")
|
||||
private Integer harmonicOrder;
|
||||
@TableField("max_value")
|
||||
private BigDecimal maxValue;
|
||||
@TableField("min_value")
|
||||
private BigDecimal minValue;
|
||||
@TableField("avg_value")
|
||||
private BigDecimal avgValue;
|
||||
@TableField("cp95_value")
|
||||
private BigDecimal cp95Value;
|
||||
@TableField("even_harmonic_order")
|
||||
private Integer evenHarmonicOrder;
|
||||
@TableField("even_value")
|
||||
private BigDecimal evenValue;
|
||||
@TableField("odd_harmonic_orders_json")
|
||||
private String oddHarmonicOrdersJson;
|
||||
@TableField("odd_values_json")
|
||||
private String oddValuesJson;
|
||||
@TableField("odd_median_value")
|
||||
private BigDecimal oddMedianValue;
|
||||
@TableField("threshold_multiplier")
|
||||
private BigDecimal thresholdMultiplier;
|
||||
@TableField("create_time")
|
||||
private LocalDateTime createTime;
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
package com.njcn.gather.steady.checksquare.pojo.po;
|
||||
|
||||
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("steady_checksquare_item")
|
||||
public class SteadyChecksquareItemPO implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@TableId("id")
|
||||
private String id;
|
||||
@TableField("task_id")
|
||||
private String taskId;
|
||||
@TableField("item_key")
|
||||
private String itemKey;
|
||||
@TableField("indicator_code")
|
||||
private String indicatorCode;
|
||||
@TableField("indicator_name")
|
||||
private String indicatorName;
|
||||
@TableField("harmonic_order")
|
||||
private Integer harmonicOrder;
|
||||
@TableField("interval_minutes")
|
||||
private Integer intervalMinutes;
|
||||
@TableField("has_data")
|
||||
private Integer hasData;
|
||||
@TableField("expected_point_count")
|
||||
private Integer expectedPointCount;
|
||||
@TableField("actual_point_count")
|
||||
private Integer actualPointCount;
|
||||
@TableField("missing_point_count")
|
||||
private Integer missingPointCount;
|
||||
@TableField("data_integrity")
|
||||
private BigDecimal dataIntegrity;
|
||||
@TableField("data_integrity_text")
|
||||
private String dataIntegrityText;
|
||||
@TableField("abnormal")
|
||||
private Integer abnormal;
|
||||
@TableField("abnormal_point_count")
|
||||
private Integer abnormalPointCount;
|
||||
@TableField("harmonic_parity_abnormal")
|
||||
private Integer harmonicParityAbnormal;
|
||||
@TableField("harmonic_parity_abnormal_point_count")
|
||||
private Integer harmonicParityAbnormalPointCount;
|
||||
@TableField("state")
|
||||
private Integer state;
|
||||
@TableField("create_time")
|
||||
private LocalDateTime createTime;
|
||||
@TableField("update_time")
|
||||
private LocalDateTime updateTime;
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
package com.njcn.gather.steady.checksquare.pojo.po;
|
||||
|
||||
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("steady_checksquare_stat_summary")
|
||||
public class SteadyChecksquareStatSummaryPO implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@TableId("id")
|
||||
private String id;
|
||||
@TableField("item_id")
|
||||
private String itemId;
|
||||
@TableField("stat_type")
|
||||
private String statType;
|
||||
@TableField("supported")
|
||||
private Integer supported;
|
||||
@TableField("has_data")
|
||||
private Integer hasData;
|
||||
@TableField("expected_point_count")
|
||||
private Integer expectedPointCount;
|
||||
@TableField("actual_point_count")
|
||||
private Integer actualPointCount;
|
||||
@TableField("missing_point_count")
|
||||
private Integer missingPointCount;
|
||||
@TableField("data_integrity")
|
||||
private BigDecimal dataIntegrity;
|
||||
@TableField("data_integrity_text")
|
||||
private String dataIntegrityText;
|
||||
@TableField("create_time")
|
||||
private LocalDateTime createTime;
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
package com.njcn.gather.steady.checksquare.pojo.po;
|
||||
|
||||
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("steady_checksquare_task")
|
||||
public class SteadyChecksquareTaskPO implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@TableId("id")
|
||||
private String id;
|
||||
@TableField("task_no")
|
||||
private String taskNo;
|
||||
@TableField("line_id")
|
||||
private String lineId;
|
||||
@TableField("line_name")
|
||||
private String lineName;
|
||||
@TableField("time_start")
|
||||
private LocalDateTime timeStart;
|
||||
@TableField("time_end")
|
||||
private LocalDateTime timeEnd;
|
||||
@TableField("interval_minutes")
|
||||
private Integer intervalMinutes;
|
||||
@TableField("indicator_codes_json")
|
||||
private String indicatorCodesJson;
|
||||
@TableField("indicator_codes_text")
|
||||
private String indicatorCodesText;
|
||||
@TableField("task_status")
|
||||
private String taskStatus;
|
||||
@TableField("item_count")
|
||||
private Integer itemCount;
|
||||
@TableField("abnormal_item_count")
|
||||
private Integer abnormalItemCount;
|
||||
@TableField("min_data_integrity")
|
||||
private BigDecimal minDataIntegrity;
|
||||
@TableField("result_message")
|
||||
private String resultMessage;
|
||||
@TableField("state")
|
||||
private Integer state;
|
||||
@TableField("create_by")
|
||||
private String createBy;
|
||||
@TableField("create_time")
|
||||
private LocalDateTime createTime;
|
||||
@TableField("update_by")
|
||||
private String updateBy;
|
||||
@TableField("update_time")
|
||||
private LocalDateTime updateTime;
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
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;
|
||||
|
||||
/**
|
||||
* 新增数据校验结果。
|
||||
*/
|
||||
@Data
|
||||
@ApiModel("新增数据校验结果")
|
||||
public class SteadyChecksquareCreateVO implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@ApiModelProperty("任务 ID")
|
||||
private String taskId;
|
||||
|
||||
@ApiModelProperty("任务编号")
|
||||
private String taskNo;
|
||||
|
||||
@ApiModelProperty("监测点 ID")
|
||||
private String lineId;
|
||||
|
||||
@ApiModelProperty("监测点名称")
|
||||
private String lineName;
|
||||
|
||||
@ApiModelProperty("开始时间")
|
||||
private String timeStart;
|
||||
|
||||
@ApiModelProperty("结束时间")
|
||||
private String timeEnd;
|
||||
|
||||
@ApiModelProperty("统计间隔,单位分钟")
|
||||
private Integer intervalMinutes;
|
||||
|
||||
@ApiModelProperty("检测项数量")
|
||||
private Integer itemCount;
|
||||
|
||||
@ApiModelProperty("异常检测项数量")
|
||||
private Integer abnormalItemCount;
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
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;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 谐波奇偶关系异常明细。
|
||||
*/
|
||||
@Data
|
||||
@ApiModel("谐波奇偶关系异常明细")
|
||||
public class SteadyChecksquareHarmonicParityDetailVO implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@ApiModelProperty("时间")
|
||||
private String time;
|
||||
|
||||
@ApiModelProperty("相别")
|
||||
private String phase;
|
||||
|
||||
@ApiModelProperty("统计类型")
|
||||
private String statType;
|
||||
|
||||
@ApiModelProperty("偶次谐波次数")
|
||||
private Integer evenHarmonicOrder;
|
||||
|
||||
@ApiModelProperty("偶次谐波值")
|
||||
private BigDecimal evenValue;
|
||||
|
||||
@ApiModelProperty("参与比较的奇次谐波次数")
|
||||
private List<Integer> oddHarmonicOrders = new ArrayList<Integer>();
|
||||
|
||||
@ApiModelProperty("参与比较的奇次谐波值")
|
||||
private List<BigDecimal> oddValues = new ArrayList<BigDecimal>();
|
||||
|
||||
@ApiModelProperty("奇次谐波中位数")
|
||||
private BigDecimal oddMedianValue;
|
||||
|
||||
@ApiModelProperty("异常阈值倍数")
|
||||
private BigDecimal thresholdMultiplier;
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
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 SteadyChecksquareHarmonicParityRuleVO implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private Boolean abnormal = false;
|
||||
|
||||
private Integer abnormalPointCount = 0;
|
||||
|
||||
private List<SteadyChecksquareHarmonicParityDetailVO> abnormalDetails =
|
||||
new ArrayList<SteadyChecksquareHarmonicParityDetailVO>();
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
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.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 数据校验检测项明细。
|
||||
*/
|
||||
@Data
|
||||
@ApiModel("数据校验检测项明细")
|
||||
public class SteadyChecksquareItemDetailVO implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@ApiModelProperty("检测项 ID")
|
||||
private String itemId;
|
||||
|
||||
@ApiModelProperty("明细类型")
|
||||
private String detailType;
|
||||
|
||||
@ApiModelProperty("统计类型")
|
||||
private String statType;
|
||||
|
||||
@ApiModelProperty("当前页码;未分页查询时为空")
|
||||
private Integer pageNum;
|
||||
|
||||
@ApiModelProperty("每页条数;未分页查询时为空")
|
||||
private Integer pageSize;
|
||||
|
||||
@ApiModelProperty("总记录数;未分页查询时为空")
|
||||
private Long total;
|
||||
|
||||
@ApiModelProperty("缺失区间")
|
||||
private List<SteadyChecksquareSegmentVO> segments = new ArrayList<SteadyChecksquareSegmentVO>();
|
||||
|
||||
@ApiModelProperty("大小关系异常明细")
|
||||
private List<SteadyChecksquareValueOrderDetailVO> valueOrderDetails =
|
||||
new ArrayList<SteadyChecksquareValueOrderDetailVO>();
|
||||
|
||||
@ApiModelProperty("谐波奇偶关系异常明细")
|
||||
private List<SteadyChecksquareHarmonicParityDetailVO> harmonicParityDetails =
|
||||
new ArrayList<SteadyChecksquareHarmonicParityDetailVO>();
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
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;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 数据校验总览项。
|
||||
*/
|
||||
@Data
|
||||
@ApiModel("数据校验总览项")
|
||||
public class SteadyChecksquareItemVO implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@ApiModelProperty("检测项 ID")
|
||||
private String itemId;
|
||||
|
||||
@ApiModelProperty("校验项唯一键")
|
||||
private String itemKey;
|
||||
|
||||
@ApiModelProperty("指标编码")
|
||||
private String indicatorCode;
|
||||
|
||||
@ApiModelProperty("指标名称")
|
||||
private String indicatorName;
|
||||
|
||||
@ApiModelProperty("谐波次数")
|
||||
private Integer harmonicOrder;
|
||||
|
||||
@ApiModelProperty("当前校验项统计间隔,单位分钟")
|
||||
private Integer intervalMinutes;
|
||||
|
||||
@ApiModelProperty("时间范围内是否存在任意数据")
|
||||
private Boolean hasData;
|
||||
|
||||
@ApiModelProperty("期望点数")
|
||||
private Integer expectedPointCount;
|
||||
|
||||
@ApiModelProperty("实际点数")
|
||||
private Integer actualPointCount;
|
||||
|
||||
@ApiModelProperty("缺失点数")
|
||||
private Integer missingPointCount;
|
||||
|
||||
@ApiModelProperty("数据完整性")
|
||||
private BigDecimal dataIntegrity;
|
||||
|
||||
@ApiModelProperty("数据完整性文本")
|
||||
private String dataIntegrityText;
|
||||
|
||||
@ApiModelProperty("指标值大小关系是否异常")
|
||||
private Boolean abnormal;
|
||||
|
||||
@ApiModelProperty("指标值大小关系异常累计值")
|
||||
private Integer abnormalPointCount;
|
||||
|
||||
@ApiModelProperty("指标值大小关系异常明细")
|
||||
private List<SteadyChecksquareValueOrderDetailVO> abnormalDetails = new ArrayList<SteadyChecksquareValueOrderDetailVO>();
|
||||
|
||||
@ApiModelProperty("谐波奇偶关系是否异常")
|
||||
private Boolean harmonicParityAbnormal;
|
||||
|
||||
@ApiModelProperty("谐波奇偶关系异常累计值")
|
||||
private Integer harmonicParityAbnormalPointCount;
|
||||
|
||||
@ApiModelProperty("谐波奇偶关系异常明细")
|
||||
private List<SteadyChecksquareHarmonicParityDetailVO> harmonicParityAbnormalDetails =
|
||||
new ArrayList<SteadyChecksquareHarmonicParityDetailVO>();
|
||||
|
||||
@ApiModelProperty("统计类型摘要")
|
||||
private List<SteadyChecksquareStatSummaryVO> statSummaries = new ArrayList<SteadyChecksquareStatSummaryVO>();
|
||||
|
||||
@ApiModelProperty("统计类型明细")
|
||||
private List<SteadyChecksquareStatDetailVO> statDetails = new ArrayList<SteadyChecksquareStatDetailVO>();
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
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.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 数据校验查询结果。
|
||||
*/
|
||||
@Data
|
||||
@ApiModel("数据校验查询结果")
|
||||
public class SteadyChecksquareQueryVO implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@ApiModelProperty("任务 ID")
|
||||
private String taskId;
|
||||
|
||||
@ApiModelProperty("任务编号")
|
||||
private String taskNo;
|
||||
|
||||
@ApiModelProperty("监测点 ID")
|
||||
private String lineId;
|
||||
|
||||
@ApiModelProperty("监测点名称")
|
||||
private String lineName;
|
||||
|
||||
@ApiModelProperty("开始时间")
|
||||
private String timeStart;
|
||||
|
||||
@ApiModelProperty("结束时间")
|
||||
private String timeEnd;
|
||||
|
||||
@ApiModelProperty("统计间隔,单位分钟")
|
||||
private Integer intervalMinutes;
|
||||
|
||||
@ApiModelProperty("校验项")
|
||||
private List<SteadyChecksquareItemVO> items = new ArrayList<SteadyChecksquareItemVO>();
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
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;
|
||||
|
||||
/**
|
||||
* 数据校验连续性区间。
|
||||
*/
|
||||
@Data
|
||||
@ApiModel("数据校验连续性区间")
|
||||
public class SteadyChecksquareSegmentVO implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@ApiModelProperty("开始时间")
|
||||
private String startTime;
|
||||
|
||||
@ApiModelProperty("结束时间")
|
||||
private String endTime;
|
||||
|
||||
@ApiModelProperty("状态,NORMAL/MISSING")
|
||||
private String status;
|
||||
|
||||
@ApiModelProperty("谐波次数")
|
||||
private Integer harmonicOrder;
|
||||
|
||||
@ApiModelProperty("缺失点数")
|
||||
private Integer missingPointCount;
|
||||
|
||||
@ApiModelProperty("持续时长,单位分钟")
|
||||
private Integer durationMinutes;
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
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.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 数据校验统计类型明细。
|
||||
*/
|
||||
@Data
|
||||
@ApiModel("数据校验统计类型明细")
|
||||
public class SteadyChecksquareStatDetailVO implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@ApiModelProperty("统计类型")
|
||||
private String statType;
|
||||
|
||||
@ApiModelProperty("是否支持")
|
||||
private Boolean supported;
|
||||
|
||||
@ApiModelProperty("连续性区间")
|
||||
private List<SteadyChecksquareSegmentVO> segments = new ArrayList<SteadyChecksquareSegmentVO>();
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
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 SteadyChecksquareStatSummaryVO implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@ApiModelProperty("统计类型")
|
||||
private String statType;
|
||||
|
||||
@ApiModelProperty("是否支持")
|
||||
private Boolean supported;
|
||||
|
||||
@ApiModelProperty("是否存在数据")
|
||||
private Boolean hasData;
|
||||
|
||||
@ApiModelProperty("期望点数")
|
||||
private Integer expectedPointCount;
|
||||
|
||||
@ApiModelProperty("实际点数")
|
||||
private Integer actualPointCount;
|
||||
|
||||
@ApiModelProperty("缺失点数")
|
||||
private Integer missingPointCount;
|
||||
|
||||
@ApiModelProperty("数据完整性")
|
||||
private BigDecimal dataIntegrity;
|
||||
|
||||
@ApiModelProperty("数据完整性文本")
|
||||
private String dataIntegrityText;
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
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 SteadyChecksquareTaskVO implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@ApiModelProperty("任务 ID")
|
||||
private String taskId;
|
||||
|
||||
@ApiModelProperty("任务编号")
|
||||
private String taskNo;
|
||||
|
||||
@ApiModelProperty("监测点 ID")
|
||||
private String lineId;
|
||||
|
||||
@ApiModelProperty("监测点名称")
|
||||
private String lineName;
|
||||
|
||||
@ApiModelProperty("开始时间")
|
||||
private String timeStart;
|
||||
|
||||
@ApiModelProperty("结束时间")
|
||||
private String timeEnd;
|
||||
|
||||
@ApiModelProperty("统计间隔,单位分钟")
|
||||
private Integer intervalMinutes;
|
||||
|
||||
@ApiModelProperty("任务状态")
|
||||
private String taskStatus;
|
||||
|
||||
@ApiModelProperty("检测项数量")
|
||||
private Integer itemCount;
|
||||
|
||||
@ApiModelProperty("异常检测项数量")
|
||||
private Integer abnormalItemCount;
|
||||
|
||||
@ApiModelProperty("最低数据完整性")
|
||||
private BigDecimal minDataIntegrity;
|
||||
|
||||
@ApiModelProperty("创建时间")
|
||||
private String createTime;
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
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 Integer harmonicOrder;
|
||||
|
||||
@ApiModelProperty("最大值")
|
||||
private BigDecimal maxValue;
|
||||
|
||||
@ApiModelProperty("最小值")
|
||||
private BigDecimal minValue;
|
||||
|
||||
@ApiModelProperty("平均值")
|
||||
private BigDecimal avgValue;
|
||||
|
||||
@ApiModelProperty("CP95 值")
|
||||
private BigDecimal cp95Value;
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
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>();
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
package com.njcn.gather.steady.checksquare.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.njcn.gather.steady.checksquare.pojo.po.SteadyChecksquareDetailPO;
|
||||
|
||||
/**
|
||||
* 数据校验明细服务。
|
||||
*/
|
||||
public interface SteadyChecksquareDetailService extends IService<SteadyChecksquareDetailPO> {
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
package com.njcn.gather.steady.checksquare.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.njcn.gather.steady.checksquare.pojo.po.SteadyChecksquareItemPO;
|
||||
|
||||
/**
|
||||
* 数据校验检测项服务。
|
||||
*/
|
||||
public interface SteadyChecksquareItemService extends IService<SteadyChecksquareItemPO> {
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
package com.njcn.gather.steady.checksquare.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.njcn.gather.steady.checksquare.pojo.param.SteadyChecksquareHistoryQueryParam;
|
||||
import com.njcn.gather.steady.checksquare.pojo.param.SteadyChecksquareQueryParam;
|
||||
import com.njcn.gather.steady.checksquare.pojo.vo.SteadyChecksquareCreateVO;
|
||||
import com.njcn.gather.steady.checksquare.pojo.vo.SteadyChecksquareItemDetailVO;
|
||||
import com.njcn.gather.steady.checksquare.pojo.vo.SteadyChecksquareQueryVO;
|
||||
import com.njcn.gather.steady.checksquare.pojo.vo.SteadyChecksquareTaskVO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 数据校验服务。
|
||||
*/
|
||||
public interface SteadyChecksquareService {
|
||||
|
||||
Page<SteadyChecksquareTaskVO> query(SteadyChecksquareHistoryQueryParam param);
|
||||
|
||||
SteadyChecksquareCreateVO create(SteadyChecksquareQueryParam param);
|
||||
|
||||
boolean delete(List<String> taskIds);
|
||||
|
||||
SteadyChecksquareQueryVO detail(String taskId);
|
||||
|
||||
SteadyChecksquareItemDetailVO itemDetail(String itemId, String detailType, String statType);
|
||||
|
||||
SteadyChecksquareItemDetailVO itemDetail(String itemId, String detailType, String statType,
|
||||
Integer pageNum, Integer pageSize);
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
package com.njcn.gather.steady.checksquare.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.njcn.gather.steady.checksquare.pojo.po.SteadyChecksquareStatSummaryPO;
|
||||
|
||||
/**
|
||||
* 数据校验统计摘要服务。
|
||||
*/
|
||||
public interface SteadyChecksquareStatSummaryService extends IService<SteadyChecksquareStatSummaryPO> {
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
package com.njcn.gather.steady.checksquare.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.njcn.gather.steady.checksquare.pojo.po.SteadyChecksquareTaskPO;
|
||||
|
||||
/**
|
||||
* 数据校验任务服务。
|
||||
*/
|
||||
public interface SteadyChecksquareTaskService extends IService<SteadyChecksquareTaskPO> {
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
package com.njcn.gather.steady.checksquare.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.njcn.gather.steady.checksquare.mapper.SteadyChecksquareDetailMapper;
|
||||
import com.njcn.gather.steady.checksquare.pojo.po.SteadyChecksquareDetailPO;
|
||||
import com.njcn.gather.steady.checksquare.service.SteadyChecksquareDetailService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* 数据校验明细服务实现。
|
||||
*/
|
||||
@Service
|
||||
public class SteadyChecksquareDetailServiceImpl extends ServiceImpl<SteadyChecksquareDetailMapper, SteadyChecksquareDetailPO>
|
||||
implements SteadyChecksquareDetailService {
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
package com.njcn.gather.steady.checksquare.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.njcn.gather.steady.checksquare.mapper.SteadyChecksquareItemMapper;
|
||||
import com.njcn.gather.steady.checksquare.pojo.po.SteadyChecksquareItemPO;
|
||||
import com.njcn.gather.steady.checksquare.service.SteadyChecksquareItemService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* 数据校验检测项服务实现。
|
||||
*/
|
||||
@Service
|
||||
public class SteadyChecksquareItemServiceImpl extends ServiceImpl<SteadyChecksquareItemMapper, SteadyChecksquareItemPO>
|
||||
implements SteadyChecksquareItemService {
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,16 +0,0 @@
|
||||
package com.njcn.gather.steady.checksquare.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.njcn.gather.steady.checksquare.mapper.SteadyChecksquareStatSummaryMapper;
|
||||
import com.njcn.gather.steady.checksquare.pojo.po.SteadyChecksquareStatSummaryPO;
|
||||
import com.njcn.gather.steady.checksquare.service.SteadyChecksquareStatSummaryService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* 数据校验统计摘要服务实现。
|
||||
*/
|
||||
@Service
|
||||
public class SteadyChecksquareStatSummaryServiceImpl
|
||||
extends ServiceImpl<SteadyChecksquareStatSummaryMapper, SteadyChecksquareStatSummaryPO>
|
||||
implements SteadyChecksquareStatSummaryService {
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
package com.njcn.gather.steady.checksquare.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.njcn.gather.steady.checksquare.mapper.SteadyChecksquareTaskMapper;
|
||||
import com.njcn.gather.steady.checksquare.pojo.po.SteadyChecksquareTaskPO;
|
||||
import com.njcn.gather.steady.checksquare.service.SteadyChecksquareTaskService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* 数据校验任务服务实现。
|
||||
*/
|
||||
@Service
|
||||
public class SteadyChecksquareTaskServiceImpl extends ServiceImpl<SteadyChecksquareTaskMapper, SteadyChecksquareTaskPO>
|
||||
implements SteadyChecksquareTaskService {
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
package com.njcn.gather.steady.checksquare.util;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* 数据校验编号工具。
|
||||
*/
|
||||
public final class SteadyChecksquareIdUtil {
|
||||
|
||||
private static final DateTimeFormatter TASK_FORMATTER = DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS");
|
||||
|
||||
private SteadyChecksquareIdUtil() {
|
||||
}
|
||||
|
||||
public static String uuid() {
|
||||
return UUID.randomUUID().toString().replace("-", "");
|
||||
}
|
||||
|
||||
public static String taskNo() {
|
||||
return "CS" + LocalDateTime.now().format(TASK_FORMATTER);
|
||||
}
|
||||
}
|
||||
@@ -1,102 +0,0 @@
|
||||
CREATE TABLE IF NOT EXISTS `steady_checksquare_task` (
|
||||
`id` VARCHAR(64) NOT NULL COMMENT '主键',
|
||||
`task_no` VARCHAR(64) NOT NULL COMMENT '检测任务编号',
|
||||
`line_id` VARCHAR(64) NOT NULL COMMENT '监测点ID',
|
||||
`line_name` VARCHAR(255) NULL COMMENT '监测点名称',
|
||||
`time_start` DATETIME NOT NULL COMMENT '检测开始时间',
|
||||
`time_end` DATETIME NOT NULL COMMENT '检测结束时间',
|
||||
`interval_minutes` INT NULL COMMENT '默认统计间隔,单位分钟',
|
||||
`indicator_codes_json` JSON NULL COMMENT '请求指标编码列表',
|
||||
`indicator_codes_text` VARCHAR(2000) NULL COMMENT '请求指标编码检索文本,格式 |code1|code2|',
|
||||
`task_status` VARCHAR(32) NOT NULL DEFAULT 'SUCCESS' COMMENT '任务状态:SUCCESS/FAIL',
|
||||
`item_count` INT NOT NULL DEFAULT 0 COMMENT '检测项数量',
|
||||
`abnormal_item_count` INT NOT NULL DEFAULT 0 COMMENT '异常检测项数量',
|
||||
`min_data_integrity` DECIMAL(12,6) NOT NULL DEFAULT 0.000000 COMMENT '最低数据完整性',
|
||||
`result_message` VARCHAR(2000) NULL COMMENT '执行结果说明',
|
||||
`state` TINYINT NOT NULL DEFAULT 1 COMMENT '状态:0-删除,1-正常',
|
||||
`create_by` VARCHAR(64) NULL COMMENT '创建人',
|
||||
`create_time` DATETIME NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`update_by` VARCHAR(64) NULL COMMENT '更新人',
|
||||
`update_time` DATETIME NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_steady_checksquare_task_no` (`task_no`),
|
||||
KEY `idx_steady_checksquare_task_line_time` (`line_id`, `time_start`, `time_end`),
|
||||
KEY `idx_steady_checksquare_task_status` (`task_status`),
|
||||
KEY `idx_steady_checksquare_task_indicator_text` (`indicator_codes_text`(255)),
|
||||
KEY `idx_steady_checksquare_task_create_time` (`create_time`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='稳态数据校验任务表';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `steady_checksquare_item` (
|
||||
`id` VARCHAR(64) NOT NULL COMMENT '主键',
|
||||
`task_id` VARCHAR(64) NOT NULL COMMENT '检测任务ID',
|
||||
`item_key` VARCHAR(255) NOT NULL COMMENT '检测项唯一键',
|
||||
`indicator_code` VARCHAR(64) NOT NULL COMMENT '指标编码',
|
||||
`indicator_name` VARCHAR(255) NULL COMMENT '指标名称',
|
||||
`harmonic_order` INT NULL COMMENT '谐波次数;聚合项为空',
|
||||
`interval_minutes` INT NULL COMMENT '当前检测项统计间隔,单位分钟',
|
||||
`has_data` TINYINT NOT NULL DEFAULT 0 COMMENT '是否存在任意数据:0-否,1-是',
|
||||
`expected_point_count` INT NOT NULL DEFAULT 0 COMMENT '期望点数',
|
||||
`actual_point_count` INT NOT NULL DEFAULT 0 COMMENT '实际点数',
|
||||
`missing_point_count` INT NOT NULL DEFAULT 0 COMMENT '缺失点数',
|
||||
`data_integrity` DECIMAL(12,6) NOT NULL DEFAULT 0.000000 COMMENT '数据完整性',
|
||||
`data_integrity_text` VARCHAR(32) NULL COMMENT '数据完整性文本',
|
||||
`abnormal` TINYINT NOT NULL DEFAULT 0 COMMENT '指标值大小关系是否异常',
|
||||
`abnormal_point_count` INT NOT NULL DEFAULT 0 COMMENT '大小关系异常点数',
|
||||
`harmonic_parity_abnormal` TINYINT NOT NULL DEFAULT 0 COMMENT '谐波奇偶关系是否异常',
|
||||
`harmonic_parity_abnormal_point_count` INT NOT NULL DEFAULT 0 COMMENT '谐波奇偶关系异常点数',
|
||||
`state` TINYINT NOT NULL DEFAULT 1 COMMENT '状态:0-删除,1-正常',
|
||||
`create_time` DATETIME NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`update_time` DATETIME NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_steady_checksquare_item` (`task_id`, `item_key`),
|
||||
KEY `idx_steady_checksquare_item_indicator` (`indicator_code`),
|
||||
KEY `idx_steady_checksquare_item_abnormal` (`abnormal`, `harmonic_parity_abnormal`),
|
||||
KEY `idx_steady_checksquare_item_data_integrity` (`data_integrity`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='稳态数据校验检测项表';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `steady_checksquare_stat_summary` (
|
||||
`id` VARCHAR(64) NOT NULL COMMENT '主键',
|
||||
`item_id` VARCHAR(64) NOT NULL COMMENT '检测项ID',
|
||||
`stat_type` VARCHAR(16) NOT NULL COMMENT '统计类型:AVG/MAX/MIN/CP95',
|
||||
`supported` TINYINT NOT NULL DEFAULT 1 COMMENT '是否支持',
|
||||
`has_data` TINYINT NOT NULL DEFAULT 0 COMMENT '是否存在数据',
|
||||
`expected_point_count` INT NOT NULL DEFAULT 0 COMMENT '期望点数',
|
||||
`actual_point_count` INT NOT NULL DEFAULT 0 COMMENT '实际点数',
|
||||
`missing_point_count` INT NOT NULL DEFAULT 0 COMMENT '缺失点数',
|
||||
`data_integrity` DECIMAL(12,6) NOT NULL DEFAULT 0.000000 COMMENT '数据完整性',
|
||||
`data_integrity_text` VARCHAR(32) NULL COMMENT '数据完整性文本',
|
||||
`create_time` DATETIME NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_steady_checksquare_stat` (`item_id`, `stat_type`),
|
||||
KEY `idx_steady_checksquare_stat_type` (`stat_type`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='稳态数据校验统计摘要表';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `steady_checksquare_detail` (
|
||||
`id` VARCHAR(64) NOT NULL COMMENT '主键',
|
||||
`item_id` VARCHAR(64) NOT NULL COMMENT '检测项ID',
|
||||
`detail_type` VARCHAR(32) NOT NULL COMMENT '明细类型:SEGMENT/VALUE_ORDER/HARMONIC_PARITY',
|
||||
`stat_type` VARCHAR(16) NULL COMMENT '统计类型',
|
||||
`start_time` DATETIME NULL COMMENT '区间开始时间',
|
||||
`end_time` DATETIME NULL COMMENT '区间结束时间',
|
||||
`point_time` DATETIME NULL COMMENT '异常点时间',
|
||||
`segment_status` VARCHAR(16) NULL COMMENT '区间状态:NORMAL/MISSING',
|
||||
`missing_point_count` INT NULL COMMENT '缺失点数',
|
||||
`duration_minutes` INT NULL COMMENT '持续时长,单位分钟',
|
||||
`phase` VARCHAR(16) NULL COMMENT '相别',
|
||||
`harmonic_order` INT NULL COMMENT '谐波次数',
|
||||
`max_value` DECIMAL(24,8) NULL COMMENT '最大值',
|
||||
`min_value` DECIMAL(24,8) NULL COMMENT '最小值',
|
||||
`avg_value` DECIMAL(24,8) NULL COMMENT '平均值',
|
||||
`cp95_value` DECIMAL(24,8) NULL COMMENT 'CP95值',
|
||||
`even_harmonic_order` INT NULL COMMENT '偶次谐波次数',
|
||||
`even_value` DECIMAL(24,8) NULL COMMENT '偶次谐波值',
|
||||
`odd_harmonic_orders_json` JSON NULL COMMENT '参与比较的奇次谐波次数',
|
||||
`odd_values_json` JSON NULL COMMENT '参与比较的奇次谐波值',
|
||||
`odd_median_value` DECIMAL(24,8) NULL COMMENT '奇次谐波中位数',
|
||||
`threshold_multiplier` DECIMAL(12,6) NULL COMMENT '异常阈值倍数',
|
||||
`create_time` DATETIME NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_steady_checksquare_detail_item_type` (`item_id`, `detail_type`),
|
||||
KEY `idx_steady_checksquare_detail_point_time` (`point_time`),
|
||||
KEY `idx_steady_checksquare_detail_segment` (`start_time`, `end_time`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='稳态数据校验明细表';
|
||||
@@ -1,14 +0,0 @@
|
||||
ALTER TABLE `steady_checksquare_task`
|
||||
CHANGE COLUMN `max_missing_rate` `min_data_integrity` DECIMAL(12,6) NOT NULL DEFAULT 0.000000 COMMENT '最低数据完整性';
|
||||
|
||||
ALTER TABLE `steady_checksquare_item`
|
||||
DROP INDEX `idx_steady_checksquare_item_missing_rate`,
|
||||
CHANGE COLUMN `missing_rate` `data_integrity` DECIMAL(12,6) NOT NULL DEFAULT 0.000000 COMMENT '数据完整性',
|
||||
CHANGE COLUMN `missing_rate_text` `data_integrity_text` VARCHAR(32) NULL COMMENT '数据完整性文本',
|
||||
DROP COLUMN `max_continuous_missing_minutes`,
|
||||
ADD KEY `idx_steady_checksquare_item_data_integrity` (`data_integrity`);
|
||||
|
||||
ALTER TABLE `steady_checksquare_stat_summary`
|
||||
CHANGE COLUMN `missing_rate` `data_integrity` DECIMAL(12,6) NOT NULL DEFAULT 0.000000 COMMENT '数据完整性',
|
||||
CHANGE COLUMN `missing_rate_text` `data_integrity_text` VARCHAR(32) NULL COMMENT '数据完整性文本',
|
||||
DROP COLUMN `max_continuous_missing_minutes`;
|
||||
@@ -1,38 +0,0 @@
|
||||
package com.njcn.gather.steady.checksquare.component;
|
||||
|
||||
import com.njcn.gather.steady.checksquare.pojo.vo.SteadyChecksquareSegmentVO;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 数据校验缺失区间计算测试。
|
||||
*/
|
||||
class SteadyChecksquareCalculatorTest {
|
||||
|
||||
@Test
|
||||
void shouldMergeContinuousMissingSlots() {
|
||||
SteadyChecksquareCalculator calculator = new SteadyChecksquareCalculator();
|
||||
List<LocalDateTime> slots = Arrays.asList(
|
||||
LocalDateTime.of(2026, 5, 1, 0, 0),
|
||||
LocalDateTime.of(2026, 5, 1, 0, 1),
|
||||
LocalDateTime.of(2026, 5, 1, 0, 2),
|
||||
LocalDateTime.of(2026, 5, 1, 0, 3),
|
||||
LocalDateTime.of(2026, 5, 1, 0, 4)
|
||||
);
|
||||
|
||||
List<SteadyChecksquareSegmentVO> segments = calculator.buildSegments(slots,
|
||||
new HashSet<LocalDateTime>(Arrays.asList(slots.get(0), slots.get(3))), 1);
|
||||
|
||||
Assertions.assertEquals(4, segments.size());
|
||||
Assertions.assertEquals("MISSING", segments.get(1).getStatus());
|
||||
Assertions.assertEquals("2026-05-01 00:01:00", segments.get(1).getStartTime());
|
||||
Assertions.assertEquals("2026-05-01 00:02:00", segments.get(1).getEndTime());
|
||||
Assertions.assertEquals(Integer.valueOf(2), segments.get(1).getMissingPointCount());
|
||||
Assertions.assertEquals(Integer.valueOf(2), segments.get(1).getDurationMinutes());
|
||||
}
|
||||
}
|
||||
@@ -1,147 +0,0 @@
|
||||
package com.njcn.gather.steady.checksquare.component;
|
||||
|
||||
import com.njcn.gather.steady.checksquare.pojo.bo.SteadyChecksquareValuePointBO;
|
||||
import com.njcn.gather.steady.checksquare.pojo.vo.SteadyChecksquareHarmonicParityDetailVO;
|
||||
import com.njcn.gather.steady.checksquare.pojo.vo.SteadyChecksquareHarmonicParityRuleVO;
|
||||
import com.njcn.gather.steady.datavie.component.SteadyTrendIndicatorCatalog;
|
||||
import com.njcn.gather.steady.datavie.pojo.bo.SteadyTrendIndicatorDefinitionBO;
|
||||
import com.njcn.gather.steady.datavie.pojo.bo.SteadyTrendResolvedFieldBO;
|
||||
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 java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* 谐波奇偶关系规则测试。
|
||||
*/
|
||||
class SteadyChecksquareHarmonicParityRuleComponentTest {
|
||||
|
||||
@Test
|
||||
void shouldRecordAbnormalWhenEvenHarmonicExceedsOddMedianThreshold() {
|
||||
SteadyChecksquareInfluxQueryComponent influxQueryComponent = mock(SteadyChecksquareInfluxQueryComponent.class);
|
||||
SteadyChecksquareHarmonicParityRuleComponent component = new SteadyChecksquareHarmonicParityRuleComponent(influxQueryComponent);
|
||||
LocalDateTime time = LocalDateTime.of(2026, 5, 1, 0, 0);
|
||||
when(influxQueryComponent.queryValuePointMap(any(),
|
||||
any(LocalDateTime.class), any(LocalDateTime.class), anyInt()))
|
||||
.thenAnswer(invocation -> {
|
||||
Map<String, List<SteadyChecksquareValuePointBO>> values = emptyBatchResult(invocation.getArgument(0));
|
||||
putPoint(values, "v_3", time, "10");
|
||||
putPoint(values, "v_4", time, "31");
|
||||
putPoint(values, "v_5", time, "12");
|
||||
putPoint(values, "v_7", time, "14");
|
||||
return values;
|
||||
});
|
||||
SteadyTrendIndicatorDefinitionBO indicator = new SteadyTrendIndicatorCatalog().getIndicator("V_HARMONIC");
|
||||
|
||||
SteadyChecksquareHarmonicParityRuleVO result = component.check("line-001", indicator,
|
||||
time, time, 1);
|
||||
|
||||
Assertions.assertEquals(Boolean.TRUE, result.getAbnormal());
|
||||
Assertions.assertEquals(Integer.valueOf(1), result.getAbnormalPointCount());
|
||||
SteadyChecksquareHarmonicParityDetailVO detail = result.getAbnormalDetails().get(0);
|
||||
Assertions.assertEquals("2026-05-01 00:00:00", detail.getTime());
|
||||
Assertions.assertEquals("A", detail.getPhase());
|
||||
Assertions.assertEquals("AVG", detail.getStatType());
|
||||
Assertions.assertEquals(Integer.valueOf(4), detail.getEvenHarmonicOrder());
|
||||
Assertions.assertEquals(new BigDecimal("31"), detail.getEvenValue());
|
||||
Assertions.assertEquals(Arrays.asList(3, 5, 7), detail.getOddHarmonicOrders());
|
||||
Assertions.assertEquals(new BigDecimal("12"), detail.getOddMedianValue());
|
||||
Assertions.assertEquals(new BigDecimal("2"), detail.getThresholdMultiplier());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldSkipWhenOddReferenceCountLessThanTwo() {
|
||||
SteadyChecksquareInfluxQueryComponent influxQueryComponent = mock(SteadyChecksquareInfluxQueryComponent.class);
|
||||
SteadyChecksquareHarmonicParityRuleComponent component = new SteadyChecksquareHarmonicParityRuleComponent(influxQueryComponent);
|
||||
LocalDateTime time = LocalDateTime.of(2026, 5, 1, 0, 0);
|
||||
when(influxQueryComponent.queryValuePointMap(any(),
|
||||
any(LocalDateTime.class), any(LocalDateTime.class), anyInt()))
|
||||
.thenAnswer(invocation -> {
|
||||
Map<String, List<SteadyChecksquareValuePointBO>> values = emptyBatchResult(invocation.getArgument(0));
|
||||
putPoint(values, "v_2", time, "50");
|
||||
putPoint(values, "v_3", time, "10");
|
||||
return values;
|
||||
});
|
||||
SteadyTrendIndicatorDefinitionBO indicator = new SteadyTrendIndicatorCatalog().getIndicator("V_HARMONIC");
|
||||
|
||||
SteadyChecksquareHarmonicParityRuleVO result = component.check("line-001", indicator,
|
||||
time, time, 1);
|
||||
|
||||
Assertions.assertEquals(Boolean.FALSE, result.getAbnormal());
|
||||
Assertions.assertEquals(Integer.valueOf(0), result.getAbnormalPointCount());
|
||||
Assertions.assertTrue(result.getAbnormalDetails().isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldSkipEvenHarmonicWhenValueNotGreaterThanDeadband() {
|
||||
SteadyChecksquareInfluxQueryComponent influxQueryComponent = mock(SteadyChecksquareInfluxQueryComponent.class);
|
||||
SteadyChecksquareHarmonicParityRuleComponent component = new SteadyChecksquareHarmonicParityRuleComponent(influxQueryComponent);
|
||||
LocalDateTime time = LocalDateTime.of(2026, 5, 1, 0, 0);
|
||||
when(influxQueryComponent.queryValuePointMap(any(),
|
||||
any(LocalDateTime.class), any(LocalDateTime.class), anyInt()))
|
||||
.thenAnswer(invocation -> {
|
||||
Map<String, List<SteadyChecksquareValuePointBO>> values = emptyBatchResult(invocation.getArgument(0));
|
||||
putPoint(values, "v_3", time, "0.01");
|
||||
putPoint(values, "v_4", time, "0.10");
|
||||
putPoint(values, "v_5", time, "0.02");
|
||||
return values;
|
||||
});
|
||||
SteadyTrendIndicatorDefinitionBO indicator = new SteadyTrendIndicatorCatalog().getIndicator("V_HARMONIC");
|
||||
|
||||
SteadyChecksquareHarmonicParityRuleVO result = component.check("line-001", indicator,
|
||||
time, time, 1);
|
||||
|
||||
Assertions.assertEquals(Boolean.FALSE, result.getAbnormal());
|
||||
Assertions.assertEquals(Integer.valueOf(0), result.getAbnormalPointCount());
|
||||
Assertions.assertTrue(result.getAbnormalDetails().isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldSkipNonHarmonicIndicator() {
|
||||
SteadyChecksquareInfluxQueryComponent influxQueryComponent = mock(SteadyChecksquareInfluxQueryComponent.class);
|
||||
SteadyChecksquareHarmonicParityRuleComponent component = new SteadyChecksquareHarmonicParityRuleComponent(influxQueryComponent);
|
||||
SteadyTrendIndicatorDefinitionBO indicator = new SteadyTrendIndicatorCatalog().getIndicator("V_RMS");
|
||||
|
||||
SteadyChecksquareHarmonicParityRuleVO result = component.check("line-001", indicator,
|
||||
LocalDateTime.of(2026, 5, 1, 0, 0),
|
||||
LocalDateTime.of(2026, 5, 1, 0, 1), 1);
|
||||
|
||||
Assertions.assertEquals(Boolean.FALSE, result.getAbnormal());
|
||||
Assertions.assertEquals(Integer.valueOf(0), result.getAbnormalPointCount());
|
||||
Assertions.assertTrue(result.getAbnormalDetails().isEmpty());
|
||||
}
|
||||
|
||||
private SteadyChecksquareValuePointBO point(LocalDateTime time, String value) {
|
||||
SteadyChecksquareValuePointBO point = new SteadyChecksquareValuePointBO();
|
||||
point.setTime(time);
|
||||
point.setValue(new BigDecimal(value));
|
||||
return point;
|
||||
}
|
||||
|
||||
private Map<String, List<SteadyChecksquareValuePointBO>> emptyBatchResult(List<SteadyTrendResolvedFieldBO> fields) {
|
||||
Map<String, List<SteadyChecksquareValuePointBO>> result =
|
||||
new LinkedHashMap<String, List<SteadyChecksquareValuePointBO>>();
|
||||
for (SteadyTrendResolvedFieldBO field : fields) {
|
||||
result.put(field.getField(), Collections.<SteadyChecksquareValuePointBO>emptyList());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private void putPoint(Map<String, List<SteadyChecksquareValuePointBO>> values, String field,
|
||||
LocalDateTime time, String value) {
|
||||
if (values.containsKey(field)) {
|
||||
values.put(field, Collections.singletonList(point(time, value)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,153 +0,0 @@
|
||||
package com.njcn.gather.steady.checksquare.component;
|
||||
|
||||
import com.njcn.gather.steady.datavie.config.SteadyInfluxDbProperties;
|
||||
import com.njcn.gather.steady.datavie.pojo.bo.SteadyTrendResolvedFieldBO;
|
||||
import com.sun.net.httpserver.HttpServer;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* 数据校验 InfluxQL 构造契约测试。
|
||||
*/
|
||||
class SteadyChecksquareInfluxQueryComponentTest {
|
||||
|
||||
@Test
|
||||
void shouldBuildChecksquareQueryWithoutQualityFlag() {
|
||||
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("AVG");
|
||||
|
||||
String query = component.buildChecksquareQuery(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\" = 'AVG'"));
|
||||
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"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReuseValuePointQueryWithinRequestCache() throws Exception {
|
||||
AtomicInteger requestCount = new AtomicInteger();
|
||||
HttpServer server = HttpServer.create(new InetSocketAddress(0), 0);
|
||||
server.createContext("/query", exchange -> {
|
||||
requestCount.incrementAndGet();
|
||||
byte[] body = ("{\"results\":[{\"series\":[{\"values\":["
|
||||
+ "[\"2026-05-01T00:00:00Z\",1.23],"
|
||||
+ "[\"2026-05-01T00:01:00Z\",2.34]"
|
||||
+ "]}]}]}").getBytes(StandardCharsets.UTF_8);
|
||||
exchange.sendResponseHeaders(200, body.length);
|
||||
exchange.getResponseBody().write(body);
|
||||
exchange.close();
|
||||
});
|
||||
server.start();
|
||||
try {
|
||||
SteadyInfluxDbProperties properties = new SteadyInfluxDbProperties();
|
||||
properties.setUrl("http://127.0.0.1:" + server.getAddress().getPort());
|
||||
properties.setDatabase("steady");
|
||||
SteadyChecksquareInfluxQueryComponent component = new SteadyChecksquareInfluxQueryComponent(properties);
|
||||
SteadyTrendResolvedFieldBO field = new SteadyTrendResolvedFieldBO();
|
||||
field.setMeasurement("data_v");
|
||||
field.setField("rms");
|
||||
field.setLineId("line-001");
|
||||
field.setPhase("A");
|
||||
field.setStatType("AVG");
|
||||
|
||||
component.enableRequestCache();
|
||||
component.queryExistingSlots(field,
|
||||
LocalDateTime.of(2026, 5, 1, 0, 0, 0),
|
||||
LocalDateTime.of(2026, 5, 1, 0, 1, 0), 1);
|
||||
component.queryValuePoints(field,
|
||||
LocalDateTime.of(2026, 5, 1, 0, 0, 0),
|
||||
LocalDateTime.of(2026, 5, 1, 0, 1, 0), 1);
|
||||
component.clearRequestCache();
|
||||
|
||||
Assertions.assertEquals(1, requestCount.get());
|
||||
} finally {
|
||||
server.stop(0);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldQueryMultipleValueFieldsOnce() throws Exception {
|
||||
AtomicInteger requestCount = new AtomicInteger();
|
||||
HttpServer server = HttpServer.create(new InetSocketAddress(0), 0);
|
||||
server.createContext("/query", exchange -> {
|
||||
requestCount.incrementAndGet();
|
||||
byte[] body = ("{\"results\":[{\"series\":[{\"columns\":[\"time\",\"h_2\",\"h_3\"],\"values\":["
|
||||
+ "[\"2026-05-01T00:00:00Z\",1.23,2.34],"
|
||||
+ "[\"2026-05-01T00:01:00Z\",3.45,null]"
|
||||
+ "]}]}]}").getBytes(StandardCharsets.UTF_8);
|
||||
exchange.sendResponseHeaders(200, body.length);
|
||||
exchange.getResponseBody().write(body);
|
||||
exchange.close();
|
||||
});
|
||||
server.start();
|
||||
try {
|
||||
SteadyInfluxDbProperties properties = new SteadyInfluxDbProperties();
|
||||
properties.setUrl("http://127.0.0.1:" + server.getAddress().getPort());
|
||||
properties.setDatabase("steady");
|
||||
SteadyChecksquareInfluxQueryComponent component = new SteadyChecksquareInfluxQueryComponent(properties);
|
||||
SteadyTrendResolvedFieldBO h2 = buildField("h_2");
|
||||
SteadyTrendResolvedFieldBO h3 = buildField("h_3");
|
||||
|
||||
component.enableRequestCache();
|
||||
Map<String, java.util.List<com.njcn.gather.steady.checksquare.pojo.bo.SteadyChecksquareValuePointBO>> result =
|
||||
component.queryValuePointMap(Arrays.asList(h2, h3),
|
||||
LocalDateTime.of(2026, 5, 1, 0, 0, 0),
|
||||
LocalDateTime.of(2026, 5, 1, 0, 1, 0), 1);
|
||||
component.clearRequestCache();
|
||||
|
||||
Assertions.assertEquals(1, requestCount.get());
|
||||
Assertions.assertEquals(2, result.get("h_2").size());
|
||||
Assertions.assertEquals(1, result.get("h_3").size());
|
||||
} finally {
|
||||
server.stop(0);
|
||||
}
|
||||
}
|
||||
|
||||
private SteadyTrendResolvedFieldBO buildField(String fieldName) {
|
||||
SteadyTrendResolvedFieldBO field = new SteadyTrendResolvedFieldBO();
|
||||
field.setMeasurement("data_harmonic");
|
||||
field.setField(fieldName);
|
||||
field.setLineId("line-001");
|
||||
field.setPhase("A");
|
||||
field.setStatType("AVG");
|
||||
return field;
|
||||
}
|
||||
}
|
||||
@@ -1,219 +0,0 @@
|
||||
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, "9"), 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, "9"));
|
||||
}
|
||||
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("9"), result.getAbnormalDetails().get(0).getCp95Value());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldTreatEqualAdjacentStatValuesAsNormal() {
|
||||
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, "10"));
|
||||
}
|
||||
if ("AVG".equals(statType)) {
|
||||
return Collections.singletonList(point(time, "8"));
|
||||
}
|
||||
if ("MIN".equals(statType)) {
|
||||
return Collections.singletonList(point(time, "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, 1), 1);
|
||||
|
||||
Assertions.assertEquals(Integer.valueOf(0), result.getAbnormalPointCount());
|
||||
Assertions.assertEquals(Boolean.FALSE, result.getAbnormal());
|
||||
Assertions.assertTrue(result.getAbnormalDetails().isEmpty());
|
||||
}
|
||||
|
||||
@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, "8"));
|
||||
}
|
||||
if ("CP95".equals(statType)) {
|
||||
return Collections.singletonList(point(time, "10"));
|
||||
}
|
||||
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 shouldFillHarmonicOrderInAbnormalDetailForHarmonicIndicator() {
|
||||
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, "8"));
|
||||
}
|
||||
if ("CP95".equals(statType)) {
|
||||
return Collections.singletonList(point(time, "10"));
|
||||
}
|
||||
if ("AVG".equals(statType)) {
|
||||
return Collections.singletonList(point(time, "8"));
|
||||
}
|
||||
if ("MIN".equals(statType)) {
|
||||
return Collections.singletonList(point(time, "1"));
|
||||
}
|
||||
return Collections.emptyList();
|
||||
});
|
||||
SteadyTrendIndicatorDefinitionBO indicator = indicator();
|
||||
indicator.setHarmonic(true);
|
||||
indicator.setHarmonicFieldPrefix("v");
|
||||
|
||||
SteadyChecksquareValueOrderRuleVO result = component.check("line-001", indicator, 5,
|
||||
LocalDateTime.of(2026, 5, 1, 0, 0), LocalDateTime.of(2026, 5, 1, 0, 1), 1);
|
||||
|
||||
Assertions.assertEquals(1, result.getAbnormalDetails().size());
|
||||
Assertions.assertEquals(Integer.valueOf(5), result.getAbnormalDetails().get(0).getHarmonicOrder());
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
package com.njcn.gather.steady.checksquare.controller;
|
||||
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
|
||||
import java.util.List;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* 数据校验接口契约测试。
|
||||
*/
|
||||
class SteadyChecksquareControllerTest {
|
||||
|
||||
@Test
|
||||
void shouldExposeChecksquareQueryEndpointInSeparateController() throws Exception {
|
||||
RequestMapping requestMapping = SteadyChecksquareController.class.getAnnotation(RequestMapping.class);
|
||||
Assertions.assertArrayEquals(new String[]{"/steady/data-view/checksquare"}, requestMapping.value());
|
||||
|
||||
Method queryMethod = SteadyChecksquareController.class.getDeclaredMethod("query", com.njcn.gather.steady.checksquare.pojo.param.SteadyChecksquareHistoryQueryParam.class);
|
||||
PostMapping queryMapping = queryMethod.getAnnotation(PostMapping.class);
|
||||
Assertions.assertArrayEquals(new String[]{"/query"}, queryMapping.value());
|
||||
|
||||
Method createMethod = SteadyChecksquareController.class.getDeclaredMethod("create", com.njcn.gather.steady.checksquare.pojo.param.SteadyChecksquareQueryParam.class);
|
||||
PostMapping createMapping = createMethod.getAnnotation(PostMapping.class);
|
||||
Assertions.assertArrayEquals(new String[]{"/create"}, createMapping.value());
|
||||
|
||||
Method detailMethod = SteadyChecksquareController.class.getDeclaredMethod("detail", String.class);
|
||||
GetMapping detailMapping = detailMethod.getAnnotation(GetMapping.class);
|
||||
Assertions.assertArrayEquals(new String[]{"/detail"}, detailMapping.value());
|
||||
|
||||
Method itemDetailMethod = SteadyChecksquareController.class.getDeclaredMethod("itemDetail",
|
||||
String.class, String.class, String.class, Integer.class, Integer.class);
|
||||
GetMapping itemDetailMapping = itemDetailMethod.getAnnotation(GetMapping.class);
|
||||
Assertions.assertArrayEquals(new String[]{"/item-detail"}, itemDetailMapping.value());
|
||||
|
||||
Method deleteMethod = SteadyChecksquareController.class.getDeclaredMethod("delete", List.class);
|
||||
PostMapping deleteMapping = deleteMethod.getAnnotation(PostMapping.class);
|
||||
Assertions.assertArrayEquals(new String[]{"/delete"}, deleteMapping.value());
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
package com.njcn.gather.steady.checksquare.pojo.param;
|
||||
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
/**
|
||||
* 数据校验查询参数契约测试。
|
||||
*/
|
||||
class SteadyChecksquareQueryParamTest {
|
||||
|
||||
@Test
|
||||
void shouldOnlyExposeChecksquareQueryFields() {
|
||||
Assertions.assertNotNull(field("lineId"));
|
||||
Assertions.assertNotNull(field("indicatorCodes"));
|
||||
Assertions.assertNotNull(field("timeStart"));
|
||||
Assertions.assertNotNull(field("timeEnd"));
|
||||
Assertions.assertNull(field("qualityFlag"));
|
||||
Assertions.assertNull(field("statTypes"));
|
||||
Assertions.assertNull(field("phases"));
|
||||
Assertions.assertNull(field("harmonicOrders"));
|
||||
}
|
||||
|
||||
private Field field(String name) {
|
||||
try {
|
||||
return SteadyChecksquareQueryParam.class.getDeclaredField(name);
|
||||
} catch (NoSuchFieldException ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,714 +0,0 @@
|
||||
package com.njcn.gather.steady.checksquare.service.impl;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.njcn.gather.steady.checksquare.component.SteadyChecksquareCalculator;
|
||||
import com.njcn.gather.steady.checksquare.component.SteadyChecksquareHarmonicParityRuleComponent;
|
||||
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.po.SteadyChecksquareDetailPO;
|
||||
import com.njcn.gather.steady.checksquare.pojo.po.SteadyChecksquareItemPO;
|
||||
import com.njcn.gather.steady.checksquare.pojo.po.SteadyChecksquareStatSummaryPO;
|
||||
import com.njcn.gather.steady.checksquare.pojo.po.SteadyChecksquareTaskPO;
|
||||
import com.njcn.gather.steady.checksquare.pojo.vo.SteadyChecksquareHarmonicParityDetailVO;
|
||||
import com.njcn.gather.steady.checksquare.pojo.vo.SteadyChecksquareHarmonicParityRuleVO;
|
||||
import com.njcn.gather.steady.checksquare.pojo.vo.SteadyChecksquareItemDetailVO;
|
||||
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.SteadyChecksquareStatSummaryVO;
|
||||
import com.njcn.gather.steady.checksquare.pojo.vo.SteadyChecksquareValueOrderDetailVO;
|
||||
import com.njcn.gather.steady.checksquare.pojo.vo.SteadyChecksquareValueOrderRuleVO;
|
||||
import com.njcn.gather.steady.checksquare.service.SteadyChecksquareDetailService;
|
||||
import com.njcn.gather.steady.checksquare.service.SteadyChecksquareItemService;
|
||||
import com.njcn.gather.steady.checksquare.service.SteadyChecksquareStatSummaryService;
|
||||
import com.njcn.gather.steady.checksquare.service.SteadyChecksquareTaskService;
|
||||
import com.njcn.gather.steady.datavie.component.SteadyTrendIndicatorCatalog;
|
||||
import com.njcn.gather.steady.datavie.pojo.bo.SteadyTrendIndicatorDefinitionBO;
|
||||
import com.njcn.gather.steady.datavie.pojo.bo.SteadyTrendResolvedFieldBO;
|
||||
import com.njcn.gather.tool.adddata.component.AddDataTimeSlotCalculator;
|
||||
import com.njcn.gather.tool.addledger.pojo.vo.AddLedgerLinePathVO;
|
||||
import com.njcn.gather.tool.addledger.service.AddLedgerService;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
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.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* 数据校验服务测试。
|
||||
*/
|
||||
class SteadyChecksquareServiceImplTest {
|
||||
|
||||
@Test
|
||||
void shouldNotOpenTransactionAroundCreateCalculation() throws Exception {
|
||||
Method createMethod = SteadyChecksquareServiceImpl.class.getMethod("create", SteadyChecksquareQueryParam.class);
|
||||
|
||||
Assertions.assertNull(createMethod.getAnnotation(Transactional.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldUseFixedFlickerIntervalsPerIndicator() {
|
||||
SteadyChecksquareInfluxQueryComponent influxQueryComponent = mock(SteadyChecksquareInfluxQueryComponent.class);
|
||||
SteadyChecksquareValueOrderRuleComponent valueOrderRuleComponent = mock(SteadyChecksquareValueOrderRuleComponent.class);
|
||||
SteadyChecksquareHarmonicParityRuleComponent harmonicParityRuleComponent = mock(SteadyChecksquareHarmonicParityRuleComponent.class);
|
||||
AddLedgerService addLedgerService = mock(AddLedgerService.class);
|
||||
SteadyChecksquareServiceImpl service = new SteadyChecksquareServiceImpl(new SteadyTrendIndicatorCatalog(),
|
||||
influxQueryComponent, new SteadyChecksquareCalculator(), valueOrderRuleComponent, harmonicParityRuleComponent,
|
||||
new AddDataTimeSlotCalculator(), addLedgerService, mock(SteadyChecksquareTaskService.class),
|
||||
mock(SteadyChecksquareItemService.class), mock(SteadyChecksquareStatSummaryService.class),
|
||||
mock(SteadyChecksquareDetailService.class), new ObjectMapper());
|
||||
when(valueOrderRuleComponent.check(any(), any(), any(), any(LocalDateTime.class), any(LocalDateTime.class), anyInt()))
|
||||
.thenReturn(emptyRuleResult());
|
||||
when(harmonicParityRuleComponent.check(any(), any(), any(LocalDateTime.class), any(LocalDateTime.class), anyInt()))
|
||||
.thenReturn(emptyHarmonicParityRuleResult());
|
||||
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(10)))
|
||||
.thenReturn(new HashSet<LocalDateTime>(Arrays.asList(
|
||||
LocalDateTime.of(2026, 5, 1, 0, 0),
|
||||
LocalDateTime.of(2026, 5, 1, 0, 10))));
|
||||
when(influxQueryComponent.queryExistingSlots(any(), any(LocalDateTime.class), any(LocalDateTime.class), eq(120)))
|
||||
.thenReturn(new HashSet<LocalDateTime>(Collections.singletonList(
|
||||
LocalDateTime.of(2026, 5, 1, 0, 0))));
|
||||
|
||||
SteadyChecksquareQueryParam param = new SteadyChecksquareQueryParam();
|
||||
param.setLineId("line-001");
|
||||
param.setIndicatorCodes(Arrays.asList("FLUC", "PST", "PLT"));
|
||||
param.setTimeStart("2026-05-01 00:00:00");
|
||||
param.setTimeEnd("2026-05-01 02:00:00");
|
||||
|
||||
SteadyChecksquareQueryVO result = calculate(service, param);
|
||||
|
||||
Assertions.assertEquals(Integer.valueOf(1), result.getIntervalMinutes());
|
||||
Assertions.assertEquals(3, result.getItems().size());
|
||||
assertItemInterval(result.getItems().get(0), "FLUC", 10, 13);
|
||||
assertItemInterval(result.getItems().get(1), "PST", 10, 13);
|
||||
assertItemInterval(result.getItems().get(2), "PLT", 120, 2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldAggregateAllHarmonicOrdersIntoIndicatorItem() {
|
||||
SteadyChecksquareInfluxQueryComponent influxQueryComponent = mock(SteadyChecksquareInfluxQueryComponent.class);
|
||||
SteadyChecksquareValueOrderRuleComponent valueOrderRuleComponent = mock(SteadyChecksquareValueOrderRuleComponent.class);
|
||||
SteadyChecksquareHarmonicParityRuleComponent harmonicParityRuleComponent = mock(SteadyChecksquareHarmonicParityRuleComponent.class);
|
||||
AddLedgerService addLedgerService = mock(AddLedgerService.class);
|
||||
SteadyChecksquareServiceImpl service = new SteadyChecksquareServiceImpl(new SteadyTrendIndicatorCatalog(),
|
||||
influxQueryComponent, new SteadyChecksquareCalculator(), valueOrderRuleComponent, harmonicParityRuleComponent,
|
||||
new AddDataTimeSlotCalculator(), addLedgerService, mock(SteadyChecksquareTaskService.class),
|
||||
mock(SteadyChecksquareItemService.class), mock(SteadyChecksquareStatSummaryService.class),
|
||||
mock(SteadyChecksquareDetailService.class), new ObjectMapper());
|
||||
when(valueOrderRuleComponent.check(any(), any(), any(), any(LocalDateTime.class), any(LocalDateTime.class), anyInt()))
|
||||
.thenReturn(emptyRuleResult());
|
||||
when(harmonicParityRuleComponent.check(any(), any(), any(LocalDateTime.class), any(LocalDateTime.class), anyInt()))
|
||||
.thenReturn(emptyHarmonicParityRuleResult());
|
||||
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))));
|
||||
|
||||
SteadyChecksquareQueryParam param = new SteadyChecksquareQueryParam();
|
||||
param.setLineId("line-001");
|
||||
param.setIndicatorCodes(Collections.singletonList("V_HARMONIC"));
|
||||
param.setTimeStart("2026-05-01 00:00:00");
|
||||
param.setTimeEnd("2026-05-01 00:01:00");
|
||||
|
||||
SteadyChecksquareQueryVO result = calculate(service, param);
|
||||
|
||||
Assertions.assertEquals(1, result.getItems().size());
|
||||
Assertions.assertEquals("line-001|V_HARMONIC", result.getItems().get(0).getItemKey());
|
||||
Assertions.assertNull(result.getItems().get(0).getHarmonicOrder());
|
||||
Assertions.assertEquals(Integer.valueOf(2), result.getItems().get(0).getStatDetails().get(0).getSegments().get(0).getHarmonicOrder());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldAverageHarmonicOrderResultsAndMarkAbnormalWhenAnyOrderAbnormal() {
|
||||
SteadyChecksquareInfluxQueryComponent influxQueryComponent = mock(SteadyChecksquareInfluxQueryComponent.class);
|
||||
SteadyChecksquareValueOrderRuleComponent valueOrderRuleComponent = mock(SteadyChecksquareValueOrderRuleComponent.class);
|
||||
SteadyChecksquareHarmonicParityRuleComponent harmonicParityRuleComponent = mock(SteadyChecksquareHarmonicParityRuleComponent.class);
|
||||
AddLedgerService addLedgerService = mock(AddLedgerService.class);
|
||||
SteadyChecksquareServiceImpl service = new SteadyChecksquareServiceImpl(new SteadyTrendIndicatorCatalog(),
|
||||
influxQueryComponent, new SteadyChecksquareCalculator(), valueOrderRuleComponent, harmonicParityRuleComponent,
|
||||
new AddDataTimeSlotCalculator(), addLedgerService, mock(SteadyChecksquareTaskService.class),
|
||||
mock(SteadyChecksquareItemService.class), mock(SteadyChecksquareStatSummaryService.class),
|
||||
mock(SteadyChecksquareDetailService.class), new ObjectMapper());
|
||||
when(valueOrderRuleComponent.check(any(), any(), any(), any(LocalDateTime.class), any(LocalDateTime.class), anyInt()))
|
||||
.thenReturn(emptyRuleResult());
|
||||
when(harmonicParityRuleComponent.check(any(), any(), any(LocalDateTime.class), any(LocalDateTime.class), anyInt()))
|
||||
.thenReturn(emptyHarmonicParityRuleResult());
|
||||
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 normalRuleResult = new SteadyChecksquareValueOrderRuleVO();
|
||||
SteadyChecksquareValueOrderRuleVO abnormalRuleResult = new SteadyChecksquareValueOrderRuleVO();
|
||||
SteadyChecksquareValueOrderDetailVO abnormalDetail = new SteadyChecksquareValueOrderDetailVO();
|
||||
abnormalDetail.setTime("2026-05-01 00:00:00");
|
||||
abnormalDetail.setPhase("A");
|
||||
abnormalDetail.setHarmonicOrder(2);
|
||||
abnormalRuleResult.setAbnormal(true);
|
||||
abnormalRuleResult.setAbnormalPointCount(4);
|
||||
abnormalRuleResult.setAbnormalDetails(Collections.singletonList(abnormalDetail));
|
||||
when(valueOrderRuleComponent.check(any(), any(), any(), any(LocalDateTime.class), any(LocalDateTime.class), anyInt()))
|
||||
.thenReturn(normalRuleResult);
|
||||
when(valueOrderRuleComponent.check(any(), any(), eq(2), any(LocalDateTime.class), any(LocalDateTime.class), anyInt()))
|
||||
.thenReturn(abnormalRuleResult);
|
||||
when(valueOrderRuleComponent.check(any(), any(), eq(3), any(LocalDateTime.class), any(LocalDateTime.class), anyInt()))
|
||||
.thenReturn(normalRuleResult);
|
||||
|
||||
SteadyChecksquareQueryParam param = new SteadyChecksquareQueryParam();
|
||||
param.setLineId("line-001");
|
||||
param.setIndicatorCodes(Collections.singletonList("V_HARMONIC"));
|
||||
param.setTimeStart("2026-05-01 00:00:00");
|
||||
param.setTimeEnd("2026-05-01 00:01:00");
|
||||
|
||||
SteadyChecksquareQueryVO result = calculate(service, param);
|
||||
|
||||
List<SteadyChecksquareItemVO> items = result.getItems();
|
||||
Assertions.assertEquals(1, items.size());
|
||||
Assertions.assertEquals(Boolean.TRUE, items.get(0).getAbnormal());
|
||||
Assertions.assertEquals(Integer.valueOf(1), items.get(0).getAbnormalPointCount());
|
||||
Assertions.assertEquals(1, items.get(0).getAbnormalDetails().size());
|
||||
Assertions.assertEquals(Integer.valueOf(2), items.get(0).getAbnormalDetails().get(0).getHarmonicOrder());
|
||||
Assertions.assertEquals(Integer.valueOf(8), items.get(0).getExpectedPointCount());
|
||||
Assertions.assertEquals(Integer.valueOf(4), items.get(0).getActualPointCount());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldAssembleValueOrderRuleResultIntoItem() {
|
||||
SteadyChecksquareInfluxQueryComponent influxQueryComponent = mock(SteadyChecksquareInfluxQueryComponent.class);
|
||||
SteadyChecksquareValueOrderRuleComponent valueOrderRuleComponent = mock(SteadyChecksquareValueOrderRuleComponent.class);
|
||||
SteadyChecksquareHarmonicParityRuleComponent harmonicParityRuleComponent = mock(SteadyChecksquareHarmonicParityRuleComponent.class);
|
||||
AddLedgerService addLedgerService = mock(AddLedgerService.class);
|
||||
SteadyChecksquareServiceImpl service = new SteadyChecksquareServiceImpl(new SteadyTrendIndicatorCatalog(),
|
||||
influxQueryComponent, new SteadyChecksquareCalculator(), valueOrderRuleComponent, harmonicParityRuleComponent,
|
||||
new AddDataTimeSlotCalculator(), addLedgerService, mock(SteadyChecksquareTaskService.class),
|
||||
mock(SteadyChecksquareItemService.class), mock(SteadyChecksquareStatSummaryService.class),
|
||||
mock(SteadyChecksquareDetailService.class), new ObjectMapper());
|
||||
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 = calculate(service, 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());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldPrefetchNormalIndicatorFieldsByMeasurementPhaseAndStat() {
|
||||
SteadyChecksquareInfluxQueryComponent influxQueryComponent = mock(SteadyChecksquareInfluxQueryComponent.class);
|
||||
SteadyChecksquareValueOrderRuleComponent valueOrderRuleComponent = mock(SteadyChecksquareValueOrderRuleComponent.class);
|
||||
SteadyChecksquareHarmonicParityRuleComponent harmonicParityRuleComponent = mock(SteadyChecksquareHarmonicParityRuleComponent.class);
|
||||
AddLedgerService addLedgerService = mock(AddLedgerService.class);
|
||||
SteadyChecksquareServiceImpl service = new SteadyChecksquareServiceImpl(new SteadyTrendIndicatorCatalog(),
|
||||
influxQueryComponent, new SteadyChecksquareCalculator(), valueOrderRuleComponent, harmonicParityRuleComponent,
|
||||
new AddDataTimeSlotCalculator(), addLedgerService, mock(SteadyChecksquareTaskService.class),
|
||||
mock(SteadyChecksquareItemService.class), mock(SteadyChecksquareStatSummaryService.class),
|
||||
mock(SteadyChecksquareDetailService.class), new ObjectMapper());
|
||||
when(valueOrderRuleComponent.check(any(), any(), any(), any(LocalDateTime.class), any(LocalDateTime.class), anyInt()))
|
||||
.thenReturn(emptyRuleResult());
|
||||
when(harmonicParityRuleComponent.check(any(), any(), any(LocalDateTime.class), any(LocalDateTime.class), anyInt()))
|
||||
.thenReturn(emptyHarmonicParityRuleResult());
|
||||
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))));
|
||||
|
||||
SteadyChecksquareQueryParam param = new SteadyChecksquareQueryParam();
|
||||
param.setLineId("line-001");
|
||||
param.setIndicatorCodes(Arrays.asList("V_RMS", "V_LINE_RMS"));
|
||||
param.setTimeStart("2026-05-01 00:00:00");
|
||||
param.setTimeEnd("2026-05-01 00:01:00");
|
||||
|
||||
calculate(service, param);
|
||||
|
||||
ArgumentCaptor<List> captor = ArgumentCaptor.forClass(List.class);
|
||||
verify(influxQueryComponent, times(12)).queryValuePointMap(captor.capture(),
|
||||
any(LocalDateTime.class), any(LocalDateTime.class), eq(1));
|
||||
boolean foundBatch = false;
|
||||
for (List fields : captor.getAllValues()) {
|
||||
if (fields.size() == 2) {
|
||||
List<String> fieldNames = new ArrayList<String>();
|
||||
for (Object field : fields) {
|
||||
fieldNames.add(((SteadyTrendResolvedFieldBO) field).getField());
|
||||
}
|
||||
foundBatch = fieldNames.contains("rms") && fieldNames.contains("rms_lvr");
|
||||
}
|
||||
}
|
||||
Assertions.assertTrue(foundBatch);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldAssembleHarmonicParityRuleResultIntoAggregateItem() {
|
||||
SteadyChecksquareInfluxQueryComponent influxQueryComponent = mock(SteadyChecksquareInfluxQueryComponent.class);
|
||||
SteadyChecksquareValueOrderRuleComponent valueOrderRuleComponent = mock(SteadyChecksquareValueOrderRuleComponent.class);
|
||||
SteadyChecksquareHarmonicParityRuleComponent harmonicParityRuleComponent = mock(SteadyChecksquareHarmonicParityRuleComponent.class);
|
||||
AddLedgerService addLedgerService = mock(AddLedgerService.class);
|
||||
SteadyChecksquareServiceImpl service = new SteadyChecksquareServiceImpl(new SteadyTrendIndicatorCatalog(),
|
||||
influxQueryComponent, new SteadyChecksquareCalculator(), valueOrderRuleComponent, harmonicParityRuleComponent,
|
||||
new AddDataTimeSlotCalculator(), addLedgerService, mock(SteadyChecksquareTaskService.class),
|
||||
mock(SteadyChecksquareItemService.class), mock(SteadyChecksquareStatSummaryService.class),
|
||||
mock(SteadyChecksquareDetailService.class), new ObjectMapper());
|
||||
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("进线一");
|
||||
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))));
|
||||
SteadyChecksquareHarmonicParityRuleVO ruleResult = new SteadyChecksquareHarmonicParityRuleVO();
|
||||
SteadyChecksquareHarmonicParityDetailVO detail = new SteadyChecksquareHarmonicParityDetailVO();
|
||||
detail.setTime("2026-05-01 00:00:00");
|
||||
detail.setPhase("A");
|
||||
detail.setStatType("AVG");
|
||||
detail.setEvenHarmonicOrder(4);
|
||||
ruleResult.setAbnormal(true);
|
||||
ruleResult.setAbnormalPointCount(1);
|
||||
ruleResult.setAbnormalDetails(Collections.singletonList(detail));
|
||||
when(harmonicParityRuleComponent.check(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_HARMONIC"));
|
||||
param.setTimeStart("2026-05-01 00:00:00");
|
||||
param.setTimeEnd("2026-05-01 00:01:00");
|
||||
|
||||
SteadyChecksquareQueryVO result = calculate(service, param);
|
||||
|
||||
SteadyChecksquareItemVO item = result.getItems().get(0);
|
||||
Assertions.assertNull(item.getHarmonicOrder());
|
||||
Assertions.assertEquals(Boolean.TRUE, item.getHarmonicParityAbnormal());
|
||||
Assertions.assertEquals(Integer.valueOf(1), item.getHarmonicParityAbnormalPointCount());
|
||||
Assertions.assertEquals("AVG", item.getHarmonicParityAbnormalDetails().get(0).getStatType());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldRejectUnsupportedItemDetailType() {
|
||||
SteadyChecksquareItemService itemService = mock(SteadyChecksquareItemService.class);
|
||||
SteadyChecksquareItemPO item = new SteadyChecksquareItemPO();
|
||||
item.setId("item-001");
|
||||
item.setState(1);
|
||||
when(itemService.getById("item-001")).thenReturn(item);
|
||||
SteadyChecksquareServiceImpl service = new SteadyChecksquareServiceImpl(new SteadyTrendIndicatorCatalog(),
|
||||
mock(SteadyChecksquareInfluxQueryComponent.class), new SteadyChecksquareCalculator(),
|
||||
mock(SteadyChecksquareValueOrderRuleComponent.class), mock(SteadyChecksquareHarmonicParityRuleComponent.class),
|
||||
new AddDataTimeSlotCalculator(), mock(AddLedgerService.class), mock(SteadyChecksquareTaskService.class),
|
||||
itemService, mock(SteadyChecksquareStatSummaryService.class),
|
||||
mock(SteadyChecksquareDetailService.class), new ObjectMapper());
|
||||
|
||||
Assertions.assertThrows(RuntimeException.class, () -> service.itemDetail("item-001", "UNKNOWN", null));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldLoadDetailSummariesInSingleBatch() {
|
||||
SteadyChecksquareTaskService taskService = mock(SteadyChecksquareTaskService.class);
|
||||
SteadyChecksquareItemService itemService = mock(SteadyChecksquareItemService.class);
|
||||
SteadyChecksquareStatSummaryService statSummaryService = mock(SteadyChecksquareStatSummaryService.class);
|
||||
LambdaQueryChainWrapper<SteadyChecksquareItemPO> itemQuery = mock(LambdaQueryChainWrapper.class);
|
||||
LambdaQueryChainWrapper<SteadyChecksquareStatSummaryPO> summaryQuery = mock(LambdaQueryChainWrapper.class);
|
||||
SteadyChecksquareTaskPO task = new SteadyChecksquareTaskPO();
|
||||
task.setId("task-001");
|
||||
task.setState(1);
|
||||
task.setLineId("line-001");
|
||||
task.setLineName("进线一");
|
||||
task.setTimeStart(LocalDateTime.of(2026, 5, 1, 0, 0));
|
||||
task.setTimeEnd(LocalDateTime.of(2026, 5, 1, 0, 1));
|
||||
task.setIntervalMinutes(1);
|
||||
SteadyChecksquareItemPO item1 = buildItemPO("item-001", "V_RMS");
|
||||
SteadyChecksquareItemPO item2 = buildItemPO("item-002", "FREQ");
|
||||
SteadyChecksquareStatSummaryPO summary1 = buildSummaryPO("item-001", "AVG");
|
||||
SteadyChecksquareStatSummaryPO summary2 = buildSummaryPO("item-002", "AVG");
|
||||
when(taskService.getById("task-001")).thenReturn(task);
|
||||
when(itemService.lambdaQuery()).thenReturn(itemQuery);
|
||||
when(itemQuery.eq(any(), any())).thenReturn(itemQuery);
|
||||
when(itemQuery.list()).thenReturn(Arrays.asList(item1, item2));
|
||||
when(statSummaryService.lambdaQuery()).thenReturn(summaryQuery);
|
||||
when(summaryQuery.in(any(), any(List.class))).thenReturn(summaryQuery);
|
||||
when(summaryQuery.list()).thenReturn(Arrays.asList(summary1, summary2));
|
||||
SteadyChecksquareServiceImpl service = new SteadyChecksquareServiceImpl(new SteadyTrendIndicatorCatalog(),
|
||||
mock(SteadyChecksquareInfluxQueryComponent.class), new SteadyChecksquareCalculator(),
|
||||
mock(SteadyChecksquareValueOrderRuleComponent.class), mock(SteadyChecksquareHarmonicParityRuleComponent.class),
|
||||
new AddDataTimeSlotCalculator(), mock(AddLedgerService.class), taskService,
|
||||
itemService, statSummaryService, mock(SteadyChecksquareDetailService.class), new ObjectMapper());
|
||||
|
||||
SteadyChecksquareQueryVO result = service.detail("task-001");
|
||||
|
||||
Assertions.assertEquals(2, result.getItems().size());
|
||||
Assertions.assertEquals(1, result.getItems().get(0).getStatSummaries().size());
|
||||
Assertions.assertEquals(1, result.getItems().get(1).getStatSummaries().size());
|
||||
verify(statSummaryService, times(1)).lambdaQuery();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldPageItemDetailWhenPageArgumentsPresent() {
|
||||
SteadyChecksquareItemService itemService = mock(SteadyChecksquareItemService.class);
|
||||
SteadyChecksquareDetailService detailService = mock(SteadyChecksquareDetailService.class);
|
||||
SteadyChecksquareItemPO item = new SteadyChecksquareItemPO();
|
||||
item.setId("item-001");
|
||||
item.setState(1);
|
||||
SteadyChecksquareDetailPO detail = new SteadyChecksquareDetailPO();
|
||||
detail.setItemId("item-001");
|
||||
detail.setDetailType("VALUE_ORDER");
|
||||
detail.setPointTime(LocalDateTime.of(2026, 5, 1, 0, 0));
|
||||
detail.setPhase("A");
|
||||
Page<SteadyChecksquareDetailPO> page = new Page<SteadyChecksquareDetailPO>(2, 1);
|
||||
page.setTotal(1);
|
||||
page.setRecords(Collections.singletonList(detail));
|
||||
when(itemService.getById("item-001")).thenReturn(item);
|
||||
when(detailService.page(any(Page.class), any())).thenReturn(page);
|
||||
SteadyChecksquareServiceImpl service = new SteadyChecksquareServiceImpl(new SteadyTrendIndicatorCatalog(),
|
||||
mock(SteadyChecksquareInfluxQueryComponent.class), new SteadyChecksquareCalculator(),
|
||||
mock(SteadyChecksquareValueOrderRuleComponent.class), mock(SteadyChecksquareHarmonicParityRuleComponent.class),
|
||||
new AddDataTimeSlotCalculator(), mock(AddLedgerService.class), mock(SteadyChecksquareTaskService.class),
|
||||
itemService, mock(SteadyChecksquareStatSummaryService.class),
|
||||
detailService, new ObjectMapper());
|
||||
|
||||
SteadyChecksquareItemDetailVO result = service.itemDetail("item-001", "VALUE_ORDER", null, 2, 1);
|
||||
|
||||
Assertions.assertEquals(Integer.valueOf(2), result.getPageNum());
|
||||
Assertions.assertEquals(Integer.valueOf(1), result.getPageSize());
|
||||
Assertions.assertEquals(Long.valueOf(1L), result.getTotal());
|
||||
Assertions.assertEquals(1, result.getValueOrderDetails().size());
|
||||
verify(detailService).page(any(Page.class), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldDeleteTasksAndItemsLogically() {
|
||||
SteadyChecksquareTaskService taskService = mock(SteadyChecksquareTaskService.class);
|
||||
SteadyChecksquareItemService itemService = mock(SteadyChecksquareItemService.class);
|
||||
LambdaQueryChainWrapper<SteadyChecksquareTaskPO> taskQuery = mock(LambdaQueryChainWrapper.class);
|
||||
SteadyChecksquareTaskPO task = new SteadyChecksquareTaskPO();
|
||||
task.setId("task-001");
|
||||
task.setState(1);
|
||||
when(taskService.lambdaQuery()).thenReturn(taskQuery);
|
||||
when(taskQuery.in(any(), any(List.class))).thenReturn(taskQuery);
|
||||
when(taskQuery.eq(any(), any())).thenReturn(taskQuery);
|
||||
when(taskQuery.list()).thenReturn(Collections.singletonList(task));
|
||||
when(taskService.update(any())).thenReturn(true);
|
||||
when(itemService.update(any())).thenReturn(true);
|
||||
SteadyChecksquareServiceImpl service = new SteadyChecksquareServiceImpl(new SteadyTrendIndicatorCatalog(),
|
||||
mock(SteadyChecksquareInfluxQueryComponent.class), new SteadyChecksquareCalculator(),
|
||||
mock(SteadyChecksquareValueOrderRuleComponent.class), mock(SteadyChecksquareHarmonicParityRuleComponent.class),
|
||||
new AddDataTimeSlotCalculator(), mock(AddLedgerService.class), taskService,
|
||||
itemService, mock(SteadyChecksquareStatSummaryService.class),
|
||||
mock(SteadyChecksquareDetailService.class), new ObjectMapper());
|
||||
|
||||
boolean result = service.delete(Collections.singletonList("task-001"));
|
||||
|
||||
Assertions.assertTrue(result);
|
||||
verify(taskService).update(any());
|
||||
verify(itemService).update(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldSaveChecksquareResultsInBatch() {
|
||||
SteadyChecksquareTaskService taskService = mock(SteadyChecksquareTaskService.class);
|
||||
SteadyChecksquareItemService itemService = mock(SteadyChecksquareItemService.class);
|
||||
SteadyChecksquareStatSummaryService statSummaryService = mock(SteadyChecksquareStatSummaryService.class);
|
||||
SteadyChecksquareDetailService detailService = mock(SteadyChecksquareDetailService.class);
|
||||
SteadyChecksquareServiceImpl service = new SteadyChecksquareServiceImpl(new SteadyTrendIndicatorCatalog(),
|
||||
mock(SteadyChecksquareInfluxQueryComponent.class), new SteadyChecksquareCalculator(),
|
||||
mock(SteadyChecksquareValueOrderRuleComponent.class), mock(SteadyChecksquareHarmonicParityRuleComponent.class),
|
||||
new AddDataTimeSlotCalculator(), mock(AddLedgerService.class), taskService,
|
||||
itemService, statSummaryService, detailService, new ObjectMapper());
|
||||
SteadyChecksquareQueryParam param = new SteadyChecksquareQueryParam();
|
||||
param.setIndicatorCodes(Collections.singletonList("V_RMS"));
|
||||
SteadyChecksquareQueryVO result = new SteadyChecksquareQueryVO();
|
||||
result.setLineId("line-001");
|
||||
result.setLineName("进线一");
|
||||
result.setTimeStart("2026-05-01 00:00:00");
|
||||
result.setTimeEnd("2026-05-01 00:01:00");
|
||||
result.setIntervalMinutes(1);
|
||||
SteadyChecksquareItemVO item = new SteadyChecksquareItemVO();
|
||||
item.setItemKey("line-001|V_RMS");
|
||||
item.setIndicatorCode("V_RMS");
|
||||
item.setIndicatorName("相电压有效值");
|
||||
item.setIntervalMinutes(1);
|
||||
item.setHasData(true);
|
||||
item.setExpectedPointCount(2);
|
||||
item.setActualPointCount(2);
|
||||
item.setMissingPointCount(0);
|
||||
item.setDataIntegrity(BigDecimal.ONE.setScale(6));
|
||||
item.setDataIntegrityText("100.00%");
|
||||
item.setAbnormal(false);
|
||||
item.setAbnormalPointCount(0);
|
||||
item.setHarmonicParityAbnormal(false);
|
||||
item.setHarmonicParityAbnormalPointCount(0);
|
||||
SteadyChecksquareStatSummaryVO summary = new SteadyChecksquareStatSummaryVO();
|
||||
summary.setStatType("AVG");
|
||||
summary.setSupported(true);
|
||||
summary.setHasData(true);
|
||||
summary.setExpectedPointCount(2);
|
||||
summary.setActualPointCount(2);
|
||||
summary.setMissingPointCount(0);
|
||||
summary.setDataIntegrity(BigDecimal.ONE.setScale(6));
|
||||
summary.setDataIntegrityText("100.00%");
|
||||
item.getStatSummaries().add(summary);
|
||||
result.getItems().add(item);
|
||||
|
||||
saveResult(service, param, result);
|
||||
|
||||
verify(taskService).save(any());
|
||||
verify(itemService).saveBatch(any());
|
||||
verify(statSummaryService).saveBatch(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldCountNoDataItemAsAbnormalWhenSavingTask() {
|
||||
SteadyChecksquareTaskService taskService = mock(SteadyChecksquareTaskService.class);
|
||||
SteadyChecksquareServiceImpl service = new SteadyChecksquareServiceImpl(new SteadyTrendIndicatorCatalog(),
|
||||
mock(SteadyChecksquareInfluxQueryComponent.class), new SteadyChecksquareCalculator(),
|
||||
mock(SteadyChecksquareValueOrderRuleComponent.class), mock(SteadyChecksquareHarmonicParityRuleComponent.class),
|
||||
new AddDataTimeSlotCalculator(), mock(AddLedgerService.class), taskService,
|
||||
mock(SteadyChecksquareItemService.class), mock(SteadyChecksquareStatSummaryService.class),
|
||||
mock(SteadyChecksquareDetailService.class), new ObjectMapper());
|
||||
SteadyChecksquareQueryParam param = new SteadyChecksquareQueryParam();
|
||||
param.setIndicatorCodes(Collections.singletonList("V_RMS"));
|
||||
SteadyChecksquareQueryVO result = new SteadyChecksquareQueryVO();
|
||||
result.setLineId("line-001");
|
||||
result.setLineName("进线一");
|
||||
result.setTimeStart("2026-05-01 00:00:00");
|
||||
result.setTimeEnd("2026-05-01 00:01:00");
|
||||
result.setIntervalMinutes(1);
|
||||
SteadyChecksquareItemVO item = new SteadyChecksquareItemVO();
|
||||
item.setItemKey("line-001|V_RMS");
|
||||
item.setIndicatorCode("V_RMS");
|
||||
item.setHasData(false);
|
||||
item.setExpectedPointCount(2);
|
||||
item.setActualPointCount(0);
|
||||
item.setMissingPointCount(2);
|
||||
item.setDataIntegrity(BigDecimal.ZERO.setScale(6));
|
||||
item.setDataIntegrityText("0.00%");
|
||||
item.setAbnormal(false);
|
||||
item.setHarmonicParityAbnormal(false);
|
||||
result.getItems().add(item);
|
||||
|
||||
saveResult(service, param, result);
|
||||
|
||||
ArgumentCaptor<SteadyChecksquareTaskPO> captor = ArgumentCaptor.forClass(SteadyChecksquareTaskPO.class);
|
||||
verify(taskService).save(captor.capture());
|
||||
Assertions.assertEquals(Integer.valueOf(1), captor.getValue().getAbnormalItemCount());
|
||||
Assertions.assertEquals(BigDecimal.ZERO.setScale(6), captor.getValue().getMinDataIntegrity());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldMarkAggregateHarmonicItemNoDataWhenDataIntegrityIsZero() {
|
||||
SteadyChecksquareServiceImpl service = newService();
|
||||
SteadyTrendIndicatorDefinitionBO indicator = buildHarmonicIndicator();
|
||||
SteadyChecksquareItemVO orderItem = buildOrderItem(true, BigDecimal.ZERO.setScale(6));
|
||||
orderItem.getStatSummaries().add(buildSummaryVO(true, BigDecimal.ZERO.setScale(6)));
|
||||
|
||||
SteadyChecksquareItemVO result = aggregateHarmonicItems(service, indicator, Collections.singletonList(orderItem));
|
||||
|
||||
Assertions.assertEquals(BigDecimal.ZERO.setScale(6), result.getDataIntegrity());
|
||||
Assertions.assertEquals(Boolean.FALSE, result.getHasData());
|
||||
Assertions.assertEquals(Boolean.FALSE, result.getStatSummaries().get(0).getHasData());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldMarkAggregateHarmonicItemHasDataWhenDataIntegrityIsGreaterThanZero() {
|
||||
SteadyChecksquareServiceImpl service = newService();
|
||||
SteadyTrendIndicatorDefinitionBO indicator = buildHarmonicIndicator();
|
||||
SteadyChecksquareItemVO orderItem = buildOrderItem(false, new BigDecimal("0.500000"));
|
||||
orderItem.getStatSummaries().add(buildSummaryVO(false, new BigDecimal("0.500000")));
|
||||
|
||||
SteadyChecksquareItemVO result = aggregateHarmonicItems(service, indicator, Collections.singletonList(orderItem));
|
||||
|
||||
Assertions.assertEquals(new BigDecimal("0.500000"), result.getDataIntegrity());
|
||||
Assertions.assertEquals(Boolean.TRUE, result.getHasData());
|
||||
Assertions.assertEquals(Boolean.TRUE, result.getStatSummaries().get(0).getHasData());
|
||||
}
|
||||
|
||||
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 SteadyChecksquareServiceImpl newService() {
|
||||
return new SteadyChecksquareServiceImpl(new SteadyTrendIndicatorCatalog(),
|
||||
mock(SteadyChecksquareInfluxQueryComponent.class), new SteadyChecksquareCalculator(),
|
||||
mock(SteadyChecksquareValueOrderRuleComponent.class), mock(SteadyChecksquareHarmonicParityRuleComponent.class),
|
||||
new AddDataTimeSlotCalculator(), mock(AddLedgerService.class), mock(SteadyChecksquareTaskService.class),
|
||||
mock(SteadyChecksquareItemService.class), mock(SteadyChecksquareStatSummaryService.class),
|
||||
mock(SteadyChecksquareDetailService.class), new ObjectMapper());
|
||||
}
|
||||
|
||||
private SteadyChecksquareQueryVO calculate(SteadyChecksquareServiceImpl service, SteadyChecksquareQueryParam param) {
|
||||
try {
|
||||
Method method = SteadyChecksquareServiceImpl.class.getDeclaredMethod("calculate", SteadyChecksquareQueryParam.class);
|
||||
method.setAccessible(true);
|
||||
return (SteadyChecksquareQueryVO) method.invoke(service, param);
|
||||
} catch (Exception exception) {
|
||||
throw new RuntimeException(exception);
|
||||
}
|
||||
}
|
||||
|
||||
private void saveResult(SteadyChecksquareServiceImpl service, SteadyChecksquareQueryParam param, SteadyChecksquareQueryVO result) {
|
||||
try {
|
||||
Method method = SteadyChecksquareServiceImpl.class.getDeclaredMethod("saveResult",
|
||||
SteadyChecksquareQueryParam.class, SteadyChecksquareQueryVO.class);
|
||||
method.setAccessible(true);
|
||||
method.invoke(service, param, result);
|
||||
} catch (Exception exception) {
|
||||
throw new RuntimeException(exception);
|
||||
}
|
||||
}
|
||||
|
||||
private SteadyChecksquareItemVO aggregateHarmonicItems(SteadyChecksquareServiceImpl service,
|
||||
SteadyTrendIndicatorDefinitionBO indicator,
|
||||
List<SteadyChecksquareItemVO> orderItems) {
|
||||
try {
|
||||
Method method = SteadyChecksquareServiceImpl.class.getDeclaredMethod("aggregateHarmonicItems",
|
||||
String.class, SteadyTrendIndicatorDefinitionBO.class, List.class, int.class);
|
||||
method.setAccessible(true);
|
||||
return (SteadyChecksquareItemVO) method.invoke(service, "line-001", indicator, orderItems, 1);
|
||||
} catch (Exception exception) {
|
||||
throw new RuntimeException(exception);
|
||||
}
|
||||
}
|
||||
|
||||
private SteadyTrendIndicatorDefinitionBO buildHarmonicIndicator() {
|
||||
SteadyTrendIndicatorDefinitionBO indicator = new SteadyTrendIndicatorDefinitionBO();
|
||||
indicator.setIndicatorCode("V_HARMONIC");
|
||||
indicator.setName("V_HARMONIC");
|
||||
indicator.setHarmonic(true);
|
||||
return indicator;
|
||||
}
|
||||
|
||||
private SteadyChecksquareItemVO buildOrderItem(boolean hasData, BigDecimal dataIntegrity) {
|
||||
SteadyChecksquareItemVO item = new SteadyChecksquareItemVO();
|
||||
item.setItemKey("line-001|V_HARMONIC|2");
|
||||
item.setIndicatorCode("V_HARMONIC");
|
||||
item.setIndicatorName("V_HARMONIC");
|
||||
item.setHarmonicOrder(2);
|
||||
item.setIntervalMinutes(1);
|
||||
item.setHasData(hasData);
|
||||
item.setExpectedPointCount(2);
|
||||
item.setActualPointCount(dataIntegrity.compareTo(BigDecimal.ZERO) > 0 ? 1 : 0);
|
||||
item.setMissingPointCount(dataIntegrity.compareTo(BigDecimal.ZERO) > 0 ? 1 : 2);
|
||||
item.setDataIntegrity(dataIntegrity);
|
||||
item.setAbnormal(false);
|
||||
item.setAbnormalPointCount(0);
|
||||
item.setHarmonicParityAbnormal(false);
|
||||
item.setHarmonicParityAbnormalPointCount(0);
|
||||
return item;
|
||||
}
|
||||
|
||||
private SteadyChecksquareStatSummaryVO buildSummaryVO(boolean hasData, BigDecimal dataIntegrity) {
|
||||
SteadyChecksquareStatSummaryVO summary = new SteadyChecksquareStatSummaryVO();
|
||||
summary.setStatType("AVG");
|
||||
summary.setSupported(true);
|
||||
summary.setHasData(hasData);
|
||||
summary.setExpectedPointCount(2);
|
||||
summary.setActualPointCount(dataIntegrity.compareTo(BigDecimal.ZERO) > 0 ? 1 : 0);
|
||||
summary.setMissingPointCount(dataIntegrity.compareTo(BigDecimal.ZERO) > 0 ? 1 : 2);
|
||||
summary.setDataIntegrity(dataIntegrity);
|
||||
return summary;
|
||||
}
|
||||
|
||||
private SteadyChecksquareItemPO buildItemPO(String itemId, String indicatorCode) {
|
||||
SteadyChecksquareItemPO item = new SteadyChecksquareItemPO();
|
||||
item.setId(itemId);
|
||||
item.setIndicatorCode(indicatorCode);
|
||||
item.setIndicatorName(indicatorCode);
|
||||
item.setState(1);
|
||||
item.setHasData(1);
|
||||
item.setExpectedPointCount(1);
|
||||
item.setActualPointCount(1);
|
||||
item.setMissingPointCount(0);
|
||||
item.setDataIntegrity(BigDecimal.ONE.setScale(6));
|
||||
item.setAbnormal(0);
|
||||
item.setAbnormalPointCount(0);
|
||||
item.setHarmonicParityAbnormal(0);
|
||||
item.setHarmonicParityAbnormalPointCount(0);
|
||||
return item;
|
||||
}
|
||||
|
||||
private SteadyChecksquareStatSummaryPO buildSummaryPO(String itemId, String statType) {
|
||||
SteadyChecksquareStatSummaryPO summary = new SteadyChecksquareStatSummaryPO();
|
||||
summary.setItemId(itemId);
|
||||
summary.setStatType(statType);
|
||||
summary.setSupported(1);
|
||||
summary.setHasData(1);
|
||||
summary.setExpectedPointCount(1);
|
||||
summary.setActualPointCount(1);
|
||||
summary.setMissingPointCount(0);
|
||||
summary.setDataIntegrity(BigDecimal.ONE.setScale(6));
|
||||
return summary;
|
||||
}
|
||||
|
||||
private SteadyChecksquareValueOrderRuleVO emptyRuleResult() {
|
||||
return new SteadyChecksquareValueOrderRuleVO();
|
||||
}
|
||||
|
||||
private SteadyChecksquareHarmonicParityRuleVO emptyHarmonicParityRuleResult() {
|
||||
return new SteadyChecksquareHarmonicParityRuleVO();
|
||||
}
|
||||
}
|
||||
@@ -1,522 +0,0 @@
|
||||
# 数据校验 API 调试文档
|
||||
|
||||
## 1. 基础信息
|
||||
|
||||
- 模块:`steady/steady-DataView`
|
||||
- 控制器:`com.njcn.gather.steady.checksquare.controller.SteadyChecksquareController`
|
||||
- 接口前缀:`/steady/data-view/checksquare`
|
||||
- 本地默认地址:`http://localhost:18192`
|
||||
- Content-Type:`application/json`
|
||||
- 认证:除登录和 Swagger 资源外,请求需要携带登录后的 `Authorization` 请求头。
|
||||
- 数据库结果表:`steady_checksquare_task`、`steady_checksquare_item`、`steady_checksquare_stat_summary`、`steady_checksquare_detail`
|
||||
|
||||
通用请求头:
|
||||
|
||||
```http
|
||||
Authorization: Bearer <token>
|
||||
Content-Type: application/json
|
||||
```
|
||||
|
||||
通用返回结构:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "success",
|
||||
"data": {}
|
||||
}
|
||||
```
|
||||
|
||||
> 实际 `code`、`message` 字段以项目公共 `HttpResult` 封装为准,下面示例重点展示 `data` 内容。
|
||||
|
||||
## 2. 查询数据校验历史记录
|
||||
|
||||
- 方法:`POST`
|
||||
- 路径:`/steady/data-view/checksquare/query`
|
||||
- 返回:`HttpResult<Page<SteadyChecksquareTaskVO>>`
|
||||
- 说明:分页查询已落库的数据校验任务,按创建时间倒序返回。
|
||||
|
||||
请求示例:
|
||||
|
||||
```json
|
||||
{
|
||||
"pageNum": 1,
|
||||
"pageSize": 10,
|
||||
"lineId": "line-001",
|
||||
"indicatorCode": "V_RMS",
|
||||
"timeStart": "2026-05-01 00:00:00",
|
||||
"timeEnd": "2026-05-01 23:59:59",
|
||||
"hasAbnormal": true
|
||||
}
|
||||
```
|
||||
|
||||
请求字段:
|
||||
|
||||
| 字段 | 必填 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `pageNum` | 否 | 当前页码,继承自 `BaseParam` |
|
||||
| `pageSize` | 否 | 每页数量,继承自 `BaseParam` |
|
||||
| `lineId` | 否 | 监测点 ID,精确匹配 |
|
||||
| `indicatorCode` | 否 | 指标编码,按任务指标集合匹配 |
|
||||
| `timeStart` | 否 | 检测开始时间下限,格式 `yyyy-MM-dd HH:mm:ss` |
|
||||
| `timeEnd` | 否 | 检测结束时间上限,格式 `yyyy-MM-dd HH:mm:ss` |
|
||||
| `hasAbnormal` | 否 | 是否只查询存在异常项的任务;`true` 表示 `abnormalItemCount > 0` |
|
||||
|
||||
返回示例:
|
||||
|
||||
```json
|
||||
{
|
||||
"records": [
|
||||
{
|
||||
"taskId": "8f7a4d6d1f3145a88b6f9d7a8e6c1001",
|
||||
"taskNo": "CS202606101630001",
|
||||
"lineId": "line-001",
|
||||
"lineName": "进线一",
|
||||
"timeStart": "2026-05-01 00:00:00",
|
||||
"timeEnd": "2026-05-01 23:59:59",
|
||||
"intervalMinutes": 1,
|
||||
"taskStatus": "SUCCESS",
|
||||
"itemCount": 2,
|
||||
"abnormalItemCount": 1,
|
||||
"minDataIntegrity": 0.999306,
|
||||
"createTime": "2026-06-10 16:30:00"
|
||||
}
|
||||
],
|
||||
"total": 1,
|
||||
"size": 10,
|
||||
"current": 1,
|
||||
"pages": 1
|
||||
}
|
||||
```
|
||||
|
||||
cURL 示例:
|
||||
|
||||
```bash
|
||||
curl -X POST "http://localhost:18192/steady/data-view/checksquare/query" \
|
||||
-H "Authorization: Bearer <token>" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"pageNum":1,"pageSize":10,"lineId":"line-001","indicatorCode":"V_RMS","hasAbnormal":true}'
|
||||
```
|
||||
|
||||
## 3. 新增数据校验记录
|
||||
|
||||
- 方法:`POST`
|
||||
- 路径:`/steady/data-view/checksquare/create`
|
||||
- 返回:`HttpResult<SteadyChecksquareCreateVO>`
|
||||
- 说明:按监测点、指标和时间范围实时查询 InfluxDB,执行缺数校验、指标值大小关系校验、谐波奇偶关系校验,并将任务、检测项、统计摘要和明细写入 MySQL。
|
||||
|
||||
请求示例:
|
||||
|
||||
```json
|
||||
{
|
||||
"lineId": "line-001",
|
||||
"indicatorCodes": ["V_RMS", "FREQ", "V_HARMONIC"],
|
||||
"timeStart": "2026-05-01 00:00:00",
|
||||
"timeEnd": "2026-05-01 23:59:59"
|
||||
}
|
||||
```
|
||||
|
||||
请求字段:
|
||||
|
||||
| 字段 | 必填 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `lineId` | 是 | 监测点 ID,需要能在台账中找到且处于可用状态 |
|
||||
| `indicatorCodes` | 是 | 指标编码列表,来自 `/steady/data-view/indicator-tree` |
|
||||
| `timeStart` | 是 | 检测开始时间,格式 `yyyy-MM-dd HH:mm:ss` |
|
||||
| `timeEnd` | 是 | 检测结束时间,格式 `yyyy-MM-dd HH:mm:ss` |
|
||||
|
||||
返回示例:
|
||||
|
||||
```json
|
||||
{
|
||||
"taskId": "8f7a4d6d1f3145a88b6f9d7a8e6c1001",
|
||||
"taskNo": "CS202606101630001",
|
||||
"lineId": "line-001",
|
||||
"lineName": "进线一",
|
||||
"timeStart": "2026-05-01 00:00:00",
|
||||
"timeEnd": "2026-05-01 23:59:59",
|
||||
"intervalMinutes": 1,
|
||||
"itemCount": 3,
|
||||
"abnormalItemCount": 1
|
||||
}
|
||||
```
|
||||
|
||||
cURL 示例:
|
||||
|
||||
```bash
|
||||
curl -X POST "http://localhost:18192/steady/data-view/checksquare/create" \
|
||||
-H "Authorization: Bearer <token>" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"lineId":"line-001","indicatorCodes":["V_RMS","FREQ","V_HARMONIC"],"timeStart":"2026-05-01 00:00:00","timeEnd":"2026-05-01 23:59:59"}'
|
||||
```
|
||||
|
||||
调试建议:
|
||||
|
||||
- 新增接口会实际写入 MySQL,重复调用会生成新的任务记录。
|
||||
- 时间范围越大、指标越多,InfluxDB 查询和明细落库耗时越高。
|
||||
- 谐波类指标固定按 2-50 次聚合检测,不需要在请求体传 `harmonicOrders`。
|
||||
|
||||
## 4. 查询数据校验任务详情
|
||||
|
||||
- 方法:`GET`
|
||||
- 路径:`/steady/data-view/checksquare/detail`
|
||||
- 返回:`HttpResult<SteadyChecksquareQueryVO>`
|
||||
- 说明:按任务 ID 查询任务基础信息、检测项列表和各检测项统计摘要。检测项的明细列表通过 `/item-detail` 按需查询。
|
||||
|
||||
请求参数:
|
||||
|
||||
| 参数 | 必填 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `taskId` | 是 | 数据校验任务 ID,即 `/query` 或 `/create` 返回的 `taskId` |
|
||||
|
||||
请求示例:
|
||||
|
||||
```http
|
||||
GET /steady/data-view/checksquare/detail?taskId=8f7a4d6d1f3145a88b6f9d7a8e6c1001
|
||||
```
|
||||
|
||||
返回示例:
|
||||
|
||||
```json
|
||||
{
|
||||
"taskId": "8f7a4d6d1f3145a88b6f9d7a8e6c1001",
|
||||
"taskNo": "CS202606101630001",
|
||||
"lineId": "line-001",
|
||||
"lineName": "进线一",
|
||||
"timeStart": "2026-05-01 00:00:00",
|
||||
"timeEnd": "2026-05-01 23:59:59",
|
||||
"intervalMinutes": 1,
|
||||
"items": [
|
||||
{
|
||||
"itemId": "0f4b6d6c3e9d400a902a2df101d10001",
|
||||
"itemKey": "line-001|V_RMS",
|
||||
"indicatorCode": "V_RMS",
|
||||
"indicatorName": "相电压有效值",
|
||||
"harmonicOrder": null,
|
||||
"intervalMinutes": 1,
|
||||
"hasData": true,
|
||||
"expectedPointCount": 5760,
|
||||
"actualPointCount": 5756,
|
||||
"missingPointCount": 4,
|
||||
"dataIntegrity": 0.999306,
|
||||
"dataIntegrityText": "99.93%",
|
||||
"abnormal": true,
|
||||
"abnormalPointCount": 2,
|
||||
"harmonicParityAbnormal": false,
|
||||
"harmonicParityAbnormalPointCount": 0,
|
||||
"statSummaries": [
|
||||
{
|
||||
"statType": "AVG",
|
||||
"supported": true,
|
||||
"hasData": true,
|
||||
"expectedPointCount": 1440,
|
||||
"actualPointCount": 1439,
|
||||
"missingPointCount": 1,
|
||||
"dataIntegrity": 0.999306,
|
||||
"dataIntegrityText": "99.93%",
|
||||
}
|
||||
],
|
||||
"statDetails": []
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
cURL 示例:
|
||||
|
||||
```bash
|
||||
curl -X GET "http://localhost:18192/steady/data-view/checksquare/detail?taskId=8f7a4d6d1f3145a88b6f9d7a8e6c1001" \
|
||||
-H "Authorization: Bearer <token>"
|
||||
```
|
||||
|
||||
## 5. 查询检测项明细
|
||||
|
||||
- 方法:`GET`
|
||||
- 路径:`/steady/data-view/checksquare/item-detail`
|
||||
- 返回:`HttpResult<SteadyChecksquareItemDetailVO>`
|
||||
- 说明:按检测项 ID、明细类型查询缺数连续区间、指标值大小关系异常点或谐波奇偶关系异常点。`pageNum` 和 `pageSize` 未同时传入时保持全量返回;两者同时传入且均大于 0 时返回当前页明细,并在结果中带回分页元数据。
|
||||
|
||||
请求参数:
|
||||
|
||||
| 参数 | 必填 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `itemId` | 是 | 检测项 ID,即任务详情中每个 `items[].itemId` |
|
||||
| `detailType` | 是 | 明细类型:`SEGMENT`、`VALUE_ORDER`、`HARMONIC_PARITY` |
|
||||
| `statType` | 否 | 统计类型:`AVG`、`MAX`、`MIN`、`CP95`;查询缺数区间时建议传入 |
|
||||
| `pageNum` | 否 | 明细分页页码;与 `pageSize` 同时传入且大于 0 时启用分页 |
|
||||
| `pageSize` | 否 | 明细分页条数;与 `pageNum` 同时传入且大于 0 时启用分页 |
|
||||
|
||||
### 5.1 查询缺数连续区间
|
||||
|
||||
请求示例:
|
||||
|
||||
```http
|
||||
GET /steady/data-view/checksquare/item-detail?itemId=0f4b6d6c3e9d400a902a2df101d10001&detailType=SEGMENT&statType=AVG
|
||||
```
|
||||
|
||||
返回示例:
|
||||
|
||||
```json
|
||||
{
|
||||
"itemId": "0f4b6d6c3e9d400a902a2df101d10001",
|
||||
"detailType": "SEGMENT",
|
||||
"statType": "AVG",
|
||||
"pageNum": null,
|
||||
"pageSize": null,
|
||||
"total": null,
|
||||
"segments": [
|
||||
{
|
||||
"startTime": "2026-05-01 00:00:00",
|
||||
"endTime": "2026-05-01 00:09:00",
|
||||
"status": "NORMAL",
|
||||
"harmonicOrder": null,
|
||||
"missingPointCount": 0,
|
||||
"durationMinutes": 10
|
||||
},
|
||||
{
|
||||
"startTime": "2026-05-01 00:10:00",
|
||||
"endTime": "2026-05-01 00:10:00",
|
||||
"status": "MISSING",
|
||||
"harmonicOrder": null,
|
||||
"missingPointCount": 1,
|
||||
"durationMinutes": 1
|
||||
}
|
||||
],
|
||||
"valueOrderDetails": [],
|
||||
"harmonicParityDetails": []
|
||||
}
|
||||
```
|
||||
|
||||
### 5.2 查询指标值大小关系异常明细
|
||||
|
||||
请求示例:
|
||||
|
||||
```http
|
||||
GET /steady/data-view/checksquare/item-detail?itemId=0f4b6d6c3e9d400a902a2df101d10001&detailType=VALUE_ORDER&pageNum=1&pageSize=20
|
||||
```
|
||||
|
||||
返回示例:
|
||||
|
||||
```json
|
||||
{
|
||||
"itemId": "0f4b6d6c3e9d400a902a2df101d10001",
|
||||
"detailType": "VALUE_ORDER",
|
||||
"statType": null,
|
||||
"pageNum": 1,
|
||||
"pageSize": 20,
|
||||
"total": 1,
|
||||
"segments": [],
|
||||
"valueOrderDetails": [
|
||||
{
|
||||
"time": "2026-05-01 00:10:00",
|
||||
"phase": "A",
|
||||
"harmonicOrder": null,
|
||||
"maxValue": 219.8,
|
||||
"minValue": 218.1,
|
||||
"avgValue": 219.0,
|
||||
"cp95Value": 219.8
|
||||
}
|
||||
],
|
||||
"harmonicParityDetails": []
|
||||
}
|
||||
```
|
||||
|
||||
### 5.3 查询谐波奇偶关系异常明细
|
||||
|
||||
请求示例:
|
||||
|
||||
```http
|
||||
GET /steady/data-view/checksquare/item-detail?itemId=0f4b6d6c3e9d400a902a2df101d10002&detailType=HARMONIC_PARITY&pageNum=1&pageSize=20
|
||||
```
|
||||
|
||||
返回示例:
|
||||
|
||||
```json
|
||||
{
|
||||
"itemId": "0f4b6d6c3e9d400a902a2df101d10002",
|
||||
"detailType": "HARMONIC_PARITY",
|
||||
"statType": null,
|
||||
"pageNum": 1,
|
||||
"pageSize": 20,
|
||||
"total": 1,
|
||||
"segments": [],
|
||||
"valueOrderDetails": [],
|
||||
"harmonicParityDetails": [
|
||||
{
|
||||
"time": "2026-05-01 00:10:00",
|
||||
"phase": "A",
|
||||
"statType": "AVG",
|
||||
"evenHarmonicOrder": 4,
|
||||
"evenValue": 0.32,
|
||||
"oddHarmonicOrders": [3, 5],
|
||||
"oddValues": [0.08, 0.09],
|
||||
"oddMedianValue": 0.085,
|
||||
"thresholdMultiplier": 3.0
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
cURL 示例:
|
||||
|
||||
```bash
|
||||
curl -X GET "http://localhost:18192/steady/data-view/checksquare/item-detail?itemId=0f4b6d6c3e9d400a902a2df101d10001&detailType=VALUE_ORDER" \
|
||||
-H "Authorization: Bearer <token>"
|
||||
```
|
||||
|
||||
分页 cURL 示例:
|
||||
|
||||
```bash
|
||||
curl -X GET "http://localhost:18192/steady/data-view/checksquare/item-detail?itemId=0f4b6d6c3e9d400a902a2df101d10001&detailType=VALUE_ORDER&pageNum=1&pageSize=20" \
|
||||
-H "Authorization: Bearer <token>"
|
||||
```
|
||||
|
||||
## 6. 删除数据校验任务
|
||||
|
||||
- 方法:`POST`
|
||||
- 路径:`/steady/data-view/checksquare/delete`
|
||||
- 返回:`HttpResult<Boolean>`
|
||||
- 说明:按任务 ID 批量逻辑删除数据校验任务,并同步将任务下检测项置为删除态。删除后历史查询不再返回该任务,详情和检测项明细会按不存在处理。
|
||||
|
||||
请求示例:
|
||||
|
||||
```json
|
||||
[
|
||||
"8f7a4d6d1f3145a88b6f9d7a8e6c1001"
|
||||
]
|
||||
```
|
||||
|
||||
请求字段:
|
||||
|
||||
| 字段 | 必填 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| 请求体 | 是 | 数据校验任务 ID 数组 |
|
||||
|
||||
返回示例:
|
||||
|
||||
```json
|
||||
true
|
||||
```
|
||||
|
||||
cURL 示例:
|
||||
|
||||
```bash
|
||||
curl -X POST "http://localhost:18192/steady/data-view/checksquare/delete" \
|
||||
-H "Authorization: Bearer <token>" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '["8f7a4d6d1f3145a88b6f9d7a8e6c1001"]'
|
||||
```
|
||||
|
||||
## 7. 校验规则说明
|
||||
|
||||
### 7.1 缺数校验
|
||||
|
||||
- 按检测项支持的统计类型分别查询实际存在的时间点。
|
||||
- 返回期望点数、实际点数、缺失点数、数据完整性和最大连续缺失时长。
|
||||
- `SEGMENT` 明细按连续区间返回,`status` 可取 `NORMAL`、`MISSING`。
|
||||
- `FLUC`、`PST` 固定按 10 分钟间隔校验,`PLT` 固定按 120 分钟间隔校验,其余指标按监测点统计间隔校验。
|
||||
|
||||
### 7.2 指标值大小关系校验
|
||||
|
||||
- 同一时间点、同一指标、同一相别需要满足 `MAX >= CP95 >= AVG >= MIN`。
|
||||
- 缺少 `MAX`、`CP95`、`AVG`、`MIN` 任一值的时间点不计入大小关系异常,由缺数校验体现。
|
||||
- 异常点写入 `VALUE_ORDER` 明细,前端可通过 `item-detail` 拉取后弹窗展示。
|
||||
|
||||
### 7.3 谐波奇偶关系校验
|
||||
|
||||
- 谐波指标固定聚合 2-50 次检测。
|
||||
- 偶次谐波值需要大于 `0.1` 才继续参与奇偶关系检测。
|
||||
- 异常点写入 `HARMONIC_PARITY` 明细,包含偶次谐波值、参与比较的奇次谐波值、中位数和阈值倍数。
|
||||
|
||||
## 8. 字段说明
|
||||
|
||||
### 8.1 任务字段
|
||||
|
||||
| 字段 | 说明 |
|
||||
| --- | --- |
|
||||
| `taskId` | 数据校验任务 ID |
|
||||
| `taskNo` | 数据校验任务编号 |
|
||||
| `lineId` | 监测点 ID |
|
||||
| `lineName` | 监测点名称 |
|
||||
| `timeStart` | 检测开始时间 |
|
||||
| `timeEnd` | 检测结束时间 |
|
||||
| `intervalMinutes` | 监测点统计间隔,单位分钟 |
|
||||
| `taskStatus` | 任务状态,当前成功任务为 `SUCCESS` |
|
||||
| `itemCount` | 检测项数量 |
|
||||
| `abnormalItemCount` | 存在异常的检测项数量 |
|
||||
| `minDataIntegrity` | 检测项中的最低数据完整性 |
|
||||
| `createTime` | 任务创建时间 |
|
||||
|
||||
### 8.2 检测项字段
|
||||
|
||||
| 字段 | 说明 |
|
||||
| --- | --- |
|
||||
| `itemId` | 检测项 ID |
|
||||
| `itemKey` | 检测项唯一键 |
|
||||
| `indicatorCode` | 指标编码 |
|
||||
| `indicatorName` | 指标名称 |
|
||||
| `harmonicOrder` | 谐波次数;聚合检测项为空 |
|
||||
| `hasData` | 当前检测项是否存在任意数据 |
|
||||
| `expectedPointCount` | 期望点数 |
|
||||
| `actualPointCount` | 实际点数 |
|
||||
| `missingPointCount` | 缺失点数 |
|
||||
| `dataIntegrity` | 数据完整性数值 |
|
||||
| `dataIntegrityText` | 数据完整性文本 |
|
||||
| `abnormal` | 指标值大小关系是否异常 |
|
||||
| `abnormalPointCount` | 指标值大小关系异常点数 |
|
||||
| `harmonicParityAbnormal` | 谐波奇偶关系是否异常 |
|
||||
| `harmonicParityAbnormalPointCount` | 谐波奇偶关系异常点数 |
|
||||
| `statSummaries` | 各统计类型缺数摘要 |
|
||||
| `statDetails` | 兼容字段;明细建议通过 `/item-detail` 按需查询 |
|
||||
|
||||
### 8.3 检测项明细字段
|
||||
|
||||
| 字段 | 说明 |
|
||||
| --- | --- |
|
||||
| `itemId` | 检测项 ID |
|
||||
| `detailType` | 明细类型:`SEGMENT`、`VALUE_ORDER`、`HARMONIC_PARITY` |
|
||||
| `statType` | 统计类型;未传入或当前明细类型不按统计类型过滤时为空 |
|
||||
| `pageNum` | 当前页码;未启用分页时为空 |
|
||||
| `pageSize` | 每页条数;未启用分页时为空 |
|
||||
| `total` | 当前查询条件下的总明细数;未启用分页时为空 |
|
||||
| `segments` | 缺数连续区间,仅 `SEGMENT` 查询返回数据 |
|
||||
| `valueOrderDetails` | 指标值大小关系异常点,仅 `VALUE_ORDER` 查询返回数据 |
|
||||
| `harmonicParityDetails` | 谐波奇偶关系异常点,仅 `HARMONIC_PARITY` 查询返回数据 |
|
||||
|
||||
## 9. 常见错误场景
|
||||
|
||||
| 场景 | 后端提示 |
|
||||
| --- | --- |
|
||||
| 新增时 `lineId` 为空 | `监测点 ID 不能为空` |
|
||||
| 新增时 `indicatorCodes` 为空 | `指标不能为空` |
|
||||
| 新增时开始时间为空 | `开始时间不能为空` |
|
||||
| 新增时结束时间为空 | `结束时间不能为空` |
|
||||
| 开始时间大于结束时间 | `开始时间不能大于结束时间` |
|
||||
| 时间格式不正确 | `时间格式不正确,仅支持 yyyy-MM-dd HH:mm:ss` |
|
||||
| 监测点不存在或不可用 | `监测点不存在或不可用` |
|
||||
| 指标编码不支持 | `稳态指标不支持:xxx` |
|
||||
| 任务不存在或已删除 | `数据校验任务不存在` |
|
||||
| 检测项不存在或已删除 | `数据校验检测项不存在` |
|
||||
| 删除时任务 ID 为空 | `数据校验任务 ID 不能为空` |
|
||||
| 删除时任务不存在或已删除 | `数据校验任务不存在或已删除` |
|
||||
| 明细类型为空 | `明细类型不能为空` |
|
||||
| 明细类型不支持 | `明细类型不支持:xxx` |
|
||||
|
||||
## 10. 调试流程建议
|
||||
|
||||
1. 调用 `/steady/data-view/ledger-tree` 获取可用监测点,取叶子节点 `lineId`。
|
||||
2. 调用 `/steady/data-view/indicator-tree` 获取可用指标编码。
|
||||
3. 调用 `/steady/data-view/checksquare/create` 新增校验任务。
|
||||
4. 使用返回的 `taskId` 调用 `/steady/data-view/checksquare/detail` 查看任务详情和检测项列表。
|
||||
5. 使用 `items[].itemId` 调用 `/steady/data-view/checksquare/item-detail` 查看缺数区间或异常点明细。
|
||||
6. 调用 `/steady/data-view/checksquare/query` 验证任务已落库,并按监测点、指标、时间和异常状态筛选历史记录。
|
||||
7. 调用 `/steady/data-view/checksquare/delete` 删除任务后,再调用 `/query`、`/detail` 验证任务已不可见。
|
||||
|
||||
## 11. 环境依赖
|
||||
|
||||
- MySQL:需要已初始化 4 张 `steady_checksquare_*` 结果表。
|
||||
- InfluxDB:新增校验任务时需要可访问 `steady.influxdb` 配置的时序库。
|
||||
- 台账:新增校验任务时需要 `lineId` 能在台账中解析到监测点路径和统计间隔。
|
||||
- 指标目录:`indicatorCodes` 必须来自后端趋势指标目录。
|
||||
|
||||
|
||||
Reference in New Issue
Block a user