feat(steady-checksquare): 新增数据校验功能模块
- 添加数据校验历史记录查询接口 - 实现数据校验任务创建功能 - 新增数据校验详情查询接口 - 添加谐波奇偶关系异常检测规则 - 实现数据校验明细数据结构 - 添加数据校验编号生成工具 - 优化InfluxDB查询组件并增加缓存机制 - 添加数据校验常量定义 - 实现数据校验值生成器中的派生字段处理逻辑 - 新增数据校验相关的VO、PO、DTO类 - 添加数据校验组件单元测试
This commit is contained in:
@@ -0,0 +1,193 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,11 +11,11 @@ import lombok.RequiredArgsConstructor;
|
|||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
|
import java.math.BigDecimal;
|
||||||
import java.net.HttpURLConnection;
|
import java.net.HttpURLConnection;
|
||||||
import java.net.URLEncoder;
|
import java.net.URLEncoder;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
@@ -26,7 +26,9 @@ import java.time.ZoneOffset;
|
|||||||
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeFormatter;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -39,37 +41,49 @@ public class SteadyChecksquareInfluxQueryComponent {
|
|||||||
|
|
||||||
private static final DateTimeFormatter INFLUX_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss'Z'");
|
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 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;
|
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,
|
public Set<LocalDateTime> queryExistingSlots(SteadyTrendResolvedFieldBO field, LocalDateTime startTime,
|
||||||
LocalDateTime endTime, int intervalMinutes) {
|
LocalDateTime endTime, int intervalMinutes) {
|
||||||
validateConfig();
|
List<SteadyChecksquareValuePointBO> points = queryValuePoints(field, startTime, endTime, intervalMinutes);
|
||||||
String query = buildChecksquareQuery(field, startTime, endTime);
|
Set<LocalDateTime> result = new HashSet<LocalDateTime>();
|
||||||
long startMillis = System.currentTimeMillis();
|
for (SteadyChecksquareValuePointBO point : points) {
|
||||||
log.info("数据校验 InfluxDB 查询开始,measurement={},field={},lineId={},phase={},statType={},query={}",
|
if (point != null && point.getTime() != null) {
|
||||||
field.getMeasurement(), field.getField(), field.getLineId(), field.getPhase(), field.getStatType(), query);
|
result.add(point.getTime());
|
||||||
try {
|
|
||||||
String body = executeQuery(query);
|
|
||||||
Set<LocalDateTime> slots = parseExistingSlots(body, intervalMinutes);
|
|
||||||
log.info("数据校验 InfluxDB 查询结束,slotCount={},costMs={}", slots.size(), System.currentTimeMillis() - startMillis);
|
|
||||||
return slots;
|
|
||||||
} catch (RuntimeException ex) {
|
|
||||||
log.warn("数据校验 InfluxDB 查询异常,costMs={},error={}", System.currentTimeMillis() - startMillis, ex.getMessage());
|
|
||||||
throw ex;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
public List<SteadyChecksquareValuePointBO> queryValuePoints(SteadyTrendResolvedFieldBO field, LocalDateTime startTime,
|
public List<SteadyChecksquareValuePointBO> queryValuePoints(SteadyTrendResolvedFieldBO field, LocalDateTime startTime,
|
||||||
LocalDateTime endTime, int intervalMinutes) {
|
LocalDateTime endTime, int intervalMinutes) {
|
||||||
validateConfig();
|
validateConfig();
|
||||||
String query = buildValuePointQuery(field, startTime, endTime);
|
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();
|
long startMillis = System.currentTimeMillis();
|
||||||
log.info("数据校验指标值 InfluxDB 查询开始,measurement={},field={},lineId={},phase={},statType={},query={}",
|
log.info("数据校验指标值 InfluxDB 查询开始,measurement={},field={},lineId={},phase={},statType={},query={}",
|
||||||
field.getMeasurement(), field.getField(), field.getLineId(), field.getPhase(), field.getStatType(), query);
|
field.getMeasurement(), field.getField(), field.getLineId(), field.getPhase(), field.getStatType(), query);
|
||||||
try {
|
try {
|
||||||
String body = executeQuery(query);
|
String body = executeQuery(query);
|
||||||
List<SteadyChecksquareValuePointBO> points = parseValuePoints(body, intervalMinutes);
|
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);
|
log.info("数据校验指标值 InfluxDB 查询结束,pointCount={},costMs={}", points.size(), System.currentTimeMillis() - startMillis);
|
||||||
return points;
|
return points;
|
||||||
} catch (RuntimeException ex) {
|
} catch (RuntimeException ex) {
|
||||||
@@ -78,10 +92,69 @@ public class SteadyChecksquareInfluxQueryComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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) {
|
public String buildChecksquareQuery(SteadyTrendResolvedFieldBO field, LocalDateTime startTime, LocalDateTime endTime) {
|
||||||
return buildValuePointQuery(field, startTime, 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) {
|
public String buildValuePointQuery(SteadyTrendResolvedFieldBO field, LocalDateTime startTime, LocalDateTime endTime) {
|
||||||
StringBuilder sql = new StringBuilder();
|
StringBuilder sql = new StringBuilder();
|
||||||
sql.append("SELECT \"").append(field.getField()).append("\" AS \"value\"");
|
sql.append("SELECT \"").append(field.getField()).append("\" AS \"value\"");
|
||||||
@@ -97,27 +170,26 @@ public class SteadyChecksquareInfluxQueryComponent {
|
|||||||
return sql.toString();
|
return sql.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
private Set<LocalDateTime> parseExistingSlots(String body, int intervalMinutes) {
|
public String buildBatchValuePointQuery(List<SteadyTrendResolvedFieldBO> fields, LocalDateTime startTime, LocalDateTime endTime) {
|
||||||
try {
|
SteadyTrendResolvedFieldBO first = fields.get(0);
|
||||||
JsonNode root = OBJECT_MAPPER.readTree(body);
|
StringBuilder sql = new StringBuilder("SELECT ");
|
||||||
JsonNode values = root.path("results").path(0).path("series").path(0).path("values");
|
for (int i = 0; i < fields.size(); i++) {
|
||||||
Set<LocalDateTime> result = new HashSet<LocalDateTime>();
|
SteadyTrendResolvedFieldBO field = fields.get(i);
|
||||||
if (!values.isArray()) {
|
if (i > 0) {
|
||||||
return result;
|
sql.append(", ");
|
||||||
}
|
}
|
||||||
for (JsonNode value : values) {
|
sql.append("\"").append(field.getField()).append("\" AS \"").append(field.getField()).append("\"");
|
||||||
if (value.size() < 2 || value.get(1).isNull()) {
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
LocalDateTime time = parseInfluxTime(value.get(0).asText());
|
sql.append(" FROM \"").append(first.getMeasurement()).append("\"");
|
||||||
if (time != null) {
|
sql.append(" WHERE time >= '").append(INFLUX_TIME_FORMATTER.format(startTime)).append("'");
|
||||||
result.add(alignToPreviousSlot(time, intervalMinutes));
|
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("'");
|
||||||
return result;
|
if (hasValueTypeTag(first.getMeasurement())) {
|
||||||
} catch (IOException ex) {
|
sql.append(" AND \"value_type\" = '").append(resolveValueType(first.getStatType())).append("'");
|
||||||
throw fail("InfluxDB 返回结果解析失败:" + ex.getMessage());
|
|
||||||
}
|
}
|
||||||
|
sql.append(" ORDER BY time ASC");
|
||||||
|
return sql.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<SteadyChecksquareValuePointBO> parseValuePoints(String body, int intervalMinutes) {
|
private List<SteadyChecksquareValuePointBO> parseValuePoints(String body, int intervalMinutes) {
|
||||||
@@ -149,6 +221,51 @@ public class SteadyChecksquareInfluxQueryComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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) {
|
private LocalDateTime alignToPreviousSlot(LocalDateTime time, int intervalMinutes) {
|
||||||
LocalDateTime minuteFloor = time.withSecond(0).withNano(0);
|
LocalDateTime minuteFloor = time.withSecond(0).withNano(0);
|
||||||
int minuteOfDay = minuteFloor.getHour() * 60 + minuteFloor.getMinute();
|
int minuteOfDay = minuteFloor.getHour() * 60 + minuteFloor.getMinute();
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ public class SteadyChecksquareValueOrderRuleComponent {
|
|||||||
for (String phase : indicator.getPhaseCodes()) {
|
for (String phase : indicator.getPhaseCodes()) {
|
||||||
Map<String, Map<LocalDateTime, BigDecimal>> statValueMap = queryStatValueMap(lineId, indicator,
|
Map<String, Map<LocalDateTime, BigDecimal>> statValueMap = queryStatValueMap(lineId, indicator,
|
||||||
harmonicOrder, phase, startTime, endTime, intervalMinutes);
|
harmonicOrder, phase, startTime, endTime, intervalMinutes);
|
||||||
appendAbnormalDetails(result, phase, statValueMap);
|
appendAbnormalDetails(result, phase, harmonicOrder, statValueMap);
|
||||||
}
|
}
|
||||||
result.setAbnormalPointCount(result.getAbnormalDetails().size());
|
result.setAbnormalPointCount(result.getAbnormalDetails().size());
|
||||||
result.setAbnormal(result.getAbnormalPointCount() > ABNORMAL_THRESHOLD);
|
result.setAbnormal(result.getAbnormalPointCount() > ABNORMAL_THRESHOLD);
|
||||||
@@ -65,7 +65,7 @@ public class SteadyChecksquareValueOrderRuleComponent {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void appendAbnormalDetails(SteadyChecksquareValueOrderRuleVO result, String phase,
|
private void appendAbnormalDetails(SteadyChecksquareValueOrderRuleVO result, String phase, Integer harmonicOrder,
|
||||||
Map<String, Map<LocalDateTime, BigDecimal>> statValueMap) {
|
Map<String, Map<LocalDateTime, BigDecimal>> statValueMap) {
|
||||||
Map<LocalDateTime, BigDecimal> maxValues = statValueMap.get("MAX");
|
Map<LocalDateTime, BigDecimal> maxValues = statValueMap.get("MAX");
|
||||||
Map<LocalDateTime, BigDecimal> cp95Values = statValueMap.get("CP95");
|
Map<LocalDateTime, BigDecimal> cp95Values = statValueMap.get("CP95");
|
||||||
@@ -84,18 +84,20 @@ public class SteadyChecksquareValueOrderRuleComponent {
|
|||||||
if (maxValue == null || cp95Value == null || avgValue == null || minValue == null) {
|
if (maxValue == null || cp95Value == null || avgValue == null || minValue == null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (maxValue.compareTo(cp95Value) > 0 && cp95Value.compareTo(avgValue) > 0 && avgValue.compareTo(minValue) > 0) {
|
if (maxValue.compareTo(cp95Value) >= 0 && cp95Value.compareTo(avgValue) >= 0 && avgValue.compareTo(minValue) >= 0) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
result.getAbnormalDetails().add(buildDetail(time, phase, maxValue, minValue, avgValue, cp95Value));
|
result.getAbnormalDetails().add(buildDetail(time, phase, harmonicOrder, maxValue, minValue, avgValue, cp95Value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private SteadyChecksquareValueOrderDetailVO buildDetail(LocalDateTime time, String phase, BigDecimal maxValue,
|
private SteadyChecksquareValueOrderDetailVO buildDetail(LocalDateTime time, String phase, Integer harmonicOrder,
|
||||||
BigDecimal minValue, BigDecimal avgValue, BigDecimal cp95Value) {
|
BigDecimal maxValue, BigDecimal minValue,
|
||||||
|
BigDecimal avgValue, BigDecimal cp95Value) {
|
||||||
SteadyChecksquareValueOrderDetailVO detail = new SteadyChecksquareValueOrderDetailVO();
|
SteadyChecksquareValueOrderDetailVO detail = new SteadyChecksquareValueOrderDetailVO();
|
||||||
detail.setTime(OUTPUT_TIME_FORMATTER.format(time));
|
detail.setTime(OUTPUT_TIME_FORMATTER.format(time));
|
||||||
detail.setPhase(phase);
|
detail.setPhase(phase);
|
||||||
|
detail.setHarmonicOrder(harmonicOrder);
|
||||||
detail.setMaxValue(maxValue);
|
detail.setMaxValue(maxValue);
|
||||||
detail.setMinValue(minValue);
|
detail.setMinValue(minValue);
|
||||||
detail.setAvgValue(avgValue);
|
detail.setAvgValue(avgValue);
|
||||||
|
|||||||
@@ -1,12 +1,18 @@
|
|||||||
package com.njcn.gather.steady.checksquare.controller;
|
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.annotation.OperateInfo;
|
||||||
|
import com.njcn.common.pojo.constant.OperateType;
|
||||||
import com.njcn.common.pojo.enums.common.LogEnum;
|
import com.njcn.common.pojo.enums.common.LogEnum;
|
||||||
import com.njcn.common.pojo.enums.response.CommonResponseEnum;
|
import com.njcn.common.pojo.enums.response.CommonResponseEnum;
|
||||||
import com.njcn.common.pojo.response.HttpResult;
|
import com.njcn.common.pojo.response.HttpResult;
|
||||||
import com.njcn.common.utils.LogUtil;
|
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.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.SteadyChecksquareQueryVO;
|
||||||
|
import com.njcn.gather.steady.checksquare.pojo.vo.SteadyChecksquareTaskVO;
|
||||||
import com.njcn.gather.steady.checksquare.service.SteadyChecksquareService;
|
import com.njcn.gather.steady.checksquare.service.SteadyChecksquareService;
|
||||||
import com.njcn.web.controller.BaseController;
|
import com.njcn.web.controller.BaseController;
|
||||||
import com.njcn.web.utils.HttpResultUtil;
|
import com.njcn.web.utils.HttpResultUtil;
|
||||||
@@ -14,9 +20,12 @@ import io.swagger.annotations.Api;
|
|||||||
import io.swagger.annotations.ApiOperation;
|
import io.swagger.annotations.ApiOperation;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
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.PostMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestBody;
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -32,12 +41,43 @@ public class SteadyChecksquareController extends BaseController {
|
|||||||
private final SteadyChecksquareService checksquareService;
|
private final SteadyChecksquareService checksquareService;
|
||||||
|
|
||||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
|
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
|
||||||
@ApiOperation("查询数据校验结果")
|
@ApiOperation("查询数据校验历史记录")
|
||||||
@PostMapping("/query")
|
@PostMapping("/query")
|
||||||
public HttpResult<SteadyChecksquareQueryVO> query(@RequestBody SteadyChecksquareQueryParam param) {
|
public HttpResult<Page<SteadyChecksquareTaskVO>> query(@RequestBody @Validated SteadyChecksquareHistoryQueryParam param) {
|
||||||
String methodDescribe = getMethodDescribe("query");
|
String methodDescribe = getMethodDescribe("query");
|
||||||
LogUtil.njcnDebug(log, "{},开始查询数据校验结果,param={}", methodDescribe, param);
|
LogUtil.njcnDebug(log, "{},开始查询数据校验历史记录,param={}", methodDescribe, param);
|
||||||
SteadyChecksquareQueryVO result = checksquareService.query(param);
|
Page<SteadyChecksquareTaskVO> result = checksquareService.query(param);
|
||||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe);
|
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)
|
||||||
|
@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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
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> {
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
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> {
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
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> {
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
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> {
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
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() {
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
@@ -8,10 +8,10 @@ import java.io.Serializable;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 数据校验查询参数。
|
* 数据校验新增检测参数。
|
||||||
*/
|
*/
|
||||||
@Data
|
@Data
|
||||||
@ApiModel("数据校验查询参数")
|
@ApiModel("数据校验新增检测参数")
|
||||||
public class SteadyChecksquareQueryParam implements Serializable {
|
public class SteadyChecksquareQueryParam implements Serializable {
|
||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
@@ -27,7 +27,4 @@ public class SteadyChecksquareQueryParam implements Serializable {
|
|||||||
|
|
||||||
@ApiModelProperty("结束时间,格式 yyyy-MM-dd HH:mm:ss")
|
@ApiModelProperty("结束时间,格式 yyyy-MM-dd HH:mm:ss")
|
||||||
private String timeEnd;
|
private String timeEnd;
|
||||||
|
|
||||||
@ApiModelProperty("谐波次数,谐波指标按请求次数查询")
|
|
||||||
private List<Integer> harmonicOrders;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,67 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
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("missing_rate")
|
||||||
|
private BigDecimal missingRate;
|
||||||
|
@TableField("missing_rate_text")
|
||||||
|
private String missingRateText;
|
||||||
|
@TableField("max_continuous_missing_minutes")
|
||||||
|
private Integer maxContinuousMissingMinutes;
|
||||||
|
@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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
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("missing_rate")
|
||||||
|
private BigDecimal missingRate;
|
||||||
|
@TableField("missing_rate_text")
|
||||||
|
private String missingRateText;
|
||||||
|
@TableField("max_continuous_missing_minutes")
|
||||||
|
private Integer maxContinuousMissingMinutes;
|
||||||
|
@TableField("create_time")
|
||||||
|
private LocalDateTime createTime;
|
||||||
|
}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
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("max_missing_rate")
|
||||||
|
private BigDecimal maxMissingRate;
|
||||||
|
@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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
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>();
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
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>();
|
||||||
|
}
|
||||||
@@ -18,6 +18,9 @@ public class SteadyChecksquareItemVO implements Serializable {
|
|||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@ApiModelProperty("检测项 ID")
|
||||||
|
private String itemId;
|
||||||
|
|
||||||
@ApiModelProperty("校验项唯一键")
|
@ApiModelProperty("校验项唯一键")
|
||||||
private String itemKey;
|
private String itemKey;
|
||||||
|
|
||||||
@@ -63,6 +66,16 @@ public class SteadyChecksquareItemVO implements Serializable {
|
|||||||
@ApiModelProperty("指标值大小关系异常明细")
|
@ApiModelProperty("指标值大小关系异常明细")
|
||||||
private List<SteadyChecksquareValueOrderDetailVO> abnormalDetails = new ArrayList<SteadyChecksquareValueOrderDetailVO>();
|
private List<SteadyChecksquareValueOrderDetailVO> abnormalDetails = new ArrayList<SteadyChecksquareValueOrderDetailVO>();
|
||||||
|
|
||||||
|
@ApiModelProperty("谐波奇偶关系是否异常")
|
||||||
|
private Boolean harmonicParityAbnormal;
|
||||||
|
|
||||||
|
@ApiModelProperty("谐波奇偶关系异常累计值")
|
||||||
|
private Integer harmonicParityAbnormalPointCount;
|
||||||
|
|
||||||
|
@ApiModelProperty("谐波奇偶关系异常明细")
|
||||||
|
private List<SteadyChecksquareHarmonicParityDetailVO> harmonicParityAbnormalDetails =
|
||||||
|
new ArrayList<SteadyChecksquareHarmonicParityDetailVO>();
|
||||||
|
|
||||||
@ApiModelProperty("统计类型摘要")
|
@ApiModelProperty("统计类型摘要")
|
||||||
private List<SteadyChecksquareStatSummaryVO> statSummaries = new ArrayList<SteadyChecksquareStatSummaryVO>();
|
private List<SteadyChecksquareStatSummaryVO> statSummaries = new ArrayList<SteadyChecksquareStatSummaryVO>();
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,12 @@ public class SteadyChecksquareQueryVO implements Serializable {
|
|||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@ApiModelProperty("任务 ID")
|
||||||
|
private String taskId;
|
||||||
|
|
||||||
|
@ApiModelProperty("任务编号")
|
||||||
|
private String taskNo;
|
||||||
|
|
||||||
@ApiModelProperty("监测点 ID")
|
@ApiModelProperty("监测点 ID")
|
||||||
private String lineId;
|
private String lineId;
|
||||||
|
|
||||||
|
|||||||
@@ -24,6 +24,9 @@ public class SteadyChecksquareSegmentVO implements Serializable {
|
|||||||
@ApiModelProperty("状态,NORMAL/MISSING")
|
@ApiModelProperty("状态,NORMAL/MISSING")
|
||||||
private String status;
|
private String status;
|
||||||
|
|
||||||
|
@ApiModelProperty("谐波次数")
|
||||||
|
private Integer harmonicOrder;
|
||||||
|
|
||||||
@ApiModelProperty("缺失点数")
|
@ApiModelProperty("缺失点数")
|
||||||
private Integer missingPointCount;
|
private Integer missingPointCount;
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,54 @@
|
|||||||
|
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 maxMissingRate;
|
||||||
|
|
||||||
|
@ApiModelProperty("创建时间")
|
||||||
|
private String createTime;
|
||||||
|
}
|
||||||
@@ -22,6 +22,9 @@ public class SteadyChecksquareValueOrderDetailVO implements Serializable {
|
|||||||
@ApiModelProperty("相别")
|
@ApiModelProperty("相别")
|
||||||
private String phase;
|
private String phase;
|
||||||
|
|
||||||
|
@ApiModelProperty("谐波次数")
|
||||||
|
private Integer harmonicOrder;
|
||||||
|
|
||||||
@ApiModelProperty("最大值")
|
@ApiModelProperty("最大值")
|
||||||
private BigDecimal maxValue;
|
private BigDecimal maxValue;
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
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> {
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
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,12 +1,26 @@
|
|||||||
package com.njcn.gather.steady.checksquare.service;
|
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.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.SteadyChecksquareQueryVO;
|
||||||
|
import com.njcn.gather.steady.checksquare.pojo.vo.SteadyChecksquareTaskVO;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 数据校验服务。
|
* 数据校验服务。
|
||||||
*/
|
*/
|
||||||
public interface SteadyChecksquareService {
|
public interface SteadyChecksquareService {
|
||||||
|
|
||||||
SteadyChecksquareQueryVO query(SteadyChecksquareQueryParam param);
|
Page<SteadyChecksquareTaskVO> query(SteadyChecksquareHistoryQueryParam param);
|
||||||
|
|
||||||
|
SteadyChecksquareCreateVO create(SteadyChecksquareQueryParam param);
|
||||||
|
|
||||||
|
SteadyChecksquareQueryVO detail(String taskId);
|
||||||
|
|
||||||
|
SteadyChecksquareItemDetailVO itemDetail(String itemId, String detailType, String statType);
|
||||||
|
|
||||||
|
SteadyChecksquareItemDetailVO itemDetail(String itemId, String detailType, String statType,
|
||||||
|
Integer pageNum, Integer pageSize);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
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> {
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
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> {
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
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 {
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
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
@@ -0,0 +1,16 @@
|
|||||||
|
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 {
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
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 {
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,147 @@
|
|||||||
|
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)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,10 +2,16 @@ package com.njcn.gather.steady.checksquare.component;
|
|||||||
|
|
||||||
import com.njcn.gather.steady.datavie.config.SteadyInfluxDbProperties;
|
import com.njcn.gather.steady.datavie.config.SteadyInfluxDbProperties;
|
||||||
import com.njcn.gather.steady.datavie.pojo.bo.SteadyTrendResolvedFieldBO;
|
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.Assertions;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 数据校验 InfluxQL 构造契约测试。
|
* 数据校验 InfluxQL 构造契约测试。
|
||||||
@@ -54,4 +60,94 @@ class SteadyChecksquareInfluxQueryComponentTest {
|
|||||||
Assertions.assertTrue(query.contains("\"value_type\" = 'CP95'"));
|
Assertions.assertTrue(query.contains("\"value_type\" = 'CP95'"));
|
||||||
Assertions.assertTrue(query.endsWith("ORDER BY time ASC"));
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,13 +35,13 @@ class SteadyChecksquareValueOrderRuleComponentTest {
|
|||||||
return Arrays.asList(point(firstTime, "8"), point(secondTime, "9"));
|
return Arrays.asList(point(firstTime, "8"), point(secondTime, "9"));
|
||||||
}
|
}
|
||||||
if ("CP95".equals(statType)) {
|
if ("CP95".equals(statType)) {
|
||||||
return Arrays.asList(point(firstTime, "8"), point(secondTime, "10"));
|
return Arrays.asList(point(firstTime, "9"), point(secondTime, "10"));
|
||||||
}
|
}
|
||||||
if ("AVG".equals(statType)) {
|
if ("AVG".equals(statType)) {
|
||||||
return Arrays.asList(point(firstTime, "7"), point(secondTime, "8"));
|
return Arrays.asList(point(firstTime, "7"), point(secondTime, "8"));
|
||||||
}
|
}
|
||||||
if ("MIN".equals(statType)) {
|
if ("MIN".equals(statType)) {
|
||||||
return Arrays.asList(point(firstTime, "1"), point(secondTime, "8"));
|
return Arrays.asList(point(firstTime, "1"), point(secondTime, "9"));
|
||||||
}
|
}
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
});
|
});
|
||||||
@@ -57,7 +57,38 @@ class SteadyChecksquareValueOrderRuleComponentTest {
|
|||||||
Assertions.assertEquals(new BigDecimal("8"), result.getAbnormalDetails().get(0).getMaxValue());
|
Assertions.assertEquals(new BigDecimal("8"), result.getAbnormalDetails().get(0).getMaxValue());
|
||||||
Assertions.assertEquals(new BigDecimal("1"), result.getAbnormalDetails().get(0).getMinValue());
|
Assertions.assertEquals(new BigDecimal("1"), result.getAbnormalDetails().get(0).getMinValue());
|
||||||
Assertions.assertEquals(new BigDecimal("7"), result.getAbnormalDetails().get(0).getAvgValue());
|
Assertions.assertEquals(new BigDecimal("7"), result.getAbnormalDetails().get(0).getAvgValue());
|
||||||
Assertions.assertEquals(new BigDecimal("8"), result.getAbnormalDetails().get(0).getCp95Value());
|
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
|
@Test
|
||||||
@@ -69,10 +100,10 @@ class SteadyChecksquareValueOrderRuleComponentTest {
|
|||||||
.thenAnswer(invocation -> {
|
.thenAnswer(invocation -> {
|
||||||
String statType = invocation.getArgument(0, com.njcn.gather.steady.datavie.pojo.bo.SteadyTrendResolvedFieldBO.class).getStatType();
|
String statType = invocation.getArgument(0, com.njcn.gather.steady.datavie.pojo.bo.SteadyTrendResolvedFieldBO.class).getStatType();
|
||||||
if ("MAX".equals(statType)) {
|
if ("MAX".equals(statType)) {
|
||||||
return Collections.singletonList(point(time, "10"));
|
return Collections.singletonList(point(time, "8"));
|
||||||
}
|
}
|
||||||
if ("CP95".equals(statType)) {
|
if ("CP95".equals(statType)) {
|
||||||
return Collections.singletonList(point(time, "8"));
|
return Collections.singletonList(point(time, "10"));
|
||||||
}
|
}
|
||||||
if ("AVG".equals(statType)) {
|
if ("AVG".equals(statType)) {
|
||||||
return Collections.singletonList(point(time, "8"));
|
return Collections.singletonList(point(time, "8"));
|
||||||
@@ -91,6 +122,39 @@ class SteadyChecksquareValueOrderRuleComponentTest {
|
|||||||
Assertions.assertEquals(1, result.getAbnormalDetails().size());
|
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
|
@Test
|
||||||
void shouldSkipPointWhenAnyRequiredStatValueMissing() {
|
void shouldSkipPointWhenAnyRequiredStatValueMissing() {
|
||||||
SteadyChecksquareInfluxQueryComponent influxQueryComponent = mock(SteadyChecksquareInfluxQueryComponent.class);
|
SteadyChecksquareInfluxQueryComponent influxQueryComponent = mock(SteadyChecksquareInfluxQueryComponent.class);
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package com.njcn.gather.steady.checksquare.controller;
|
|||||||
|
|
||||||
import org.junit.jupiter.api.Assertions;
|
import org.junit.jupiter.api.Assertions;
|
||||||
import org.junit.jupiter.api.Test;
|
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.PostMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
|
||||||
@@ -17,8 +18,21 @@ class SteadyChecksquareControllerTest {
|
|||||||
RequestMapping requestMapping = SteadyChecksquareController.class.getAnnotation(RequestMapping.class);
|
RequestMapping requestMapping = SteadyChecksquareController.class.getAnnotation(RequestMapping.class);
|
||||||
Assertions.assertArrayEquals(new String[]{"/steady/data-view/checksquare"}, requestMapping.value());
|
Assertions.assertArrayEquals(new String[]{"/steady/data-view/checksquare"}, requestMapping.value());
|
||||||
|
|
||||||
Method method = SteadyChecksquareController.class.getDeclaredMethod("query", com.njcn.gather.steady.checksquare.pojo.param.SteadyChecksquareQueryParam.class);
|
Method queryMethod = SteadyChecksquareController.class.getDeclaredMethod("query", com.njcn.gather.steady.checksquare.pojo.param.SteadyChecksquareHistoryQueryParam.class);
|
||||||
PostMapping postMapping = method.getAnnotation(PostMapping.class);
|
PostMapping queryMapping = queryMethod.getAnnotation(PostMapping.class);
|
||||||
Assertions.assertArrayEquals(new String[]{"/query"}, postMapping.value());
|
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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ class SteadyChecksquareQueryParamTest {
|
|||||||
Assertions.assertNull(field("qualityFlag"));
|
Assertions.assertNull(field("qualityFlag"));
|
||||||
Assertions.assertNull(field("statTypes"));
|
Assertions.assertNull(field("statTypes"));
|
||||||
Assertions.assertNull(field("phases"));
|
Assertions.assertNull(field("phases"));
|
||||||
Assertions.assertNotNull(field("harmonicOrders"));
|
Assertions.assertNull(field("harmonicOrders"));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Field field(String name) {
|
private Field field(String name) {
|
||||||
|
|||||||
@@ -1,21 +1,43 @@
|
|||||||
package com.njcn.gather.steady.checksquare.service.impl;
|
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.SteadyChecksquareCalculator;
|
||||||
|
import com.njcn.gather.steady.checksquare.component.SteadyChecksquareHarmonicParityRuleComponent;
|
||||||
import com.njcn.gather.steady.checksquare.component.SteadyChecksquareInfluxQueryComponent;
|
import com.njcn.gather.steady.checksquare.component.SteadyChecksquareInfluxQueryComponent;
|
||||||
import com.njcn.gather.steady.checksquare.component.SteadyChecksquareValueOrderRuleComponent;
|
import com.njcn.gather.steady.checksquare.component.SteadyChecksquareValueOrderRuleComponent;
|
||||||
import com.njcn.gather.steady.checksquare.pojo.param.SteadyChecksquareQueryParam;
|
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.SteadyChecksquareItemVO;
|
||||||
import com.njcn.gather.steady.checksquare.pojo.vo.SteadyChecksquareQueryVO;
|
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.SteadyChecksquareValueOrderDetailVO;
|
||||||
import com.njcn.gather.steady.checksquare.pojo.vo.SteadyChecksquareValueOrderRuleVO;
|
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.component.SteadyTrendIndicatorCatalog;
|
||||||
|
import com.njcn.gather.steady.datavie.pojo.bo.SteadyTrendResolvedFieldBO;
|
||||||
import com.njcn.gather.tool.adddata.component.AddDataTimeSlotCalculator;
|
import com.njcn.gather.tool.adddata.component.AddDataTimeSlotCalculator;
|
||||||
import com.njcn.gather.tool.addledger.pojo.vo.AddLedgerLinePathVO;
|
import com.njcn.gather.tool.addledger.pojo.vo.AddLedgerLinePathVO;
|
||||||
import com.njcn.gather.tool.addledger.service.AddLedgerService;
|
import com.njcn.gather.tool.addledger.service.AddLedgerService;
|
||||||
import org.junit.jupiter.api.Assertions;
|
import org.junit.jupiter.api.Assertions;
|
||||||
import org.junit.jupiter.api.Test;
|
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.time.LocalDateTime;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
@@ -25,6 +47,8 @@ import static org.mockito.ArgumentMatchers.any;
|
|||||||
import static org.mockito.ArgumentMatchers.anyInt;
|
import static org.mockito.ArgumentMatchers.anyInt;
|
||||||
import static org.mockito.ArgumentMatchers.eq;
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.times;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -32,15 +56,28 @@ import static org.mockito.Mockito.when;
|
|||||||
*/
|
*/
|
||||||
class SteadyChecksquareServiceImplTest {
|
class SteadyChecksquareServiceImplTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldNotOpenTransactionAroundCreateCalculation() throws Exception {
|
||||||
|
Method createMethod = SteadyChecksquareServiceImpl.class.getMethod("create", SteadyChecksquareQueryParam.class);
|
||||||
|
|
||||||
|
Assertions.assertNull(createMethod.getAnnotation(Transactional.class));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void shouldUseFixedFlickerIntervalsPerIndicator() {
|
void shouldUseFixedFlickerIntervalsPerIndicator() {
|
||||||
SteadyChecksquareInfluxQueryComponent influxQueryComponent = mock(SteadyChecksquareInfluxQueryComponent.class);
|
SteadyChecksquareInfluxQueryComponent influxQueryComponent = mock(SteadyChecksquareInfluxQueryComponent.class);
|
||||||
SteadyChecksquareValueOrderRuleComponent valueOrderRuleComponent = mock(SteadyChecksquareValueOrderRuleComponent.class);
|
SteadyChecksquareValueOrderRuleComponent valueOrderRuleComponent = mock(SteadyChecksquareValueOrderRuleComponent.class);
|
||||||
|
SteadyChecksquareHarmonicParityRuleComponent harmonicParityRuleComponent = mock(SteadyChecksquareHarmonicParityRuleComponent.class);
|
||||||
AddLedgerService addLedgerService = mock(AddLedgerService.class);
|
AddLedgerService addLedgerService = mock(AddLedgerService.class);
|
||||||
SteadyChecksquareServiceImpl service = new SteadyChecksquareServiceImpl(new SteadyTrendIndicatorCatalog(),
|
SteadyChecksquareServiceImpl service = new SteadyChecksquareServiceImpl(new SteadyTrendIndicatorCatalog(),
|
||||||
influxQueryComponent, new SteadyChecksquareCalculator(), valueOrderRuleComponent, new AddDataTimeSlotCalculator(), addLedgerService);
|
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()))
|
when(valueOrderRuleComponent.check(any(), any(), any(), any(LocalDateTime.class), any(LocalDateTime.class), anyInt()))
|
||||||
.thenReturn(emptyRuleResult());
|
.thenReturn(emptyRuleResult());
|
||||||
|
when(harmonicParityRuleComponent.check(any(), any(), any(LocalDateTime.class), any(LocalDateTime.class), anyInt()))
|
||||||
|
.thenReturn(emptyHarmonicParityRuleResult());
|
||||||
AddLedgerLinePathVO linePath = new AddLedgerLinePathVO();
|
AddLedgerLinePathVO linePath = new AddLedgerLinePathVO();
|
||||||
linePath.setLineId("line-001");
|
linePath.setLineId("line-001");
|
||||||
linePath.setLineName("进线一");
|
linePath.setLineName("进线一");
|
||||||
@@ -61,7 +98,7 @@ class SteadyChecksquareServiceImplTest {
|
|||||||
param.setTimeStart("2026-05-01 00:00:00");
|
param.setTimeStart("2026-05-01 00:00:00");
|
||||||
param.setTimeEnd("2026-05-01 02:00:00");
|
param.setTimeEnd("2026-05-01 02:00:00");
|
||||||
|
|
||||||
SteadyChecksquareQueryVO result = service.query(param);
|
SteadyChecksquareQueryVO result = calculate(service, param);
|
||||||
|
|
||||||
Assertions.assertEquals(Integer.valueOf(1), result.getIntervalMinutes());
|
Assertions.assertEquals(Integer.valueOf(1), result.getIntervalMinutes());
|
||||||
Assertions.assertEquals(3, result.getItems().size());
|
Assertions.assertEquals(3, result.getItems().size());
|
||||||
@@ -71,14 +108,20 @@ class SteadyChecksquareServiceImplTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void shouldOnlyQueryRequestedHarmonicOrders() {
|
void shouldAggregateAllHarmonicOrdersIntoIndicatorItem() {
|
||||||
SteadyChecksquareInfluxQueryComponent influxQueryComponent = mock(SteadyChecksquareInfluxQueryComponent.class);
|
SteadyChecksquareInfluxQueryComponent influxQueryComponent = mock(SteadyChecksquareInfluxQueryComponent.class);
|
||||||
SteadyChecksquareValueOrderRuleComponent valueOrderRuleComponent = mock(SteadyChecksquareValueOrderRuleComponent.class);
|
SteadyChecksquareValueOrderRuleComponent valueOrderRuleComponent = mock(SteadyChecksquareValueOrderRuleComponent.class);
|
||||||
|
SteadyChecksquareHarmonicParityRuleComponent harmonicParityRuleComponent = mock(SteadyChecksquareHarmonicParityRuleComponent.class);
|
||||||
AddLedgerService addLedgerService = mock(AddLedgerService.class);
|
AddLedgerService addLedgerService = mock(AddLedgerService.class);
|
||||||
SteadyChecksquareServiceImpl service = new SteadyChecksquareServiceImpl(new SteadyTrendIndicatorCatalog(),
|
SteadyChecksquareServiceImpl service = new SteadyChecksquareServiceImpl(new SteadyTrendIndicatorCatalog(),
|
||||||
influxQueryComponent, new SteadyChecksquareCalculator(), valueOrderRuleComponent, new AddDataTimeSlotCalculator(), addLedgerService);
|
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()))
|
when(valueOrderRuleComponent.check(any(), any(), any(), any(LocalDateTime.class), any(LocalDateTime.class), anyInt()))
|
||||||
.thenReturn(emptyRuleResult());
|
.thenReturn(emptyRuleResult());
|
||||||
|
when(harmonicParityRuleComponent.check(any(), any(), any(LocalDateTime.class), any(LocalDateTime.class), anyInt()))
|
||||||
|
.thenReturn(emptyHarmonicParityRuleResult());
|
||||||
AddLedgerLinePathVO linePath = new AddLedgerLinePathVO();
|
AddLedgerLinePathVO linePath = new AddLedgerLinePathVO();
|
||||||
linePath.setLineId("line-001");
|
linePath.setLineId("line-001");
|
||||||
linePath.setLineName("进线一");
|
linePath.setLineName("进线一");
|
||||||
@@ -92,25 +135,32 @@ class SteadyChecksquareServiceImplTest {
|
|||||||
SteadyChecksquareQueryParam param = new SteadyChecksquareQueryParam();
|
SteadyChecksquareQueryParam param = new SteadyChecksquareQueryParam();
|
||||||
param.setLineId("line-001");
|
param.setLineId("line-001");
|
||||||
param.setIndicatorCodes(Collections.singletonList("V_HARMONIC"));
|
param.setIndicatorCodes(Collections.singletonList("V_HARMONIC"));
|
||||||
param.setHarmonicOrders(Collections.singletonList(5));
|
|
||||||
param.setTimeStart("2026-05-01 00:00:00");
|
param.setTimeStart("2026-05-01 00:00:00");
|
||||||
param.setTimeEnd("2026-05-01 00:01:00");
|
param.setTimeEnd("2026-05-01 00:01:00");
|
||||||
|
|
||||||
SteadyChecksquareQueryVO result = service.query(param);
|
SteadyChecksquareQueryVO result = calculate(service, param);
|
||||||
|
|
||||||
Assertions.assertEquals(1, result.getItems().size());
|
Assertions.assertEquals(1, result.getItems().size());
|
||||||
Assertions.assertEquals(Integer.valueOf(5), result.getItems().get(0).getHarmonicOrder());
|
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
|
@Test
|
||||||
void shouldKeepRequestedHarmonicOrdersDistinctAndOrdered() {
|
void shouldAverageHarmonicOrderResultsAndMarkAbnormalWhenAnyOrderAbnormal() {
|
||||||
SteadyChecksquareInfluxQueryComponent influxQueryComponent = mock(SteadyChecksquareInfluxQueryComponent.class);
|
SteadyChecksquareInfluxQueryComponent influxQueryComponent = mock(SteadyChecksquareInfluxQueryComponent.class);
|
||||||
SteadyChecksquareValueOrderRuleComponent valueOrderRuleComponent = mock(SteadyChecksquareValueOrderRuleComponent.class);
|
SteadyChecksquareValueOrderRuleComponent valueOrderRuleComponent = mock(SteadyChecksquareValueOrderRuleComponent.class);
|
||||||
|
SteadyChecksquareHarmonicParityRuleComponent harmonicParityRuleComponent = mock(SteadyChecksquareHarmonicParityRuleComponent.class);
|
||||||
AddLedgerService addLedgerService = mock(AddLedgerService.class);
|
AddLedgerService addLedgerService = mock(AddLedgerService.class);
|
||||||
SteadyChecksquareServiceImpl service = new SteadyChecksquareServiceImpl(new SteadyTrendIndicatorCatalog(),
|
SteadyChecksquareServiceImpl service = new SteadyChecksquareServiceImpl(new SteadyTrendIndicatorCatalog(),
|
||||||
influxQueryComponent, new SteadyChecksquareCalculator(), valueOrderRuleComponent, new AddDataTimeSlotCalculator(), addLedgerService);
|
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()))
|
when(valueOrderRuleComponent.check(any(), any(), any(), any(LocalDateTime.class), any(LocalDateTime.class), anyInt()))
|
||||||
.thenReturn(emptyRuleResult());
|
.thenReturn(emptyRuleResult());
|
||||||
|
when(harmonicParityRuleComponent.check(any(), any(), any(LocalDateTime.class), any(LocalDateTime.class), anyInt()))
|
||||||
|
.thenReturn(emptyHarmonicParityRuleResult());
|
||||||
AddLedgerLinePathVO linePath = new AddLedgerLinePathVO();
|
AddLedgerLinePathVO linePath = new AddLedgerLinePathVO();
|
||||||
linePath.setLineId("line-001");
|
linePath.setLineId("line-001");
|
||||||
linePath.setLineName("进线一");
|
linePath.setLineName("进线一");
|
||||||
@@ -118,30 +168,53 @@ class SteadyChecksquareServiceImplTest {
|
|||||||
when(addLedgerService.listLinePathByLineIds(eq(Collections.singletonList("line-001"))))
|
when(addLedgerService.listLinePathByLineIds(eq(Collections.singletonList("line-001"))))
|
||||||
.thenReturn(Collections.singletonMap("line-001", linePath));
|
.thenReturn(Collections.singletonMap("line-001", linePath));
|
||||||
when(influxQueryComponent.queryExistingSlots(any(), any(LocalDateTime.class), any(LocalDateTime.class), eq(1)))
|
when(influxQueryComponent.queryExistingSlots(any(), any(LocalDateTime.class), any(LocalDateTime.class), eq(1)))
|
||||||
.thenReturn(new HashSet<LocalDateTime>());
|
.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();
|
SteadyChecksquareQueryParam param = new SteadyChecksquareQueryParam();
|
||||||
param.setLineId("line-001");
|
param.setLineId("line-001");
|
||||||
param.setIndicatorCodes(Collections.singletonList("V_HARMONIC"));
|
param.setIndicatorCodes(Collections.singletonList("V_HARMONIC"));
|
||||||
param.setHarmonicOrders(Arrays.asList(7, 5, 7));
|
|
||||||
param.setTimeStart("2026-05-01 00:00:00");
|
param.setTimeStart("2026-05-01 00:00:00");
|
||||||
param.setTimeEnd("2026-05-01 00:01:00");
|
param.setTimeEnd("2026-05-01 00:01:00");
|
||||||
|
|
||||||
SteadyChecksquareQueryVO result = service.query(param);
|
SteadyChecksquareQueryVO result = calculate(service, param);
|
||||||
|
|
||||||
List<SteadyChecksquareItemVO> items = result.getItems();
|
List<SteadyChecksquareItemVO> items = result.getItems();
|
||||||
Assertions.assertEquals(2, items.size());
|
Assertions.assertEquals(1, items.size());
|
||||||
Assertions.assertEquals(Integer.valueOf(7), items.get(0).getHarmonicOrder());
|
Assertions.assertEquals(Boolean.TRUE, items.get(0).getAbnormal());
|
||||||
Assertions.assertEquals(Integer.valueOf(5), items.get(1).getHarmonicOrder());
|
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
|
@Test
|
||||||
void shouldAssembleValueOrderRuleResultIntoItem() {
|
void shouldAssembleValueOrderRuleResultIntoItem() {
|
||||||
SteadyChecksquareInfluxQueryComponent influxQueryComponent = mock(SteadyChecksquareInfluxQueryComponent.class);
|
SteadyChecksquareInfluxQueryComponent influxQueryComponent = mock(SteadyChecksquareInfluxQueryComponent.class);
|
||||||
SteadyChecksquareValueOrderRuleComponent valueOrderRuleComponent = mock(SteadyChecksquareValueOrderRuleComponent.class);
|
SteadyChecksquareValueOrderRuleComponent valueOrderRuleComponent = mock(SteadyChecksquareValueOrderRuleComponent.class);
|
||||||
|
SteadyChecksquareHarmonicParityRuleComponent harmonicParityRuleComponent = mock(SteadyChecksquareHarmonicParityRuleComponent.class);
|
||||||
AddLedgerService addLedgerService = mock(AddLedgerService.class);
|
AddLedgerService addLedgerService = mock(AddLedgerService.class);
|
||||||
SteadyChecksquareServiceImpl service = new SteadyChecksquareServiceImpl(new SteadyTrendIndicatorCatalog(),
|
SteadyChecksquareServiceImpl service = new SteadyChecksquareServiceImpl(new SteadyTrendIndicatorCatalog(),
|
||||||
influxQueryComponent, new SteadyChecksquareCalculator(), valueOrderRuleComponent, new AddDataTimeSlotCalculator(), addLedgerService);
|
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();
|
AddLedgerLinePathVO linePath = new AddLedgerLinePathVO();
|
||||||
linePath.setLineId("line-001");
|
linePath.setLineId("line-001");
|
||||||
linePath.setLineName("进线一");
|
linePath.setLineName("进线一");
|
||||||
@@ -167,7 +240,7 @@ class SteadyChecksquareServiceImplTest {
|
|||||||
param.setTimeStart("2026-05-01 00:00:00");
|
param.setTimeStart("2026-05-01 00:00:00");
|
||||||
param.setTimeEnd("2026-05-01 00:01:00");
|
param.setTimeEnd("2026-05-01 00:01:00");
|
||||||
|
|
||||||
SteadyChecksquareQueryVO result = service.query(param);
|
SteadyChecksquareQueryVO result = calculate(service, param);
|
||||||
|
|
||||||
SteadyChecksquareItemVO item = result.getItems().get(0);
|
SteadyChecksquareItemVO item = result.getItems().get(0);
|
||||||
Assertions.assertEquals(Boolean.TRUE, item.getAbnormal());
|
Assertions.assertEquals(Boolean.TRUE, item.getAbnormal());
|
||||||
@@ -176,13 +249,314 @@ class SteadyChecksquareServiceImplTest {
|
|||||||
Assertions.assertEquals("A", item.getAbnormalDetails().get(0).getPhase());
|
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 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.setMissingRate(BigDecimal.ZERO.setScale(6));
|
||||||
|
item.setMissingRateText("0.00%");
|
||||||
|
item.setMaxContinuousMissingMinutes(0);
|
||||||
|
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.setMissingRate(BigDecimal.ZERO.setScale(6));
|
||||||
|
summary.setMissingRateText("0.00%");
|
||||||
|
summary.setMaxContinuousMissingMinutes(0);
|
||||||
|
item.getStatSummaries().add(summary);
|
||||||
|
result.getItems().add(item);
|
||||||
|
|
||||||
|
saveResult(service, param, result);
|
||||||
|
|
||||||
|
verify(taskService).save(any());
|
||||||
|
verify(itemService).saveBatch(any());
|
||||||
|
verify(statSummaryService).saveBatch(any());
|
||||||
|
}
|
||||||
|
|
||||||
private void assertItemInterval(SteadyChecksquareItemVO item, String indicatorCode, int intervalMinutes, int expectedPointCount) {
|
private void assertItemInterval(SteadyChecksquareItemVO item, String indicatorCode, int intervalMinutes, int expectedPointCount) {
|
||||||
Assertions.assertEquals(indicatorCode, item.getIndicatorCode());
|
Assertions.assertEquals(indicatorCode, item.getIndicatorCode());
|
||||||
Assertions.assertEquals(Integer.valueOf(intervalMinutes), item.getIntervalMinutes());
|
Assertions.assertEquals(Integer.valueOf(intervalMinutes), item.getIntervalMinutes());
|
||||||
Assertions.assertEquals(Integer.valueOf(expectedPointCount), item.getExpectedPointCount());
|
Assertions.assertEquals(Integer.valueOf(expectedPointCount), item.getExpectedPointCount());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 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.setMissingRate(BigDecimal.ZERO.setScale(6));
|
||||||
|
item.setMaxContinuousMissingMinutes(0);
|
||||||
|
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.setMissingRate(BigDecimal.ZERO.setScale(6));
|
||||||
|
summary.setMaxContinuousMissingMinutes(0);
|
||||||
|
return summary;
|
||||||
|
}
|
||||||
|
|
||||||
private SteadyChecksquareValueOrderRuleVO emptyRuleResult() {
|
private SteadyChecksquareValueOrderRuleVO emptyRuleResult() {
|
||||||
return new SteadyChecksquareValueOrderRuleVO();
|
return new SteadyChecksquareValueOrderRuleVO();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private SteadyChecksquareHarmonicParityRuleVO emptyHarmonicParityRuleResult() {
|
||||||
|
return new SteadyChecksquareHarmonicParityRuleVO();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -223,7 +223,8 @@ public class AddDataValueGenerator {
|
|||||||
if (baseValue == null) {
|
if (baseValue == null) {
|
||||||
throw new IllegalStateException("派生字段缺少主值:" + column);
|
throw new IllegalStateException("派生字段缺少主值:" + column);
|
||||||
}
|
}
|
||||||
double factor = noise(state.sharedSeed + column.hashCode(), 0.01D, 0.05D);
|
String baseColumn = resolveDerivedBaseColumn(column, metricType);
|
||||||
|
double factor = noise(state.sharedSeed + baseColumn.hashCode(), 0.01D, 0.05D);
|
||||||
double delta = Math.max(Math.abs(baseValue) * factor, 0.005D);
|
double delta = Math.max(Math.abs(baseValue) * factor, 0.005D);
|
||||||
double value;
|
double value;
|
||||||
if (MetricType.MAX.equals(metricType)) {
|
if (MetricType.MAX.equals(metricType)) {
|
||||||
@@ -239,6 +240,16 @@ public class AddDataValueGenerator {
|
|||||||
return round(value, 4);
|
return round(value, 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String resolveDerivedBaseColumn(String column, MetricType metricType) {
|
||||||
|
if (MetricType.MAX.equals(metricType)) {
|
||||||
|
return removeSuffix(column, SUFFIX_MAX);
|
||||||
|
}
|
||||||
|
if (MetricType.MIN.equals(metricType)) {
|
||||||
|
return removeSuffix(column, SUFFIX_MIN);
|
||||||
|
}
|
||||||
|
return removeSuffix(column, SUFFIX_CP95);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 构建同源基础状态。
|
* 构建同源基础状态。
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -25,4 +25,24 @@ class AddDataValueGeneratorTest {
|
|||||||
|
|
||||||
Assertions.assertEquals(0, row.get(3));
|
Assertions.assertEquals(0, row.get(3));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldKeepStatValuesOrderedForSameTimeAndMetric() {
|
||||||
|
AddDataValueGenerator generator = new AddDataValueGenerator();
|
||||||
|
AddDataTableDefinition definition = new AddDataTableDefinition("data_v",
|
||||||
|
Arrays.asList("TIMEID", "LINEID", "PHASIC_TYPE", "QUALITYFLAG",
|
||||||
|
"RMS", "RMS_MAX", "RMS_CP95", "RMS_MIN"),
|
||||||
|
Arrays.asList("A"), 100, AddDataTableDefinition.TimeAxisType.REQUEST_INTERVAL);
|
||||||
|
|
||||||
|
List<Object> row = generator.generateRow(definition, "line-001",
|
||||||
|
LocalDateTime.of(2026, 5, 18, 10, 0, 0), "A");
|
||||||
|
|
||||||
|
double avg = ((Number) row.get(4)).doubleValue();
|
||||||
|
double max = ((Number) row.get(5)).doubleValue();
|
||||||
|
double cp95 = ((Number) row.get(6)).doubleValue();
|
||||||
|
double min = ((Number) row.get(7)).doubleValue();
|
||||||
|
Assertions.assertTrue(max >= cp95, "MAX should be greater than or equal to CP95");
|
||||||
|
Assertions.assertTrue(cp95 >= avg, "CP95 should be greater than or equal to AVG");
|
||||||
|
Assertions.assertTrue(avg >= min, "AVG should be greater than or equal to MIN");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user