refactor(steady): 重构数据校验功能并新增PQDIF解析预留模块
- 将数据校验中的缺失率相关字段替换为数据完整性字段 - 新增数据校验任务删除功能及相应测试 - 在tools模块中添加parse-pqdif子模块作为PQDIF文件解析预留 - 更新README文档以反映新的模块结构和依赖关系 - 优化数据校验统计汇总逻辑和测试覆盖 - 在entrance模块中集成parse-pqdif依赖 - 重构数据校验服务层实现和数据对象映射
This commit is contained in:
@@ -45,19 +45,6 @@ public class SteadyChecksquareCalculator {
|
||||
return result;
|
||||
}
|
||||
|
||||
public int maxContinuousMissingMinutes(List<SteadyChecksquareSegmentVO> segments) {
|
||||
int result = 0;
|
||||
if (segments == null) {
|
||||
return result;
|
||||
}
|
||||
for (SteadyChecksquareSegmentVO segment : segments) {
|
||||
if (segment != null && STATUS_MISSING.equals(segment.getStatus()) && segment.getDurationMinutes() != null) {
|
||||
result = Math.max(result, segment.getDurationMinutes());
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private SteadyChecksquareSegmentVO buildSegment(LocalDateTime startTime, LocalDateTime endTime, String status,
|
||||
int pointCount, int intervalMinutes) {
|
||||
SteadyChecksquareSegmentVO segment = new SteadyChecksquareSegmentVO();
|
||||
|
||||
@@ -28,6 +28,8 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 数据校验接口。
|
||||
*/
|
||||
@@ -60,6 +62,16 @@ public class SteadyChecksquareController extends BaseController {
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe);
|
||||
}
|
||||
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON, operateType = OperateType.DELETE)
|
||||
@ApiOperation("删除数据校验任务")
|
||||
@PostMapping("/delete")
|
||||
public HttpResult<Boolean> delete(@RequestBody List<String> taskIds) {
|
||||
String methodDescribe = getMethodDescribe("delete");
|
||||
LogUtil.njcnDebug(log, "{},开始删除数据校验任务,taskIds={}", methodDescribe, taskIds);
|
||||
boolean result = checksquareService.delete(taskIds);
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe);
|
||||
}
|
||||
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
|
||||
@ApiOperation("查询数据校验任务详情")
|
||||
@GetMapping("/detail")
|
||||
|
||||
@@ -40,12 +40,10 @@ public class SteadyChecksquareItemPO implements Serializable {
|
||||
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("data_integrity")
|
||||
private BigDecimal dataIntegrity;
|
||||
@TableField("data_integrity_text")
|
||||
private String dataIntegrityText;
|
||||
@TableField("abnormal")
|
||||
private Integer abnormal;
|
||||
@TableField("abnormal_point_count")
|
||||
|
||||
@@ -34,12 +34,10 @@ public class SteadyChecksquareStatSummaryPO implements Serializable {
|
||||
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("data_integrity")
|
||||
private BigDecimal dataIntegrity;
|
||||
@TableField("data_integrity_text")
|
||||
private String dataIntegrityText;
|
||||
@TableField("create_time")
|
||||
private LocalDateTime createTime;
|
||||
}
|
||||
|
||||
@@ -42,8 +42,8 @@ public class SteadyChecksquareTaskPO implements Serializable {
|
||||
private Integer itemCount;
|
||||
@TableField("abnormal_item_count")
|
||||
private Integer abnormalItemCount;
|
||||
@TableField("max_missing_rate")
|
||||
private BigDecimal maxMissingRate;
|
||||
@TableField("min_data_integrity")
|
||||
private BigDecimal minDataIntegrity;
|
||||
@TableField("result_message")
|
||||
private String resultMessage;
|
||||
@TableField("state")
|
||||
|
||||
@@ -48,14 +48,11 @@ public class SteadyChecksquareItemVO implements Serializable {
|
||||
@ApiModelProperty("缺失点数")
|
||||
private Integer missingPointCount;
|
||||
|
||||
@ApiModelProperty("缺失率")
|
||||
private BigDecimal missingRate;
|
||||
@ApiModelProperty("数据完整性")
|
||||
private BigDecimal dataIntegrity;
|
||||
|
||||
@ApiModelProperty("缺失率文本")
|
||||
private String missingRateText;
|
||||
|
||||
@ApiModelProperty("最大连续缺失时长,单位分钟")
|
||||
private Integer maxContinuousMissingMinutes;
|
||||
@ApiModelProperty("数据完整性文本")
|
||||
private String dataIntegrityText;
|
||||
|
||||
@ApiModelProperty("指标值大小关系是否异常")
|
||||
private Boolean abnormal;
|
||||
|
||||
@@ -34,12 +34,9 @@ public class SteadyChecksquareStatSummaryVO implements Serializable {
|
||||
@ApiModelProperty("缺失点数")
|
||||
private Integer missingPointCount;
|
||||
|
||||
@ApiModelProperty("缺失率")
|
||||
private BigDecimal missingRate;
|
||||
@ApiModelProperty("数据完整性")
|
||||
private BigDecimal dataIntegrity;
|
||||
|
||||
@ApiModelProperty("缺失率文本")
|
||||
private String missingRateText;
|
||||
|
||||
@ApiModelProperty("最大连续缺失时长,单位分钟")
|
||||
private Integer maxContinuousMissingMinutes;
|
||||
@ApiModelProperty("数据完整性文本")
|
||||
private String dataIntegrityText;
|
||||
}
|
||||
|
||||
@@ -46,8 +46,8 @@ public class SteadyChecksquareTaskVO implements Serializable {
|
||||
@ApiModelProperty("异常检测项数量")
|
||||
private Integer abnormalItemCount;
|
||||
|
||||
@ApiModelProperty("最大缺失率")
|
||||
private BigDecimal maxMissingRate;
|
||||
@ApiModelProperty("最低数据完整性")
|
||||
private BigDecimal minDataIntegrity;
|
||||
|
||||
@ApiModelProperty("创建时间")
|
||||
private String createTime;
|
||||
|
||||
@@ -8,6 +8,8 @@ import com.njcn.gather.steady.checksquare.pojo.vo.SteadyChecksquareItemDetailVO;
|
||||
import com.njcn.gather.steady.checksquare.pojo.vo.SteadyChecksquareQueryVO;
|
||||
import com.njcn.gather.steady.checksquare.pojo.vo.SteadyChecksquareTaskVO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 数据校验服务。
|
||||
*/
|
||||
@@ -17,6 +19,8 @@ public interface SteadyChecksquareService {
|
||||
|
||||
SteadyChecksquareCreateVO create(SteadyChecksquareQueryParam param);
|
||||
|
||||
boolean delete(List<String> taskIds);
|
||||
|
||||
SteadyChecksquareQueryVO detail(String taskId);
|
||||
|
||||
SteadyChecksquareItemDetailVO itemDetail(String itemId, String detailType, String statType);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.njcn.gather.steady.checksquare.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.njcn.common.pojo.enums.response.CommonResponseEnum;
|
||||
@@ -127,6 +128,25 @@ public class SteadyChecksquareServiceImpl implements SteadyChecksquareService {
|
||||
return toCreateVO(task);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean delete(List<String> taskIds) {
|
||||
List<String> ids = normalizeTextList(taskIds);
|
||||
if (ids.isEmpty()) {
|
||||
throw fail("数据校验任务 ID 不能为空");
|
||||
}
|
||||
List<SteadyChecksquareTaskPO> tasks = taskService.lambdaQuery()
|
||||
.in(SteadyChecksquareTaskPO::getId, ids)
|
||||
.eq(SteadyChecksquareTaskPO::getState, SteadyChecksquareConst.STATE_ENABLED)
|
||||
.list();
|
||||
if (tasks == null || tasks.size() != ids.size()) {
|
||||
throw fail("数据校验任务不存在或已删除");
|
||||
}
|
||||
if (transactionTemplate != null) {
|
||||
return Boolean.TRUE.equals(transactionTemplate.execute(status -> deleteTasksAndItems(ids)));
|
||||
}
|
||||
return deleteTasksAndItems(ids);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SteadyChecksquareQueryVO detail(String taskId) {
|
||||
SteadyChecksquareTaskPO task = requireTask(taskId);
|
||||
@@ -198,6 +218,21 @@ public class SteadyChecksquareServiceImpl implements SteadyChecksquareService {
|
||||
return result;
|
||||
}
|
||||
|
||||
private boolean deleteTasksAndItems(List<String> taskIds) {
|
||||
LambdaUpdateWrapper<SteadyChecksquareTaskPO> taskWrapper = new LambdaUpdateWrapper<SteadyChecksquareTaskPO>()
|
||||
.set(SteadyChecksquareTaskPO::getState, SteadyChecksquareConst.STATE_DELETED)
|
||||
.in(SteadyChecksquareTaskPO::getId, taskIds)
|
||||
.eq(SteadyChecksquareTaskPO::getState, SteadyChecksquareConst.STATE_ENABLED);
|
||||
boolean taskResult = taskService.update(taskWrapper);
|
||||
// 检测项同步置为删除态,避免已删任务下的 item-detail 被继续访问。
|
||||
LambdaUpdateWrapper<SteadyChecksquareItemPO> itemWrapper = new LambdaUpdateWrapper<SteadyChecksquareItemPO>()
|
||||
.set(SteadyChecksquareItemPO::getState, SteadyChecksquareConst.STATE_DELETED)
|
||||
.in(SteadyChecksquareItemPO::getTaskId, taskIds)
|
||||
.eq(SteadyChecksquareItemPO::getState, SteadyChecksquareConst.STATE_ENABLED);
|
||||
itemService.update(itemWrapper);
|
||||
return taskResult;
|
||||
}
|
||||
|
||||
private SteadyChecksquareQueryVO calculate(SteadyChecksquareQueryParam param) {
|
||||
validateParam(param);
|
||||
String lineId = trimToNull(param.getLineId());
|
||||
@@ -256,7 +291,7 @@ public class SteadyChecksquareServiceImpl implements SteadyChecksquareService {
|
||||
task.setTaskStatus(SteadyChecksquareConst.TASK_STATUS_SUCCESS);
|
||||
task.setItemCount(result.getItems().size());
|
||||
task.setAbnormalItemCount(countAbnormalItems(result.getItems()));
|
||||
task.setMaxMissingRate(maxMissingRate(result.getItems()));
|
||||
task.setMinDataIntegrity(minDataIntegrity(result.getItems()));
|
||||
task.setResultMessage("数据校验完成");
|
||||
task.setState(SteadyChecksquareConst.STATE_ENABLED);
|
||||
task.setCreateTime(now);
|
||||
@@ -298,9 +333,8 @@ public class SteadyChecksquareServiceImpl implements SteadyChecksquareService {
|
||||
po.setExpectedPointCount(nullToZero(item.getExpectedPointCount()));
|
||||
po.setActualPointCount(nullToZero(item.getActualPointCount()));
|
||||
po.setMissingPointCount(nullToZero(item.getMissingPointCount()));
|
||||
po.setMissingRate(item.getMissingRate() == null ? BigDecimal.ZERO.setScale(6, RoundingMode.HALF_UP) : item.getMissingRate());
|
||||
po.setMissingRateText(item.getMissingRateText());
|
||||
po.setMaxContinuousMissingMinutes(nullToZero(item.getMaxContinuousMissingMinutes()));
|
||||
po.setDataIntegrity(item.getDataIntegrity() == null ? BigDecimal.ZERO.setScale(6, RoundingMode.HALF_UP) : item.getDataIntegrity());
|
||||
po.setDataIntegrityText(item.getDataIntegrityText());
|
||||
po.setAbnormal(toFlag(item.getAbnormal()));
|
||||
po.setAbnormalPointCount(nullToZero(item.getAbnormalPointCount()));
|
||||
po.setHarmonicParityAbnormal(toFlag(item.getHarmonicParityAbnormal()));
|
||||
@@ -323,9 +357,8 @@ public class SteadyChecksquareServiceImpl implements SteadyChecksquareService {
|
||||
po.setExpectedPointCount(nullToZero(summary.getExpectedPointCount()));
|
||||
po.setActualPointCount(nullToZero(summary.getActualPointCount()));
|
||||
po.setMissingPointCount(nullToZero(summary.getMissingPointCount()));
|
||||
po.setMissingRate(summary.getMissingRate() == null ? BigDecimal.ZERO.setScale(6, RoundingMode.HALF_UP) : summary.getMissingRate());
|
||||
po.setMissingRateText(summary.getMissingRateText());
|
||||
po.setMaxContinuousMissingMinutes(nullToZero(summary.getMaxContinuousMissingMinutes()));
|
||||
po.setDataIntegrity(summary.getDataIntegrity() == null ? BigDecimal.ZERO.setScale(6, RoundingMode.HALF_UP) : summary.getDataIntegrity());
|
||||
po.setDataIntegrityText(summary.getDataIntegrityText());
|
||||
po.setCreateTime(now);
|
||||
result.add(po);
|
||||
}
|
||||
@@ -463,13 +496,12 @@ public class SteadyChecksquareServiceImpl implements SteadyChecksquareService {
|
||||
result.setIndicatorName(indicator.getName());
|
||||
result.setHarmonicOrder(null);
|
||||
result.setIntervalMinutes(intervalMinutes);
|
||||
result.setHasData(anyHasData(orderItems));
|
||||
result.setExpectedPointCount(averageInteger(orderItems, "expectedPointCount"));
|
||||
result.setActualPointCount(averageInteger(orderItems, "actualPointCount"));
|
||||
result.setMissingPointCount(averageInteger(orderItems, "missingPointCount"));
|
||||
result.setMissingRate(averageRate(orderItems));
|
||||
result.setMissingRateText(formatRateText(result.getMissingRate()));
|
||||
result.setMaxContinuousMissingMinutes(averageInteger(orderItems, "maxContinuousMissingMinutes"));
|
||||
result.setDataIntegrity(averageDataIntegrity(orderItems));
|
||||
result.setHasData(hasDataByIntegrity(result.getDataIntegrity()));
|
||||
result.setDataIntegrityText(formatRateText(result.getDataIntegrity()));
|
||||
result.setAbnormal(anyAbnormal(orderItems));
|
||||
result.setAbnormalPointCount(averageAbnormalCount(orderItems, "abnormalPointCount", result.getAbnormal()));
|
||||
result.setHarmonicParityAbnormal(anyHarmonicParityAbnormal(orderItems));
|
||||
@@ -501,13 +533,12 @@ public class SteadyChecksquareServiceImpl implements SteadyChecksquareService {
|
||||
SteadyChecksquareStatSummaryVO summary = new SteadyChecksquareStatSummaryVO();
|
||||
summary.setStatType(entry.getKey());
|
||||
summary.setSupported(true);
|
||||
summary.setHasData(anySummaryHasData(entry.getValue()));
|
||||
summary.setExpectedPointCount(averageSummaryInteger(entry.getValue(), "expectedPointCount"));
|
||||
summary.setActualPointCount(averageSummaryInteger(entry.getValue(), "actualPointCount"));
|
||||
summary.setMissingPointCount(averageSummaryInteger(entry.getValue(), "missingPointCount"));
|
||||
summary.setMissingRate(averageSummaryRate(entry.getValue()));
|
||||
summary.setMissingRateText(formatRateText(summary.getMissingRate()));
|
||||
summary.setMaxContinuousMissingMinutes(averageSummaryInteger(entry.getValue(), "maxContinuousMissingMinutes"));
|
||||
summary.setDataIntegrity(averageSummaryDataIntegrity(entry.getValue()));
|
||||
summary.setHasData(hasDataByIntegrity(summary.getDataIntegrity()));
|
||||
summary.setDataIntegrityText(formatRateText(summary.getDataIntegrity()));
|
||||
result.add(summary);
|
||||
}
|
||||
return result;
|
||||
@@ -549,8 +580,6 @@ public class SteadyChecksquareServiceImpl implements SteadyChecksquareService {
|
||||
|
||||
int totalExpected = 0;
|
||||
int totalActual = 0;
|
||||
int maxContinuousMissingMinutes = 0;
|
||||
boolean hasData = false;
|
||||
for (String statType : indicator.getSupportStats()) {
|
||||
Set<LocalDateTime> actualSlots = queryMergedActualSlots(lineId, indicator, harmonicOrder, statType, startTime, endTime, intervalMinutes);
|
||||
Set<LocalDateTime> effectiveActualSlots = retainExpectedSlots(slots, actualSlots);
|
||||
@@ -561,17 +590,14 @@ public class SteadyChecksquareServiceImpl implements SteadyChecksquareService {
|
||||
item.getStatDetails().add(detail);
|
||||
totalExpected += summary.getExpectedPointCount();
|
||||
totalActual += summary.getActualPointCount();
|
||||
maxContinuousMissingMinutes = Math.max(maxContinuousMissingMinutes, summary.getMaxContinuousMissingMinutes());
|
||||
hasData = hasData || Boolean.TRUE.equals(summary.getHasData());
|
||||
}
|
||||
|
||||
item.setHasData(hasData);
|
||||
item.setExpectedPointCount(totalExpected);
|
||||
item.setActualPointCount(totalActual);
|
||||
item.setMissingPointCount(Math.max(0, totalExpected - totalActual));
|
||||
item.setMissingRate(calculateRate(item.getMissingPointCount(), totalExpected));
|
||||
item.setMissingRateText(formatRateText(item.getMissingRate()));
|
||||
item.setMaxContinuousMissingMinutes(maxContinuousMissingMinutes);
|
||||
item.setDataIntegrity(calculateDataIntegrity(totalActual, totalExpected));
|
||||
item.setHasData(hasDataByIntegrity(item.getDataIntegrity()));
|
||||
item.setDataIntegrityText(formatRateText(item.getDataIntegrity()));
|
||||
fillValueOrderRuleResult(item, lineId, indicator, harmonicOrder, startTime, endTime, intervalMinutes);
|
||||
return item;
|
||||
}
|
||||
@@ -676,13 +702,12 @@ public class SteadyChecksquareServiceImpl implements SteadyChecksquareService {
|
||||
SteadyChecksquareStatSummaryVO summary = new SteadyChecksquareStatSummaryVO();
|
||||
summary.setStatType(statType);
|
||||
summary.setSupported(true);
|
||||
summary.setHasData(actualCount > 0);
|
||||
summary.setExpectedPointCount(expectedCount);
|
||||
summary.setActualPointCount(actualCount);
|
||||
summary.setMissingPointCount(Math.max(0, expectedCount - actualCount));
|
||||
summary.setMissingRate(calculateRate(summary.getMissingPointCount(), expectedCount));
|
||||
summary.setMissingRateText(formatRateText(summary.getMissingRate()));
|
||||
summary.setMaxContinuousMissingMinutes(calculator.maxContinuousMissingMinutes(segments));
|
||||
summary.setDataIntegrity(calculateDataIntegrity(actualCount, expectedCount));
|
||||
summary.setHasData(hasDataByIntegrity(summary.getDataIntegrity()));
|
||||
summary.setDataIntegrityText(formatRateText(summary.getDataIntegrity()));
|
||||
return summary;
|
||||
}
|
||||
|
||||
@@ -884,7 +909,7 @@ public class SteadyChecksquareServiceImpl implements SteadyChecksquareService {
|
||||
vo.setTaskStatus(task.getTaskStatus());
|
||||
vo.setItemCount(task.getItemCount());
|
||||
vo.setAbnormalItemCount(task.getAbnormalItemCount());
|
||||
vo.setMaxMissingRate(task.getMaxMissingRate());
|
||||
vo.setMinDataIntegrity(task.getMinDataIntegrity());
|
||||
vo.setCreateTime(formatTime(task.getCreateTime()));
|
||||
return vo;
|
||||
}
|
||||
@@ -915,9 +940,8 @@ public class SteadyChecksquareServiceImpl implements SteadyChecksquareService {
|
||||
vo.setExpectedPointCount(item.getExpectedPointCount());
|
||||
vo.setActualPointCount(item.getActualPointCount());
|
||||
vo.setMissingPointCount(item.getMissingPointCount());
|
||||
vo.setMissingRate(item.getMissingRate());
|
||||
vo.setMissingRateText(item.getMissingRateText());
|
||||
vo.setMaxContinuousMissingMinutes(item.getMaxContinuousMissingMinutes());
|
||||
vo.setDataIntegrity(item.getDataIntegrity());
|
||||
vo.setDataIntegrityText(item.getDataIntegrityText());
|
||||
vo.setAbnormal(toBoolean(item.getAbnormal()));
|
||||
vo.setAbnormalPointCount(item.getAbnormalPointCount());
|
||||
vo.setHarmonicParityAbnormal(toBoolean(item.getHarmonicParityAbnormal()));
|
||||
@@ -933,39 +957,34 @@ public class SteadyChecksquareServiceImpl implements SteadyChecksquareService {
|
||||
vo.setExpectedPointCount(summary.getExpectedPointCount());
|
||||
vo.setActualPointCount(summary.getActualPointCount());
|
||||
vo.setMissingPointCount(summary.getMissingPointCount());
|
||||
vo.setMissingRate(summary.getMissingRate());
|
||||
vo.setMissingRateText(summary.getMissingRateText());
|
||||
vo.setMaxContinuousMissingMinutes(summary.getMaxContinuousMissingMinutes());
|
||||
vo.setDataIntegrity(summary.getDataIntegrity());
|
||||
vo.setDataIntegrityText(summary.getDataIntegrityText());
|
||||
return vo;
|
||||
}
|
||||
|
||||
private int countAbnormalItems(List<SteadyChecksquareItemVO> items) {
|
||||
int count = 0;
|
||||
for (SteadyChecksquareItemVO item : items) {
|
||||
if (Boolean.TRUE.equals(item.getAbnormal()) || Boolean.TRUE.equals(item.getHarmonicParityAbnormal())) {
|
||||
if (!Boolean.TRUE.equals(item.getHasData())
|
||||
|| Boolean.TRUE.equals(item.getAbnormal())
|
||||
|| Boolean.TRUE.equals(item.getHarmonicParityAbnormal())) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
private BigDecimal maxMissingRate(List<SteadyChecksquareItemVO> items) {
|
||||
BigDecimal max = BigDecimal.ZERO.setScale(6, RoundingMode.HALF_UP);
|
||||
private BigDecimal minDataIntegrity(List<SteadyChecksquareItemVO> items) {
|
||||
BigDecimal min = BigDecimal.ONE.setScale(6, RoundingMode.HALF_UP);
|
||||
if (items == null || items.isEmpty()) {
|
||||
return BigDecimal.ZERO.setScale(6, RoundingMode.HALF_UP);
|
||||
}
|
||||
for (SteadyChecksquareItemVO item : items) {
|
||||
if (item.getMissingRate() != null && item.getMissingRate().compareTo(max) > 0) {
|
||||
max = item.getMissingRate();
|
||||
if (item.getDataIntegrity() != null && item.getDataIntegrity().compareTo(min) < 0) {
|
||||
min = item.getDataIntegrity();
|
||||
}
|
||||
}
|
||||
return max;
|
||||
}
|
||||
|
||||
private Boolean anyHasData(List<SteadyChecksquareItemVO> items) {
|
||||
for (SteadyChecksquareItemVO item : items) {
|
||||
if (Boolean.TRUE.equals(item.getHasData())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return min;
|
||||
}
|
||||
|
||||
private Boolean anyAbnormal(List<SteadyChecksquareItemVO> items) {
|
||||
@@ -986,15 +1005,6 @@ public class SteadyChecksquareServiceImpl implements SteadyChecksquareService {
|
||||
return false;
|
||||
}
|
||||
|
||||
private Boolean anySummaryHasData(List<SteadyChecksquareStatSummaryVO> summaries) {
|
||||
for (SteadyChecksquareStatSummaryVO summary : summaries) {
|
||||
if (Boolean.TRUE.equals(summary.getHasData())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private Integer averageInteger(List<SteadyChecksquareItemVO> items, String fieldName) {
|
||||
if (items == null || items.isEmpty()) {
|
||||
return 0;
|
||||
@@ -1025,24 +1035,24 @@ public class SteadyChecksquareServiceImpl implements SteadyChecksquareService {
|
||||
return new BigDecimal(total).divide(new BigDecimal(summaries.size()), 0, RoundingMode.HALF_UP).intValue();
|
||||
}
|
||||
|
||||
private BigDecimal averageRate(List<SteadyChecksquareItemVO> items) {
|
||||
private BigDecimal averageDataIntegrity(List<SteadyChecksquareItemVO> items) {
|
||||
if (items == null || items.isEmpty()) {
|
||||
return BigDecimal.ZERO.setScale(6, RoundingMode.HALF_UP);
|
||||
}
|
||||
BigDecimal total = BigDecimal.ZERO;
|
||||
for (SteadyChecksquareItemVO item : items) {
|
||||
total = total.add(item.getMissingRate() == null ? BigDecimal.ZERO : item.getMissingRate());
|
||||
total = total.add(item.getDataIntegrity() == null ? BigDecimal.ZERO : item.getDataIntegrity());
|
||||
}
|
||||
return total.divide(new BigDecimal(items.size()), 6, RoundingMode.HALF_UP);
|
||||
}
|
||||
|
||||
private BigDecimal averageSummaryRate(List<SteadyChecksquareStatSummaryVO> summaries) {
|
||||
private BigDecimal averageSummaryDataIntegrity(List<SteadyChecksquareStatSummaryVO> summaries) {
|
||||
if (summaries == null || summaries.isEmpty()) {
|
||||
return BigDecimal.ZERO.setScale(6, RoundingMode.HALF_UP);
|
||||
}
|
||||
BigDecimal total = BigDecimal.ZERO;
|
||||
for (SteadyChecksquareStatSummaryVO summary : summaries) {
|
||||
total = total.add(summary.getMissingRate() == null ? BigDecimal.ZERO : summary.getMissingRate());
|
||||
total = total.add(summary.getDataIntegrity() == null ? BigDecimal.ZERO : summary.getDataIntegrity());
|
||||
}
|
||||
return total.divide(new BigDecimal(summaries.size()), 6, RoundingMode.HALF_UP);
|
||||
}
|
||||
@@ -1057,9 +1067,6 @@ public class SteadyChecksquareServiceImpl implements SteadyChecksquareService {
|
||||
if ("missingPointCount".equals(fieldName)) {
|
||||
return nullToZero(item.getMissingPointCount());
|
||||
}
|
||||
if ("maxContinuousMissingMinutes".equals(fieldName)) {
|
||||
return nullToZero(item.getMaxContinuousMissingMinutes());
|
||||
}
|
||||
if ("abnormalPointCount".equals(fieldName)) {
|
||||
return nullToZero(item.getAbnormalPointCount());
|
||||
}
|
||||
@@ -1079,9 +1086,6 @@ public class SteadyChecksquareServiceImpl implements SteadyChecksquareService {
|
||||
if ("missingPointCount".equals(fieldName)) {
|
||||
return nullToZero(summary.getMissingPointCount());
|
||||
}
|
||||
if ("maxContinuousMissingMinutes".equals(fieldName)) {
|
||||
return nullToZero(summary.getMaxContinuousMissingMinutes());
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1089,11 +1093,15 @@ public class SteadyChecksquareServiceImpl implements SteadyChecksquareService {
|
||||
return value == null ? 0 : value;
|
||||
}
|
||||
|
||||
private BigDecimal calculateRate(int missingCount, int expectedCount) {
|
||||
private BigDecimal calculateDataIntegrity(int actualCount, int expectedCount) {
|
||||
if (expectedCount <= 0) {
|
||||
return BigDecimal.ZERO.setScale(6, RoundingMode.HALF_UP);
|
||||
}
|
||||
return new BigDecimal(missingCount).divide(new BigDecimal(expectedCount), 6, RoundingMode.HALF_UP);
|
||||
return new BigDecimal(actualCount).divide(new BigDecimal(expectedCount), 6, RoundingMode.HALF_UP);
|
||||
}
|
||||
|
||||
private boolean hasDataByIntegrity(BigDecimal dataIntegrity) {
|
||||
return dataIntegrity != null && dataIntegrity.compareTo(BigDecimal.ZERO) > 0;
|
||||
}
|
||||
|
||||
private String formatRateText(BigDecimal rate) {
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
CREATE TABLE IF NOT EXISTS `steady_checksquare_task` (
|
||||
`id` VARCHAR(64) NOT NULL COMMENT '主键',
|
||||
`task_no` VARCHAR(64) NOT NULL COMMENT '检测任务编号',
|
||||
`line_id` VARCHAR(64) NOT NULL COMMENT '监测点ID',
|
||||
`line_name` VARCHAR(255) NULL COMMENT '监测点名称',
|
||||
`time_start` DATETIME NOT NULL COMMENT '检测开始时间',
|
||||
`time_end` DATETIME NOT NULL COMMENT '检测结束时间',
|
||||
`interval_minutes` INT NULL COMMENT '默认统计间隔,单位分钟',
|
||||
`indicator_codes_json` JSON NULL COMMENT '请求指标编码列表',
|
||||
`indicator_codes_text` VARCHAR(2000) NULL COMMENT '请求指标编码检索文本,格式 |code1|code2|',
|
||||
`task_status` VARCHAR(32) NOT NULL DEFAULT 'SUCCESS' COMMENT '任务状态:SUCCESS/FAIL',
|
||||
`item_count` INT NOT NULL DEFAULT 0 COMMENT '检测项数量',
|
||||
`abnormal_item_count` INT NOT NULL DEFAULT 0 COMMENT '异常检测项数量',
|
||||
`min_data_integrity` DECIMAL(12,6) NOT NULL DEFAULT 0.000000 COMMENT '最低数据完整性',
|
||||
`result_message` VARCHAR(2000) NULL COMMENT '执行结果说明',
|
||||
`state` TINYINT NOT NULL DEFAULT 1 COMMENT '状态:0-删除,1-正常',
|
||||
`create_by` VARCHAR(64) NULL COMMENT '创建人',
|
||||
`create_time` DATETIME NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`update_by` VARCHAR(64) NULL COMMENT '更新人',
|
||||
`update_time` DATETIME NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_steady_checksquare_task_no` (`task_no`),
|
||||
KEY `idx_steady_checksquare_task_line_time` (`line_id`, `time_start`, `time_end`),
|
||||
KEY `idx_steady_checksquare_task_status` (`task_status`),
|
||||
KEY `idx_steady_checksquare_task_indicator_text` (`indicator_codes_text`(255)),
|
||||
KEY `idx_steady_checksquare_task_create_time` (`create_time`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='稳态数据校验任务表';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `steady_checksquare_item` (
|
||||
`id` VARCHAR(64) NOT NULL COMMENT '主键',
|
||||
`task_id` VARCHAR(64) NOT NULL COMMENT '检测任务ID',
|
||||
`item_key` VARCHAR(255) NOT NULL COMMENT '检测项唯一键',
|
||||
`indicator_code` VARCHAR(64) NOT NULL COMMENT '指标编码',
|
||||
`indicator_name` VARCHAR(255) NULL COMMENT '指标名称',
|
||||
`harmonic_order` INT NULL COMMENT '谐波次数;聚合项为空',
|
||||
`interval_minutes` INT NULL COMMENT '当前检测项统计间隔,单位分钟',
|
||||
`has_data` TINYINT NOT NULL DEFAULT 0 COMMENT '是否存在任意数据:0-否,1-是',
|
||||
`expected_point_count` INT NOT NULL DEFAULT 0 COMMENT '期望点数',
|
||||
`actual_point_count` INT NOT NULL DEFAULT 0 COMMENT '实际点数',
|
||||
`missing_point_count` INT NOT NULL DEFAULT 0 COMMENT '缺失点数',
|
||||
`data_integrity` DECIMAL(12,6) NOT NULL DEFAULT 0.000000 COMMENT '数据完整性',
|
||||
`data_integrity_text` VARCHAR(32) NULL COMMENT '数据完整性文本',
|
||||
`abnormal` TINYINT NOT NULL DEFAULT 0 COMMENT '指标值大小关系是否异常',
|
||||
`abnormal_point_count` INT NOT NULL DEFAULT 0 COMMENT '大小关系异常点数',
|
||||
`harmonic_parity_abnormal` TINYINT NOT NULL DEFAULT 0 COMMENT '谐波奇偶关系是否异常',
|
||||
`harmonic_parity_abnormal_point_count` INT NOT NULL DEFAULT 0 COMMENT '谐波奇偶关系异常点数',
|
||||
`state` TINYINT NOT NULL DEFAULT 1 COMMENT '状态:0-删除,1-正常',
|
||||
`create_time` DATETIME NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`update_time` DATETIME NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_steady_checksquare_item` (`task_id`, `item_key`),
|
||||
KEY `idx_steady_checksquare_item_indicator` (`indicator_code`),
|
||||
KEY `idx_steady_checksquare_item_abnormal` (`abnormal`, `harmonic_parity_abnormal`),
|
||||
KEY `idx_steady_checksquare_item_data_integrity` (`data_integrity`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='稳态数据校验检测项表';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `steady_checksquare_stat_summary` (
|
||||
`id` VARCHAR(64) NOT NULL COMMENT '主键',
|
||||
`item_id` VARCHAR(64) NOT NULL COMMENT '检测项ID',
|
||||
`stat_type` VARCHAR(16) NOT NULL COMMENT '统计类型:AVG/MAX/MIN/CP95',
|
||||
`supported` TINYINT NOT NULL DEFAULT 1 COMMENT '是否支持',
|
||||
`has_data` TINYINT NOT NULL DEFAULT 0 COMMENT '是否存在数据',
|
||||
`expected_point_count` INT NOT NULL DEFAULT 0 COMMENT '期望点数',
|
||||
`actual_point_count` INT NOT NULL DEFAULT 0 COMMENT '实际点数',
|
||||
`missing_point_count` INT NOT NULL DEFAULT 0 COMMENT '缺失点数',
|
||||
`data_integrity` DECIMAL(12,6) NOT NULL DEFAULT 0.000000 COMMENT '数据完整性',
|
||||
`data_integrity_text` VARCHAR(32) NULL COMMENT '数据完整性文本',
|
||||
`create_time` DATETIME NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_steady_checksquare_stat` (`item_id`, `stat_type`),
|
||||
KEY `idx_steady_checksquare_stat_type` (`stat_type`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='稳态数据校验统计摘要表';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `steady_checksquare_detail` (
|
||||
`id` VARCHAR(64) NOT NULL COMMENT '主键',
|
||||
`item_id` VARCHAR(64) NOT NULL COMMENT '检测项ID',
|
||||
`detail_type` VARCHAR(32) NOT NULL COMMENT '明细类型:SEGMENT/VALUE_ORDER/HARMONIC_PARITY',
|
||||
`stat_type` VARCHAR(16) NULL COMMENT '统计类型',
|
||||
`start_time` DATETIME NULL COMMENT '区间开始时间',
|
||||
`end_time` DATETIME NULL COMMENT '区间结束时间',
|
||||
`point_time` DATETIME NULL COMMENT '异常点时间',
|
||||
`segment_status` VARCHAR(16) NULL COMMENT '区间状态:NORMAL/MISSING',
|
||||
`missing_point_count` INT NULL COMMENT '缺失点数',
|
||||
`duration_minutes` INT NULL COMMENT '持续时长,单位分钟',
|
||||
`phase` VARCHAR(16) NULL COMMENT '相别',
|
||||
`harmonic_order` INT NULL COMMENT '谐波次数',
|
||||
`max_value` DECIMAL(24,8) NULL COMMENT '最大值',
|
||||
`min_value` DECIMAL(24,8) NULL COMMENT '最小值',
|
||||
`avg_value` DECIMAL(24,8) NULL COMMENT '平均值',
|
||||
`cp95_value` DECIMAL(24,8) NULL COMMENT 'CP95值',
|
||||
`even_harmonic_order` INT NULL COMMENT '偶次谐波次数',
|
||||
`even_value` DECIMAL(24,8) NULL COMMENT '偶次谐波值',
|
||||
`odd_harmonic_orders_json` JSON NULL COMMENT '参与比较的奇次谐波次数',
|
||||
`odd_values_json` JSON NULL COMMENT '参与比较的奇次谐波值',
|
||||
`odd_median_value` DECIMAL(24,8) NULL COMMENT '奇次谐波中位数',
|
||||
`threshold_multiplier` DECIMAL(12,6) NULL COMMENT '异常阈值倍数',
|
||||
`create_time` DATETIME NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_steady_checksquare_detail_item_type` (`item_id`, `detail_type`),
|
||||
KEY `idx_steady_checksquare_detail_point_time` (`point_time`),
|
||||
KEY `idx_steady_checksquare_detail_segment` (`start_time`, `end_time`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='稳态数据校验明细表';
|
||||
@@ -0,0 +1,14 @@
|
||||
ALTER TABLE `steady_checksquare_task`
|
||||
CHANGE COLUMN `max_missing_rate` `min_data_integrity` DECIMAL(12,6) NOT NULL DEFAULT 0.000000 COMMENT '最低数据完整性';
|
||||
|
||||
ALTER TABLE `steady_checksquare_item`
|
||||
DROP INDEX `idx_steady_checksquare_item_missing_rate`,
|
||||
CHANGE COLUMN `missing_rate` `data_integrity` DECIMAL(12,6) NOT NULL DEFAULT 0.000000 COMMENT '数据完整性',
|
||||
CHANGE COLUMN `missing_rate_text` `data_integrity_text` VARCHAR(32) NULL COMMENT '数据完整性文本',
|
||||
DROP COLUMN `max_continuous_missing_minutes`,
|
||||
ADD KEY `idx_steady_checksquare_item_data_integrity` (`data_integrity`);
|
||||
|
||||
ALTER TABLE `steady_checksquare_stat_summary`
|
||||
CHANGE COLUMN `missing_rate` `data_integrity` DECIMAL(12,6) NOT NULL DEFAULT 0.000000 COMMENT '数据完整性',
|
||||
CHANGE COLUMN `missing_rate_text` `data_integrity_text` VARCHAR(32) NULL COMMENT '数据完整性文本',
|
||||
DROP COLUMN `max_continuous_missing_minutes`;
|
||||
@@ -6,6 +6,7 @@ import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
|
||||
import java.util.List;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
@@ -34,5 +35,9 @@ class SteadyChecksquareControllerTest {
|
||||
String.class, String.class, String.class, Integer.class, Integer.class);
|
||||
GetMapping itemDetailMapping = itemDetailMethod.getAnnotation(GetMapping.class);
|
||||
Assertions.assertArrayEquals(new String[]{"/item-detail"}, itemDetailMapping.value());
|
||||
|
||||
Method deleteMethod = SteadyChecksquareController.class.getDeclaredMethod("delete", List.class);
|
||||
PostMapping deleteMapping = deleteMethod.getAnnotation(PostMapping.class);
|
||||
Assertions.assertArrayEquals(new String[]{"/delete"}, deleteMapping.value());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ import com.njcn.gather.steady.checksquare.service.SteadyChecksquareItemService;
|
||||
import com.njcn.gather.steady.checksquare.service.SteadyChecksquareStatSummaryService;
|
||||
import com.njcn.gather.steady.checksquare.service.SteadyChecksquareTaskService;
|
||||
import com.njcn.gather.steady.datavie.component.SteadyTrendIndicatorCatalog;
|
||||
import com.njcn.gather.steady.datavie.pojo.bo.SteadyTrendIndicatorDefinitionBO;
|
||||
import com.njcn.gather.steady.datavie.pojo.bo.SteadyTrendResolvedFieldBO;
|
||||
import com.njcn.gather.tool.adddata.component.AddDataTimeSlotCalculator;
|
||||
import com.njcn.gather.tool.addledger.pojo.vo.AddLedgerLinePathVO;
|
||||
@@ -437,6 +438,34 @@ class SteadyChecksquareServiceImplTest {
|
||||
verify(detailService).page(any(Page.class), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldDeleteTasksAndItemsLogically() {
|
||||
SteadyChecksquareTaskService taskService = mock(SteadyChecksquareTaskService.class);
|
||||
SteadyChecksquareItemService itemService = mock(SteadyChecksquareItemService.class);
|
||||
LambdaQueryChainWrapper<SteadyChecksquareTaskPO> taskQuery = mock(LambdaQueryChainWrapper.class);
|
||||
SteadyChecksquareTaskPO task = new SteadyChecksquareTaskPO();
|
||||
task.setId("task-001");
|
||||
task.setState(1);
|
||||
when(taskService.lambdaQuery()).thenReturn(taskQuery);
|
||||
when(taskQuery.in(any(), any(List.class))).thenReturn(taskQuery);
|
||||
when(taskQuery.eq(any(), any())).thenReturn(taskQuery);
|
||||
when(taskQuery.list()).thenReturn(Collections.singletonList(task));
|
||||
when(taskService.update(any())).thenReturn(true);
|
||||
when(itemService.update(any())).thenReturn(true);
|
||||
SteadyChecksquareServiceImpl service = new SteadyChecksquareServiceImpl(new SteadyTrendIndicatorCatalog(),
|
||||
mock(SteadyChecksquareInfluxQueryComponent.class), new SteadyChecksquareCalculator(),
|
||||
mock(SteadyChecksquareValueOrderRuleComponent.class), mock(SteadyChecksquareHarmonicParityRuleComponent.class),
|
||||
new AddDataTimeSlotCalculator(), mock(AddLedgerService.class), taskService,
|
||||
itemService, mock(SteadyChecksquareStatSummaryService.class),
|
||||
mock(SteadyChecksquareDetailService.class), new ObjectMapper());
|
||||
|
||||
boolean result = service.delete(Collections.singletonList("task-001"));
|
||||
|
||||
Assertions.assertTrue(result);
|
||||
verify(taskService).update(any());
|
||||
verify(itemService).update(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldSaveChecksquareResultsInBatch() {
|
||||
SteadyChecksquareTaskService taskService = mock(SteadyChecksquareTaskService.class);
|
||||
@@ -465,9 +494,8 @@ class SteadyChecksquareServiceImplTest {
|
||||
item.setExpectedPointCount(2);
|
||||
item.setActualPointCount(2);
|
||||
item.setMissingPointCount(0);
|
||||
item.setMissingRate(BigDecimal.ZERO.setScale(6));
|
||||
item.setMissingRateText("0.00%");
|
||||
item.setMaxContinuousMissingMinutes(0);
|
||||
item.setDataIntegrity(BigDecimal.ONE.setScale(6));
|
||||
item.setDataIntegrityText("100.00%");
|
||||
item.setAbnormal(false);
|
||||
item.setAbnormalPointCount(0);
|
||||
item.setHarmonicParityAbnormal(false);
|
||||
@@ -479,9 +507,8 @@ class SteadyChecksquareServiceImplTest {
|
||||
summary.setExpectedPointCount(2);
|
||||
summary.setActualPointCount(2);
|
||||
summary.setMissingPointCount(0);
|
||||
summary.setMissingRate(BigDecimal.ZERO.setScale(6));
|
||||
summary.setMissingRateText("0.00%");
|
||||
summary.setMaxContinuousMissingMinutes(0);
|
||||
summary.setDataIntegrity(BigDecimal.ONE.setScale(6));
|
||||
summary.setDataIntegrityText("100.00%");
|
||||
item.getStatSummaries().add(summary);
|
||||
result.getItems().add(item);
|
||||
|
||||
@@ -492,12 +519,87 @@ class SteadyChecksquareServiceImplTest {
|
||||
verify(statSummaryService).saveBatch(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldCountNoDataItemAsAbnormalWhenSavingTask() {
|
||||
SteadyChecksquareTaskService taskService = mock(SteadyChecksquareTaskService.class);
|
||||
SteadyChecksquareServiceImpl service = new SteadyChecksquareServiceImpl(new SteadyTrendIndicatorCatalog(),
|
||||
mock(SteadyChecksquareInfluxQueryComponent.class), new SteadyChecksquareCalculator(),
|
||||
mock(SteadyChecksquareValueOrderRuleComponent.class), mock(SteadyChecksquareHarmonicParityRuleComponent.class),
|
||||
new AddDataTimeSlotCalculator(), mock(AddLedgerService.class), taskService,
|
||||
mock(SteadyChecksquareItemService.class), mock(SteadyChecksquareStatSummaryService.class),
|
||||
mock(SteadyChecksquareDetailService.class), new ObjectMapper());
|
||||
SteadyChecksquareQueryParam param = new SteadyChecksquareQueryParam();
|
||||
param.setIndicatorCodes(Collections.singletonList("V_RMS"));
|
||||
SteadyChecksquareQueryVO result = new SteadyChecksquareQueryVO();
|
||||
result.setLineId("line-001");
|
||||
result.setLineName("进线一");
|
||||
result.setTimeStart("2026-05-01 00:00:00");
|
||||
result.setTimeEnd("2026-05-01 00:01:00");
|
||||
result.setIntervalMinutes(1);
|
||||
SteadyChecksquareItemVO item = new SteadyChecksquareItemVO();
|
||||
item.setItemKey("line-001|V_RMS");
|
||||
item.setIndicatorCode("V_RMS");
|
||||
item.setHasData(false);
|
||||
item.setExpectedPointCount(2);
|
||||
item.setActualPointCount(0);
|
||||
item.setMissingPointCount(2);
|
||||
item.setDataIntegrity(BigDecimal.ZERO.setScale(6));
|
||||
item.setDataIntegrityText("0.00%");
|
||||
item.setAbnormal(false);
|
||||
item.setHarmonicParityAbnormal(false);
|
||||
result.getItems().add(item);
|
||||
|
||||
saveResult(service, param, result);
|
||||
|
||||
ArgumentCaptor<SteadyChecksquareTaskPO> captor = ArgumentCaptor.forClass(SteadyChecksquareTaskPO.class);
|
||||
verify(taskService).save(captor.capture());
|
||||
Assertions.assertEquals(Integer.valueOf(1), captor.getValue().getAbnormalItemCount());
|
||||
Assertions.assertEquals(BigDecimal.ZERO.setScale(6), captor.getValue().getMinDataIntegrity());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldMarkAggregateHarmonicItemNoDataWhenDataIntegrityIsZero() {
|
||||
SteadyChecksquareServiceImpl service = newService();
|
||||
SteadyTrendIndicatorDefinitionBO indicator = buildHarmonicIndicator();
|
||||
SteadyChecksquareItemVO orderItem = buildOrderItem(true, BigDecimal.ZERO.setScale(6));
|
||||
orderItem.getStatSummaries().add(buildSummaryVO(true, BigDecimal.ZERO.setScale(6)));
|
||||
|
||||
SteadyChecksquareItemVO result = aggregateHarmonicItems(service, indicator, Collections.singletonList(orderItem));
|
||||
|
||||
Assertions.assertEquals(BigDecimal.ZERO.setScale(6), result.getDataIntegrity());
|
||||
Assertions.assertEquals(Boolean.FALSE, result.getHasData());
|
||||
Assertions.assertEquals(Boolean.FALSE, result.getStatSummaries().get(0).getHasData());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldMarkAggregateHarmonicItemHasDataWhenDataIntegrityIsGreaterThanZero() {
|
||||
SteadyChecksquareServiceImpl service = newService();
|
||||
SteadyTrendIndicatorDefinitionBO indicator = buildHarmonicIndicator();
|
||||
SteadyChecksquareItemVO orderItem = buildOrderItem(false, new BigDecimal("0.500000"));
|
||||
orderItem.getStatSummaries().add(buildSummaryVO(false, new BigDecimal("0.500000")));
|
||||
|
||||
SteadyChecksquareItemVO result = aggregateHarmonicItems(service, indicator, Collections.singletonList(orderItem));
|
||||
|
||||
Assertions.assertEquals(new BigDecimal("0.500000"), result.getDataIntegrity());
|
||||
Assertions.assertEquals(Boolean.TRUE, result.getHasData());
|
||||
Assertions.assertEquals(Boolean.TRUE, result.getStatSummaries().get(0).getHasData());
|
||||
}
|
||||
|
||||
private void assertItemInterval(SteadyChecksquareItemVO item, String indicatorCode, int intervalMinutes, int expectedPointCount) {
|
||||
Assertions.assertEquals(indicatorCode, item.getIndicatorCode());
|
||||
Assertions.assertEquals(Integer.valueOf(intervalMinutes), item.getIntervalMinutes());
|
||||
Assertions.assertEquals(Integer.valueOf(expectedPointCount), item.getExpectedPointCount());
|
||||
}
|
||||
|
||||
private SteadyChecksquareServiceImpl newService() {
|
||||
return new SteadyChecksquareServiceImpl(new SteadyTrendIndicatorCatalog(),
|
||||
mock(SteadyChecksquareInfluxQueryComponent.class), new SteadyChecksquareCalculator(),
|
||||
mock(SteadyChecksquareValueOrderRuleComponent.class), mock(SteadyChecksquareHarmonicParityRuleComponent.class),
|
||||
new AddDataTimeSlotCalculator(), mock(AddLedgerService.class), mock(SteadyChecksquareTaskService.class),
|
||||
mock(SteadyChecksquareItemService.class), mock(SteadyChecksquareStatSummaryService.class),
|
||||
mock(SteadyChecksquareDetailService.class), new ObjectMapper());
|
||||
}
|
||||
|
||||
private SteadyChecksquareQueryVO calculate(SteadyChecksquareServiceImpl service, SteadyChecksquareQueryParam param) {
|
||||
try {
|
||||
Method method = SteadyChecksquareServiceImpl.class.getDeclaredMethod("calculate", SteadyChecksquareQueryParam.class);
|
||||
@@ -519,6 +621,58 @@ class SteadyChecksquareServiceImplTest {
|
||||
}
|
||||
}
|
||||
|
||||
private SteadyChecksquareItemVO aggregateHarmonicItems(SteadyChecksquareServiceImpl service,
|
||||
SteadyTrendIndicatorDefinitionBO indicator,
|
||||
List<SteadyChecksquareItemVO> orderItems) {
|
||||
try {
|
||||
Method method = SteadyChecksquareServiceImpl.class.getDeclaredMethod("aggregateHarmonicItems",
|
||||
String.class, SteadyTrendIndicatorDefinitionBO.class, List.class, int.class);
|
||||
method.setAccessible(true);
|
||||
return (SteadyChecksquareItemVO) method.invoke(service, "line-001", indicator, orderItems, 1);
|
||||
} catch (Exception exception) {
|
||||
throw new RuntimeException(exception);
|
||||
}
|
||||
}
|
||||
|
||||
private SteadyTrendIndicatorDefinitionBO buildHarmonicIndicator() {
|
||||
SteadyTrendIndicatorDefinitionBO indicator = new SteadyTrendIndicatorDefinitionBO();
|
||||
indicator.setIndicatorCode("V_HARMONIC");
|
||||
indicator.setName("V_HARMONIC");
|
||||
indicator.setHarmonic(true);
|
||||
return indicator;
|
||||
}
|
||||
|
||||
private SteadyChecksquareItemVO buildOrderItem(boolean hasData, BigDecimal dataIntegrity) {
|
||||
SteadyChecksquareItemVO item = new SteadyChecksquareItemVO();
|
||||
item.setItemKey("line-001|V_HARMONIC|2");
|
||||
item.setIndicatorCode("V_HARMONIC");
|
||||
item.setIndicatorName("V_HARMONIC");
|
||||
item.setHarmonicOrder(2);
|
||||
item.setIntervalMinutes(1);
|
||||
item.setHasData(hasData);
|
||||
item.setExpectedPointCount(2);
|
||||
item.setActualPointCount(dataIntegrity.compareTo(BigDecimal.ZERO) > 0 ? 1 : 0);
|
||||
item.setMissingPointCount(dataIntegrity.compareTo(BigDecimal.ZERO) > 0 ? 1 : 2);
|
||||
item.setDataIntegrity(dataIntegrity);
|
||||
item.setAbnormal(false);
|
||||
item.setAbnormalPointCount(0);
|
||||
item.setHarmonicParityAbnormal(false);
|
||||
item.setHarmonicParityAbnormalPointCount(0);
|
||||
return item;
|
||||
}
|
||||
|
||||
private SteadyChecksquareStatSummaryVO buildSummaryVO(boolean hasData, BigDecimal dataIntegrity) {
|
||||
SteadyChecksquareStatSummaryVO summary = new SteadyChecksquareStatSummaryVO();
|
||||
summary.setStatType("AVG");
|
||||
summary.setSupported(true);
|
||||
summary.setHasData(hasData);
|
||||
summary.setExpectedPointCount(2);
|
||||
summary.setActualPointCount(dataIntegrity.compareTo(BigDecimal.ZERO) > 0 ? 1 : 0);
|
||||
summary.setMissingPointCount(dataIntegrity.compareTo(BigDecimal.ZERO) > 0 ? 1 : 2);
|
||||
summary.setDataIntegrity(dataIntegrity);
|
||||
return summary;
|
||||
}
|
||||
|
||||
private SteadyChecksquareItemPO buildItemPO(String itemId, String indicatorCode) {
|
||||
SteadyChecksquareItemPO item = new SteadyChecksquareItemPO();
|
||||
item.setId(itemId);
|
||||
@@ -529,8 +683,7 @@ class SteadyChecksquareServiceImplTest {
|
||||
item.setExpectedPointCount(1);
|
||||
item.setActualPointCount(1);
|
||||
item.setMissingPointCount(0);
|
||||
item.setMissingRate(BigDecimal.ZERO.setScale(6));
|
||||
item.setMaxContinuousMissingMinutes(0);
|
||||
item.setDataIntegrity(BigDecimal.ONE.setScale(6));
|
||||
item.setAbnormal(0);
|
||||
item.setAbnormalPointCount(0);
|
||||
item.setHarmonicParityAbnormal(0);
|
||||
@@ -547,8 +700,7 @@ class SteadyChecksquareServiceImplTest {
|
||||
summary.setExpectedPointCount(1);
|
||||
summary.setActualPointCount(1);
|
||||
summary.setMissingPointCount(0);
|
||||
summary.setMissingRate(BigDecimal.ZERO.setScale(6));
|
||||
summary.setMaxContinuousMissingMinutes(0);
|
||||
summary.setDataIntegrity(BigDecimal.ONE.setScale(6));
|
||||
return summary;
|
||||
}
|
||||
|
||||
|
||||
522
steady/steady-DataView/steady-checksquare-api-debug_20260610.md
Normal file
522
steady/steady-DataView/steady-checksquare-api-debug_20260610.md
Normal file
@@ -0,0 +1,522 @@
|
||||
# 数据校验 API 调试文档
|
||||
|
||||
## 1. 基础信息
|
||||
|
||||
- 模块:`steady/steady-DataView`
|
||||
- 控制器:`com.njcn.gather.steady.checksquare.controller.SteadyChecksquareController`
|
||||
- 接口前缀:`/steady/data-view/checksquare`
|
||||
- 本地默认地址:`http://localhost:18192`
|
||||
- Content-Type:`application/json`
|
||||
- 认证:除登录和 Swagger 资源外,请求需要携带登录后的 `Authorization` 请求头。
|
||||
- 数据库结果表:`steady_checksquare_task`、`steady_checksquare_item`、`steady_checksquare_stat_summary`、`steady_checksquare_detail`
|
||||
|
||||
通用请求头:
|
||||
|
||||
```http
|
||||
Authorization: Bearer <token>
|
||||
Content-Type: application/json
|
||||
```
|
||||
|
||||
通用返回结构:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "success",
|
||||
"data": {}
|
||||
}
|
||||
```
|
||||
|
||||
> 实际 `code`、`message` 字段以项目公共 `HttpResult` 封装为准,下面示例重点展示 `data` 内容。
|
||||
|
||||
## 2. 查询数据校验历史记录
|
||||
|
||||
- 方法:`POST`
|
||||
- 路径:`/steady/data-view/checksquare/query`
|
||||
- 返回:`HttpResult<Page<SteadyChecksquareTaskVO>>`
|
||||
- 说明:分页查询已落库的数据校验任务,按创建时间倒序返回。
|
||||
|
||||
请求示例:
|
||||
|
||||
```json
|
||||
{
|
||||
"pageNum": 1,
|
||||
"pageSize": 10,
|
||||
"lineId": "line-001",
|
||||
"indicatorCode": "V_RMS",
|
||||
"timeStart": "2026-05-01 00:00:00",
|
||||
"timeEnd": "2026-05-01 23:59:59",
|
||||
"hasAbnormal": true
|
||||
}
|
||||
```
|
||||
|
||||
请求字段:
|
||||
|
||||
| 字段 | 必填 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `pageNum` | 否 | 当前页码,继承自 `BaseParam` |
|
||||
| `pageSize` | 否 | 每页数量,继承自 `BaseParam` |
|
||||
| `lineId` | 否 | 监测点 ID,精确匹配 |
|
||||
| `indicatorCode` | 否 | 指标编码,按任务指标集合匹配 |
|
||||
| `timeStart` | 否 | 检测开始时间下限,格式 `yyyy-MM-dd HH:mm:ss` |
|
||||
| `timeEnd` | 否 | 检测结束时间上限,格式 `yyyy-MM-dd HH:mm:ss` |
|
||||
| `hasAbnormal` | 否 | 是否只查询存在异常项的任务;`true` 表示 `abnormalItemCount > 0` |
|
||||
|
||||
返回示例:
|
||||
|
||||
```json
|
||||
{
|
||||
"records": [
|
||||
{
|
||||
"taskId": "8f7a4d6d1f3145a88b6f9d7a8e6c1001",
|
||||
"taskNo": "CS202606101630001",
|
||||
"lineId": "line-001",
|
||||
"lineName": "进线一",
|
||||
"timeStart": "2026-05-01 00:00:00",
|
||||
"timeEnd": "2026-05-01 23:59:59",
|
||||
"intervalMinutes": 1,
|
||||
"taskStatus": "SUCCESS",
|
||||
"itemCount": 2,
|
||||
"abnormalItemCount": 1,
|
||||
"minDataIntegrity": 0.999306,
|
||||
"createTime": "2026-06-10 16:30:00"
|
||||
}
|
||||
],
|
||||
"total": 1,
|
||||
"size": 10,
|
||||
"current": 1,
|
||||
"pages": 1
|
||||
}
|
||||
```
|
||||
|
||||
cURL 示例:
|
||||
|
||||
```bash
|
||||
curl -X POST "http://localhost:18192/steady/data-view/checksquare/query" \
|
||||
-H "Authorization: Bearer <token>" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"pageNum":1,"pageSize":10,"lineId":"line-001","indicatorCode":"V_RMS","hasAbnormal":true}'
|
||||
```
|
||||
|
||||
## 3. 新增数据校验记录
|
||||
|
||||
- 方法:`POST`
|
||||
- 路径:`/steady/data-view/checksquare/create`
|
||||
- 返回:`HttpResult<SteadyChecksquareCreateVO>`
|
||||
- 说明:按监测点、指标和时间范围实时查询 InfluxDB,执行缺数校验、指标值大小关系校验、谐波奇偶关系校验,并将任务、检测项、统计摘要和明细写入 MySQL。
|
||||
|
||||
请求示例:
|
||||
|
||||
```json
|
||||
{
|
||||
"lineId": "line-001",
|
||||
"indicatorCodes": ["V_RMS", "FREQ", "V_HARMONIC"],
|
||||
"timeStart": "2026-05-01 00:00:00",
|
||||
"timeEnd": "2026-05-01 23:59:59"
|
||||
}
|
||||
```
|
||||
|
||||
请求字段:
|
||||
|
||||
| 字段 | 必填 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `lineId` | 是 | 监测点 ID,需要能在台账中找到且处于可用状态 |
|
||||
| `indicatorCodes` | 是 | 指标编码列表,来自 `/steady/data-view/indicator-tree` |
|
||||
| `timeStart` | 是 | 检测开始时间,格式 `yyyy-MM-dd HH:mm:ss` |
|
||||
| `timeEnd` | 是 | 检测结束时间,格式 `yyyy-MM-dd HH:mm:ss` |
|
||||
|
||||
返回示例:
|
||||
|
||||
```json
|
||||
{
|
||||
"taskId": "8f7a4d6d1f3145a88b6f9d7a8e6c1001",
|
||||
"taskNo": "CS202606101630001",
|
||||
"lineId": "line-001",
|
||||
"lineName": "进线一",
|
||||
"timeStart": "2026-05-01 00:00:00",
|
||||
"timeEnd": "2026-05-01 23:59:59",
|
||||
"intervalMinutes": 1,
|
||||
"itemCount": 3,
|
||||
"abnormalItemCount": 1
|
||||
}
|
||||
```
|
||||
|
||||
cURL 示例:
|
||||
|
||||
```bash
|
||||
curl -X POST "http://localhost:18192/steady/data-view/checksquare/create" \
|
||||
-H "Authorization: Bearer <token>" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"lineId":"line-001","indicatorCodes":["V_RMS","FREQ","V_HARMONIC"],"timeStart":"2026-05-01 00:00:00","timeEnd":"2026-05-01 23:59:59"}'
|
||||
```
|
||||
|
||||
调试建议:
|
||||
|
||||
- 新增接口会实际写入 MySQL,重复调用会生成新的任务记录。
|
||||
- 时间范围越大、指标越多,InfluxDB 查询和明细落库耗时越高。
|
||||
- 谐波类指标固定按 2-50 次聚合检测,不需要在请求体传 `harmonicOrders`。
|
||||
|
||||
## 4. 查询数据校验任务详情
|
||||
|
||||
- 方法:`GET`
|
||||
- 路径:`/steady/data-view/checksquare/detail`
|
||||
- 返回:`HttpResult<SteadyChecksquareQueryVO>`
|
||||
- 说明:按任务 ID 查询任务基础信息、检测项列表和各检测项统计摘要。检测项的明细列表通过 `/item-detail` 按需查询。
|
||||
|
||||
请求参数:
|
||||
|
||||
| 参数 | 必填 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `taskId` | 是 | 数据校验任务 ID,即 `/query` 或 `/create` 返回的 `taskId` |
|
||||
|
||||
请求示例:
|
||||
|
||||
```http
|
||||
GET /steady/data-view/checksquare/detail?taskId=8f7a4d6d1f3145a88b6f9d7a8e6c1001
|
||||
```
|
||||
|
||||
返回示例:
|
||||
|
||||
```json
|
||||
{
|
||||
"taskId": "8f7a4d6d1f3145a88b6f9d7a8e6c1001",
|
||||
"taskNo": "CS202606101630001",
|
||||
"lineId": "line-001",
|
||||
"lineName": "进线一",
|
||||
"timeStart": "2026-05-01 00:00:00",
|
||||
"timeEnd": "2026-05-01 23:59:59",
|
||||
"intervalMinutes": 1,
|
||||
"items": [
|
||||
{
|
||||
"itemId": "0f4b6d6c3e9d400a902a2df101d10001",
|
||||
"itemKey": "line-001|V_RMS",
|
||||
"indicatorCode": "V_RMS",
|
||||
"indicatorName": "相电压有效值",
|
||||
"harmonicOrder": null,
|
||||
"intervalMinutes": 1,
|
||||
"hasData": true,
|
||||
"expectedPointCount": 5760,
|
||||
"actualPointCount": 5756,
|
||||
"missingPointCount": 4,
|
||||
"dataIntegrity": 0.999306,
|
||||
"dataIntegrityText": "99.93%",
|
||||
"abnormal": true,
|
||||
"abnormalPointCount": 2,
|
||||
"harmonicParityAbnormal": false,
|
||||
"harmonicParityAbnormalPointCount": 0,
|
||||
"statSummaries": [
|
||||
{
|
||||
"statType": "AVG",
|
||||
"supported": true,
|
||||
"hasData": true,
|
||||
"expectedPointCount": 1440,
|
||||
"actualPointCount": 1439,
|
||||
"missingPointCount": 1,
|
||||
"dataIntegrity": 0.999306,
|
||||
"dataIntegrityText": "99.93%",
|
||||
}
|
||||
],
|
||||
"statDetails": []
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
cURL 示例:
|
||||
|
||||
```bash
|
||||
curl -X GET "http://localhost:18192/steady/data-view/checksquare/detail?taskId=8f7a4d6d1f3145a88b6f9d7a8e6c1001" \
|
||||
-H "Authorization: Bearer <token>"
|
||||
```
|
||||
|
||||
## 5. 查询检测项明细
|
||||
|
||||
- 方法:`GET`
|
||||
- 路径:`/steady/data-view/checksquare/item-detail`
|
||||
- 返回:`HttpResult<SteadyChecksquareItemDetailVO>`
|
||||
- 说明:按检测项 ID、明细类型查询缺数连续区间、指标值大小关系异常点或谐波奇偶关系异常点。`pageNum` 和 `pageSize` 未同时传入时保持全量返回;两者同时传入且均大于 0 时返回当前页明细,并在结果中带回分页元数据。
|
||||
|
||||
请求参数:
|
||||
|
||||
| 参数 | 必填 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `itemId` | 是 | 检测项 ID,即任务详情中每个 `items[].itemId` |
|
||||
| `detailType` | 是 | 明细类型:`SEGMENT`、`VALUE_ORDER`、`HARMONIC_PARITY` |
|
||||
| `statType` | 否 | 统计类型:`AVG`、`MAX`、`MIN`、`CP95`;查询缺数区间时建议传入 |
|
||||
| `pageNum` | 否 | 明细分页页码;与 `pageSize` 同时传入且大于 0 时启用分页 |
|
||||
| `pageSize` | 否 | 明细分页条数;与 `pageNum` 同时传入且大于 0 时启用分页 |
|
||||
|
||||
### 5.1 查询缺数连续区间
|
||||
|
||||
请求示例:
|
||||
|
||||
```http
|
||||
GET /steady/data-view/checksquare/item-detail?itemId=0f4b6d6c3e9d400a902a2df101d10001&detailType=SEGMENT&statType=AVG
|
||||
```
|
||||
|
||||
返回示例:
|
||||
|
||||
```json
|
||||
{
|
||||
"itemId": "0f4b6d6c3e9d400a902a2df101d10001",
|
||||
"detailType": "SEGMENT",
|
||||
"statType": "AVG",
|
||||
"pageNum": null,
|
||||
"pageSize": null,
|
||||
"total": null,
|
||||
"segments": [
|
||||
{
|
||||
"startTime": "2026-05-01 00:00:00",
|
||||
"endTime": "2026-05-01 00:09:00",
|
||||
"status": "NORMAL",
|
||||
"harmonicOrder": null,
|
||||
"missingPointCount": 0,
|
||||
"durationMinutes": 10
|
||||
},
|
||||
{
|
||||
"startTime": "2026-05-01 00:10:00",
|
||||
"endTime": "2026-05-01 00:10:00",
|
||||
"status": "MISSING",
|
||||
"harmonicOrder": null,
|
||||
"missingPointCount": 1,
|
||||
"durationMinutes": 1
|
||||
}
|
||||
],
|
||||
"valueOrderDetails": [],
|
||||
"harmonicParityDetails": []
|
||||
}
|
||||
```
|
||||
|
||||
### 5.2 查询指标值大小关系异常明细
|
||||
|
||||
请求示例:
|
||||
|
||||
```http
|
||||
GET /steady/data-view/checksquare/item-detail?itemId=0f4b6d6c3e9d400a902a2df101d10001&detailType=VALUE_ORDER&pageNum=1&pageSize=20
|
||||
```
|
||||
|
||||
返回示例:
|
||||
|
||||
```json
|
||||
{
|
||||
"itemId": "0f4b6d6c3e9d400a902a2df101d10001",
|
||||
"detailType": "VALUE_ORDER",
|
||||
"statType": null,
|
||||
"pageNum": 1,
|
||||
"pageSize": 20,
|
||||
"total": 1,
|
||||
"segments": [],
|
||||
"valueOrderDetails": [
|
||||
{
|
||||
"time": "2026-05-01 00:10:00",
|
||||
"phase": "A",
|
||||
"harmonicOrder": null,
|
||||
"maxValue": 219.8,
|
||||
"minValue": 218.1,
|
||||
"avgValue": 219.0,
|
||||
"cp95Value": 219.8
|
||||
}
|
||||
],
|
||||
"harmonicParityDetails": []
|
||||
}
|
||||
```
|
||||
|
||||
### 5.3 查询谐波奇偶关系异常明细
|
||||
|
||||
请求示例:
|
||||
|
||||
```http
|
||||
GET /steady/data-view/checksquare/item-detail?itemId=0f4b6d6c3e9d400a902a2df101d10002&detailType=HARMONIC_PARITY&pageNum=1&pageSize=20
|
||||
```
|
||||
|
||||
返回示例:
|
||||
|
||||
```json
|
||||
{
|
||||
"itemId": "0f4b6d6c3e9d400a902a2df101d10002",
|
||||
"detailType": "HARMONIC_PARITY",
|
||||
"statType": null,
|
||||
"pageNum": 1,
|
||||
"pageSize": 20,
|
||||
"total": 1,
|
||||
"segments": [],
|
||||
"valueOrderDetails": [],
|
||||
"harmonicParityDetails": [
|
||||
{
|
||||
"time": "2026-05-01 00:10:00",
|
||||
"phase": "A",
|
||||
"statType": "AVG",
|
||||
"evenHarmonicOrder": 4,
|
||||
"evenValue": 0.32,
|
||||
"oddHarmonicOrders": [3, 5],
|
||||
"oddValues": [0.08, 0.09],
|
||||
"oddMedianValue": 0.085,
|
||||
"thresholdMultiplier": 3.0
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
cURL 示例:
|
||||
|
||||
```bash
|
||||
curl -X GET "http://localhost:18192/steady/data-view/checksquare/item-detail?itemId=0f4b6d6c3e9d400a902a2df101d10001&detailType=VALUE_ORDER" \
|
||||
-H "Authorization: Bearer <token>"
|
||||
```
|
||||
|
||||
分页 cURL 示例:
|
||||
|
||||
```bash
|
||||
curl -X GET "http://localhost:18192/steady/data-view/checksquare/item-detail?itemId=0f4b6d6c3e9d400a902a2df101d10001&detailType=VALUE_ORDER&pageNum=1&pageSize=20" \
|
||||
-H "Authorization: Bearer <token>"
|
||||
```
|
||||
|
||||
## 6. 删除数据校验任务
|
||||
|
||||
- 方法:`POST`
|
||||
- 路径:`/steady/data-view/checksquare/delete`
|
||||
- 返回:`HttpResult<Boolean>`
|
||||
- 说明:按任务 ID 批量逻辑删除数据校验任务,并同步将任务下检测项置为删除态。删除后历史查询不再返回该任务,详情和检测项明细会按不存在处理。
|
||||
|
||||
请求示例:
|
||||
|
||||
```json
|
||||
[
|
||||
"8f7a4d6d1f3145a88b6f9d7a8e6c1001"
|
||||
]
|
||||
```
|
||||
|
||||
请求字段:
|
||||
|
||||
| 字段 | 必填 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| 请求体 | 是 | 数据校验任务 ID 数组 |
|
||||
|
||||
返回示例:
|
||||
|
||||
```json
|
||||
true
|
||||
```
|
||||
|
||||
cURL 示例:
|
||||
|
||||
```bash
|
||||
curl -X POST "http://localhost:18192/steady/data-view/checksquare/delete" \
|
||||
-H "Authorization: Bearer <token>" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '["8f7a4d6d1f3145a88b6f9d7a8e6c1001"]'
|
||||
```
|
||||
|
||||
## 7. 校验规则说明
|
||||
|
||||
### 7.1 缺数校验
|
||||
|
||||
- 按检测项支持的统计类型分别查询实际存在的时间点。
|
||||
- 返回期望点数、实际点数、缺失点数、数据完整性和最大连续缺失时长。
|
||||
- `SEGMENT` 明细按连续区间返回,`status` 可取 `NORMAL`、`MISSING`。
|
||||
- `FLUC`、`PST` 固定按 10 分钟间隔校验,`PLT` 固定按 120 分钟间隔校验,其余指标按监测点统计间隔校验。
|
||||
|
||||
### 7.2 指标值大小关系校验
|
||||
|
||||
- 同一时间点、同一指标、同一相别需要满足 `MAX >= CP95 >= AVG >= MIN`。
|
||||
- 缺少 `MAX`、`CP95`、`AVG`、`MIN` 任一值的时间点不计入大小关系异常,由缺数校验体现。
|
||||
- 异常点写入 `VALUE_ORDER` 明细,前端可通过 `item-detail` 拉取后弹窗展示。
|
||||
|
||||
### 7.3 谐波奇偶关系校验
|
||||
|
||||
- 谐波指标固定聚合 2-50 次检测。
|
||||
- 偶次谐波值需要大于 `0.1` 才继续参与奇偶关系检测。
|
||||
- 异常点写入 `HARMONIC_PARITY` 明细,包含偶次谐波值、参与比较的奇次谐波值、中位数和阈值倍数。
|
||||
|
||||
## 8. 字段说明
|
||||
|
||||
### 8.1 任务字段
|
||||
|
||||
| 字段 | 说明 |
|
||||
| --- | --- |
|
||||
| `taskId` | 数据校验任务 ID |
|
||||
| `taskNo` | 数据校验任务编号 |
|
||||
| `lineId` | 监测点 ID |
|
||||
| `lineName` | 监测点名称 |
|
||||
| `timeStart` | 检测开始时间 |
|
||||
| `timeEnd` | 检测结束时间 |
|
||||
| `intervalMinutes` | 监测点统计间隔,单位分钟 |
|
||||
| `taskStatus` | 任务状态,当前成功任务为 `SUCCESS` |
|
||||
| `itemCount` | 检测项数量 |
|
||||
| `abnormalItemCount` | 存在异常的检测项数量 |
|
||||
| `minDataIntegrity` | 检测项中的最低数据完整性 |
|
||||
| `createTime` | 任务创建时间 |
|
||||
|
||||
### 8.2 检测项字段
|
||||
|
||||
| 字段 | 说明 |
|
||||
| --- | --- |
|
||||
| `itemId` | 检测项 ID |
|
||||
| `itemKey` | 检测项唯一键 |
|
||||
| `indicatorCode` | 指标编码 |
|
||||
| `indicatorName` | 指标名称 |
|
||||
| `harmonicOrder` | 谐波次数;聚合检测项为空 |
|
||||
| `hasData` | 当前检测项是否存在任意数据 |
|
||||
| `expectedPointCount` | 期望点数 |
|
||||
| `actualPointCount` | 实际点数 |
|
||||
| `missingPointCount` | 缺失点数 |
|
||||
| `dataIntegrity` | 数据完整性数值 |
|
||||
| `dataIntegrityText` | 数据完整性文本 |
|
||||
| `abnormal` | 指标值大小关系是否异常 |
|
||||
| `abnormalPointCount` | 指标值大小关系异常点数 |
|
||||
| `harmonicParityAbnormal` | 谐波奇偶关系是否异常 |
|
||||
| `harmonicParityAbnormalPointCount` | 谐波奇偶关系异常点数 |
|
||||
| `statSummaries` | 各统计类型缺数摘要 |
|
||||
| `statDetails` | 兼容字段;明细建议通过 `/item-detail` 按需查询 |
|
||||
|
||||
### 8.3 检测项明细字段
|
||||
|
||||
| 字段 | 说明 |
|
||||
| --- | --- |
|
||||
| `itemId` | 检测项 ID |
|
||||
| `detailType` | 明细类型:`SEGMENT`、`VALUE_ORDER`、`HARMONIC_PARITY` |
|
||||
| `statType` | 统计类型;未传入或当前明细类型不按统计类型过滤时为空 |
|
||||
| `pageNum` | 当前页码;未启用分页时为空 |
|
||||
| `pageSize` | 每页条数;未启用分页时为空 |
|
||||
| `total` | 当前查询条件下的总明细数;未启用分页时为空 |
|
||||
| `segments` | 缺数连续区间,仅 `SEGMENT` 查询返回数据 |
|
||||
| `valueOrderDetails` | 指标值大小关系异常点,仅 `VALUE_ORDER` 查询返回数据 |
|
||||
| `harmonicParityDetails` | 谐波奇偶关系异常点,仅 `HARMONIC_PARITY` 查询返回数据 |
|
||||
|
||||
## 9. 常见错误场景
|
||||
|
||||
| 场景 | 后端提示 |
|
||||
| --- | --- |
|
||||
| 新增时 `lineId` 为空 | `监测点 ID 不能为空` |
|
||||
| 新增时 `indicatorCodes` 为空 | `指标不能为空` |
|
||||
| 新增时开始时间为空 | `开始时间不能为空` |
|
||||
| 新增时结束时间为空 | `结束时间不能为空` |
|
||||
| 开始时间大于结束时间 | `开始时间不能大于结束时间` |
|
||||
| 时间格式不正确 | `时间格式不正确,仅支持 yyyy-MM-dd HH:mm:ss` |
|
||||
| 监测点不存在或不可用 | `监测点不存在或不可用` |
|
||||
| 指标编码不支持 | `稳态指标不支持:xxx` |
|
||||
| 任务不存在或已删除 | `数据校验任务不存在` |
|
||||
| 检测项不存在或已删除 | `数据校验检测项不存在` |
|
||||
| 删除时任务 ID 为空 | `数据校验任务 ID 不能为空` |
|
||||
| 删除时任务不存在或已删除 | `数据校验任务不存在或已删除` |
|
||||
| 明细类型为空 | `明细类型不能为空` |
|
||||
| 明细类型不支持 | `明细类型不支持:xxx` |
|
||||
|
||||
## 10. 调试流程建议
|
||||
|
||||
1. 调用 `/steady/data-view/ledger-tree` 获取可用监测点,取叶子节点 `lineId`。
|
||||
2. 调用 `/steady/data-view/indicator-tree` 获取可用指标编码。
|
||||
3. 调用 `/steady/data-view/checksquare/create` 新增校验任务。
|
||||
4. 使用返回的 `taskId` 调用 `/steady/data-view/checksquare/detail` 查看任务详情和检测项列表。
|
||||
5. 使用 `items[].itemId` 调用 `/steady/data-view/checksquare/item-detail` 查看缺数区间或异常点明细。
|
||||
6. 调用 `/steady/data-view/checksquare/query` 验证任务已落库,并按监测点、指标、时间和异常状态筛选历史记录。
|
||||
7. 调用 `/steady/data-view/checksquare/delete` 删除任务后,再调用 `/query`、`/detail` 验证任务已不可见。
|
||||
|
||||
## 11. 环境依赖
|
||||
|
||||
- MySQL:需要已初始化 4 张 `steady_checksquare_*` 结果表。
|
||||
- InfluxDB:新增校验任务时需要可访问 `steady.influxdb` 配置的时序库。
|
||||
- 台账:新增校验任务时需要 `lineId` 能在台账中解析到监测点路径和统计间隔。
|
||||
- 指标目录:`indicatorCodes` 必须来自后端趋势指标目录。
|
||||
|
||||
|
||||
Reference in New Issue
Block a user