feat(icd): 完善ICD映射管理功能
- 在AuthGlobalFilter中添加稳态检验相关接口的免认证路径 - 修改CsDevTypeMapper.xml移除icdPath字段返回避免数据冗余 - 在CsIcdPathController中新增查询参照ICD列表和ICD校验详情接口 - 更新CsIcdPathMapper添加selectReferenceIcdPathList等方法实现 - 移除CsIcdPath相关实体和参数中的path字段简化数据结构 - 扩展ICD类型定义支持手动录入和上游解析的标准/非标准分类 - 重构激活标准ICD逻辑支持不同类型间的正确转换 - 新增ICD一致性校验排除规则跳过特定描述的DOI项检查 - 优化报告映射规则应用逻辑提升校验准确性 - 添加去除重复DOI项功能确保数据唯一性
This commit is contained in:
@@ -148,6 +148,65 @@ public class SteadyChecksquareInfluxQueryComponent {
|
||||
return result;
|
||||
}
|
||||
|
||||
public Map<String, List<SteadyChecksquareValuePointBO>> queryStatValuePointMap(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 (!hasValueTypeTag(fields.get(0).getMeasurement())) {
|
||||
for (SteadyTrendResolvedFieldBO field : fields) {
|
||||
result.put(resolveValueType(field.getStatType()), 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 statType = resolveValueType(field.getStatType());
|
||||
String cacheKey = buildCacheKey(buildValuePointQuery(field, startTime, endTime), intervalMinutes);
|
||||
if (cache != null && cache.containsKey(cacheKey)) {
|
||||
result.put(statType, new ArrayList<SteadyChecksquareValuePointBO>(cache.get(cacheKey)));
|
||||
} else {
|
||||
missingFields.add(field);
|
||||
}
|
||||
}
|
||||
if (!missingFields.isEmpty()) {
|
||||
String query = buildStatValuePointQuery(missingFields, startTime, endTime);
|
||||
long startMillis = System.currentTimeMillis();
|
||||
SteadyTrendResolvedFieldBO first = missingFields.get(0);
|
||||
log.info("数据校验指标值 InfluxDB 统计类型批量查询开始,measurement={},field={},statCount={},lineId={},phase={},query={}",
|
||||
first.getMeasurement(), first.getField(), missingFields.size(), first.getLineId(), first.getPhase(), query);
|
||||
try {
|
||||
Map<String, List<SteadyChecksquareValuePointBO>> queried =
|
||||
queryStatValuePointsByWindow(missingFields, startTime, endTime, intervalMinutes);
|
||||
for (SteadyTrendResolvedFieldBO field : missingFields) {
|
||||
String statType = resolveValueType(field.getStatType());
|
||||
List<SteadyChecksquareValuePointBO> points = queried.get(statType);
|
||||
if (points == null) {
|
||||
points = new ArrayList<SteadyChecksquareValuePointBO>();
|
||||
}
|
||||
result.put(statType, points);
|
||||
if (cache != null) {
|
||||
String cacheKey = buildCacheKey(buildValuePointQuery(field, startTime, endTime), intervalMinutes);
|
||||
cache.put(cacheKey, new ArrayList<SteadyChecksquareValuePointBO>(points));
|
||||
}
|
||||
}
|
||||
log.info("数据校验指标值 InfluxDB 统计类型批量查询结束,statCount={},costMs={}",
|
||||
missingFields.size(), System.currentTimeMillis() - startMillis);
|
||||
} catch (RuntimeException ex) {
|
||||
log.warn("数据校验指标值 InfluxDB 统计类型批量查询异常,statCount={},costMs={},error={}",
|
||||
missingFields.size(), System.currentTimeMillis() - startMillis, ex.getMessage());
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public String buildChecksquareQuery(SteadyTrendResolvedFieldBO field, LocalDateTime startTime, LocalDateTime endTime) {
|
||||
return buildValuePointQuery(field, startTime, endTime);
|
||||
}
|
||||
@@ -197,6 +256,33 @@ public class SteadyChecksquareInfluxQueryComponent {
|
||||
return result;
|
||||
}
|
||||
|
||||
private Map<String, List<SteadyChecksquareValuePointBO>> queryStatValuePointsByWindow(List<SteadyTrendResolvedFieldBO> fields,
|
||||
LocalDateTime startTime,
|
||||
LocalDateTime endTime,
|
||||
int intervalMinutes) {
|
||||
Map<String, List<SteadyChecksquareValuePointBO>> result =
|
||||
new LinkedHashMap<String, List<SteadyChecksquareValuePointBO>>();
|
||||
for (SteadyTrendResolvedFieldBO field : fields) {
|
||||
result.put(resolveValueType(field.getStatType()), new ArrayList<SteadyChecksquareValuePointBO>());
|
||||
}
|
||||
LocalDateTime windowStart = startTime;
|
||||
while (!windowStart.isAfter(endTime)) {
|
||||
LocalDateTime windowEnd = min(windowStart.plusDays(QUERY_WINDOW_DAYS).minusNanos(1), endTime);
|
||||
Map<String, List<SteadyChecksquareValuePointBO>> windowResult =
|
||||
parseStatValuePoints(executeQuery(buildStatValuePointQuery(fields, windowStart, windowEnd)), intervalMinutes);
|
||||
for (Map.Entry<String, List<SteadyChecksquareValuePointBO>> entry : windowResult.entrySet()) {
|
||||
List<SteadyChecksquareValuePointBO> points = result.get(entry.getKey());
|
||||
if (points == null) {
|
||||
points = new ArrayList<SteadyChecksquareValuePointBO>();
|
||||
result.put(entry.getKey(), points);
|
||||
}
|
||||
points.addAll(entry.getValue());
|
||||
}
|
||||
windowStart = windowEnd.plusNanos(1);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private LocalDateTime min(LocalDateTime first, LocalDateTime second) {
|
||||
return first.isAfter(second) ? second : first;
|
||||
}
|
||||
@@ -238,6 +324,28 @@ public class SteadyChecksquareInfluxQueryComponent {
|
||||
return sql.toString();
|
||||
}
|
||||
|
||||
public String buildStatValuePointQuery(List<SteadyTrendResolvedFieldBO> fields, LocalDateTime startTime, LocalDateTime endTime) {
|
||||
SteadyTrendResolvedFieldBO first = fields.get(0);
|
||||
StringBuilder sql = new StringBuilder();
|
||||
sql.append("SELECT \"").append(first.getField()).append("\" AS \"value\"");
|
||||
sql.append(" FROM \"").append(first.getMeasurement()).append("\"");
|
||||
sql.append(" WHERE time >= '").append(INFLUX_TIME_FORMATTER.format(startTime)).append("'");
|
||||
sql.append(" AND time <= '").append(INFLUX_TIME_FORMATTER.format(endTime)).append("'");
|
||||
sql.append(" AND \"line_id\" = '").append(escapeTagValue(first.getLineId())).append("'");
|
||||
sql.append(" AND \"phasic_type\" = '").append(escapeTagValue(first.getPhase())).append("'");
|
||||
sql.append(" AND \"value_type\" =~ /^(");
|
||||
for (int i = 0; i < fields.size(); i++) {
|
||||
if (i > 0) {
|
||||
sql.append("|");
|
||||
}
|
||||
sql.append(escapeRegexValue(resolveValueType(fields.get(i).getStatType())));
|
||||
}
|
||||
sql.append(")$/");
|
||||
sql.append(" GROUP BY \"value_type\"");
|
||||
sql.append(" ORDER BY time ASC");
|
||||
return sql.toString();
|
||||
}
|
||||
|
||||
private List<SteadyChecksquareValuePointBO> parseValuePoints(String body, int intervalMinutes) {
|
||||
try {
|
||||
JsonNode root = OBJECT_MAPPER.readTree(body);
|
||||
@@ -312,6 +420,52 @@ public class SteadyChecksquareInfluxQueryComponent {
|
||||
}
|
||||
}
|
||||
|
||||
private Map<String, List<SteadyChecksquareValuePointBO>> parseStatValuePoints(String body, int intervalMinutes) {
|
||||
try {
|
||||
JsonNode root = OBJECT_MAPPER.readTree(body);
|
||||
JsonNode seriesArray = root.path("results").path(0).path("series");
|
||||
Map<String, List<SteadyChecksquareValuePointBO>> result =
|
||||
new LinkedHashMap<String, List<SteadyChecksquareValuePointBO>>();
|
||||
if (!seriesArray.isArray()) {
|
||||
return result;
|
||||
}
|
||||
for (JsonNode series : seriesArray) {
|
||||
String statType = series.path("tags").path("value_type").asText(null);
|
||||
if (statType == null || statType.trim().isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
statType = resolveValueType(statType);
|
||||
List<SteadyChecksquareValuePointBO> points = result.get(statType);
|
||||
if (points == null) {
|
||||
points = new ArrayList<SteadyChecksquareValuePointBO>();
|
||||
result.put(statType, points);
|
||||
}
|
||||
JsonNode values = series.path("values");
|
||||
if (!values.isArray()) {
|
||||
continue;
|
||||
}
|
||||
for (JsonNode value : values) {
|
||||
if (value.size() < 2 || value.get(1).isNull()) {
|
||||
continue;
|
||||
}
|
||||
LocalDateTime time = parseInfluxTime(value.get(0).asText());
|
||||
if (time == null) {
|
||||
continue;
|
||||
}
|
||||
SteadyChecksquareValuePointBO point = new SteadyChecksquareValuePointBO();
|
||||
point.setTime(alignToPreviousSlot(time, intervalMinutes));
|
||||
point.setValue(new BigDecimal(value.get(1).asText()));
|
||||
points.add(point);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
} catch (IOException ex) {
|
||||
throw fail("InfluxDB 返回结果解析失败:" + ex.getMessage());
|
||||
} catch (NumberFormatException ex) {
|
||||
throw fail("InfluxDB 返回指标值格式不正确:" + ex.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private LocalDateTime alignToPreviousSlot(LocalDateTime time, int intervalMinutes) {
|
||||
LocalDateTime minuteFloor = time.withSecond(0).withNano(0);
|
||||
int minuteOfDay = minuteFloor.getHour() * 60 + minuteFloor.getMinute();
|
||||
@@ -390,6 +544,16 @@ public class SteadyChecksquareInfluxQueryComponent {
|
||||
return value == null ? "" : value.replace("\\", "\\\\").replace("'", "\\'");
|
||||
}
|
||||
|
||||
private String escapeRegexValue(String value) {
|
||||
return value == null ? "" : value.replace("\\", "\\\\")
|
||||
.replace("|", "\\|")
|
||||
.replace("(", "\\(")
|
||||
.replace(")", "\\)")
|
||||
.replace("^", "\\^")
|
||||
.replace("$", "\\$")
|
||||
.replace(".", "\\.");
|
||||
}
|
||||
|
||||
private String resolveValueType(String statType) {
|
||||
if (statType == null || statType.trim().isEmpty()) {
|
||||
return "AVG";
|
||||
|
||||
@@ -12,6 +12,7 @@ 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.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
@@ -58,9 +59,18 @@ public class SteadyChecksquareValueOrderRuleComponent {
|
||||
LocalDateTime startTime, LocalDateTime endTime,
|
||||
int intervalMinutes) {
|
||||
Map<String, Map<LocalDateTime, BigDecimal>> result = new LinkedHashMap<String, Map<LocalDateTime, BigDecimal>>();
|
||||
List<SteadyTrendResolvedFieldBO> fields = new ArrayList<SteadyTrendResolvedFieldBO>();
|
||||
for (String statType : REQUIRED_STATS) {
|
||||
SteadyTrendResolvedFieldBO field = buildResolvedField(lineId, indicator, harmonicOrder, phase, statType);
|
||||
result.put(statType, toValueMap(influxQueryComponent.queryValuePoints(field, startTime, endTime, intervalMinutes)));
|
||||
fields.add(field);
|
||||
}
|
||||
Map<String, List<SteadyChecksquareValuePointBO>> fieldValueMap =
|
||||
influxQueryComponent.queryStatValuePointMap(fields, startTime, endTime, intervalMinutes);
|
||||
if (fieldValueMap == null) {
|
||||
fieldValueMap = Collections.emptyMap();
|
||||
}
|
||||
for (String statType : REQUIRED_STATS) {
|
||||
result.put(statType, toValueMap(fieldValueMap.get(statType)));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.njcn.gather.steady.checksquare.config;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.RejectedExecutionException;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* 数据校验后台任务线程池配置。
|
||||
*/
|
||||
@Slf4j
|
||||
@Configuration
|
||||
public class SteadyChecksquareExecutorConfig {
|
||||
|
||||
@Bean(name = "steadyChecksquareExecutorService", destroyMethod = "shutdown")
|
||||
public ExecutorService steadyChecksquareExecutorService() {
|
||||
AtomicInteger threadIndex = new AtomicInteger(1);
|
||||
return new ThreadPoolExecutor(
|
||||
1,
|
||||
1,
|
||||
30,
|
||||
TimeUnit.SECONDS,
|
||||
new LinkedBlockingQueue<Runnable>(8),
|
||||
runnable -> {
|
||||
Thread thread = new Thread(runnable);
|
||||
thread.setName("steady-checksquare-task-" + threadIndex.getAndIncrement());
|
||||
return thread;
|
||||
},
|
||||
(runnable, executor) -> {
|
||||
log.warn("数据校验任务线程池已满,拒绝新的校验任务");
|
||||
throw new RejectedExecutionException("数据校验任务线程池已满");
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -61,6 +61,16 @@ public class SteadyChecksquareController extends BaseController {
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe);
|
||||
}
|
||||
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON, operateType = OperateType.UPDATE)
|
||||
@ApiOperation("Restart failed checksquare task")
|
||||
@PostMapping("/restart")
|
||||
public HttpResult<SteadyChecksquareTaskVO> restart(@RequestParam("taskId") String taskId) {
|
||||
String methodDescribe = getMethodDescribe("restart");
|
||||
LogUtil.njcnDebug(log, "{} restart checksquare task, taskId={}", methodDescribe, taskId);
|
||||
SteadyChecksquareTaskVO result = checksquareService.restart(taskId);
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe);
|
||||
}
|
||||
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON, operateType = OperateType.DELETE)
|
||||
@ApiOperation("删除数据校验任务")
|
||||
@PostMapping("/delete")
|
||||
|
||||
@@ -19,6 +19,9 @@ public class SteadyChecksquareQueryParam implements Serializable {
|
||||
@ApiModelProperty("监测点 ID")
|
||||
private String lineId;
|
||||
|
||||
@ApiModelProperty("监测点 ID 列表")
|
||||
private List<String> lineIds;
|
||||
|
||||
@ApiModelProperty("指标编码")
|
||||
private List<String> indicatorCodes;
|
||||
|
||||
|
||||
@@ -24,6 +24,10 @@ public class SteadyChecksquareItemPO implements Serializable {
|
||||
private String taskId;
|
||||
@TableField("item_key")
|
||||
private String itemKey;
|
||||
@TableField("line_id")
|
||||
private String lineId;
|
||||
@TableField("line_name")
|
||||
private String lineName;
|
||||
@TableField("indicator_code")
|
||||
private String indicatorCode;
|
||||
@TableField("indicator_name")
|
||||
|
||||
@@ -26,6 +26,10 @@ public class SteadyChecksquareTaskPO implements Serializable {
|
||||
private String lineId;
|
||||
@TableField("line_name")
|
||||
private String lineName;
|
||||
@TableField("line_ids_json")
|
||||
private String lineIdsJson;
|
||||
@TableField("line_ids_text")
|
||||
private String lineIdsText;
|
||||
@TableField("time_start")
|
||||
private LocalDateTime timeStart;
|
||||
@TableField("time_end")
|
||||
|
||||
@@ -20,6 +20,12 @@ public class SteadyChecksquareItemDetailVO implements Serializable {
|
||||
@ApiModelProperty("检测项 ID")
|
||||
private String itemId;
|
||||
|
||||
@ApiModelProperty("监测点 ID")
|
||||
private String lineId;
|
||||
|
||||
@ApiModelProperty("监测点名称")
|
||||
private String lineName;
|
||||
|
||||
@ApiModelProperty("明细类型")
|
||||
private String detailType;
|
||||
|
||||
|
||||
@@ -24,6 +24,12 @@ public class SteadyChecksquareItemVO implements Serializable {
|
||||
@ApiModelProperty("校验项唯一键")
|
||||
private String itemKey;
|
||||
|
||||
@ApiModelProperty("监测点 ID")
|
||||
private String lineId;
|
||||
|
||||
@ApiModelProperty("监测点名称")
|
||||
private String lineName;
|
||||
|
||||
@ApiModelProperty("指标编码")
|
||||
private String indicatorCode;
|
||||
|
||||
|
||||
@@ -26,6 +26,9 @@ public class SteadyChecksquareQueryVO implements Serializable {
|
||||
@ApiModelProperty("监测点 ID")
|
||||
private String lineId;
|
||||
|
||||
@ApiModelProperty("监测点 ID 列表")
|
||||
private List<String> lineIds = new ArrayList<String>();
|
||||
|
||||
@ApiModelProperty("监测点名称")
|
||||
private String lineName;
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 数据校验历史任务。
|
||||
@@ -25,6 +26,9 @@ public class SteadyChecksquareTaskVO implements Serializable {
|
||||
@ApiModelProperty("监测点 ID")
|
||||
private String lineId;
|
||||
|
||||
@ApiModelProperty("监测点 ID 列表")
|
||||
private List<String> lineIds;
|
||||
|
||||
@ApiModelProperty("监测点名称")
|
||||
private String lineName;
|
||||
|
||||
|
||||
@@ -18,6 +18,8 @@ public interface SteadyChecksquareService {
|
||||
|
||||
SteadyChecksquareTaskVO create(SteadyChecksquareQueryParam param);
|
||||
|
||||
SteadyChecksquareTaskVO restart(String taskId);
|
||||
|
||||
boolean delete(List<String> taskIds);
|
||||
|
||||
SteadyChecksquareQueryVO detail(String taskId);
|
||||
|
||||
@@ -46,6 +46,7 @@ import com.njcn.web.factory.PageFactory;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.support.TransactionTemplate;
|
||||
|
||||
@@ -64,6 +65,8 @@ import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.RejectedExecutionException;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
@@ -98,12 +101,16 @@ public class SteadyChecksquareServiceImpl implements SteadyChecksquareService {
|
||||
@Autowired(required = false)
|
||||
private TransactionTemplate transactionTemplate;
|
||||
|
||||
@Autowired(required = false)
|
||||
@Qualifier("steadyChecksquareExecutorService")
|
||||
private ExecutorService steadyChecksquareExecutorService;
|
||||
|
||||
@Override
|
||||
public Page<SteadyChecksquareTaskVO> query(SteadyChecksquareHistoryQueryParam param) {
|
||||
SteadyChecksquareHistoryQueryParam query = param == null ? new SteadyChecksquareHistoryQueryParam() : param;
|
||||
LambdaQueryWrapper<SteadyChecksquareTaskPO> wrapper = new LambdaQueryWrapper<SteadyChecksquareTaskPO>()
|
||||
.eq(SteadyChecksquareTaskPO::getState, SteadyChecksquareConst.STATE_ENABLED)
|
||||
.eq(trimToNull(query.getLineId()) != null, SteadyChecksquareTaskPO::getLineId, trimToNull(query.getLineId()))
|
||||
.like(trimToNull(query.getLineId()) != null, SteadyChecksquareTaskPO::getLineIdsText, "|" + trimToNull(query.getLineId()) + "|")
|
||||
.like(trimToNull(query.getIndicatorCode()) != null, SteadyChecksquareTaskPO::getIndicatorCodesText, "|" + trimToNull(query.getIndicatorCode()) + "|")
|
||||
.ge(trimToNull(query.getTimeStart()) != null, SteadyChecksquareTaskPO::getTimeStart, parseOptionalTime(query.getTimeStart()))
|
||||
.le(trimToNull(query.getTimeEnd()) != null, SteadyChecksquareTaskPO::getTimeEnd, parseOptionalTime(query.getTimeEnd()))
|
||||
@@ -118,24 +125,47 @@ public class SteadyChecksquareServiceImpl implements SteadyChecksquareService {
|
||||
|
||||
@Override
|
||||
public SteadyChecksquareTaskVO create(SteadyChecksquareQueryParam param) {
|
||||
long createStartMillis = System.currentTimeMillis();
|
||||
validateCreateBaseParam(param);
|
||||
String lineId = trimToNull(param.getLineId());
|
||||
List<String> lineIds = resolveLineIds(param);
|
||||
LocalDateTime startTime = parseRequiredTime(param.getTimeStart(), "开始时间不能为空");
|
||||
LocalDateTime endTime = parseRequiredTime(param.getTimeEnd(), "结束时间不能为空");
|
||||
SteadyChecksquareTaskPO existedTask = findExistingTask(lineId, startTime, endTime);
|
||||
SteadyChecksquareTaskPO existedTask = findExistingTask(lineIds, startTime, endTime);
|
||||
if (existedTask != null) {
|
||||
log.info("数据校验创建命中已有任务,taskId={},lineId={},costMs={}",
|
||||
existedTask.getId(), lineIds, System.currentTimeMillis() - createStartMillis);
|
||||
return toTaskVO(existedTask);
|
||||
}
|
||||
prepareCreateContext(param);
|
||||
influxQueryComponent.enableRequestCache();
|
||||
CreateContext context = prepareCreateContext(param);
|
||||
SteadyChecksquareQueryParam taskParam = copyCreateParam(param, context.indicatorCodes);
|
||||
SteadyChecksquareTaskPO task = saveRunningTaskInTransaction(taskParam, context);
|
||||
try {
|
||||
SteadyChecksquareQueryVO result = calculate(param);
|
||||
SteadyChecksquareTaskPO task = saveResultInTransaction(param, result);
|
||||
return toTaskVO(task);
|
||||
} finally {
|
||||
influxQueryComponent.clearRequestCache();
|
||||
submitCreateTask(task.getId(), taskParam);
|
||||
} catch (RejectedExecutionException ex) {
|
||||
markTaskFail(task.getId(), ex.getMessage());
|
||||
throw fail("数据校验任务线程池已满,请稍后重试");
|
||||
}
|
||||
log.info("数据校验创建任务已提交后台执行,taskId={},lineId={},indicatorCount={},costMs={}",
|
||||
task.getId(), context.lineIds, context.indicatorCodes.size(), System.currentTimeMillis() - createStartMillis);
|
||||
return toTaskVO(task);
|
||||
}
|
||||
@Override
|
||||
public SteadyChecksquareTaskVO restart(String taskId) {
|
||||
SteadyChecksquareTaskPO task = requireTask(taskId);
|
||||
if (!SteadyChecksquareConst.TASK_STATUS_FAIL.equals(task.getTaskStatus())) {
|
||||
throw fail("只有执行失败的数据校验任务允许重新启动");
|
||||
}
|
||||
SteadyChecksquareQueryParam taskParam = buildRestartParam(task);
|
||||
SteadyChecksquareTaskPO runningTask = resetFailTaskInTransaction(task);
|
||||
try {
|
||||
submitCreateTask(runningTask.getId(), taskParam);
|
||||
} catch (RejectedExecutionException ex) {
|
||||
markTaskFail(runningTask.getId(), ex.getMessage());
|
||||
throw fail("数据校验任务线程池已满,请稍后重试");
|
||||
}
|
||||
return toTaskVO(runningTask);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean delete(List<String> taskIds) {
|
||||
List<String> ids = normalizeTextList(taskIds);
|
||||
@@ -166,6 +196,7 @@ public class SteadyChecksquareServiceImpl implements SteadyChecksquareService {
|
||||
result.setTaskId(task.getId());
|
||||
result.setTaskNo(task.getTaskNo());
|
||||
result.setLineId(task.getLineId());
|
||||
result.setLineIds(readTaskLineIds(task));
|
||||
result.setLineName(task.getLineName());
|
||||
result.setTimeStart(formatTime(task.getTimeStart()));
|
||||
result.setTimeEnd(formatTime(task.getTimeEnd()));
|
||||
@@ -198,6 +229,8 @@ public class SteadyChecksquareServiceImpl implements SteadyChecksquareService {
|
||||
}
|
||||
SteadyChecksquareItemDetailVO result = new SteadyChecksquareItemDetailVO();
|
||||
result.setItemId(item.getId());
|
||||
result.setLineId(item.getLineId());
|
||||
result.setLineName(item.getLineName());
|
||||
result.setDetailType(type);
|
||||
result.setStatType(statType);
|
||||
LambdaQueryWrapper<SteadyChecksquareDetailPO> wrapper = new LambdaQueryWrapper<SteadyChecksquareDetailPO>()
|
||||
@@ -240,22 +273,85 @@ public class SteadyChecksquareServiceImpl implements SteadyChecksquareService {
|
||||
return taskResult;
|
||||
}
|
||||
|
||||
private SteadyChecksquareTaskPO resetFailTaskInTransaction(SteadyChecksquareTaskPO task) {
|
||||
if (transactionTemplate == null) {
|
||||
return resetFailTask(task);
|
||||
}
|
||||
return transactionTemplate.execute(status -> resetFailTask(task));
|
||||
}
|
||||
|
||||
private SteadyChecksquareTaskPO resetFailTask(SteadyChecksquareTaskPO task) {
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
LambdaUpdateWrapper<SteadyChecksquareTaskPO> wrapper = new LambdaUpdateWrapper<SteadyChecksquareTaskPO>()
|
||||
.set(SteadyChecksquareTaskPO::getTaskStatus, SteadyChecksquareConst.TASK_STATUS_RUNNING)
|
||||
.set(SteadyChecksquareTaskPO::getItemCount, 0)
|
||||
.set(SteadyChecksquareTaskPO::getAbnormalItemCount, 0)
|
||||
.set(SteadyChecksquareTaskPO::getMinDataIntegrity, BigDecimal.ZERO.setScale(6, RoundingMode.HALF_UP))
|
||||
.set(SteadyChecksquareTaskPO::getResultMessage, "数据校验任务重新执行中")
|
||||
.set(SteadyChecksquareTaskPO::getUpdateTime, now)
|
||||
.eq(SteadyChecksquareTaskPO::getId, task.getId())
|
||||
.eq(SteadyChecksquareTaskPO::getTaskStatus, SteadyChecksquareConst.TASK_STATUS_FAIL)
|
||||
.eq(SteadyChecksquareTaskPO::getState, SteadyChecksquareConst.STATE_ENABLED);
|
||||
if (!taskService.update(wrapper)) {
|
||||
throw fail("数据校验任务状态已变化,请刷新后重试");
|
||||
}
|
||||
clearTaskResults(task.getId());
|
||||
task.setTaskStatus(SteadyChecksquareConst.TASK_STATUS_RUNNING);
|
||||
task.setItemCount(0);
|
||||
task.setAbnormalItemCount(0);
|
||||
task.setMinDataIntegrity(BigDecimal.ZERO.setScale(6, RoundingMode.HALF_UP));
|
||||
task.setResultMessage("数据校验任务重新执行中");
|
||||
task.setUpdateTime(now);
|
||||
return task;
|
||||
}
|
||||
|
||||
private void clearTaskResults(String taskId) {
|
||||
List<SteadyChecksquareItemPO> items = itemService.lambdaQuery()
|
||||
.eq(SteadyChecksquareItemPO::getTaskId, taskId)
|
||||
.list();
|
||||
if (items == null || items.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
List<String> itemIds = items.stream().map(SteadyChecksquareItemPO::getId).collect(Collectors.toList());
|
||||
detailService.remove(new LambdaQueryWrapper<SteadyChecksquareDetailPO>()
|
||||
.in(SteadyChecksquareDetailPO::getItemId, itemIds));
|
||||
statSummaryService.remove(new LambdaQueryWrapper<SteadyChecksquareStatSummaryPO>()
|
||||
.in(SteadyChecksquareStatSummaryPO::getItemId, itemIds));
|
||||
itemService.remove(new LambdaQueryWrapper<SteadyChecksquareItemPO>()
|
||||
.eq(SteadyChecksquareItemPO::getTaskId, taskId));
|
||||
}
|
||||
|
||||
private SteadyChecksquareQueryParam buildRestartParam(SteadyChecksquareTaskPO task) {
|
||||
List<String> lineIds = readTaskLineIds(task);
|
||||
List<String> indicatorCodes = readStringList(task.getIndicatorCodesJson());
|
||||
if (indicatorCodes.isEmpty()) {
|
||||
indicatorCodes = parseTextListSearchValue(task.getIndicatorCodesText());
|
||||
}
|
||||
SteadyChecksquareQueryParam param = new SteadyChecksquareQueryParam();
|
||||
param.setLineId(lineIds.isEmpty() ? task.getLineId() : lineIds.get(0));
|
||||
param.setLineIds(lineIds);
|
||||
param.setIndicatorCodes(indicatorCodes);
|
||||
param.setTimeStart(formatTime(task.getTimeStart()));
|
||||
param.setTimeEnd(formatTime(task.getTimeEnd()));
|
||||
return param;
|
||||
}
|
||||
|
||||
private CreateContext prepareCreateContext(SteadyChecksquareQueryParam param) {
|
||||
validateParam(param);
|
||||
String lineId = trimToNull(param.getLineId());
|
||||
List<String> lineIds = resolveLineIds(param);
|
||||
LocalDateTime startTime = parseRequiredTime(param.getTimeStart(), "开始时间不能为空");
|
||||
LocalDateTime endTime = parseRequiredTime(param.getTimeEnd(), "结束时间不能为空");
|
||||
if (startTime.isAfter(endTime)) {
|
||||
throw fail("开始时间不能大于结束时间");
|
||||
}
|
||||
AddLedgerLinePathVO linePath = requireLinePath(lineId);
|
||||
int intervalMinutes = resolveIntervalMinutes(linePath);
|
||||
List<String> indicatorCodes = normalizeTextList(param.getIndicatorCodes());
|
||||
Map<String, AddLedgerLinePathVO> linePathMap = requireLinePaths(lineIds);
|
||||
int intervalMinutes = resolveIntervalMinutes(linePathMap.get(lineIds.get(0)));
|
||||
List<String> indicatorCodes = resolveIndicatorCodes(param);
|
||||
for (String indicatorCode : indicatorCodes) {
|
||||
requireIndicator(indicatorCode);
|
||||
}
|
||||
validateCreateTimeRange(startTime, endTime);
|
||||
return new CreateContext(lineId, linePath, startTime, endTime, intervalMinutes, indicatorCodes);
|
||||
return new CreateContext(lineIds, linePathMap, startTime, endTime, intervalMinutes, indicatorCodes);
|
||||
}
|
||||
|
||||
private void validateCreateTimeRange(LocalDateTime startTime, LocalDateTime endTime) {
|
||||
@@ -264,10 +360,10 @@ public class SteadyChecksquareServiceImpl implements SteadyChecksquareService {
|
||||
}
|
||||
}
|
||||
|
||||
private SteadyChecksquareTaskPO findExistingTask(String lineId, LocalDateTime startTime, LocalDateTime endTime) {
|
||||
private SteadyChecksquareTaskPO findExistingTask(List<String> lineIds, LocalDateTime startTime, LocalDateTime endTime) {
|
||||
List<SteadyChecksquareTaskPO> tasks = taskService.lambdaQuery()
|
||||
.eq(SteadyChecksquareTaskPO::getState, SteadyChecksquareConst.STATE_ENABLED)
|
||||
.eq(SteadyChecksquareTaskPO::getLineId, lineId)
|
||||
.eq(SteadyChecksquareTaskPO::getLineIdsText, buildTextListSearchValue(lineIds))
|
||||
.eq(SteadyChecksquareTaskPO::getTimeStart, startTime)
|
||||
.eq(SteadyChecksquareTaskPO::getTimeEnd, endTime)
|
||||
.orderByDesc(SteadyChecksquareTaskPO::getCreateTime)
|
||||
@@ -287,36 +383,43 @@ public class SteadyChecksquareServiceImpl implements SteadyChecksquareService {
|
||||
|
||||
private SteadyChecksquareQueryVO calculate(SteadyChecksquareQueryParam param) {
|
||||
validateParam(param);
|
||||
String lineId = trimToNull(param.getLineId());
|
||||
List<String> lineIds = resolveLineIds(param);
|
||||
LocalDateTime startTime = parseRequiredTime(param.getTimeStart(), "开始时间不能为空");
|
||||
LocalDateTime endTime = parseRequiredTime(param.getTimeEnd(), "结束时间不能为空");
|
||||
if (startTime.isAfter(endTime)) {
|
||||
throw fail("开始时间不能大于结束时间");
|
||||
}
|
||||
AddLedgerLinePathVO linePath = requireLinePath(lineId);
|
||||
int intervalMinutes = resolveIntervalMinutes(linePath);
|
||||
Map<String, AddLedgerLinePathVO> linePathMap = requireLinePaths(lineIds);
|
||||
int intervalMinutes = resolveIntervalMinutes(linePathMap.get(lineIds.get(0)));
|
||||
SteadyChecksquareQueryVO result = new SteadyChecksquareQueryVO();
|
||||
result.setLineId(lineId);
|
||||
result.setLineName(trimToNull(linePath.getLineName()) == null ? EMPTY_TEXT : linePath.getLineName());
|
||||
result.setLineId(lineIds.get(0));
|
||||
result.setLineIds(new ArrayList<String>(lineIds));
|
||||
result.setLineName(buildLineNames(lineIds, linePathMap));
|
||||
result.setTimeStart(param.getTimeStart());
|
||||
result.setTimeEnd(param.getTimeEnd());
|
||||
result.setIntervalMinutes(intervalMinutes);
|
||||
|
||||
long startMillis = System.currentTimeMillis();
|
||||
List<String> indicatorCodes = normalizeTextList(param.getIndicatorCodes());
|
||||
List<String> indicatorCodes = resolveIndicatorCodes(param);
|
||||
List<SteadyTrendIndicatorDefinitionBO> indicators = new ArrayList<SteadyTrendIndicatorDefinitionBO>();
|
||||
for (String indicatorCode : indicatorCodes) {
|
||||
indicators.add(requireIndicator(indicatorCode));
|
||||
}
|
||||
log.info("数据校验新增检测开始,lineId={},indicatorCount={},timeStart={},timeEnd={},intervalMinutes={}",
|
||||
lineId, indicatorCodes.size(), startTime, endTime, intervalMinutes);
|
||||
prefetchNormalIndicatorPoints(lineId, indicators, startTime, endTime, intervalMinutes);
|
||||
for (SteadyTrendIndicatorDefinitionBO indicator : indicators) {
|
||||
int itemIntervalMinutes = resolveIndicatorIntervalMinutes(indicator, intervalMinutes);
|
||||
List<LocalDateTime> itemSlots = timeSlotCalculator.buildTimeSlots(startTime, endTime, itemIntervalMinutes);
|
||||
result.getItems().addAll(buildIndicatorItems(lineId, indicator, startTime, endTime, itemSlots, itemIntervalMinutes));
|
||||
lineIds, indicatorCodes.size(), startTime, endTime, intervalMinutes);
|
||||
for (String lineId : lineIds) {
|
||||
AddLedgerLinePathVO linePath = linePathMap.get(lineId);
|
||||
int lineIntervalMinutes = resolveIntervalMinutes(linePath);
|
||||
String lineName = linePath == null ? null : trimToNull(linePath.getLineName());
|
||||
prefetchNormalIndicatorPoints(lineId, indicators, startTime, endTime, lineIntervalMinutes);
|
||||
for (SteadyTrendIndicatorDefinitionBO indicator : indicators) {
|
||||
int itemIntervalMinutes = resolveIndicatorIntervalMinutes(indicator, lineIntervalMinutes);
|
||||
List<LocalDateTime> itemSlots = timeSlotCalculator.buildTimeSlots(startTime, endTime, itemIntervalMinutes);
|
||||
result.getItems().addAll(buildIndicatorItems(lineId, lineName, indicator, startTime, endTime,
|
||||
itemSlots, itemIntervalMinutes));
|
||||
}
|
||||
}
|
||||
log.info("数据校验新增检测结束,lineId={},itemCount={},costMs={}", lineId, result.getItems().size(), System.currentTimeMillis() - startMillis);
|
||||
log.info("数据校验新增检测结束,lineIds={},itemCount={},costMs={}", lineIds, result.getItems().size(), System.currentTimeMillis() - startMillis);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -327,6 +430,13 @@ public class SteadyChecksquareServiceImpl implements SteadyChecksquareService {
|
||||
return transactionTemplate.execute(status -> saveResult(param, result));
|
||||
}
|
||||
|
||||
private SteadyChecksquareTaskPO saveRunningTaskInTransaction(SteadyChecksquareQueryParam param, CreateContext context) {
|
||||
if (transactionTemplate == null) {
|
||||
return saveRunningTask(param, context);
|
||||
}
|
||||
return transactionTemplate.execute(status -> saveRunningTask(param, context));
|
||||
}
|
||||
|
||||
private SteadyChecksquareTaskPO saveResultInTransaction(String taskId, SteadyChecksquareQueryParam param, SteadyChecksquareQueryVO result) {
|
||||
if (transactionTemplate == null) {
|
||||
return saveResult(taskId, param, result);
|
||||
@@ -338,17 +448,73 @@ public class SteadyChecksquareServiceImpl implements SteadyChecksquareService {
|
||||
return saveResult(null, param, result);
|
||||
}
|
||||
|
||||
private SteadyChecksquareTaskPO saveRunningTask(SteadyChecksquareQueryParam param, CreateContext context) {
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
SteadyChecksquareTaskPO task = new SteadyChecksquareTaskPO();
|
||||
task.setId(SteadyChecksquareIdUtil.uuid());
|
||||
task.setTaskNo(SteadyChecksquareIdUtil.taskNo());
|
||||
task.setLineId(context.lineIds.get(0));
|
||||
task.setLineName(buildLineNames(context.lineIds, context.linePathMap));
|
||||
task.setLineIdsJson(writeJson(context.lineIds));
|
||||
task.setLineIdsText(buildTextListSearchValue(context.lineIds));
|
||||
task.setTimeStart(context.startTime);
|
||||
task.setTimeEnd(context.endTime);
|
||||
task.setIntervalMinutes(context.intervalMinutes);
|
||||
task.setIndicatorCodesJson(writeJson(context.indicatorCodes));
|
||||
task.setIndicatorCodesText(buildIndicatorCodesText(context.indicatorCodes));
|
||||
task.setTaskStatus(SteadyChecksquareConst.TASK_STATUS_RUNNING);
|
||||
task.setItemCount(0);
|
||||
task.setAbnormalItemCount(0);
|
||||
task.setMinDataIntegrity(BigDecimal.ZERO.setScale(6, RoundingMode.HALF_UP));
|
||||
task.setResultMessage("数据校验任务执行中");
|
||||
task.setState(SteadyChecksquareConst.STATE_ENABLED);
|
||||
task.setCreateTime(now);
|
||||
task.setUpdateTime(now);
|
||||
taskService.save(task);
|
||||
return task;
|
||||
}
|
||||
|
||||
private void submitCreateTask(String taskId, SteadyChecksquareQueryParam param) {
|
||||
if (steadyChecksquareExecutorService == null) {
|
||||
executeCreateTask(taskId, param);
|
||||
return;
|
||||
}
|
||||
steadyChecksquareExecutorService.submit(() -> executeCreateTask(taskId, param));
|
||||
}
|
||||
|
||||
private void executeCreateTask(String taskId, SteadyChecksquareQueryParam param) {
|
||||
long startMillis = System.currentTimeMillis();
|
||||
influxQueryComponent.enableRequestCache();
|
||||
try {
|
||||
SteadyChecksquareQueryVO result = calculate(param);
|
||||
SteadyChecksquareTaskPO task = saveResultInTransaction(taskId, param, result);
|
||||
log.info("数据校验后台任务执行成功,taskId={},itemCount={},costMs={}",
|
||||
taskId, task.getItemCount(), System.currentTimeMillis() - startMillis);
|
||||
} catch (Exception ex) {
|
||||
log.error("数据校验后台任务执行失败,taskId={},costMs={}", taskId, System.currentTimeMillis() - startMillis, ex);
|
||||
markTaskFail(taskId, ex.getMessage());
|
||||
} finally {
|
||||
influxQueryComponent.clearRequestCache();
|
||||
}
|
||||
}
|
||||
|
||||
private SteadyChecksquareTaskPO saveResult(String taskId, SteadyChecksquareQueryParam param, SteadyChecksquareQueryVO result) {
|
||||
long saveStartMillis = System.currentTimeMillis();
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
SteadyChecksquareTaskPO task = new SteadyChecksquareTaskPO();
|
||||
task.setId(trimToNull(taskId) == null ? SteadyChecksquareIdUtil.uuid() : taskId);
|
||||
task.setTaskNo(SteadyChecksquareIdUtil.taskNo());
|
||||
if (trimToNull(taskId) == null) {
|
||||
task.setTaskNo(SteadyChecksquareIdUtil.taskNo());
|
||||
}
|
||||
task.setLineId(result.getLineId());
|
||||
task.setLineName(result.getLineName());
|
||||
List<String> lineIds = resolveResultLineIds(result);
|
||||
task.setLineIdsJson(writeJson(lineIds));
|
||||
task.setLineIdsText(buildTextListSearchValue(lineIds));
|
||||
task.setTimeStart(parseRequiredTime(result.getTimeStart(), "开始时间不能为空"));
|
||||
task.setTimeEnd(parseRequiredTime(result.getTimeEnd(), "结束时间不能为空"));
|
||||
task.setIntervalMinutes(result.getIntervalMinutes());
|
||||
List<String> indicatorCodes = normalizeTextList(param.getIndicatorCodes());
|
||||
List<String> indicatorCodes = resolveIndicatorCodes(param);
|
||||
task.setIndicatorCodesJson(writeJson(indicatorCodes));
|
||||
task.setIndicatorCodesText(buildIndicatorCodesText(indicatorCodes));
|
||||
task.setTaskStatus(SteadyChecksquareConst.TASK_STATUS_SUCCESS);
|
||||
@@ -384,12 +550,18 @@ public class SteadyChecksquareServiceImpl implements SteadyChecksquareService {
|
||||
if (!detailPOs.isEmpty()) {
|
||||
saveDetailBatchInChunks(detailPOs);
|
||||
}
|
||||
log.info("数据校验结果保存完成,taskId={},itemCount={},summaryCount={},detailCount={},costMs={}",
|
||||
task.getId(), itemPOs.size(), summaryPOs.size(), detailPOs.size(), System.currentTimeMillis() - saveStartMillis);
|
||||
return task;
|
||||
}
|
||||
|
||||
private void updateCompletedTask(SteadyChecksquareTaskPO task) {
|
||||
LambdaUpdateWrapper<SteadyChecksquareTaskPO> wrapper = new LambdaUpdateWrapper<SteadyChecksquareTaskPO>()
|
||||
.set(SteadyChecksquareTaskPO::getLineName, task.getLineName())
|
||||
.set(SteadyChecksquareTaskPO::getLineIdsJson, task.getLineIdsJson())
|
||||
.set(SteadyChecksquareTaskPO::getLineIdsText, task.getLineIdsText())
|
||||
.set(SteadyChecksquareTaskPO::getIndicatorCodesJson, task.getIndicatorCodesJson())
|
||||
.set(SteadyChecksquareTaskPO::getIndicatorCodesText, task.getIndicatorCodesText())
|
||||
.set(SteadyChecksquareTaskPO::getIntervalMinutes, task.getIntervalMinutes())
|
||||
.set(SteadyChecksquareTaskPO::getTaskStatus, task.getTaskStatus())
|
||||
.set(SteadyChecksquareTaskPO::getItemCount, task.getItemCount())
|
||||
@@ -414,6 +586,8 @@ public class SteadyChecksquareServiceImpl implements SteadyChecksquareService {
|
||||
po.setId(SteadyChecksquareIdUtil.uuid());
|
||||
po.setTaskId(taskId);
|
||||
po.setItemKey(item.getItemKey());
|
||||
po.setLineId(item.getLineId());
|
||||
po.setLineName(item.getLineName());
|
||||
po.setIndicatorCode(item.getIndicatorCode());
|
||||
po.setIndicatorName(item.getIndicatorName());
|
||||
po.setHarmonicOrder(item.getHarmonicOrder());
|
||||
@@ -542,7 +716,8 @@ public class SteadyChecksquareServiceImpl implements SteadyChecksquareService {
|
||||
+ field.getStatType() + "|" + intervalMinutes;
|
||||
}
|
||||
|
||||
private List<SteadyChecksquareItemVO> buildIndicatorItems(String lineId, SteadyTrendIndicatorDefinitionBO indicator,
|
||||
private List<SteadyChecksquareItemVO> buildIndicatorItems(String lineId, String lineName,
|
||||
SteadyTrendIndicatorDefinitionBO indicator,
|
||||
LocalDateTime startTime, LocalDateTime endTime,
|
||||
List<LocalDateTime> slots, int intervalMinutes) {
|
||||
List<SteadyChecksquareItemVO> result = new ArrayList<SteadyChecksquareItemVO>();
|
||||
@@ -550,12 +725,12 @@ public class SteadyChecksquareServiceImpl implements SteadyChecksquareService {
|
||||
List<Integer> harmonicOrders = buildAggregateHarmonicOrders(indicator);
|
||||
prefetchHarmonicIndicatorPoints(lineId, indicator, harmonicOrders, startTime, endTime, intervalMinutes);
|
||||
for (Integer order : harmonicOrders) {
|
||||
result.add(buildItem(lineId, indicator, order, startTime, endTime, slots, intervalMinutes));
|
||||
result.add(buildItem(lineId, lineName, indicator, order, startTime, endTime, slots, intervalMinutes));
|
||||
}
|
||||
fillHarmonicParityRuleResult(result, lineId, indicator, startTime, endTime, intervalMinutes);
|
||||
return Collections.singletonList(aggregateHarmonicItems(lineId, indicator, result, intervalMinutes));
|
||||
return Collections.singletonList(aggregateHarmonicItems(lineId, lineName, indicator, result, intervalMinutes));
|
||||
}
|
||||
result.add(buildItem(lineId, indicator, null, startTime, endTime, slots, intervalMinutes));
|
||||
result.add(buildItem(lineId, lineName, indicator, null, startTime, endTime, slots, intervalMinutes));
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -576,10 +751,13 @@ public class SteadyChecksquareServiceImpl implements SteadyChecksquareService {
|
||||
}
|
||||
}
|
||||
|
||||
private SteadyChecksquareItemVO aggregateHarmonicItems(String lineId, SteadyTrendIndicatorDefinitionBO indicator,
|
||||
private SteadyChecksquareItemVO aggregateHarmonicItems(String lineId, String lineName,
|
||||
SteadyTrendIndicatorDefinitionBO indicator,
|
||||
List<SteadyChecksquareItemVO> orderItems, int intervalMinutes) {
|
||||
SteadyChecksquareItemVO result = new SteadyChecksquareItemVO();
|
||||
result.setItemKey(buildItemKey(lineId, indicator, null));
|
||||
result.setLineId(lineId);
|
||||
result.setLineName(lineName);
|
||||
result.setIndicatorCode(indicator.getIndicatorCode());
|
||||
result.setIndicatorName(indicator.getName());
|
||||
result.setHarmonicOrder(null);
|
||||
@@ -654,11 +832,14 @@ public class SteadyChecksquareServiceImpl implements SteadyChecksquareService {
|
||||
return new ArrayList<SteadyChecksquareStatDetailVO>(detailMap.values());
|
||||
}
|
||||
|
||||
private SteadyChecksquareItemVO buildItem(String lineId, SteadyTrendIndicatorDefinitionBO indicator, Integer harmonicOrder,
|
||||
private SteadyChecksquareItemVO buildItem(String lineId, String lineName,
|
||||
SteadyTrendIndicatorDefinitionBO indicator, Integer harmonicOrder,
|
||||
LocalDateTime startTime, LocalDateTime endTime,
|
||||
List<LocalDateTime> slots, int intervalMinutes) {
|
||||
SteadyChecksquareItemVO item = new SteadyChecksquareItemVO();
|
||||
item.setItemKey(buildItemKey(lineId, indicator, harmonicOrder));
|
||||
item.setLineId(lineId);
|
||||
item.setLineName(lineName);
|
||||
item.setIndicatorCode(indicator.getIndicatorCode());
|
||||
item.setIndicatorName(indicator.getName());
|
||||
item.setHarmonicOrder(harmonicOrder);
|
||||
@@ -822,7 +1003,7 @@ public class SteadyChecksquareServiceImpl implements SteadyChecksquareService {
|
||||
if (param == null) {
|
||||
throw fail("数据校验参数不能为空");
|
||||
}
|
||||
if (trimToNull(param.getLineId()) == null) {
|
||||
if (resolveLineIds(param).isEmpty()) {
|
||||
throw fail("监测点ID不能为空");
|
||||
}
|
||||
parseRequiredTime(param.getTimeStart(), "开始时间不能为空");
|
||||
@@ -833,12 +1014,9 @@ public class SteadyChecksquareServiceImpl implements SteadyChecksquareService {
|
||||
if (param == null) {
|
||||
throw fail("数据校验参数不能为空");
|
||||
}
|
||||
if (trimToNull(param.getLineId()) == null) {
|
||||
if (resolveLineIds(param).isEmpty()) {
|
||||
throw fail("监测点ID不能为空");
|
||||
}
|
||||
if (!allowEmptyIndicators && normalizeTextList(param.getIndicatorCodes()).isEmpty()) {
|
||||
throw fail("指标不能为空");
|
||||
}
|
||||
parseRequiredTime(param.getTimeStart(), "开始时间不能为空");
|
||||
parseRequiredTime(param.getTimeEnd(), "结束时间不能为空");
|
||||
}
|
||||
@@ -872,6 +1050,26 @@ public class SteadyChecksquareServiceImpl implements SteadyChecksquareService {
|
||||
return linePath;
|
||||
}
|
||||
|
||||
private Map<String, AddLedgerLinePathVO> requireLinePaths(List<String> lineIds) {
|
||||
Map<String, AddLedgerLinePathVO> linePathMap = addLedgerService.listLinePathByLineIds(lineIds);
|
||||
for (String lineId : lineIds) {
|
||||
if (linePathMap == null || linePathMap.get(lineId) == null) {
|
||||
throw fail("鐩戞祴鐐逛笉瀛樺湪鎴栦笉鍙敤");
|
||||
}
|
||||
}
|
||||
return linePathMap;
|
||||
}
|
||||
|
||||
private String buildLineNames(List<String> lineIds, Map<String, AddLedgerLinePathVO> linePathMap) {
|
||||
List<String> names = new ArrayList<String>();
|
||||
for (String lineId : lineIds) {
|
||||
AddLedgerLinePathVO linePath = linePathMap.get(lineId);
|
||||
String lineName = linePath == null ? null : trimToNull(linePath.getLineName());
|
||||
names.add(lineName == null ? EMPTY_TEXT : lineName);
|
||||
}
|
||||
return String.join("、", names);
|
||||
}
|
||||
|
||||
private int resolveIntervalMinutes(AddLedgerLinePathVO linePath) {
|
||||
Integer interval = linePath.getLineInterval();
|
||||
if (interval == null || interval <= 0) {
|
||||
@@ -1005,6 +1203,7 @@ public class SteadyChecksquareServiceImpl implements SteadyChecksquareService {
|
||||
vo.setTaskId(task.getId());
|
||||
vo.setTaskNo(task.getTaskNo());
|
||||
vo.setLineId(task.getLineId());
|
||||
vo.setLineIds(readTaskLineIds(task));
|
||||
vo.setLineName(task.getLineName());
|
||||
vo.setTimeStart(formatTime(task.getTimeStart()));
|
||||
vo.setTimeEnd(formatTime(task.getTimeEnd()));
|
||||
@@ -1021,6 +1220,8 @@ public class SteadyChecksquareServiceImpl implements SteadyChecksquareService {
|
||||
SteadyChecksquareItemVO vo = new SteadyChecksquareItemVO();
|
||||
vo.setItemId(item.getId());
|
||||
vo.setItemKey(item.getItemKey());
|
||||
vo.setLineId(item.getLineId());
|
||||
vo.setLineName(item.getLineName());
|
||||
vo.setIndicatorCode(item.getIndicatorCode());
|
||||
vo.setIndicatorName(item.getIndicatorName());
|
||||
vo.setHarmonicOrder(item.getHarmonicOrder());
|
||||
@@ -1229,6 +1430,53 @@ public class SteadyChecksquareServiceImpl implements SteadyChecksquareService {
|
||||
return new ArrayList<String>(result);
|
||||
}
|
||||
|
||||
private List<String> resolveLineIds(SteadyChecksquareQueryParam param) {
|
||||
List<String> lineIds = normalizeTextList(param == null ? null : param.getLineIds());
|
||||
if (lineIds.isEmpty() && param != null && trimToNull(param.getLineId()) != null) {
|
||||
lineIds.add(trimToNull(param.getLineId()));
|
||||
}
|
||||
return lineIds;
|
||||
}
|
||||
|
||||
private List<String> resolveIndicatorCodes(SteadyChecksquareQueryParam param) {
|
||||
List<String> indicatorCodes = normalizeTextList(param == null ? null : param.getIndicatorCodes());
|
||||
if (!indicatorCodes.isEmpty()) {
|
||||
return indicatorCodes;
|
||||
}
|
||||
List<String> allIndicatorCodes = new ArrayList<String>();
|
||||
for (SteadyTrendIndicatorDefinitionBO indicator : indicatorCatalog.listIndicators()) {
|
||||
allIndicatorCodes.add(indicator.getIndicatorCode());
|
||||
}
|
||||
return allIndicatorCodes;
|
||||
}
|
||||
|
||||
private List<String> resolveResultLineIds(SteadyChecksquareQueryVO result) {
|
||||
List<String> lineIds = normalizeTextList(result == null ? null : result.getLineIds());
|
||||
if (lineIds.isEmpty() && result != null && trimToNull(result.getLineId()) != null) {
|
||||
lineIds.add(trimToNull(result.getLineId()));
|
||||
}
|
||||
return lineIds;
|
||||
}
|
||||
|
||||
private List<String> readTaskLineIds(SteadyChecksquareTaskPO task) {
|
||||
List<String> lineIds = readStringList(task.getLineIdsJson());
|
||||
if (lineIds.isEmpty() && trimToNull(task.getLineId()) != null) {
|
||||
lineIds.add(trimToNull(task.getLineId()));
|
||||
}
|
||||
return lineIds;
|
||||
}
|
||||
|
||||
private SteadyChecksquareQueryParam copyCreateParam(SteadyChecksquareQueryParam param, List<String> indicatorCodes) {
|
||||
SteadyChecksquareQueryParam result = new SteadyChecksquareQueryParam();
|
||||
List<String> lineIds = resolveLineIds(param);
|
||||
result.setLineId(lineIds.isEmpty() ? null : lineIds.get(0));
|
||||
result.setLineIds(new ArrayList<String>(lineIds));
|
||||
result.setIndicatorCodes(new ArrayList<String>(indicatorCodes));
|
||||
result.setTimeStart(param.getTimeStart());
|
||||
result.setTimeEnd(param.getTimeEnd());
|
||||
return result;
|
||||
}
|
||||
|
||||
private String trimToNull(String value) {
|
||||
if (value == null) {
|
||||
return null;
|
||||
@@ -1246,12 +1494,16 @@ public class SteadyChecksquareServiceImpl implements SteadyChecksquareService {
|
||||
}
|
||||
|
||||
private String buildIndicatorCodesText(List<String> indicatorCodes) {
|
||||
if (indicatorCodes == null || indicatorCodes.isEmpty()) {
|
||||
return buildTextListSearchValue(indicatorCodes);
|
||||
}
|
||||
|
||||
private String buildTextListSearchValue(List<String> values) {
|
||||
if (values == null || values.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
StringBuilder builder = new StringBuilder("|");
|
||||
for (String indicatorCode : indicatorCodes) {
|
||||
builder.append(indicatorCode).append("|");
|
||||
for (String value : values) {
|
||||
builder.append(value).append("|");
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
@@ -1264,6 +1516,14 @@ public class SteadyChecksquareServiceImpl implements SteadyChecksquareService {
|
||||
return text.length() > 2000 ? text.substring(0, 2000) : text;
|
||||
}
|
||||
|
||||
private List<String> parseTextListSearchValue(String value) {
|
||||
String text = trimToNull(value);
|
||||
if (text == null) {
|
||||
return new ArrayList<String>();
|
||||
}
|
||||
return normalizeTextList(Arrays.asList(text.split("\\|")));
|
||||
}
|
||||
|
||||
private List<Integer> readIntegerList(String json) {
|
||||
if (trimToNull(json) == null) {
|
||||
return new ArrayList<Integer>();
|
||||
@@ -1305,17 +1565,17 @@ public class SteadyChecksquareServiceImpl implements SteadyChecksquareService {
|
||||
}
|
||||
|
||||
private static class CreateContext {
|
||||
private final String lineId;
|
||||
private final AddLedgerLinePathVO linePath;
|
||||
private final List<String> lineIds;
|
||||
private final Map<String, AddLedgerLinePathVO> linePathMap;
|
||||
private final LocalDateTime startTime;
|
||||
private final LocalDateTime endTime;
|
||||
private final int intervalMinutes;
|
||||
private final List<String> indicatorCodes;
|
||||
|
||||
private CreateContext(String lineId, AddLedgerLinePathVO linePath, LocalDateTime startTime,
|
||||
private CreateContext(List<String> lineIds, Map<String, AddLedgerLinePathVO> linePathMap, LocalDateTime startTime,
|
||||
LocalDateTime endTime, int intervalMinutes, List<String> indicatorCodes) {
|
||||
this.lineId = lineId;
|
||||
this.linePath = linePath;
|
||||
this.lineIds = lineIds;
|
||||
this.linePathMap = linePathMap;
|
||||
this.startTime = startTime;
|
||||
this.endTime = endTime;
|
||||
this.intervalMinutes = intervalMinutes;
|
||||
|
||||
@@ -3,6 +3,8 @@ CREATE TABLE IF NOT EXISTS `steady_checksquare_task` (
|
||||
`task_no` VARCHAR(64) NOT NULL COMMENT '检测任务编号',
|
||||
`line_id` VARCHAR(64) NOT NULL COMMENT '监测点ID',
|
||||
`line_name` VARCHAR(255) NULL COMMENT '监测点名称',
|
||||
`line_ids_json` JSON NULL COMMENT '请求监测点ID列表',
|
||||
`line_ids_text` VARCHAR(2000) NULL COMMENT '请求监测点ID检索文本,格式 |line1|line2|',
|
||||
`time_start` DATETIME NOT NULL COMMENT '检测开始时间',
|
||||
`time_end` DATETIME NOT NULL COMMENT '检测结束时间',
|
||||
`interval_minutes` INT NULL COMMENT '默认统计间隔,单位分钟',
|
||||
@@ -22,6 +24,7 @@ CREATE TABLE IF NOT EXISTS `steady_checksquare_task` (
|
||||
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_line_ids_text` (`line_ids_text`(255)),
|
||||
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='稳态数据校验任务表';
|
||||
@@ -30,6 +33,8 @@ 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 '检测项唯一键',
|
||||
`line_id` VARCHAR(64) NULL COMMENT '监测点ID',
|
||||
`line_name` VARCHAR(255) NULL COMMENT '监测点名称',
|
||||
`indicator_code` VARCHAR(64) NOT NULL COMMENT '指标编码',
|
||||
`indicator_name` VARCHAR(255) NULL COMMENT '指标名称',
|
||||
`harmonic_order` INT NULL COMMENT '谐波次数;聚合项为空',
|
||||
|
||||
@@ -142,6 +142,71 @@ class SteadyChecksquareInfluxQueryComponentTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldQueryMultipleStatTypesOnce() throws Exception {
|
||||
AtomicInteger requestCount = new AtomicInteger();
|
||||
HttpServer server = HttpServer.create(new InetSocketAddress(0), 0);
|
||||
server.createContext("/query", exchange -> {
|
||||
requestCount.incrementAndGet();
|
||||
byte[] body = ("{\"results\":[{\"series\":["
|
||||
+ "{\"tags\":{\"value_type\":\"MAX\"},\"values\":[[\"2026-05-01T00:00:00Z\",10]]},"
|
||||
+ "{\"tags\":{\"value_type\":\"AVG\"},\"values\":[[\"2026-05-01T00:00:00Z\",8]]}"
|
||||
+ "]}]}").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 max = buildField("rms");
|
||||
max.setMeasurement("data_v");
|
||||
max.setStatType("MAX");
|
||||
SteadyTrendResolvedFieldBO avg = buildField("rms");
|
||||
avg.setMeasurement("data_v");
|
||||
avg.setStatType("AVG");
|
||||
|
||||
component.enableRequestCache();
|
||||
Map<String, java.util.List<com.njcn.gather.steady.checksquare.pojo.bo.SteadyChecksquareValuePointBO>> result =
|
||||
component.queryStatValuePointMap(Arrays.asList(max, avg),
|
||||
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(1, result.get("MAX").size());
|
||||
Assertions.assertEquals(1, result.get("AVG").size());
|
||||
Assertions.assertEquals(new java.math.BigDecimal("10"), result.get("MAX").get(0).getValue());
|
||||
Assertions.assertEquals(new java.math.BigDecimal("8"), result.get("AVG").get(0).getValue());
|
||||
} finally {
|
||||
server.stop(0);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldBuildStatValuePointQueryWithValueTypeRegex() {
|
||||
SteadyChecksquareInfluxQueryComponent component = new SteadyChecksquareInfluxQueryComponent(new SteadyInfluxDbProperties());
|
||||
SteadyTrendResolvedFieldBO max = buildField("rms");
|
||||
max.setMeasurement("data_v");
|
||||
max.setStatType("MAX");
|
||||
SteadyTrendResolvedFieldBO avg = buildField("rms");
|
||||
avg.setMeasurement("data_v");
|
||||
avg.setStatType("AVG");
|
||||
|
||||
String query = component.buildStatValuePointQuery(Arrays.asList(max, avg),
|
||||
LocalDateTime.of(2026, 5, 1, 0, 0, 0),
|
||||
LocalDateTime.of(2026, 5, 1, 0, 1, 0));
|
||||
|
||||
Assertions.assertTrue(query.contains("SELECT \"rms\" AS \"value\""));
|
||||
Assertions.assertTrue(query.contains("\"value_type\" =~ /^(MAX|AVG)$/"));
|
||||
Assertions.assertTrue(query.contains("GROUP BY \"value_type\""));
|
||||
Assertions.assertTrue(query.endsWith("ORDER BY time ASC"));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
void shouldSplitLongValuePointQueryByDay() throws Exception {
|
||||
AtomicInteger requestCount = new AtomicInteger();
|
||||
|
||||
@@ -11,6 +11,9 @@ 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.eq;
|
||||
@@ -28,23 +31,12 @@ class SteadyChecksquareValueOrderRuleComponentTest {
|
||||
SteadyChecksquareValueOrderRuleComponent component = new SteadyChecksquareValueOrderRuleComponent(influxQueryComponent);
|
||||
LocalDateTime firstTime = LocalDateTime.of(2026, 5, 1, 0, 0);
|
||||
LocalDateTime secondTime = LocalDateTime.of(2026, 5, 1, 0, 1);
|
||||
when(influxQueryComponent.queryValuePoints(any(), any(LocalDateTime.class), any(LocalDateTime.class), eq(1)))
|
||||
.thenAnswer(invocation -> {
|
||||
String statType = invocation.getArgument(0, com.njcn.gather.steady.datavie.pojo.bo.SteadyTrendResolvedFieldBO.class).getStatType();
|
||||
if ("MAX".equals(statType)) {
|
||||
return Arrays.asList(point(firstTime, "8"), point(secondTime, "9"));
|
||||
}
|
||||
if ("CP95".equals(statType)) {
|
||||
return Arrays.asList(point(firstTime, "9"), point(secondTime, "10"));
|
||||
}
|
||||
if ("AVG".equals(statType)) {
|
||||
return Arrays.asList(point(firstTime, "7"), point(secondTime, "8"));
|
||||
}
|
||||
if ("MIN".equals(statType)) {
|
||||
return Arrays.asList(point(firstTime, "1"), point(secondTime, "9"));
|
||||
}
|
||||
return Collections.emptyList();
|
||||
});
|
||||
when(influxQueryComponent.queryStatValuePointMap(any(), any(LocalDateTime.class), any(LocalDateTime.class), eq(1)))
|
||||
.thenReturn(statPointMap(
|
||||
Arrays.asList(point(firstTime, "8"), point(secondTime, "9")),
|
||||
Arrays.asList(point(firstTime, "9"), point(secondTime, "10")),
|
||||
Arrays.asList(point(firstTime, "7"), point(secondTime, "8")),
|
||||
Arrays.asList(point(firstTime, "1"), point(secondTime, "9"))));
|
||||
|
||||
SteadyChecksquareValueOrderRuleVO result = component.check("line-001", indicator(), null,
|
||||
LocalDateTime.of(2026, 5, 1, 0, 0), LocalDateTime.of(2026, 5, 1, 0, 2), 1);
|
||||
@@ -65,23 +57,12 @@ class SteadyChecksquareValueOrderRuleComponentTest {
|
||||
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();
|
||||
});
|
||||
when(influxQueryComponent.queryStatValuePointMap(any(), any(LocalDateTime.class), any(LocalDateTime.class), eq(1)))
|
||||
.thenReturn(statPointMap(
|
||||
Collections.singletonList(point(time, "10")),
|
||||
Collections.singletonList(point(time, "10")),
|
||||
Collections.singletonList(point(time, "8")),
|
||||
Collections.singletonList(point(time, "8"))));
|
||||
|
||||
SteadyChecksquareValueOrderRuleVO result = component.check("line-001", indicator(), null,
|
||||
LocalDateTime.of(2026, 5, 1, 0, 0), LocalDateTime.of(2026, 5, 1, 0, 1), 1);
|
||||
@@ -96,23 +77,12 @@ class SteadyChecksquareValueOrderRuleComponentTest {
|
||||
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();
|
||||
});
|
||||
when(influxQueryComponent.queryStatValuePointMap(any(), any(LocalDateTime.class), any(LocalDateTime.class), eq(1)))
|
||||
.thenReturn(statPointMap(
|
||||
Collections.singletonList(point(time, "8")),
|
||||
Collections.singletonList(point(time, "10")),
|
||||
Collections.singletonList(point(time, "8")),
|
||||
Collections.singletonList(point(time, "1"))));
|
||||
|
||||
SteadyChecksquareValueOrderRuleVO result = component.check("line-001", indicator(), null,
|
||||
LocalDateTime.of(2026, 5, 1, 0, 0), LocalDateTime.of(2026, 5, 1, 0, 1), 1);
|
||||
@@ -127,23 +97,12 @@ class SteadyChecksquareValueOrderRuleComponentTest {
|
||||
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();
|
||||
});
|
||||
when(influxQueryComponent.queryStatValuePointMap(any(), any(LocalDateTime.class), any(LocalDateTime.class), eq(1)))
|
||||
.thenReturn(statPointMap(
|
||||
Collections.singletonList(point(time, "8")),
|
||||
Collections.singletonList(point(time, "10")),
|
||||
Collections.singletonList(point(time, "8")),
|
||||
Collections.singletonList(point(time, "1"))));
|
||||
SteadyTrendIndicatorDefinitionBO indicator = indicator();
|
||||
indicator.setHarmonic(true);
|
||||
indicator.setHarmonicFieldPrefix("v");
|
||||
@@ -160,20 +119,12 @@ class SteadyChecksquareValueOrderRuleComponentTest {
|
||||
SteadyChecksquareInfluxQueryComponent influxQueryComponent = mock(SteadyChecksquareInfluxQueryComponent.class);
|
||||
SteadyChecksquareValueOrderRuleComponent component = new SteadyChecksquareValueOrderRuleComponent(influxQueryComponent);
|
||||
LocalDateTime time = LocalDateTime.of(2026, 5, 1, 0, 0);
|
||||
when(influxQueryComponent.queryValuePoints(any(), any(LocalDateTime.class), any(LocalDateTime.class), eq(1)))
|
||||
.thenAnswer(invocation -> {
|
||||
String statType = invocation.getArgument(0, com.njcn.gather.steady.datavie.pojo.bo.SteadyTrendResolvedFieldBO.class).getStatType();
|
||||
if ("MAX".equals(statType)) {
|
||||
return Collections.singletonList(point(time, "10"));
|
||||
}
|
||||
if ("CP95".equals(statType)) {
|
||||
return Collections.singletonList(point(time, "11"));
|
||||
}
|
||||
if ("MIN".equals(statType)) {
|
||||
return Collections.singletonList(point(time, "1"));
|
||||
}
|
||||
return Collections.emptyList();
|
||||
});
|
||||
when(influxQueryComponent.queryStatValuePointMap(any(), any(LocalDateTime.class), any(LocalDateTime.class), eq(1)))
|
||||
.thenReturn(statPointMap(
|
||||
Collections.singletonList(point(time, "10")),
|
||||
Collections.singletonList(point(time, "11")),
|
||||
Collections.emptyList(),
|
||||
Collections.singletonList(point(time, "1"))));
|
||||
|
||||
SteadyChecksquareValueOrderRuleVO result = component.check("line-001", indicator(), null,
|
||||
LocalDateTime.of(2026, 5, 1, 0, 0), LocalDateTime.of(2026, 5, 1, 0, 1), 1);
|
||||
@@ -216,4 +167,17 @@ class SteadyChecksquareValueOrderRuleComponentTest {
|
||||
point.setValue(new BigDecimal(value));
|
||||
return point;
|
||||
}
|
||||
|
||||
private Map<String, List<SteadyChecksquareValuePointBO>> statPointMap(List<SteadyChecksquareValuePointBO> maxPoints,
|
||||
List<SteadyChecksquareValuePointBO> cp95Points,
|
||||
List<SteadyChecksquareValuePointBO> avgPoints,
|
||||
List<SteadyChecksquareValuePointBO> minPoints) {
|
||||
Map<String, List<SteadyChecksquareValuePointBO>> result =
|
||||
new LinkedHashMap<String, List<SteadyChecksquareValuePointBO>>();
|
||||
result.put("MAX", maxPoints);
|
||||
result.put("CP95", cp95Points);
|
||||
result.put("AVG", avgPoints);
|
||||
result.put("MIN", minPoints);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,10 @@ class SteadyChecksquareControllerTest {
|
||||
PostMapping createMapping = createMethod.getAnnotation(PostMapping.class);
|
||||
Assertions.assertArrayEquals(new String[]{"/create"}, createMapping.value());
|
||||
|
||||
Method restartMethod = SteadyChecksquareController.class.getDeclaredMethod("restart", String.class);
|
||||
PostMapping restartMapping = restartMethod.getAnnotation(PostMapping.class);
|
||||
Assertions.assertArrayEquals(new String[]{"/restart"}, restartMapping.value());
|
||||
|
||||
Method detailMethod = SteadyChecksquareController.class.getDeclaredMethod("detail", String.class);
|
||||
GetMapping detailMapping = detailMethod.getAnnotation(GetMapping.class);
|
||||
Assertions.assertArrayEquals(new String[]{"/detail"}, detailMapping.value());
|
||||
|
||||
@@ -48,6 +48,7 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
@@ -68,6 +69,7 @@ class SteadyChecksquareServiceImplTest {
|
||||
MapperBuilderAssistant assistant = new MapperBuilderAssistant(new Configuration(), "");
|
||||
TableInfoHelper.initTableInfo(assistant, SteadyChecksquareTaskPO.class);
|
||||
TableInfoHelper.initTableInfo(assistant, SteadyChecksquareItemPO.class);
|
||||
TableInfoHelper.initTableInfo(assistant, SteadyChecksquareStatSummaryPO.class);
|
||||
TableInfoHelper.initTableInfo(assistant, SteadyChecksquareDetailPO.class);
|
||||
}
|
||||
|
||||
@@ -153,7 +155,8 @@ class SteadyChecksquareServiceImplTest {
|
||||
SteadyChecksquareTaskVO result = service.create(param);
|
||||
|
||||
Assertions.assertEquals(taskCaptor.getValue().getId(), result.getTaskId());
|
||||
Assertions.assertEquals(Integer.valueOf(5), result.getItemCount());
|
||||
Assertions.assertEquals("RUNNING", result.getTaskStatus());
|
||||
Assertions.assertEquals(Integer.valueOf(0), result.getItemCount());
|
||||
verify(itemService).saveBatch(any());
|
||||
}
|
||||
|
||||
@@ -201,7 +204,7 @@ class SteadyChecksquareServiceImplTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldCreateTaskSynchronouslyAndReturnTaskSummary() {
|
||||
void shouldCreateRunningTaskAndReturnTaskSummaryBeforeCalculationCompletes() {
|
||||
AddLedgerService addLedgerService = mock(AddLedgerService.class);
|
||||
SteadyChecksquareTaskService taskService = mock(SteadyChecksquareTaskService.class);
|
||||
SteadyChecksquareInfluxQueryComponent influxQueryComponent = mock(SteadyChecksquareInfluxQueryComponent.class);
|
||||
@@ -243,12 +246,69 @@ class SteadyChecksquareServiceImplTest {
|
||||
SteadyChecksquareTaskVO result = service.create(param);
|
||||
|
||||
Assertions.assertEquals(taskCaptor.getValue().getId(), result.getTaskId());
|
||||
Assertions.assertEquals("SUCCESS", result.getTaskStatus());
|
||||
Assertions.assertEquals(Integer.valueOf(1), result.getItemCount());
|
||||
Assertions.assertEquals("RUNNING", result.getTaskStatus());
|
||||
Assertions.assertEquals(Integer.valueOf(0), result.getItemCount());
|
||||
verify(itemService).saveBatch(any());
|
||||
verify(statSummaryService).saveBatch(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldCreateSingleRunningTaskForMultipleLines() {
|
||||
AddLedgerService addLedgerService = mock(AddLedgerService.class);
|
||||
SteadyChecksquareTaskService taskService = mock(SteadyChecksquareTaskService.class);
|
||||
SteadyChecksquareInfluxQueryComponent influxQueryComponent = mock(SteadyChecksquareInfluxQueryComponent.class);
|
||||
SteadyChecksquareValueOrderRuleComponent valueOrderRuleComponent = mock(SteadyChecksquareValueOrderRuleComponent.class);
|
||||
SteadyChecksquareHarmonicParityRuleComponent harmonicParityRuleComponent = mock(SteadyChecksquareHarmonicParityRuleComponent.class);
|
||||
SteadyChecksquareItemService itemService = mock(SteadyChecksquareItemService.class);
|
||||
SteadyChecksquareStatSummaryService statSummaryService = mock(SteadyChecksquareStatSummaryService.class);
|
||||
SteadyChecksquareDetailService detailService = mock(SteadyChecksquareDetailService.class);
|
||||
LambdaQueryChainWrapper<SteadyChecksquareTaskPO> taskQuery = mock(LambdaQueryChainWrapper.class);
|
||||
when(taskService.lambdaQuery()).thenReturn(taskQuery);
|
||||
when(taskQuery.eq(any(), any())).thenReturn(taskQuery);
|
||||
when(taskQuery.and(any())).thenReturn(taskQuery);
|
||||
when(taskQuery.orderByDesc(any(SFunction.class))).thenReturn(taskQuery);
|
||||
when(taskQuery.list()).thenReturn(Collections.emptyList());
|
||||
SteadyChecksquareServiceImpl service = new SteadyChecksquareServiceImpl(new SteadyTrendIndicatorCatalog(),
|
||||
influxQueryComponent, new SteadyChecksquareCalculator(),
|
||||
valueOrderRuleComponent, harmonicParityRuleComponent,
|
||||
new AddDataTimeSlotCalculator(), addLedgerService, taskService,
|
||||
itemService, statSummaryService, detailService, new ObjectMapper());
|
||||
AddLedgerLinePathVO linePath1 = new AddLedgerLinePathVO();
|
||||
linePath1.setLineId("line-001");
|
||||
linePath1.setLineName("line-001");
|
||||
linePath1.setLineInterval(1);
|
||||
AddLedgerLinePathVO linePath2 = new AddLedgerLinePathVO();
|
||||
linePath2.setLineId("line-002");
|
||||
linePath2.setLineName("line-002");
|
||||
linePath2.setLineInterval(1);
|
||||
LinkedHashMap<String, AddLedgerLinePathVO> linePathMap = new LinkedHashMap<String, AddLedgerLinePathVO>();
|
||||
linePathMap.put("line-001", linePath1);
|
||||
linePathMap.put("line-002", linePath2);
|
||||
when(addLedgerService.listLinePathByLineIds(eq(Arrays.asList("line-001", "line-002"))))
|
||||
.thenReturn(linePathMap);
|
||||
when(influxQueryComponent.queryExistingSlots(any(), any(LocalDateTime.class), any(LocalDateTime.class), anyInt()))
|
||||
.thenReturn(new HashSet<LocalDateTime>());
|
||||
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());
|
||||
ArgumentCaptor<SteadyChecksquareTaskPO> taskCaptor = ArgumentCaptor.forClass(SteadyChecksquareTaskPO.class);
|
||||
when(taskService.save(taskCaptor.capture())).thenReturn(true);
|
||||
SteadyChecksquareQueryParam param = new SteadyChecksquareQueryParam();
|
||||
param.setLineIds(Arrays.asList("line-001", "line-002"));
|
||||
param.setIndicatorCodes(Collections.singletonList("V_RMS"));
|
||||
param.setTimeStart("2026-05-01 00:00:00");
|
||||
param.setTimeEnd("2026-05-01 00:01:00");
|
||||
|
||||
SteadyChecksquareTaskVO result = service.create(param);
|
||||
|
||||
Assertions.assertEquals(taskCaptor.getValue().getId(), result.getTaskId());
|
||||
Assertions.assertEquals(Arrays.asList("line-001", "line-002"), result.getLineIds());
|
||||
Assertions.assertEquals("|line-001|line-002|", taskCaptor.getValue().getLineIdsText());
|
||||
Assertions.assertEquals("line-001、line-002", taskCaptor.getValue().getLineName());
|
||||
verify(taskService, times(1)).save(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldUseFixedFlickerIntervalsPerIndicator() {
|
||||
SteadyChecksquareInfluxQueryComponent influxQueryComponent = mock(SteadyChecksquareInfluxQueryComponent.class);
|
||||
@@ -293,6 +353,50 @@ class SteadyChecksquareServiceImplTest {
|
||||
assertItemInterval(result.getItems().get(2), "PLT", 120, 2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldUseAllIndicatorsWhenCreateIndicatorCodesIsEmpty() {
|
||||
SteadyTrendIndicatorCatalog indicatorCatalog = new SteadyTrendIndicatorCatalog();
|
||||
SteadyChecksquareInfluxQueryComponent influxQueryComponent = mock(SteadyChecksquareInfluxQueryComponent.class);
|
||||
SteadyChecksquareValueOrderRuleComponent valueOrderRuleComponent = mock(SteadyChecksquareValueOrderRuleComponent.class);
|
||||
SteadyChecksquareHarmonicParityRuleComponent harmonicParityRuleComponent = mock(SteadyChecksquareHarmonicParityRuleComponent.class);
|
||||
AddLedgerService addLedgerService = mock(AddLedgerService.class);
|
||||
SteadyChecksquareServiceImpl service = new SteadyChecksquareServiceImpl(indicatorCatalog,
|
||||
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("line-001");
|
||||
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), anyInt()))
|
||||
.thenReturn(new HashSet<LocalDateTime>());
|
||||
|
||||
SteadyChecksquareQueryParam param = new SteadyChecksquareQueryParam();
|
||||
param.setLineId("line-001");
|
||||
param.setIndicatorCodes(Collections.<String>emptyList());
|
||||
param.setTimeStart("2026-05-01 00:00:00");
|
||||
param.setTimeEnd("2026-05-01 00:01:00");
|
||||
|
||||
SteadyChecksquareQueryVO result = calculate(service, param);
|
||||
|
||||
List<String> expectedIndicatorCodes = new ArrayList<String>();
|
||||
for (SteadyTrendIndicatorDefinitionBO indicator : indicatorCatalog.listIndicators()) {
|
||||
expectedIndicatorCodes.add(indicator.getIndicatorCode());
|
||||
}
|
||||
List<String> actualIndicatorCodes = new ArrayList<String>();
|
||||
for (SteadyChecksquareItemVO item : result.getItems()) {
|
||||
actualIndicatorCodes.add(item.getIndicatorCode());
|
||||
}
|
||||
Assertions.assertEquals(expectedIndicatorCodes, actualIndicatorCodes);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldAggregateAllHarmonicOrdersIntoIndicatorItem() {
|
||||
SteadyChecksquareInfluxQueryComponent influxQueryComponent = mock(SteadyChecksquareInfluxQueryComponent.class);
|
||||
@@ -566,7 +670,11 @@ class SteadyChecksquareServiceImplTest {
|
||||
task.setTimeEnd(LocalDateTime.of(2026, 5, 1, 0, 1));
|
||||
task.setIntervalMinutes(1);
|
||||
SteadyChecksquareItemPO item1 = buildItemPO("item-001", "V_RMS");
|
||||
item1.setLineId("line-001");
|
||||
item1.setLineName("line-001-name");
|
||||
SteadyChecksquareItemPO item2 = buildItemPO("item-002", "FREQ");
|
||||
item2.setLineId("line-002");
|
||||
item2.setLineName("line-002-name");
|
||||
SteadyChecksquareStatSummaryPO summary1 = buildSummaryPO("item-001", "AVG");
|
||||
SteadyChecksquareStatSummaryPO summary2 = buildSummaryPO("item-002", "AVG");
|
||||
when(taskService.getById("task-001")).thenReturn(task);
|
||||
@@ -585,6 +693,10 @@ class SteadyChecksquareServiceImplTest {
|
||||
SteadyChecksquareQueryVO result = service.detail("task-001");
|
||||
|
||||
Assertions.assertEquals(2, result.getItems().size());
|
||||
Assertions.assertEquals("line-001", result.getItems().get(0).getLineId());
|
||||
Assertions.assertEquals("line-001-name", result.getItems().get(0).getLineName());
|
||||
Assertions.assertEquals("line-002", result.getItems().get(1).getLineId());
|
||||
Assertions.assertEquals("line-002-name", result.getItems().get(1).getLineName());
|
||||
Assertions.assertEquals(1, result.getItems().get(0).getStatSummaries().size());
|
||||
Assertions.assertEquals(1, result.getItems().get(1).getStatSummaries().size());
|
||||
verify(statSummaryService, times(1)).lambdaQuery();
|
||||
@@ -596,6 +708,8 @@ class SteadyChecksquareServiceImplTest {
|
||||
SteadyChecksquareDetailService detailService = mock(SteadyChecksquareDetailService.class);
|
||||
SteadyChecksquareItemPO item = new SteadyChecksquareItemPO();
|
||||
item.setId("item-001");
|
||||
item.setLineId("line-001");
|
||||
item.setLineName("line-001-name");
|
||||
item.setState(1);
|
||||
SteadyChecksquareDetailPO detail = new SteadyChecksquareDetailPO();
|
||||
detail.setItemId("item-001");
|
||||
@@ -619,6 +733,8 @@ class SteadyChecksquareServiceImplTest {
|
||||
Assertions.assertEquals(Integer.valueOf(2), result.getPageNum());
|
||||
Assertions.assertEquals(Integer.valueOf(1), result.getPageSize());
|
||||
Assertions.assertEquals(Long.valueOf(1L), result.getTotal());
|
||||
Assertions.assertEquals("line-001", result.getLineId());
|
||||
Assertions.assertEquals("line-001-name", result.getLineName());
|
||||
Assertions.assertEquals(1, result.getValueOrderDetails().size());
|
||||
verify(detailService).page(any(Page.class), any());
|
||||
}
|
||||
@@ -651,6 +767,75 @@ class SteadyChecksquareServiceImplTest {
|
||||
verify(itemService).update(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldRejectRestartWhenTaskIsNotFail() {
|
||||
SteadyChecksquareTaskService taskService = mock(SteadyChecksquareTaskService.class);
|
||||
SteadyChecksquareTaskPO task = buildTask("task-001", "SUCCESS");
|
||||
when(taskService.getById("task-001")).thenReturn(task);
|
||||
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());
|
||||
|
||||
Assertions.assertThrows(RuntimeException.class, () -> service.restart("task-001"));
|
||||
|
||||
verify(taskService, never()).update(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldRestartFailTaskWithSameTaskIdAndOriginalParam() {
|
||||
AddLedgerService addLedgerService = mock(AddLedgerService.class);
|
||||
SteadyChecksquareTaskService taskService = mock(SteadyChecksquareTaskService.class);
|
||||
SteadyChecksquareInfluxQueryComponent influxQueryComponent = mock(SteadyChecksquareInfluxQueryComponent.class);
|
||||
SteadyChecksquareValueOrderRuleComponent valueOrderRuleComponent = mock(SteadyChecksquareValueOrderRuleComponent.class);
|
||||
SteadyChecksquareHarmonicParityRuleComponent harmonicParityRuleComponent = mock(SteadyChecksquareHarmonicParityRuleComponent.class);
|
||||
SteadyChecksquareItemService itemService = mock(SteadyChecksquareItemService.class);
|
||||
SteadyChecksquareStatSummaryService statSummaryService = mock(SteadyChecksquareStatSummaryService.class);
|
||||
SteadyChecksquareDetailService detailService = mock(SteadyChecksquareDetailService.class);
|
||||
LambdaQueryChainWrapper<SteadyChecksquareItemPO> itemQuery = mock(LambdaQueryChainWrapper.class);
|
||||
SteadyChecksquareTaskPO task = buildTask("task-001", "FAIL");
|
||||
SteadyChecksquareItemPO oldItem = new SteadyChecksquareItemPO();
|
||||
oldItem.setId("item-001");
|
||||
oldItem.setTaskId("task-001");
|
||||
when(taskService.getById("task-001")).thenReturn(task);
|
||||
when(taskService.update(any())).thenReturn(true);
|
||||
when(itemService.lambdaQuery()).thenReturn(itemQuery);
|
||||
when(itemQuery.eq(any(), any())).thenReturn(itemQuery);
|
||||
when(itemQuery.list()).thenReturn(Collections.singletonList(oldItem));
|
||||
when(itemService.remove(any())).thenReturn(true);
|
||||
when(statSummaryService.remove(any())).thenReturn(true);
|
||||
when(detailService.remove(any())).thenReturn(true);
|
||||
AddLedgerLinePathVO linePath = new AddLedgerLinePathVO();
|
||||
linePath.setLineId("line-001");
|
||||
linePath.setLineName("line-001");
|
||||
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), anyInt()))
|
||||
.thenReturn(new HashSet<LocalDateTime>());
|
||||
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());
|
||||
SteadyChecksquareServiceImpl service = new SteadyChecksquareServiceImpl(new SteadyTrendIndicatorCatalog(),
|
||||
influxQueryComponent, new SteadyChecksquareCalculator(),
|
||||
valueOrderRuleComponent, harmonicParityRuleComponent,
|
||||
new AddDataTimeSlotCalculator(), addLedgerService, taskService,
|
||||
itemService, statSummaryService, detailService, new ObjectMapper());
|
||||
|
||||
SteadyChecksquareTaskVO result = service.restart("task-001");
|
||||
|
||||
Assertions.assertEquals("task-001", result.getTaskId());
|
||||
Assertions.assertEquals("RUNNING", result.getTaskStatus());
|
||||
verify(detailService).remove(any());
|
||||
verify(statSummaryService).remove(any());
|
||||
verify(itemService).remove(any());
|
||||
verify(taskService, times(2)).update(any());
|
||||
verify(itemService).saveBatch(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldSaveChecksquareResultsInBatch() {
|
||||
SteadyChecksquareTaskService taskService = mock(SteadyChecksquareTaskService.class);
|
||||
@@ -672,6 +857,8 @@ class SteadyChecksquareServiceImplTest {
|
||||
result.setIntervalMinutes(1);
|
||||
SteadyChecksquareItemVO item = new SteadyChecksquareItemVO();
|
||||
item.setItemKey("line-001|V_RMS");
|
||||
item.setLineId("line-001");
|
||||
item.setLineName("line-001-name");
|
||||
item.setIndicatorCode("V_RMS");
|
||||
item.setIndicatorName("鐩哥數鍘嬫湁鏁堝€?");
|
||||
item.setIntervalMinutes(1);
|
||||
@@ -700,7 +887,11 @@ class SteadyChecksquareServiceImplTest {
|
||||
saveResult(service, param, result);
|
||||
|
||||
verify(taskService).save(any());
|
||||
verify(itemService).saveBatch(any());
|
||||
ArgumentCaptor<List> itemCaptor = ArgumentCaptor.forClass(List.class);
|
||||
verify(itemService).saveBatch(itemCaptor.capture());
|
||||
SteadyChecksquareItemPO savedItem = (SteadyChecksquareItemPO) itemCaptor.getValue().get(0);
|
||||
Assertions.assertEquals("line-001", savedItem.getLineId());
|
||||
Assertions.assertEquals("line-001-name", savedItem.getLineName());
|
||||
verify(statSummaryService).saveBatch(any());
|
||||
}
|
||||
|
||||
@@ -846,9 +1037,9 @@ class SteadyChecksquareServiceImplTest {
|
||||
List<SteadyChecksquareItemVO> orderItems) {
|
||||
try {
|
||||
Method method = SteadyChecksquareServiceImpl.class.getDeclaredMethod("aggregateHarmonicItems",
|
||||
String.class, SteadyTrendIndicatorDefinitionBO.class, List.class, int.class);
|
||||
String.class, String.class, SteadyTrendIndicatorDefinitionBO.class, List.class, int.class);
|
||||
method.setAccessible(true);
|
||||
return (SteadyChecksquareItemVO) method.invoke(service, "line-001", indicator, orderItems, 1);
|
||||
return (SteadyChecksquareItemVO) method.invoke(service, "line-001", "line-001-name", indicator, orderItems, 1);
|
||||
} catch (Exception exception) {
|
||||
throw new RuntimeException(exception);
|
||||
}
|
||||
@@ -862,6 +1053,29 @@ class SteadyChecksquareServiceImplTest {
|
||||
return indicator;
|
||||
}
|
||||
|
||||
private SteadyChecksquareTaskPO buildTask(String taskId, String taskStatus) {
|
||||
SteadyChecksquareTaskPO task = new SteadyChecksquareTaskPO();
|
||||
task.setId(taskId);
|
||||
task.setTaskNo("CS202605010001");
|
||||
task.setLineId("line-001");
|
||||
task.setLineName("line-001");
|
||||
task.setLineIdsJson("[\"line-001\"]");
|
||||
task.setLineIdsText("|line-001|");
|
||||
task.setTimeStart(LocalDateTime.of(2026, 5, 1, 0, 0));
|
||||
task.setTimeEnd(LocalDateTime.of(2026, 5, 1, 0, 1));
|
||||
task.setIntervalMinutes(1);
|
||||
task.setIndicatorCodesJson("[\"V_RMS\"]");
|
||||
task.setIndicatorCodesText("|V_RMS|");
|
||||
task.setTaskStatus(taskStatus);
|
||||
task.setItemCount(1);
|
||||
task.setAbnormalItemCount(1);
|
||||
task.setMinDataIntegrity(BigDecimal.ZERO.setScale(6));
|
||||
task.setResultMessage("failed");
|
||||
task.setState(1);
|
||||
task.setCreateTime(LocalDateTime.of(2026, 5, 1, 1, 0));
|
||||
return task;
|
||||
}
|
||||
|
||||
private SteadyChecksquareItemVO buildOrderItem(boolean hasData, BigDecimal dataIntegrity) {
|
||||
SteadyChecksquareItemVO item = new SteadyChecksquareItemVO();
|
||||
item.setItemKey("line-001|V_HARMONIC|2");
|
||||
|
||||
Reference in New Issue
Block a user