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;
|
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) {
|
public String buildChecksquareQuery(SteadyTrendResolvedFieldBO field, LocalDateTime startTime, LocalDateTime endTime) {
|
||||||
return buildValuePointQuery(field, startTime, endTime);
|
return buildValuePointQuery(field, startTime, endTime);
|
||||||
}
|
}
|
||||||
@@ -197,6 +256,33 @@ public class SteadyChecksquareInfluxQueryComponent {
|
|||||||
return result;
|
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) {
|
private LocalDateTime min(LocalDateTime first, LocalDateTime second) {
|
||||||
return first.isAfter(second) ? second : first;
|
return first.isAfter(second) ? second : first;
|
||||||
}
|
}
|
||||||
@@ -238,6 +324,28 @@ public class SteadyChecksquareInfluxQueryComponent {
|
|||||||
return sql.toString();
|
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) {
|
private List<SteadyChecksquareValuePointBO> parseValuePoints(String body, int intervalMinutes) {
|
||||||
try {
|
try {
|
||||||
JsonNode root = OBJECT_MAPPER.readTree(body);
|
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) {
|
private LocalDateTime alignToPreviousSlot(LocalDateTime time, int intervalMinutes) {
|
||||||
LocalDateTime minuteFloor = time.withSecond(0).withNano(0);
|
LocalDateTime minuteFloor = time.withSecond(0).withNano(0);
|
||||||
int minuteOfDay = minuteFloor.getHour() * 60 + minuteFloor.getMinute();
|
int minuteOfDay = minuteFloor.getHour() * 60 + minuteFloor.getMinute();
|
||||||
@@ -390,6 +544,16 @@ public class SteadyChecksquareInfluxQueryComponent {
|
|||||||
return value == null ? "" : value.replace("\\", "\\\\").replace("'", "\\'");
|
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) {
|
private String resolveValueType(String statType) {
|
||||||
if (statType == null || statType.trim().isEmpty()) {
|
if (statType == null || statType.trim().isEmpty()) {
|
||||||
return "AVG";
|
return "AVG";
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import org.springframework.stereotype.Component;
|
|||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
@@ -58,9 +59,18 @@ public class SteadyChecksquareValueOrderRuleComponent {
|
|||||||
LocalDateTime startTime, LocalDateTime endTime,
|
LocalDateTime startTime, LocalDateTime endTime,
|
||||||
int intervalMinutes) {
|
int intervalMinutes) {
|
||||||
Map<String, Map<LocalDateTime, BigDecimal>> result = new LinkedHashMap<String, Map<LocalDateTime, BigDecimal>>();
|
Map<String, Map<LocalDateTime, BigDecimal>> result = new LinkedHashMap<String, Map<LocalDateTime, BigDecimal>>();
|
||||||
|
List<SteadyTrendResolvedFieldBO> fields = new ArrayList<SteadyTrendResolvedFieldBO>();
|
||||||
for (String statType : REQUIRED_STATS) {
|
for (String statType : REQUIRED_STATS) {
|
||||||
SteadyTrendResolvedFieldBO field = buildResolvedField(lineId, indicator, harmonicOrder, phase, statType);
|
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;
|
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);
|
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)
|
@OperateInfo(info = LogEnum.BUSINESS_COMMON, operateType = OperateType.DELETE)
|
||||||
@ApiOperation("删除数据校验任务")
|
@ApiOperation("删除数据校验任务")
|
||||||
@PostMapping("/delete")
|
@PostMapping("/delete")
|
||||||
|
|||||||
@@ -19,6 +19,9 @@ public class SteadyChecksquareQueryParam implements Serializable {
|
|||||||
@ApiModelProperty("监测点 ID")
|
@ApiModelProperty("监测点 ID")
|
||||||
private String lineId;
|
private String lineId;
|
||||||
|
|
||||||
|
@ApiModelProperty("监测点 ID 列表")
|
||||||
|
private List<String> lineIds;
|
||||||
|
|
||||||
@ApiModelProperty("指标编码")
|
@ApiModelProperty("指标编码")
|
||||||
private List<String> indicatorCodes;
|
private List<String> indicatorCodes;
|
||||||
|
|
||||||
|
|||||||
@@ -24,6 +24,10 @@ public class SteadyChecksquareItemPO implements Serializable {
|
|||||||
private String taskId;
|
private String taskId;
|
||||||
@TableField("item_key")
|
@TableField("item_key")
|
||||||
private String itemKey;
|
private String itemKey;
|
||||||
|
@TableField("line_id")
|
||||||
|
private String lineId;
|
||||||
|
@TableField("line_name")
|
||||||
|
private String lineName;
|
||||||
@TableField("indicator_code")
|
@TableField("indicator_code")
|
||||||
private String indicatorCode;
|
private String indicatorCode;
|
||||||
@TableField("indicator_name")
|
@TableField("indicator_name")
|
||||||
|
|||||||
@@ -26,6 +26,10 @@ public class SteadyChecksquareTaskPO implements Serializable {
|
|||||||
private String lineId;
|
private String lineId;
|
||||||
@TableField("line_name")
|
@TableField("line_name")
|
||||||
private String lineName;
|
private String lineName;
|
||||||
|
@TableField("line_ids_json")
|
||||||
|
private String lineIdsJson;
|
||||||
|
@TableField("line_ids_text")
|
||||||
|
private String lineIdsText;
|
||||||
@TableField("time_start")
|
@TableField("time_start")
|
||||||
private LocalDateTime timeStart;
|
private LocalDateTime timeStart;
|
||||||
@TableField("time_end")
|
@TableField("time_end")
|
||||||
|
|||||||
@@ -20,6 +20,12 @@ public class SteadyChecksquareItemDetailVO implements Serializable {
|
|||||||
@ApiModelProperty("检测项 ID")
|
@ApiModelProperty("检测项 ID")
|
||||||
private String itemId;
|
private String itemId;
|
||||||
|
|
||||||
|
@ApiModelProperty("监测点 ID")
|
||||||
|
private String lineId;
|
||||||
|
|
||||||
|
@ApiModelProperty("监测点名称")
|
||||||
|
private String lineName;
|
||||||
|
|
||||||
@ApiModelProperty("明细类型")
|
@ApiModelProperty("明细类型")
|
||||||
private String detailType;
|
private String detailType;
|
||||||
|
|
||||||
|
|||||||
@@ -24,6 +24,12 @@ public class SteadyChecksquareItemVO implements Serializable {
|
|||||||
@ApiModelProperty("校验项唯一键")
|
@ApiModelProperty("校验项唯一键")
|
||||||
private String itemKey;
|
private String itemKey;
|
||||||
|
|
||||||
|
@ApiModelProperty("监测点 ID")
|
||||||
|
private String lineId;
|
||||||
|
|
||||||
|
@ApiModelProperty("监测点名称")
|
||||||
|
private String lineName;
|
||||||
|
|
||||||
@ApiModelProperty("指标编码")
|
@ApiModelProperty("指标编码")
|
||||||
private String indicatorCode;
|
private String indicatorCode;
|
||||||
|
|
||||||
|
|||||||
@@ -26,6 +26,9 @@ public class SteadyChecksquareQueryVO implements Serializable {
|
|||||||
@ApiModelProperty("监测点 ID")
|
@ApiModelProperty("监测点 ID")
|
||||||
private String lineId;
|
private String lineId;
|
||||||
|
|
||||||
|
@ApiModelProperty("监测点 ID 列表")
|
||||||
|
private List<String> lineIds = new ArrayList<String>();
|
||||||
|
|
||||||
@ApiModelProperty("监测点名称")
|
@ApiModelProperty("监测点名称")
|
||||||
private String lineName;
|
private String lineName;
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import lombok.Data;
|
|||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 数据校验历史任务。
|
* 数据校验历史任务。
|
||||||
@@ -25,6 +26,9 @@ public class SteadyChecksquareTaskVO implements Serializable {
|
|||||||
@ApiModelProperty("监测点 ID")
|
@ApiModelProperty("监测点 ID")
|
||||||
private String lineId;
|
private String lineId;
|
||||||
|
|
||||||
|
@ApiModelProperty("监测点 ID 列表")
|
||||||
|
private List<String> lineIds;
|
||||||
|
|
||||||
@ApiModelProperty("监测点名称")
|
@ApiModelProperty("监测点名称")
|
||||||
private String lineName;
|
private String lineName;
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ public interface SteadyChecksquareService {
|
|||||||
|
|
||||||
SteadyChecksquareTaskVO create(SteadyChecksquareQueryParam param);
|
SteadyChecksquareTaskVO create(SteadyChecksquareQueryParam param);
|
||||||
|
|
||||||
|
SteadyChecksquareTaskVO restart(String taskId);
|
||||||
|
|
||||||
boolean delete(List<String> taskIds);
|
boolean delete(List<String> taskIds);
|
||||||
|
|
||||||
SteadyChecksquareQueryVO detail(String taskId);
|
SteadyChecksquareQueryVO detail(String taskId);
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ import com.njcn.web.factory.PageFactory;
|
|||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.beans.factory.annotation.Qualifier;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.support.TransactionTemplate;
|
import org.springframework.transaction.support.TransactionTemplate;
|
||||||
|
|
||||||
@@ -64,6 +65,8 @@ import java.util.LinkedHashSet;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.RejectedExecutionException;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -98,12 +101,16 @@ public class SteadyChecksquareServiceImpl implements SteadyChecksquareService {
|
|||||||
@Autowired(required = false)
|
@Autowired(required = false)
|
||||||
private TransactionTemplate transactionTemplate;
|
private TransactionTemplate transactionTemplate;
|
||||||
|
|
||||||
|
@Autowired(required = false)
|
||||||
|
@Qualifier("steadyChecksquareExecutorService")
|
||||||
|
private ExecutorService steadyChecksquareExecutorService;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Page<SteadyChecksquareTaskVO> query(SteadyChecksquareHistoryQueryParam param) {
|
public Page<SteadyChecksquareTaskVO> query(SteadyChecksquareHistoryQueryParam param) {
|
||||||
SteadyChecksquareHistoryQueryParam query = param == null ? new SteadyChecksquareHistoryQueryParam() : param;
|
SteadyChecksquareHistoryQueryParam query = param == null ? new SteadyChecksquareHistoryQueryParam() : param;
|
||||||
LambdaQueryWrapper<SteadyChecksquareTaskPO> wrapper = new LambdaQueryWrapper<SteadyChecksquareTaskPO>()
|
LambdaQueryWrapper<SteadyChecksquareTaskPO> wrapper = new LambdaQueryWrapper<SteadyChecksquareTaskPO>()
|
||||||
.eq(SteadyChecksquareTaskPO::getState, SteadyChecksquareConst.STATE_ENABLED)
|
.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()) + "|")
|
.like(trimToNull(query.getIndicatorCode()) != null, SteadyChecksquareTaskPO::getIndicatorCodesText, "|" + trimToNull(query.getIndicatorCode()) + "|")
|
||||||
.ge(trimToNull(query.getTimeStart()) != null, SteadyChecksquareTaskPO::getTimeStart, parseOptionalTime(query.getTimeStart()))
|
.ge(trimToNull(query.getTimeStart()) != null, SteadyChecksquareTaskPO::getTimeStart, parseOptionalTime(query.getTimeStart()))
|
||||||
.le(trimToNull(query.getTimeEnd()) != null, SteadyChecksquareTaskPO::getTimeEnd, parseOptionalTime(query.getTimeEnd()))
|
.le(trimToNull(query.getTimeEnd()) != null, SteadyChecksquareTaskPO::getTimeEnd, parseOptionalTime(query.getTimeEnd()))
|
||||||
@@ -118,24 +125,47 @@ public class SteadyChecksquareServiceImpl implements SteadyChecksquareService {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SteadyChecksquareTaskVO create(SteadyChecksquareQueryParam param) {
|
public SteadyChecksquareTaskVO create(SteadyChecksquareQueryParam param) {
|
||||||
|
long createStartMillis = System.currentTimeMillis();
|
||||||
validateCreateBaseParam(param);
|
validateCreateBaseParam(param);
|
||||||
String lineId = trimToNull(param.getLineId());
|
List<String> lineIds = resolveLineIds(param);
|
||||||
LocalDateTime startTime = parseRequiredTime(param.getTimeStart(), "开始时间不能为空");
|
LocalDateTime startTime = parseRequiredTime(param.getTimeStart(), "开始时间不能为空");
|
||||||
LocalDateTime endTime = parseRequiredTime(param.getTimeEnd(), "结束时间不能为空");
|
LocalDateTime endTime = parseRequiredTime(param.getTimeEnd(), "结束时间不能为空");
|
||||||
SteadyChecksquareTaskPO existedTask = findExistingTask(lineId, startTime, endTime);
|
SteadyChecksquareTaskPO existedTask = findExistingTask(lineIds, startTime, endTime);
|
||||||
if (existedTask != null) {
|
if (existedTask != null) {
|
||||||
|
log.info("数据校验创建命中已有任务,taskId={},lineId={},costMs={}",
|
||||||
|
existedTask.getId(), lineIds, System.currentTimeMillis() - createStartMillis);
|
||||||
return toTaskVO(existedTask);
|
return toTaskVO(existedTask);
|
||||||
}
|
}
|
||||||
prepareCreateContext(param);
|
CreateContext context = prepareCreateContext(param);
|
||||||
influxQueryComponent.enableRequestCache();
|
SteadyChecksquareQueryParam taskParam = copyCreateParam(param, context.indicatorCodes);
|
||||||
|
SteadyChecksquareTaskPO task = saveRunningTaskInTransaction(taskParam, context);
|
||||||
try {
|
try {
|
||||||
SteadyChecksquareQueryVO result = calculate(param);
|
submitCreateTask(task.getId(), taskParam);
|
||||||
SteadyChecksquareTaskPO task = saveResultInTransaction(param, result);
|
} catch (RejectedExecutionException ex) {
|
||||||
return toTaskVO(task);
|
markTaskFail(task.getId(), ex.getMessage());
|
||||||
} finally {
|
throw fail("数据校验任务线程池已满,请稍后重试");
|
||||||
influxQueryComponent.clearRequestCache();
|
|
||||||
}
|
}
|
||||||
|
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
|
@Override
|
||||||
public boolean delete(List<String> taskIds) {
|
public boolean delete(List<String> taskIds) {
|
||||||
List<String> ids = normalizeTextList(taskIds);
|
List<String> ids = normalizeTextList(taskIds);
|
||||||
@@ -166,6 +196,7 @@ public class SteadyChecksquareServiceImpl implements SteadyChecksquareService {
|
|||||||
result.setTaskId(task.getId());
|
result.setTaskId(task.getId());
|
||||||
result.setTaskNo(task.getTaskNo());
|
result.setTaskNo(task.getTaskNo());
|
||||||
result.setLineId(task.getLineId());
|
result.setLineId(task.getLineId());
|
||||||
|
result.setLineIds(readTaskLineIds(task));
|
||||||
result.setLineName(task.getLineName());
|
result.setLineName(task.getLineName());
|
||||||
result.setTimeStart(formatTime(task.getTimeStart()));
|
result.setTimeStart(formatTime(task.getTimeStart()));
|
||||||
result.setTimeEnd(formatTime(task.getTimeEnd()));
|
result.setTimeEnd(formatTime(task.getTimeEnd()));
|
||||||
@@ -198,6 +229,8 @@ public class SteadyChecksquareServiceImpl implements SteadyChecksquareService {
|
|||||||
}
|
}
|
||||||
SteadyChecksquareItemDetailVO result = new SteadyChecksquareItemDetailVO();
|
SteadyChecksquareItemDetailVO result = new SteadyChecksquareItemDetailVO();
|
||||||
result.setItemId(item.getId());
|
result.setItemId(item.getId());
|
||||||
|
result.setLineId(item.getLineId());
|
||||||
|
result.setLineName(item.getLineName());
|
||||||
result.setDetailType(type);
|
result.setDetailType(type);
|
||||||
result.setStatType(statType);
|
result.setStatType(statType);
|
||||||
LambdaQueryWrapper<SteadyChecksquareDetailPO> wrapper = new LambdaQueryWrapper<SteadyChecksquareDetailPO>()
|
LambdaQueryWrapper<SteadyChecksquareDetailPO> wrapper = new LambdaQueryWrapper<SteadyChecksquareDetailPO>()
|
||||||
@@ -240,22 +273,85 @@ public class SteadyChecksquareServiceImpl implements SteadyChecksquareService {
|
|||||||
return taskResult;
|
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) {
|
private CreateContext prepareCreateContext(SteadyChecksquareQueryParam param) {
|
||||||
validateParam(param);
|
validateParam(param);
|
||||||
String lineId = trimToNull(param.getLineId());
|
List<String> lineIds = resolveLineIds(param);
|
||||||
LocalDateTime startTime = parseRequiredTime(param.getTimeStart(), "开始时间不能为空");
|
LocalDateTime startTime = parseRequiredTime(param.getTimeStart(), "开始时间不能为空");
|
||||||
LocalDateTime endTime = parseRequiredTime(param.getTimeEnd(), "结束时间不能为空");
|
LocalDateTime endTime = parseRequiredTime(param.getTimeEnd(), "结束时间不能为空");
|
||||||
if (startTime.isAfter(endTime)) {
|
if (startTime.isAfter(endTime)) {
|
||||||
throw fail("开始时间不能大于结束时间");
|
throw fail("开始时间不能大于结束时间");
|
||||||
}
|
}
|
||||||
AddLedgerLinePathVO linePath = requireLinePath(lineId);
|
Map<String, AddLedgerLinePathVO> linePathMap = requireLinePaths(lineIds);
|
||||||
int intervalMinutes = resolveIntervalMinutes(linePath);
|
int intervalMinutes = resolveIntervalMinutes(linePathMap.get(lineIds.get(0)));
|
||||||
List<String> indicatorCodes = normalizeTextList(param.getIndicatorCodes());
|
List<String> indicatorCodes = resolveIndicatorCodes(param);
|
||||||
for (String indicatorCode : indicatorCodes) {
|
for (String indicatorCode : indicatorCodes) {
|
||||||
requireIndicator(indicatorCode);
|
requireIndicator(indicatorCode);
|
||||||
}
|
}
|
||||||
validateCreateTimeRange(startTime, endTime);
|
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) {
|
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()
|
List<SteadyChecksquareTaskPO> tasks = taskService.lambdaQuery()
|
||||||
.eq(SteadyChecksquareTaskPO::getState, SteadyChecksquareConst.STATE_ENABLED)
|
.eq(SteadyChecksquareTaskPO::getState, SteadyChecksquareConst.STATE_ENABLED)
|
||||||
.eq(SteadyChecksquareTaskPO::getLineId, lineId)
|
.eq(SteadyChecksquareTaskPO::getLineIdsText, buildTextListSearchValue(lineIds))
|
||||||
.eq(SteadyChecksquareTaskPO::getTimeStart, startTime)
|
.eq(SteadyChecksquareTaskPO::getTimeStart, startTime)
|
||||||
.eq(SteadyChecksquareTaskPO::getTimeEnd, endTime)
|
.eq(SteadyChecksquareTaskPO::getTimeEnd, endTime)
|
||||||
.orderByDesc(SteadyChecksquareTaskPO::getCreateTime)
|
.orderByDesc(SteadyChecksquareTaskPO::getCreateTime)
|
||||||
@@ -287,36 +383,43 @@ public class SteadyChecksquareServiceImpl implements SteadyChecksquareService {
|
|||||||
|
|
||||||
private SteadyChecksquareQueryVO calculate(SteadyChecksquareQueryParam param) {
|
private SteadyChecksquareQueryVO calculate(SteadyChecksquareQueryParam param) {
|
||||||
validateParam(param);
|
validateParam(param);
|
||||||
String lineId = trimToNull(param.getLineId());
|
List<String> lineIds = resolveLineIds(param);
|
||||||
LocalDateTime startTime = parseRequiredTime(param.getTimeStart(), "开始时间不能为空");
|
LocalDateTime startTime = parseRequiredTime(param.getTimeStart(), "开始时间不能为空");
|
||||||
LocalDateTime endTime = parseRequiredTime(param.getTimeEnd(), "结束时间不能为空");
|
LocalDateTime endTime = parseRequiredTime(param.getTimeEnd(), "结束时间不能为空");
|
||||||
if (startTime.isAfter(endTime)) {
|
if (startTime.isAfter(endTime)) {
|
||||||
throw fail("开始时间不能大于结束时间");
|
throw fail("开始时间不能大于结束时间");
|
||||||
}
|
}
|
||||||
AddLedgerLinePathVO linePath = requireLinePath(lineId);
|
Map<String, AddLedgerLinePathVO> linePathMap = requireLinePaths(lineIds);
|
||||||
int intervalMinutes = resolveIntervalMinutes(linePath);
|
int intervalMinutes = resolveIntervalMinutes(linePathMap.get(lineIds.get(0)));
|
||||||
SteadyChecksquareQueryVO result = new SteadyChecksquareQueryVO();
|
SteadyChecksquareQueryVO result = new SteadyChecksquareQueryVO();
|
||||||
result.setLineId(lineId);
|
result.setLineId(lineIds.get(0));
|
||||||
result.setLineName(trimToNull(linePath.getLineName()) == null ? EMPTY_TEXT : linePath.getLineName());
|
result.setLineIds(new ArrayList<String>(lineIds));
|
||||||
|
result.setLineName(buildLineNames(lineIds, linePathMap));
|
||||||
result.setTimeStart(param.getTimeStart());
|
result.setTimeStart(param.getTimeStart());
|
||||||
result.setTimeEnd(param.getTimeEnd());
|
result.setTimeEnd(param.getTimeEnd());
|
||||||
result.setIntervalMinutes(intervalMinutes);
|
result.setIntervalMinutes(intervalMinutes);
|
||||||
|
|
||||||
long startMillis = System.currentTimeMillis();
|
long startMillis = System.currentTimeMillis();
|
||||||
List<String> indicatorCodes = normalizeTextList(param.getIndicatorCodes());
|
List<String> indicatorCodes = resolveIndicatorCodes(param);
|
||||||
List<SteadyTrendIndicatorDefinitionBO> indicators = new ArrayList<SteadyTrendIndicatorDefinitionBO>();
|
List<SteadyTrendIndicatorDefinitionBO> indicators = new ArrayList<SteadyTrendIndicatorDefinitionBO>();
|
||||||
for (String indicatorCode : indicatorCodes) {
|
for (String indicatorCode : indicatorCodes) {
|
||||||
indicators.add(requireIndicator(indicatorCode));
|
indicators.add(requireIndicator(indicatorCode));
|
||||||
}
|
}
|
||||||
log.info("数据校验新增检测开始,lineId={},indicatorCount={},timeStart={},timeEnd={},intervalMinutes={}",
|
log.info("数据校验新增检测开始,lineId={},indicatorCount={},timeStart={},timeEnd={},intervalMinutes={}",
|
||||||
lineId, indicatorCodes.size(), startTime, endTime, intervalMinutes);
|
lineIds, indicatorCodes.size(), startTime, endTime, intervalMinutes);
|
||||||
prefetchNormalIndicatorPoints(lineId, indicators, startTime, endTime, intervalMinutes);
|
for (String lineId : lineIds) {
|
||||||
for (SteadyTrendIndicatorDefinitionBO indicator : indicators) {
|
AddLedgerLinePathVO linePath = linePathMap.get(lineId);
|
||||||
int itemIntervalMinutes = resolveIndicatorIntervalMinutes(indicator, intervalMinutes);
|
int lineIntervalMinutes = resolveIntervalMinutes(linePath);
|
||||||
List<LocalDateTime> itemSlots = timeSlotCalculator.buildTimeSlots(startTime, endTime, itemIntervalMinutes);
|
String lineName = linePath == null ? null : trimToNull(linePath.getLineName());
|
||||||
result.getItems().addAll(buildIndicatorItems(lineId, indicator, startTime, endTime, itemSlots, itemIntervalMinutes));
|
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;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -327,6 +430,13 @@ public class SteadyChecksquareServiceImpl implements SteadyChecksquareService {
|
|||||||
return transactionTemplate.execute(status -> saveResult(param, result));
|
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) {
|
private SteadyChecksquareTaskPO saveResultInTransaction(String taskId, SteadyChecksquareQueryParam param, SteadyChecksquareQueryVO result) {
|
||||||
if (transactionTemplate == null) {
|
if (transactionTemplate == null) {
|
||||||
return saveResult(taskId, param, result);
|
return saveResult(taskId, param, result);
|
||||||
@@ -338,17 +448,73 @@ public class SteadyChecksquareServiceImpl implements SteadyChecksquareService {
|
|||||||
return saveResult(null, param, result);
|
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) {
|
private SteadyChecksquareTaskPO saveResult(String taskId, SteadyChecksquareQueryParam param, SteadyChecksquareQueryVO result) {
|
||||||
|
long saveStartMillis = System.currentTimeMillis();
|
||||||
LocalDateTime now = LocalDateTime.now();
|
LocalDateTime now = LocalDateTime.now();
|
||||||
SteadyChecksquareTaskPO task = new SteadyChecksquareTaskPO();
|
SteadyChecksquareTaskPO task = new SteadyChecksquareTaskPO();
|
||||||
task.setId(trimToNull(taskId) == null ? SteadyChecksquareIdUtil.uuid() : taskId);
|
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.setLineId(result.getLineId());
|
||||||
task.setLineName(result.getLineName());
|
task.setLineName(result.getLineName());
|
||||||
|
List<String> lineIds = resolveResultLineIds(result);
|
||||||
|
task.setLineIdsJson(writeJson(lineIds));
|
||||||
|
task.setLineIdsText(buildTextListSearchValue(lineIds));
|
||||||
task.setTimeStart(parseRequiredTime(result.getTimeStart(), "开始时间不能为空"));
|
task.setTimeStart(parseRequiredTime(result.getTimeStart(), "开始时间不能为空"));
|
||||||
task.setTimeEnd(parseRequiredTime(result.getTimeEnd(), "结束时间不能为空"));
|
task.setTimeEnd(parseRequiredTime(result.getTimeEnd(), "结束时间不能为空"));
|
||||||
task.setIntervalMinutes(result.getIntervalMinutes());
|
task.setIntervalMinutes(result.getIntervalMinutes());
|
||||||
List<String> indicatorCodes = normalizeTextList(param.getIndicatorCodes());
|
List<String> indicatorCodes = resolveIndicatorCodes(param);
|
||||||
task.setIndicatorCodesJson(writeJson(indicatorCodes));
|
task.setIndicatorCodesJson(writeJson(indicatorCodes));
|
||||||
task.setIndicatorCodesText(buildIndicatorCodesText(indicatorCodes));
|
task.setIndicatorCodesText(buildIndicatorCodesText(indicatorCodes));
|
||||||
task.setTaskStatus(SteadyChecksquareConst.TASK_STATUS_SUCCESS);
|
task.setTaskStatus(SteadyChecksquareConst.TASK_STATUS_SUCCESS);
|
||||||
@@ -384,12 +550,18 @@ public class SteadyChecksquareServiceImpl implements SteadyChecksquareService {
|
|||||||
if (!detailPOs.isEmpty()) {
|
if (!detailPOs.isEmpty()) {
|
||||||
saveDetailBatchInChunks(detailPOs);
|
saveDetailBatchInChunks(detailPOs);
|
||||||
}
|
}
|
||||||
|
log.info("数据校验结果保存完成,taskId={},itemCount={},summaryCount={},detailCount={},costMs={}",
|
||||||
|
task.getId(), itemPOs.size(), summaryPOs.size(), detailPOs.size(), System.currentTimeMillis() - saveStartMillis);
|
||||||
return task;
|
return task;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateCompletedTask(SteadyChecksquareTaskPO task) {
|
private void updateCompletedTask(SteadyChecksquareTaskPO task) {
|
||||||
LambdaUpdateWrapper<SteadyChecksquareTaskPO> wrapper = new LambdaUpdateWrapper<SteadyChecksquareTaskPO>()
|
LambdaUpdateWrapper<SteadyChecksquareTaskPO> wrapper = new LambdaUpdateWrapper<SteadyChecksquareTaskPO>()
|
||||||
.set(SteadyChecksquareTaskPO::getLineName, task.getLineName())
|
.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::getIntervalMinutes, task.getIntervalMinutes())
|
||||||
.set(SteadyChecksquareTaskPO::getTaskStatus, task.getTaskStatus())
|
.set(SteadyChecksquareTaskPO::getTaskStatus, task.getTaskStatus())
|
||||||
.set(SteadyChecksquareTaskPO::getItemCount, task.getItemCount())
|
.set(SteadyChecksquareTaskPO::getItemCount, task.getItemCount())
|
||||||
@@ -414,6 +586,8 @@ public class SteadyChecksquareServiceImpl implements SteadyChecksquareService {
|
|||||||
po.setId(SteadyChecksquareIdUtil.uuid());
|
po.setId(SteadyChecksquareIdUtil.uuid());
|
||||||
po.setTaskId(taskId);
|
po.setTaskId(taskId);
|
||||||
po.setItemKey(item.getItemKey());
|
po.setItemKey(item.getItemKey());
|
||||||
|
po.setLineId(item.getLineId());
|
||||||
|
po.setLineName(item.getLineName());
|
||||||
po.setIndicatorCode(item.getIndicatorCode());
|
po.setIndicatorCode(item.getIndicatorCode());
|
||||||
po.setIndicatorName(item.getIndicatorName());
|
po.setIndicatorName(item.getIndicatorName());
|
||||||
po.setHarmonicOrder(item.getHarmonicOrder());
|
po.setHarmonicOrder(item.getHarmonicOrder());
|
||||||
@@ -542,7 +716,8 @@ public class SteadyChecksquareServiceImpl implements SteadyChecksquareService {
|
|||||||
+ field.getStatType() + "|" + intervalMinutes;
|
+ 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,
|
LocalDateTime startTime, LocalDateTime endTime,
|
||||||
List<LocalDateTime> slots, int intervalMinutes) {
|
List<LocalDateTime> slots, int intervalMinutes) {
|
||||||
List<SteadyChecksquareItemVO> result = new ArrayList<SteadyChecksquareItemVO>();
|
List<SteadyChecksquareItemVO> result = new ArrayList<SteadyChecksquareItemVO>();
|
||||||
@@ -550,12 +725,12 @@ public class SteadyChecksquareServiceImpl implements SteadyChecksquareService {
|
|||||||
List<Integer> harmonicOrders = buildAggregateHarmonicOrders(indicator);
|
List<Integer> harmonicOrders = buildAggregateHarmonicOrders(indicator);
|
||||||
prefetchHarmonicIndicatorPoints(lineId, indicator, harmonicOrders, startTime, endTime, intervalMinutes);
|
prefetchHarmonicIndicatorPoints(lineId, indicator, harmonicOrders, startTime, endTime, intervalMinutes);
|
||||||
for (Integer order : harmonicOrders) {
|
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);
|
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;
|
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) {
|
List<SteadyChecksquareItemVO> orderItems, int intervalMinutes) {
|
||||||
SteadyChecksquareItemVO result = new SteadyChecksquareItemVO();
|
SteadyChecksquareItemVO result = new SteadyChecksquareItemVO();
|
||||||
result.setItemKey(buildItemKey(lineId, indicator, null));
|
result.setItemKey(buildItemKey(lineId, indicator, null));
|
||||||
|
result.setLineId(lineId);
|
||||||
|
result.setLineName(lineName);
|
||||||
result.setIndicatorCode(indicator.getIndicatorCode());
|
result.setIndicatorCode(indicator.getIndicatorCode());
|
||||||
result.setIndicatorName(indicator.getName());
|
result.setIndicatorName(indicator.getName());
|
||||||
result.setHarmonicOrder(null);
|
result.setHarmonicOrder(null);
|
||||||
@@ -654,11 +832,14 @@ public class SteadyChecksquareServiceImpl implements SteadyChecksquareService {
|
|||||||
return new ArrayList<SteadyChecksquareStatDetailVO>(detailMap.values());
|
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,
|
LocalDateTime startTime, LocalDateTime endTime,
|
||||||
List<LocalDateTime> slots, int intervalMinutes) {
|
List<LocalDateTime> slots, int intervalMinutes) {
|
||||||
SteadyChecksquareItemVO item = new SteadyChecksquareItemVO();
|
SteadyChecksquareItemVO item = new SteadyChecksquareItemVO();
|
||||||
item.setItemKey(buildItemKey(lineId, indicator, harmonicOrder));
|
item.setItemKey(buildItemKey(lineId, indicator, harmonicOrder));
|
||||||
|
item.setLineId(lineId);
|
||||||
|
item.setLineName(lineName);
|
||||||
item.setIndicatorCode(indicator.getIndicatorCode());
|
item.setIndicatorCode(indicator.getIndicatorCode());
|
||||||
item.setIndicatorName(indicator.getName());
|
item.setIndicatorName(indicator.getName());
|
||||||
item.setHarmonicOrder(harmonicOrder);
|
item.setHarmonicOrder(harmonicOrder);
|
||||||
@@ -822,7 +1003,7 @@ public class SteadyChecksquareServiceImpl implements SteadyChecksquareService {
|
|||||||
if (param == null) {
|
if (param == null) {
|
||||||
throw fail("数据校验参数不能为空");
|
throw fail("数据校验参数不能为空");
|
||||||
}
|
}
|
||||||
if (trimToNull(param.getLineId()) == null) {
|
if (resolveLineIds(param).isEmpty()) {
|
||||||
throw fail("监测点ID不能为空");
|
throw fail("监测点ID不能为空");
|
||||||
}
|
}
|
||||||
parseRequiredTime(param.getTimeStart(), "开始时间不能为空");
|
parseRequiredTime(param.getTimeStart(), "开始时间不能为空");
|
||||||
@@ -833,12 +1014,9 @@ public class SteadyChecksquareServiceImpl implements SteadyChecksquareService {
|
|||||||
if (param == null) {
|
if (param == null) {
|
||||||
throw fail("数据校验参数不能为空");
|
throw fail("数据校验参数不能为空");
|
||||||
}
|
}
|
||||||
if (trimToNull(param.getLineId()) == null) {
|
if (resolveLineIds(param).isEmpty()) {
|
||||||
throw fail("监测点ID不能为空");
|
throw fail("监测点ID不能为空");
|
||||||
}
|
}
|
||||||
if (!allowEmptyIndicators && normalizeTextList(param.getIndicatorCodes()).isEmpty()) {
|
|
||||||
throw fail("指标不能为空");
|
|
||||||
}
|
|
||||||
parseRequiredTime(param.getTimeStart(), "开始时间不能为空");
|
parseRequiredTime(param.getTimeStart(), "开始时间不能为空");
|
||||||
parseRequiredTime(param.getTimeEnd(), "结束时间不能为空");
|
parseRequiredTime(param.getTimeEnd(), "结束时间不能为空");
|
||||||
}
|
}
|
||||||
@@ -872,6 +1050,26 @@ public class SteadyChecksquareServiceImpl implements SteadyChecksquareService {
|
|||||||
return linePath;
|
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) {
|
private int resolveIntervalMinutes(AddLedgerLinePathVO linePath) {
|
||||||
Integer interval = linePath.getLineInterval();
|
Integer interval = linePath.getLineInterval();
|
||||||
if (interval == null || interval <= 0) {
|
if (interval == null || interval <= 0) {
|
||||||
@@ -1005,6 +1203,7 @@ public class SteadyChecksquareServiceImpl implements SteadyChecksquareService {
|
|||||||
vo.setTaskId(task.getId());
|
vo.setTaskId(task.getId());
|
||||||
vo.setTaskNo(task.getTaskNo());
|
vo.setTaskNo(task.getTaskNo());
|
||||||
vo.setLineId(task.getLineId());
|
vo.setLineId(task.getLineId());
|
||||||
|
vo.setLineIds(readTaskLineIds(task));
|
||||||
vo.setLineName(task.getLineName());
|
vo.setLineName(task.getLineName());
|
||||||
vo.setTimeStart(formatTime(task.getTimeStart()));
|
vo.setTimeStart(formatTime(task.getTimeStart()));
|
||||||
vo.setTimeEnd(formatTime(task.getTimeEnd()));
|
vo.setTimeEnd(formatTime(task.getTimeEnd()));
|
||||||
@@ -1021,6 +1220,8 @@ public class SteadyChecksquareServiceImpl implements SteadyChecksquareService {
|
|||||||
SteadyChecksquareItemVO vo = new SteadyChecksquareItemVO();
|
SteadyChecksquareItemVO vo = new SteadyChecksquareItemVO();
|
||||||
vo.setItemId(item.getId());
|
vo.setItemId(item.getId());
|
||||||
vo.setItemKey(item.getItemKey());
|
vo.setItemKey(item.getItemKey());
|
||||||
|
vo.setLineId(item.getLineId());
|
||||||
|
vo.setLineName(item.getLineName());
|
||||||
vo.setIndicatorCode(item.getIndicatorCode());
|
vo.setIndicatorCode(item.getIndicatorCode());
|
||||||
vo.setIndicatorName(item.getIndicatorName());
|
vo.setIndicatorName(item.getIndicatorName());
|
||||||
vo.setHarmonicOrder(item.getHarmonicOrder());
|
vo.setHarmonicOrder(item.getHarmonicOrder());
|
||||||
@@ -1229,6 +1430,53 @@ public class SteadyChecksquareServiceImpl implements SteadyChecksquareService {
|
|||||||
return new ArrayList<String>(result);
|
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) {
|
private String trimToNull(String value) {
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
return null;
|
return null;
|
||||||
@@ -1246,12 +1494,16 @@ public class SteadyChecksquareServiceImpl implements SteadyChecksquareService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private String buildIndicatorCodesText(List<String> indicatorCodes) {
|
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;
|
return null;
|
||||||
}
|
}
|
||||||
StringBuilder builder = new StringBuilder("|");
|
StringBuilder builder = new StringBuilder("|");
|
||||||
for (String indicatorCode : indicatorCodes) {
|
for (String value : values) {
|
||||||
builder.append(indicatorCode).append("|");
|
builder.append(value).append("|");
|
||||||
}
|
}
|
||||||
return builder.toString();
|
return builder.toString();
|
||||||
}
|
}
|
||||||
@@ -1264,6 +1516,14 @@ public class SteadyChecksquareServiceImpl implements SteadyChecksquareService {
|
|||||||
return text.length() > 2000 ? text.substring(0, 2000) : text;
|
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) {
|
private List<Integer> readIntegerList(String json) {
|
||||||
if (trimToNull(json) == null) {
|
if (trimToNull(json) == null) {
|
||||||
return new ArrayList<Integer>();
|
return new ArrayList<Integer>();
|
||||||
@@ -1305,17 +1565,17 @@ public class SteadyChecksquareServiceImpl implements SteadyChecksquareService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static class CreateContext {
|
private static class CreateContext {
|
||||||
private final String lineId;
|
private final List<String> lineIds;
|
||||||
private final AddLedgerLinePathVO linePath;
|
private final Map<String, AddLedgerLinePathVO> linePathMap;
|
||||||
private final LocalDateTime startTime;
|
private final LocalDateTime startTime;
|
||||||
private final LocalDateTime endTime;
|
private final LocalDateTime endTime;
|
||||||
private final int intervalMinutes;
|
private final int intervalMinutes;
|
||||||
private final List<String> indicatorCodes;
|
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) {
|
LocalDateTime endTime, int intervalMinutes, List<String> indicatorCodes) {
|
||||||
this.lineId = lineId;
|
this.lineIds = lineIds;
|
||||||
this.linePath = linePath;
|
this.linePathMap = linePathMap;
|
||||||
this.startTime = startTime;
|
this.startTime = startTime;
|
||||||
this.endTime = endTime;
|
this.endTime = endTime;
|
||||||
this.intervalMinutes = intervalMinutes;
|
this.intervalMinutes = intervalMinutes;
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ CREATE TABLE IF NOT EXISTS `steady_checksquare_task` (
|
|||||||
`task_no` VARCHAR(64) NOT NULL COMMENT '检测任务编号',
|
`task_no` VARCHAR(64) NOT NULL COMMENT '检测任务编号',
|
||||||
`line_id` VARCHAR(64) NOT NULL COMMENT '监测点ID',
|
`line_id` VARCHAR(64) NOT NULL COMMENT '监测点ID',
|
||||||
`line_name` VARCHAR(255) NULL COMMENT '监测点名称',
|
`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_start` DATETIME NOT NULL COMMENT '检测开始时间',
|
||||||
`time_end` DATETIME NOT NULL COMMENT '检测结束时间',
|
`time_end` DATETIME NOT NULL COMMENT '检测结束时间',
|
||||||
`interval_minutes` INT 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`),
|
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_line_time` (`line_id`, `time_start`, `time_end`),
|
||||||
KEY `idx_steady_checksquare_task_status` (`task_status`),
|
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_indicator_text` (`indicator_codes_text`(255)),
|
||||||
KEY `idx_steady_checksquare_task_create_time` (`create_time`)
|
KEY `idx_steady_checksquare_task_create_time` (`create_time`)
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='稳态数据校验任务表';
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='稳态数据校验任务表';
|
||||||
@@ -30,6 +33,8 @@ CREATE TABLE IF NOT EXISTS `steady_checksquare_item` (
|
|||||||
`id` VARCHAR(64) NOT NULL COMMENT '主键',
|
`id` VARCHAR(64) NOT NULL COMMENT '主键',
|
||||||
`task_id` VARCHAR(64) NOT NULL COMMENT '检测任务ID',
|
`task_id` VARCHAR(64) NOT NULL COMMENT '检测任务ID',
|
||||||
`item_key` VARCHAR(255) NOT NULL COMMENT '检测项唯一键',
|
`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_code` VARCHAR(64) NOT NULL COMMENT '指标编码',
|
||||||
`indicator_name` VARCHAR(255) NULL COMMENT '指标名称',
|
`indicator_name` VARCHAR(255) NULL COMMENT '指标名称',
|
||||||
`harmonic_order` INT 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
|
@Test
|
||||||
void shouldSplitLongValuePointQueryByDay() throws Exception {
|
void shouldSplitLongValuePointQueryByDay() throws Exception {
|
||||||
AtomicInteger requestCount = new AtomicInteger();
|
AtomicInteger requestCount = new AtomicInteger();
|
||||||
|
|||||||
@@ -11,6 +11,9 @@ import java.math.BigDecimal;
|
|||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
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.any;
|
||||||
import static org.mockito.ArgumentMatchers.eq;
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
@@ -28,23 +31,12 @@ class SteadyChecksquareValueOrderRuleComponentTest {
|
|||||||
SteadyChecksquareValueOrderRuleComponent component = new SteadyChecksquareValueOrderRuleComponent(influxQueryComponent);
|
SteadyChecksquareValueOrderRuleComponent component = new SteadyChecksquareValueOrderRuleComponent(influxQueryComponent);
|
||||||
LocalDateTime firstTime = LocalDateTime.of(2026, 5, 1, 0, 0);
|
LocalDateTime firstTime = LocalDateTime.of(2026, 5, 1, 0, 0);
|
||||||
LocalDateTime secondTime = LocalDateTime.of(2026, 5, 1, 0, 1);
|
LocalDateTime secondTime = LocalDateTime.of(2026, 5, 1, 0, 1);
|
||||||
when(influxQueryComponent.queryValuePoints(any(), any(LocalDateTime.class), any(LocalDateTime.class), eq(1)))
|
when(influxQueryComponent.queryStatValuePointMap(any(), any(LocalDateTime.class), any(LocalDateTime.class), eq(1)))
|
||||||
.thenAnswer(invocation -> {
|
.thenReturn(statPointMap(
|
||||||
String statType = invocation.getArgument(0, com.njcn.gather.steady.datavie.pojo.bo.SteadyTrendResolvedFieldBO.class).getStatType();
|
Arrays.asList(point(firstTime, "8"), point(secondTime, "9")),
|
||||||
if ("MAX".equals(statType)) {
|
Arrays.asList(point(firstTime, "9"), point(secondTime, "10")),
|
||||||
return Arrays.asList(point(firstTime, "8"), point(secondTime, "9"));
|
Arrays.asList(point(firstTime, "7"), point(secondTime, "8")),
|
||||||
}
|
Arrays.asList(point(firstTime, "1"), point(secondTime, "9"))));
|
||||||
if ("CP95".equals(statType)) {
|
|
||||||
return Arrays.asList(point(firstTime, "9"), point(secondTime, "10"));
|
|
||||||
}
|
|
||||||
if ("AVG".equals(statType)) {
|
|
||||||
return Arrays.asList(point(firstTime, "7"), point(secondTime, "8"));
|
|
||||||
}
|
|
||||||
if ("MIN".equals(statType)) {
|
|
||||||
return Arrays.asList(point(firstTime, "1"), point(secondTime, "9"));
|
|
||||||
}
|
|
||||||
return Collections.emptyList();
|
|
||||||
});
|
|
||||||
|
|
||||||
SteadyChecksquareValueOrderRuleVO result = component.check("line-001", indicator(), null,
|
SteadyChecksquareValueOrderRuleVO result = component.check("line-001", indicator(), null,
|
||||||
LocalDateTime.of(2026, 5, 1, 0, 0), LocalDateTime.of(2026, 5, 1, 0, 2), 1);
|
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);
|
SteadyChecksquareInfluxQueryComponent influxQueryComponent = mock(SteadyChecksquareInfluxQueryComponent.class);
|
||||||
SteadyChecksquareValueOrderRuleComponent component = new SteadyChecksquareValueOrderRuleComponent(influxQueryComponent);
|
SteadyChecksquareValueOrderRuleComponent component = new SteadyChecksquareValueOrderRuleComponent(influxQueryComponent);
|
||||||
LocalDateTime time = LocalDateTime.of(2026, 5, 1, 0, 0);
|
LocalDateTime time = LocalDateTime.of(2026, 5, 1, 0, 0);
|
||||||
when(influxQueryComponent.queryValuePoints(any(), any(LocalDateTime.class), any(LocalDateTime.class), eq(1)))
|
when(influxQueryComponent.queryStatValuePointMap(any(), any(LocalDateTime.class), any(LocalDateTime.class), eq(1)))
|
||||||
.thenAnswer(invocation -> {
|
.thenReturn(statPointMap(
|
||||||
String statType = invocation.getArgument(0, com.njcn.gather.steady.datavie.pojo.bo.SteadyTrendResolvedFieldBO.class).getStatType();
|
Collections.singletonList(point(time, "10")),
|
||||||
if ("MAX".equals(statType)) {
|
Collections.singletonList(point(time, "10")),
|
||||||
return Collections.singletonList(point(time, "10"));
|
Collections.singletonList(point(time, "8")),
|
||||||
}
|
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, "8"));
|
|
||||||
}
|
|
||||||
return Collections.emptyList();
|
|
||||||
});
|
|
||||||
|
|
||||||
SteadyChecksquareValueOrderRuleVO result = component.check("line-001", indicator(), null,
|
SteadyChecksquareValueOrderRuleVO result = component.check("line-001", indicator(), null,
|
||||||
LocalDateTime.of(2026, 5, 1, 0, 0), LocalDateTime.of(2026, 5, 1, 0, 1), 1);
|
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);
|
SteadyChecksquareInfluxQueryComponent influxQueryComponent = mock(SteadyChecksquareInfluxQueryComponent.class);
|
||||||
SteadyChecksquareValueOrderRuleComponent component = new SteadyChecksquareValueOrderRuleComponent(influxQueryComponent);
|
SteadyChecksquareValueOrderRuleComponent component = new SteadyChecksquareValueOrderRuleComponent(influxQueryComponent);
|
||||||
LocalDateTime time = LocalDateTime.of(2026, 5, 1, 0, 0);
|
LocalDateTime time = LocalDateTime.of(2026, 5, 1, 0, 0);
|
||||||
when(influxQueryComponent.queryValuePoints(any(), any(LocalDateTime.class), any(LocalDateTime.class), eq(1)))
|
when(influxQueryComponent.queryStatValuePointMap(any(), any(LocalDateTime.class), any(LocalDateTime.class), eq(1)))
|
||||||
.thenAnswer(invocation -> {
|
.thenReturn(statPointMap(
|
||||||
String statType = invocation.getArgument(0, com.njcn.gather.steady.datavie.pojo.bo.SteadyTrendResolvedFieldBO.class).getStatType();
|
Collections.singletonList(point(time, "8")),
|
||||||
if ("MAX".equals(statType)) {
|
Collections.singletonList(point(time, "10")),
|
||||||
return Collections.singletonList(point(time, "8"));
|
Collections.singletonList(point(time, "8")),
|
||||||
}
|
Collections.singletonList(point(time, "1"))));
|
||||||
if ("CP95".equals(statType)) {
|
|
||||||
return Collections.singletonList(point(time, "10"));
|
|
||||||
}
|
|
||||||
if ("AVG".equals(statType)) {
|
|
||||||
return Collections.singletonList(point(time, "8"));
|
|
||||||
}
|
|
||||||
if ("MIN".equals(statType)) {
|
|
||||||
return Collections.singletonList(point(time, "1"));
|
|
||||||
}
|
|
||||||
return Collections.emptyList();
|
|
||||||
});
|
|
||||||
|
|
||||||
SteadyChecksquareValueOrderRuleVO result = component.check("line-001", indicator(), null,
|
SteadyChecksquareValueOrderRuleVO result = component.check("line-001", indicator(), null,
|
||||||
LocalDateTime.of(2026, 5, 1, 0, 0), LocalDateTime.of(2026, 5, 1, 0, 1), 1);
|
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);
|
SteadyChecksquareInfluxQueryComponent influxQueryComponent = mock(SteadyChecksquareInfluxQueryComponent.class);
|
||||||
SteadyChecksquareValueOrderRuleComponent component = new SteadyChecksquareValueOrderRuleComponent(influxQueryComponent);
|
SteadyChecksquareValueOrderRuleComponent component = new SteadyChecksquareValueOrderRuleComponent(influxQueryComponent);
|
||||||
LocalDateTime time = LocalDateTime.of(2026, 5, 1, 0, 0);
|
LocalDateTime time = LocalDateTime.of(2026, 5, 1, 0, 0);
|
||||||
when(influxQueryComponent.queryValuePoints(any(), any(LocalDateTime.class), any(LocalDateTime.class), eq(1)))
|
when(influxQueryComponent.queryStatValuePointMap(any(), any(LocalDateTime.class), any(LocalDateTime.class), eq(1)))
|
||||||
.thenAnswer(invocation -> {
|
.thenReturn(statPointMap(
|
||||||
String statType = invocation.getArgument(0, com.njcn.gather.steady.datavie.pojo.bo.SteadyTrendResolvedFieldBO.class).getStatType();
|
Collections.singletonList(point(time, "8")),
|
||||||
if ("MAX".equals(statType)) {
|
Collections.singletonList(point(time, "10")),
|
||||||
return Collections.singletonList(point(time, "8"));
|
Collections.singletonList(point(time, "8")),
|
||||||
}
|
Collections.singletonList(point(time, "1"))));
|
||||||
if ("CP95".equals(statType)) {
|
|
||||||
return Collections.singletonList(point(time, "10"));
|
|
||||||
}
|
|
||||||
if ("AVG".equals(statType)) {
|
|
||||||
return Collections.singletonList(point(time, "8"));
|
|
||||||
}
|
|
||||||
if ("MIN".equals(statType)) {
|
|
||||||
return Collections.singletonList(point(time, "1"));
|
|
||||||
}
|
|
||||||
return Collections.emptyList();
|
|
||||||
});
|
|
||||||
SteadyTrendIndicatorDefinitionBO indicator = indicator();
|
SteadyTrendIndicatorDefinitionBO indicator = indicator();
|
||||||
indicator.setHarmonic(true);
|
indicator.setHarmonic(true);
|
||||||
indicator.setHarmonicFieldPrefix("v");
|
indicator.setHarmonicFieldPrefix("v");
|
||||||
@@ -160,20 +119,12 @@ class SteadyChecksquareValueOrderRuleComponentTest {
|
|||||||
SteadyChecksquareInfluxQueryComponent influxQueryComponent = mock(SteadyChecksquareInfluxQueryComponent.class);
|
SteadyChecksquareInfluxQueryComponent influxQueryComponent = mock(SteadyChecksquareInfluxQueryComponent.class);
|
||||||
SteadyChecksquareValueOrderRuleComponent component = new SteadyChecksquareValueOrderRuleComponent(influxQueryComponent);
|
SteadyChecksquareValueOrderRuleComponent component = new SteadyChecksquareValueOrderRuleComponent(influxQueryComponent);
|
||||||
LocalDateTime time = LocalDateTime.of(2026, 5, 1, 0, 0);
|
LocalDateTime time = LocalDateTime.of(2026, 5, 1, 0, 0);
|
||||||
when(influxQueryComponent.queryValuePoints(any(), any(LocalDateTime.class), any(LocalDateTime.class), eq(1)))
|
when(influxQueryComponent.queryStatValuePointMap(any(), any(LocalDateTime.class), any(LocalDateTime.class), eq(1)))
|
||||||
.thenAnswer(invocation -> {
|
.thenReturn(statPointMap(
|
||||||
String statType = invocation.getArgument(0, com.njcn.gather.steady.datavie.pojo.bo.SteadyTrendResolvedFieldBO.class).getStatType();
|
Collections.singletonList(point(time, "10")),
|
||||||
if ("MAX".equals(statType)) {
|
Collections.singletonList(point(time, "11")),
|
||||||
return Collections.singletonList(point(time, "10"));
|
Collections.emptyList(),
|
||||||
}
|
Collections.singletonList(point(time, "1"))));
|
||||||
if ("CP95".equals(statType)) {
|
|
||||||
return Collections.singletonList(point(time, "11"));
|
|
||||||
}
|
|
||||||
if ("MIN".equals(statType)) {
|
|
||||||
return Collections.singletonList(point(time, "1"));
|
|
||||||
}
|
|
||||||
return Collections.emptyList();
|
|
||||||
});
|
|
||||||
|
|
||||||
SteadyChecksquareValueOrderRuleVO result = component.check("line-001", indicator(), null,
|
SteadyChecksquareValueOrderRuleVO result = component.check("line-001", indicator(), null,
|
||||||
LocalDateTime.of(2026, 5, 1, 0, 0), LocalDateTime.of(2026, 5, 1, 0, 1), 1);
|
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));
|
point.setValue(new BigDecimal(value));
|
||||||
return point;
|
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);
|
PostMapping createMapping = createMethod.getAnnotation(PostMapping.class);
|
||||||
Assertions.assertArrayEquals(new String[]{"/create"}, createMapping.value());
|
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);
|
Method detailMethod = SteadyChecksquareController.class.getDeclaredMethod("detail", String.class);
|
||||||
GetMapping detailMapping = detailMethod.getAnnotation(GetMapping.class);
|
GetMapping detailMapping = detailMethod.getAnnotation(GetMapping.class);
|
||||||
Assertions.assertArrayEquals(new String[]{"/detail"}, detailMapping.value());
|
Assertions.assertArrayEquals(new String[]{"/detail"}, detailMapping.value());
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ import java.util.ArrayList;
|
|||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
@@ -68,6 +69,7 @@ class SteadyChecksquareServiceImplTest {
|
|||||||
MapperBuilderAssistant assistant = new MapperBuilderAssistant(new Configuration(), "");
|
MapperBuilderAssistant assistant = new MapperBuilderAssistant(new Configuration(), "");
|
||||||
TableInfoHelper.initTableInfo(assistant, SteadyChecksquareTaskPO.class);
|
TableInfoHelper.initTableInfo(assistant, SteadyChecksquareTaskPO.class);
|
||||||
TableInfoHelper.initTableInfo(assistant, SteadyChecksquareItemPO.class);
|
TableInfoHelper.initTableInfo(assistant, SteadyChecksquareItemPO.class);
|
||||||
|
TableInfoHelper.initTableInfo(assistant, SteadyChecksquareStatSummaryPO.class);
|
||||||
TableInfoHelper.initTableInfo(assistant, SteadyChecksquareDetailPO.class);
|
TableInfoHelper.initTableInfo(assistant, SteadyChecksquareDetailPO.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -153,7 +155,8 @@ class SteadyChecksquareServiceImplTest {
|
|||||||
SteadyChecksquareTaskVO result = service.create(param);
|
SteadyChecksquareTaskVO result = service.create(param);
|
||||||
|
|
||||||
Assertions.assertEquals(taskCaptor.getValue().getId(), result.getTaskId());
|
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());
|
verify(itemService).saveBatch(any());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -201,7 +204,7 @@ class SteadyChecksquareServiceImplTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void shouldCreateTaskSynchronouslyAndReturnTaskSummary() {
|
void shouldCreateRunningTaskAndReturnTaskSummaryBeforeCalculationCompletes() {
|
||||||
AddLedgerService addLedgerService = mock(AddLedgerService.class);
|
AddLedgerService addLedgerService = mock(AddLedgerService.class);
|
||||||
SteadyChecksquareTaskService taskService = mock(SteadyChecksquareTaskService.class);
|
SteadyChecksquareTaskService taskService = mock(SteadyChecksquareTaskService.class);
|
||||||
SteadyChecksquareInfluxQueryComponent influxQueryComponent = mock(SteadyChecksquareInfluxQueryComponent.class);
|
SteadyChecksquareInfluxQueryComponent influxQueryComponent = mock(SteadyChecksquareInfluxQueryComponent.class);
|
||||||
@@ -243,12 +246,69 @@ class SteadyChecksquareServiceImplTest {
|
|||||||
SteadyChecksquareTaskVO result = service.create(param);
|
SteadyChecksquareTaskVO result = service.create(param);
|
||||||
|
|
||||||
Assertions.assertEquals(taskCaptor.getValue().getId(), result.getTaskId());
|
Assertions.assertEquals(taskCaptor.getValue().getId(), result.getTaskId());
|
||||||
Assertions.assertEquals("SUCCESS", result.getTaskStatus());
|
Assertions.assertEquals("RUNNING", result.getTaskStatus());
|
||||||
Assertions.assertEquals(Integer.valueOf(1), result.getItemCount());
|
Assertions.assertEquals(Integer.valueOf(0), result.getItemCount());
|
||||||
verify(itemService).saveBatch(any());
|
verify(itemService).saveBatch(any());
|
||||||
verify(statSummaryService).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
|
@Test
|
||||||
void shouldUseFixedFlickerIntervalsPerIndicator() {
|
void shouldUseFixedFlickerIntervalsPerIndicator() {
|
||||||
SteadyChecksquareInfluxQueryComponent influxQueryComponent = mock(SteadyChecksquareInfluxQueryComponent.class);
|
SteadyChecksquareInfluxQueryComponent influxQueryComponent = mock(SteadyChecksquareInfluxQueryComponent.class);
|
||||||
@@ -293,6 +353,50 @@ class SteadyChecksquareServiceImplTest {
|
|||||||
assertItemInterval(result.getItems().get(2), "PLT", 120, 2);
|
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
|
@Test
|
||||||
void shouldAggregateAllHarmonicOrdersIntoIndicatorItem() {
|
void shouldAggregateAllHarmonicOrdersIntoIndicatorItem() {
|
||||||
SteadyChecksquareInfluxQueryComponent influxQueryComponent = mock(SteadyChecksquareInfluxQueryComponent.class);
|
SteadyChecksquareInfluxQueryComponent influxQueryComponent = mock(SteadyChecksquareInfluxQueryComponent.class);
|
||||||
@@ -566,7 +670,11 @@ class SteadyChecksquareServiceImplTest {
|
|||||||
task.setTimeEnd(LocalDateTime.of(2026, 5, 1, 0, 1));
|
task.setTimeEnd(LocalDateTime.of(2026, 5, 1, 0, 1));
|
||||||
task.setIntervalMinutes(1);
|
task.setIntervalMinutes(1);
|
||||||
SteadyChecksquareItemPO item1 = buildItemPO("item-001", "V_RMS");
|
SteadyChecksquareItemPO item1 = buildItemPO("item-001", "V_RMS");
|
||||||
|
item1.setLineId("line-001");
|
||||||
|
item1.setLineName("line-001-name");
|
||||||
SteadyChecksquareItemPO item2 = buildItemPO("item-002", "FREQ");
|
SteadyChecksquareItemPO item2 = buildItemPO("item-002", "FREQ");
|
||||||
|
item2.setLineId("line-002");
|
||||||
|
item2.setLineName("line-002-name");
|
||||||
SteadyChecksquareStatSummaryPO summary1 = buildSummaryPO("item-001", "AVG");
|
SteadyChecksquareStatSummaryPO summary1 = buildSummaryPO("item-001", "AVG");
|
||||||
SteadyChecksquareStatSummaryPO summary2 = buildSummaryPO("item-002", "AVG");
|
SteadyChecksquareStatSummaryPO summary2 = buildSummaryPO("item-002", "AVG");
|
||||||
when(taskService.getById("task-001")).thenReturn(task);
|
when(taskService.getById("task-001")).thenReturn(task);
|
||||||
@@ -585,6 +693,10 @@ class SteadyChecksquareServiceImplTest {
|
|||||||
SteadyChecksquareQueryVO result = service.detail("task-001");
|
SteadyChecksquareQueryVO result = service.detail("task-001");
|
||||||
|
|
||||||
Assertions.assertEquals(2, result.getItems().size());
|
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(0).getStatSummaries().size());
|
||||||
Assertions.assertEquals(1, result.getItems().get(1).getStatSummaries().size());
|
Assertions.assertEquals(1, result.getItems().get(1).getStatSummaries().size());
|
||||||
verify(statSummaryService, times(1)).lambdaQuery();
|
verify(statSummaryService, times(1)).lambdaQuery();
|
||||||
@@ -596,6 +708,8 @@ class SteadyChecksquareServiceImplTest {
|
|||||||
SteadyChecksquareDetailService detailService = mock(SteadyChecksquareDetailService.class);
|
SteadyChecksquareDetailService detailService = mock(SteadyChecksquareDetailService.class);
|
||||||
SteadyChecksquareItemPO item = new SteadyChecksquareItemPO();
|
SteadyChecksquareItemPO item = new SteadyChecksquareItemPO();
|
||||||
item.setId("item-001");
|
item.setId("item-001");
|
||||||
|
item.setLineId("line-001");
|
||||||
|
item.setLineName("line-001-name");
|
||||||
item.setState(1);
|
item.setState(1);
|
||||||
SteadyChecksquareDetailPO detail = new SteadyChecksquareDetailPO();
|
SteadyChecksquareDetailPO detail = new SteadyChecksquareDetailPO();
|
||||||
detail.setItemId("item-001");
|
detail.setItemId("item-001");
|
||||||
@@ -619,6 +733,8 @@ class SteadyChecksquareServiceImplTest {
|
|||||||
Assertions.assertEquals(Integer.valueOf(2), result.getPageNum());
|
Assertions.assertEquals(Integer.valueOf(2), result.getPageNum());
|
||||||
Assertions.assertEquals(Integer.valueOf(1), result.getPageSize());
|
Assertions.assertEquals(Integer.valueOf(1), result.getPageSize());
|
||||||
Assertions.assertEquals(Long.valueOf(1L), result.getTotal());
|
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());
|
Assertions.assertEquals(1, result.getValueOrderDetails().size());
|
||||||
verify(detailService).page(any(Page.class), any());
|
verify(detailService).page(any(Page.class), any());
|
||||||
}
|
}
|
||||||
@@ -651,6 +767,75 @@ class SteadyChecksquareServiceImplTest {
|
|||||||
verify(itemService).update(any());
|
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
|
@Test
|
||||||
void shouldSaveChecksquareResultsInBatch() {
|
void shouldSaveChecksquareResultsInBatch() {
|
||||||
SteadyChecksquareTaskService taskService = mock(SteadyChecksquareTaskService.class);
|
SteadyChecksquareTaskService taskService = mock(SteadyChecksquareTaskService.class);
|
||||||
@@ -672,6 +857,8 @@ class SteadyChecksquareServiceImplTest {
|
|||||||
result.setIntervalMinutes(1);
|
result.setIntervalMinutes(1);
|
||||||
SteadyChecksquareItemVO item = new SteadyChecksquareItemVO();
|
SteadyChecksquareItemVO item = new SteadyChecksquareItemVO();
|
||||||
item.setItemKey("line-001|V_RMS");
|
item.setItemKey("line-001|V_RMS");
|
||||||
|
item.setLineId("line-001");
|
||||||
|
item.setLineName("line-001-name");
|
||||||
item.setIndicatorCode("V_RMS");
|
item.setIndicatorCode("V_RMS");
|
||||||
item.setIndicatorName("鐩哥數鍘嬫湁鏁堝€?");
|
item.setIndicatorName("鐩哥數鍘嬫湁鏁堝€?");
|
||||||
item.setIntervalMinutes(1);
|
item.setIntervalMinutes(1);
|
||||||
@@ -700,7 +887,11 @@ class SteadyChecksquareServiceImplTest {
|
|||||||
saveResult(service, param, result);
|
saveResult(service, param, result);
|
||||||
|
|
||||||
verify(taskService).save(any());
|
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());
|
verify(statSummaryService).saveBatch(any());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -846,9 +1037,9 @@ class SteadyChecksquareServiceImplTest {
|
|||||||
List<SteadyChecksquareItemVO> orderItems) {
|
List<SteadyChecksquareItemVO> orderItems) {
|
||||||
try {
|
try {
|
||||||
Method method = SteadyChecksquareServiceImpl.class.getDeclaredMethod("aggregateHarmonicItems",
|
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);
|
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) {
|
} catch (Exception exception) {
|
||||||
throw new RuntimeException(exception);
|
throw new RuntimeException(exception);
|
||||||
}
|
}
|
||||||
@@ -862,6 +1053,29 @@ class SteadyChecksquareServiceImplTest {
|
|||||||
return indicator;
|
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) {
|
private SteadyChecksquareItemVO buildOrderItem(boolean hasData, BigDecimal dataIntegrity) {
|
||||||
SteadyChecksquareItemVO item = new SteadyChecksquareItemVO();
|
SteadyChecksquareItemVO item = new SteadyChecksquareItemVO();
|
||||||
item.setItemKey("line-001|V_HARMONIC|2");
|
item.setItemKey("line-001|V_HARMONIC|2");
|
||||||
|
|||||||
@@ -30,7 +30,7 @@
|
|||||||
d.name AS name,
|
d.name AS name,
|
||||||
d.icd AS icdId,
|
d.icd AS icdId,
|
||||||
p.Name AS icdName,
|
p.Name AS icdName,
|
||||||
p.Path AS icdPath,
|
NULL AS icdPath,
|
||||||
p.Result AS icdResult,
|
p.Result AS icdResult,
|
||||||
p.Msg AS icdMsg,
|
p.Msg AS icdMsg,
|
||||||
d.power AS power,
|
d.power AS power,
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import java.util.ArrayList;
|
|||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
@@ -37,6 +38,9 @@ public class IcdConsistencyCheckService {
|
|||||||
private static final String ISSUE_FILE_NAME = "icd-consistency-issues.json";
|
private static final String ISSUE_FILE_NAME = "icd-consistency-issues.json";
|
||||||
private static final List<String> REQUIRED_REPORT_DESCS = Arrays.asList("统计数据", "波动闪变", "实时数据", "暂态事件");
|
private static final List<String> REQUIRED_REPORT_DESCS = Arrays.asList("统计数据", "波动闪变", "实时数据", "暂态事件");
|
||||||
private static final List<String> REQUIRED_LN_CLASSES = Arrays.asList("MMXU", "MSQI", "MHAI", "MFLK");
|
private static final List<String> REQUIRED_LN_CLASSES = Arrays.asList("MMXU", "MSQI", "MHAI", "MFLK");
|
||||||
|
private static final Set<String> EXCLUDED_DOI_DESCS = new HashSet<String>(Arrays.asList(
|
||||||
|
"电压扰动事件启动", "电压暂降事件启动", "电压暂升事件启动", "电压中断事件启动",
|
||||||
|
"电压暂降启动定值", "电压暂升启动定值", "电压中断启动定值"));
|
||||||
|
|
||||||
private final FileStorageService fileStorageService;
|
private final FileStorageService fileStorageService;
|
||||||
private final ObjectMapper objectMapper = buildMapper();
|
private final ObjectMapper objectMapper = buildMapper();
|
||||||
@@ -45,14 +49,13 @@ public class IcdConsistencyCheckService {
|
|||||||
if (request == null) {
|
if (request == null) {
|
||||||
throw new IllegalArgumentException("ICD 一致性校验请求不能为空");
|
throw new IllegalArgumentException("ICD 一致性校验请求不能为空");
|
||||||
}
|
}
|
||||||
log.info("ICD一致性校验,标准ICD JSON={}", request.getStandardJson());
|
|
||||||
log.info("ICD一致性校验,待校验ICD JSON={}", request.getCheckedJson());
|
|
||||||
MappingDocument checked = parseMapping(request.getCheckedJson(), "待校验 JSON");
|
MappingDocument checked = parseMapping(request.getCheckedJson(), "待校验 JSON");
|
||||||
MappingDocument standard = parseMapping(request.getStandardJson(), "标准 JSON");
|
MappingDocument standard = parseMapping(request.getStandardJson(), "标准 JSON");
|
||||||
|
|
||||||
List<IcdConsistencyIssue> issues = new ArrayList<IcdConsistencyIssue>();
|
List<IcdConsistencyIssue> issues = new ArrayList<IcdConsistencyIssue>();
|
||||||
validateSelfFormat(checked, "待校验映射", issues);
|
validateSelfFormat(checked, "待校验映射", issues);
|
||||||
boolean corrected = applySelfMappingRules(checked, issues);
|
boolean corrected = applySelfMappingRules(checked, standard);
|
||||||
|
corrected = removeDuplicateDescDoiItems(checked, standard, issues) || corrected;
|
||||||
validateConsistency(checked, standard, issues);
|
validateConsistency(checked, standard, issues);
|
||||||
|
|
||||||
IcdConsistencyCheckResponse response = new IcdConsistencyCheckResponse();
|
IcdConsistencyCheckResponse response = new IcdConsistencyCheckResponse();
|
||||||
@@ -172,6 +175,9 @@ public class IcdConsistencyCheckService {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
for (DoiItem doi : inst.getDoiList()) {
|
for (DoiItem doi : inst.getDoiList()) {
|
||||||
|
if (isExcludedDoiDesc(doi.getDesc())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (isEmpty(doi.getSdiList())) {
|
if (isEmpty(doi.getSdiList())) {
|
||||||
addIssue(issues, "自身格式校验", instPath + ".doiList[" + buildKey(doi.getName(), doi.getDesc()) + "]",
|
addIssue(issues, "自身格式校验", instPath + ".doiList[" + buildKey(doi.getName(), doi.getDesc()) + "]",
|
||||||
"typeList 不能为空:" + joinDesc(group.getDesc(), inst.getDesc(), doi.getDesc()), null, null, false);
|
"typeList 不能为空:" + joinDesc(group.getDesc(), inst.getDesc(), doi.getDesc()), null, null, false);
|
||||||
@@ -197,24 +203,28 @@ public class IcdConsistencyCheckService {
|
|||||||
validateDataSetConsistency(checked, standard, issues);
|
validateDataSetConsistency(checked, standard, issues);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean applySelfMappingRules(MappingDocument checked, List<IcdConsistencyIssue> issues) {
|
private boolean applySelfMappingRules(MappingDocument checked, MappingDocument standard) {
|
||||||
if (checked.getReportMap() == null) {
|
if (checked.getReportMap() == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
Map<String, ReportMapItem> standardMap = indexReportMap(standard);
|
||||||
boolean hasRtFre = false;
|
boolean hasRtFre = false;
|
||||||
boolean corrected = false;
|
boolean corrected = false;
|
||||||
for (ReportMapItem item : checked.getReportMap()) {
|
for (ReportMapItem item : checked.getReportMap()) {
|
||||||
if ("实时数据".equals(trimToEmpty(item.getDesc())) && trimToEmpty(item.getRptId()).contains("RtFre")) {
|
if ("实时数据".equals(trimToEmpty(item.getDesc())) && containsRtFre(item.getRptId())) {
|
||||||
hasRtFre = true;
|
hasRtFre = true;
|
||||||
|
ReportMapItem standardItem = standardMap.get(buildReportKey(item));
|
||||||
|
boolean needRuleCorrection = standardItem == null
|
||||||
|
|| !equalsValue(String.valueOf(standardItem.getReportCount()), String.valueOf(item.getReportCount()))
|
||||||
|
|| !equalsValue(standardItem.getFlickerFlag(), item.getFlickerFlag());
|
||||||
|
if (!needRuleCorrection) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (!"1".equals(trimToEmpty(item.getFlickerFlag()))) {
|
if (!"1".equals(trimToEmpty(item.getFlickerFlag()))) {
|
||||||
addIssue(issues, "映射规则", "ReportMap[" + buildReportKey(item) + "].FlickerFlag",
|
|
||||||
"实时数据报告 rptID 包含 RtFre,FlickerFlag 已按规则调整为 1", "1", item.getFlickerFlag(), true);
|
|
||||||
item.setFlickerFlag("1");
|
item.setFlickerFlag("1");
|
||||||
corrected = true;
|
corrected = true;
|
||||||
}
|
}
|
||||||
if (item.getReportCount() != 0) {
|
if (item.getReportCount() != 0) {
|
||||||
addIssue(issues, "映射规则", "ReportMap[" + buildReportKey(item) + "].reportCount",
|
|
||||||
"实时数据报告 rptID 包含 RtFre,reportCount 已按规则调整为 0", "0", String.valueOf(item.getReportCount()), true);
|
|
||||||
item.setReportCount(0);
|
item.setReportCount(0);
|
||||||
corrected = true;
|
corrected = true;
|
||||||
}
|
}
|
||||||
@@ -225,11 +235,12 @@ public class IcdConsistencyCheckService {
|
|||||||
}
|
}
|
||||||
for (ReportMapItem item : checked.getReportMap()) {
|
for (ReportMapItem item : checked.getReportMap()) {
|
||||||
if ("统计数据".equals(trimToEmpty(item.getDesc()))) {
|
if ("统计数据".equals(trimToEmpty(item.getDesc()))) {
|
||||||
|
ReportMapItem standardItem = standardMap.get(buildReportKey(item));
|
||||||
|
if (standardItem != null && equalsValue(String.valueOf(standardItem.getReportCount()), String.valueOf(item.getReportCount()))) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
int adjustedCount = item.getReportCount() - 1;
|
int adjustedCount = item.getReportCount() - 1;
|
||||||
if (item.getReportCount() != adjustedCount) {
|
if (item.getReportCount() != adjustedCount) {
|
||||||
addIssue(issues, "映射规则", "ReportMap[" + buildReportKey(item) + "].reportCount",
|
|
||||||
"存在 RtFre 实时数据报告,统计数据 reportCount 已按规则减 1",
|
|
||||||
String.valueOf(adjustedCount), String.valueOf(item.getReportCount()), true);
|
|
||||||
item.setReportCount(adjustedCount);
|
item.setReportCount(adjustedCount);
|
||||||
corrected = true;
|
corrected = true;
|
||||||
}
|
}
|
||||||
@@ -238,6 +249,47 @@ public class IcdConsistencyCheckService {
|
|||||||
return corrected;
|
return corrected;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean removeDuplicateDescDoiItems(MappingDocument checked, MappingDocument standard, List<IcdConsistencyIssue> issues) {
|
||||||
|
if (checked.getDataSetList() == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Map<String, Set<String>> standardDoiKeys = indexStandardDoiKeysByInst(standard);
|
||||||
|
boolean corrected = false;
|
||||||
|
for (DataSetGroupItem group : checked.getDataSetList()) {
|
||||||
|
if (group.getInstList() == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (InstItem inst : group.getInstList()) {
|
||||||
|
if (inst.getDoiList() == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Map<String, List<DoiItem>> doiItemsByDesc = new HashMap<String, List<DoiItem>>();
|
||||||
|
for (DoiItem doi : inst.getDoiList()) {
|
||||||
|
String desc = trimToEmpty(doi.getDesc());
|
||||||
|
if (!doiItemsByDesc.containsKey(desc)) {
|
||||||
|
doiItemsByDesc.put(desc, new ArrayList<DoiItem>());
|
||||||
|
}
|
||||||
|
doiItemsByDesc.get(desc).add(doi);
|
||||||
|
}
|
||||||
|
String instPath = buildInstPath(group, inst);
|
||||||
|
Set<String> currentStandardKeys = standardDoiKeys.get(buildInstKey(group, inst));
|
||||||
|
for (Map.Entry<String, List<DoiItem>> entry : doiItemsByDesc.entrySet()) {
|
||||||
|
if (entry.getValue().size() <= 1) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
DoiItem retained = chooseRetainedDoi(entry.getValue(), currentStandardKeys);
|
||||||
|
String message = "同一个 doiList 下存在 desc 相同的指标,已仅保留 " + buildKey(retained.getDesc(), retained.getName())
|
||||||
|
+ ";重复组合:" + describeDoiStandardMatches(entry.getValue(), currentStandardKeys);
|
||||||
|
addIssue(issues, "映射规则", instPath + ".doiList[desc=" + entry.getKey() + "]", message,
|
||||||
|
null, describeDoiKeys(entry.getValue()), true);
|
||||||
|
removeDuplicatedDoiItems(inst.getDoiList(), entry.getValue(), retained);
|
||||||
|
corrected = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return corrected;
|
||||||
|
}
|
||||||
|
|
||||||
private void validateReportMapConsistency(MappingDocument checked, MappingDocument standard, List<IcdConsistencyIssue> issues) {
|
private void validateReportMapConsistency(MappingDocument checked, MappingDocument standard, List<IcdConsistencyIssue> issues) {
|
||||||
Map<String, ReportMapItem> checkedMap = new HashMap<String, ReportMapItem>();
|
Map<String, ReportMapItem> checkedMap = new HashMap<String, ReportMapItem>();
|
||||||
if (checked.getReportMap() != null) {
|
if (checked.getReportMap() != null) {
|
||||||
@@ -303,6 +355,9 @@ public class IcdConsistencyCheckService {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
for (DoiItem standardDoi : standardInst.getDoiList()) {
|
for (DoiItem standardDoi : standardInst.getDoiList()) {
|
||||||
|
if (isExcludedDoiDesc(standardDoi.getDesc())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
String doiKey = buildKey(standardDoi.getName(), standardDoi.getDesc());
|
String doiKey = buildKey(standardDoi.getName(), standardDoi.getDesc());
|
||||||
DoiItem checkedDoi = checkedDoiMap.get(doiKey);
|
DoiItem checkedDoi = checkedDoiMap.get(doiKey);
|
||||||
String path = "DataSetList[" + groupKey + "].instList[" + instKey + "].doiList[" + doiKey + "]";
|
String path = "DataSetList[" + groupKey + "].instList[" + instKey + "].doiList[" + doiKey + "]";
|
||||||
@@ -343,6 +398,17 @@ public class IcdConsistencyCheckService {
|
|||||||
issues.add(issue);
|
issues.add(issue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Map<String, ReportMapItem> indexReportMap(MappingDocument document) {
|
||||||
|
Map<String, ReportMapItem> result = new HashMap<String, ReportMapItem>();
|
||||||
|
if (document.getReportMap() == null) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
for (ReportMapItem item : document.getReportMap()) {
|
||||||
|
result.put(buildReportKey(item), item);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
private Map<String, DataSetGroupItem> indexGroups(MappingDocument document) {
|
private Map<String, DataSetGroupItem> indexGroups(MappingDocument document) {
|
||||||
Map<String, DataSetGroupItem> result = new HashMap<String, DataSetGroupItem>();
|
Map<String, DataSetGroupItem> result = new HashMap<String, DataSetGroupItem>();
|
||||||
if (document.getDataSetList() == null) {
|
if (document.getDataSetList() == null) {
|
||||||
@@ -376,10 +442,36 @@ public class IcdConsistencyCheckService {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Map<String, Set<String>> indexStandardDoiKeysByInst(MappingDocument standard) {
|
||||||
|
Map<String, Set<String>> result = new HashMap<String, Set<String>>();
|
||||||
|
if (standard.getDataSetList() == null) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
for (DataSetGroupItem group : standard.getDataSetList()) {
|
||||||
|
if (group.getInstList() == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (InstItem inst : group.getInstList()) {
|
||||||
|
Set<String> doiKeys = new HashSet<String>();
|
||||||
|
if (inst.getDoiList() != null) {
|
||||||
|
for (DoiItem doi : inst.getDoiList()) {
|
||||||
|
doiKeys.add(buildKey(doi.getDesc(), doi.getName()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result.put(buildInstKey(group, inst), doiKeys);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
private String buildReportKey(ReportMapItem item) {
|
private String buildReportKey(ReportMapItem item) {
|
||||||
return buildKey(item.getDesc(), item.getRptId(), item.getName());
|
return buildKey(item.getDesc(), item.getRptId(), item.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String buildInstKey(DataSetGroupItem group, InstItem inst) {
|
||||||
|
return buildKey(group.getLnClass(), group.getDesc(), inst.getInst(), inst.getDesc());
|
||||||
|
}
|
||||||
|
|
||||||
private String buildGroupPath(DataSetGroupItem group) {
|
private String buildGroupPath(DataSetGroupItem group) {
|
||||||
return "DataSetList[" + buildKey(group.getLnClass(), group.getDesc()) + "]";
|
return "DataSetList[" + buildKey(group.getLnClass(), group.getDesc()) + "]";
|
||||||
}
|
}
|
||||||
@@ -396,10 +488,57 @@ public class IcdConsistencyCheckService {
|
|||||||
return String.join("+", parts);
|
return String.join("+", parts);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private DoiItem chooseRetainedDoi(List<DoiItem> doiItems, Set<String> standardDoiKeys) {
|
||||||
|
if (standardDoiKeys != null) {
|
||||||
|
for (DoiItem item : doiItems) {
|
||||||
|
if (standardDoiKeys.contains(buildKey(item.getDesc(), item.getName()))) {
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return doiItems.get(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void removeDuplicatedDoiItems(List<DoiItem> allDoiItems, List<DoiItem> duplicatedItems, DoiItem retained) {
|
||||||
|
Iterator<DoiItem> iterator = allDoiItems.iterator();
|
||||||
|
while (iterator.hasNext()) {
|
||||||
|
DoiItem item = iterator.next();
|
||||||
|
if (duplicatedItems.contains(item) && item != retained) {
|
||||||
|
iterator.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String describeDoiStandardMatches(List<DoiItem> doiItems, Set<String> standardDoiKeys) {
|
||||||
|
List<String> values = new ArrayList<String>();
|
||||||
|
for (DoiItem item : doiItems) {
|
||||||
|
String key = buildKey(item.getDesc(), item.getName());
|
||||||
|
boolean existsInStandard = standardDoiKeys != null && standardDoiKeys.contains(key);
|
||||||
|
values.add(key + (existsInStandard ? " 在标准映射中存在" : " 不在标准映射中"));
|
||||||
|
}
|
||||||
|
return String.join(",", values);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String describeDoiKeys(List<DoiItem> doiItems) {
|
||||||
|
List<String> values = new ArrayList<String>();
|
||||||
|
for (DoiItem item : doiItems) {
|
||||||
|
values.add(buildKey(item.getDesc(), item.getName()));
|
||||||
|
}
|
||||||
|
return String.join(",", values);
|
||||||
|
}
|
||||||
|
|
||||||
private boolean equalsValue(String left, String right) {
|
private boolean equalsValue(String left, String right) {
|
||||||
return trimToEmpty(left).equals(trimToEmpty(right));
|
return trimToEmpty(left).equals(trimToEmpty(right));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean containsRtFre(String value) {
|
||||||
|
return trimToEmpty(value).toLowerCase().contains("rtfre");
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isExcludedDoiDesc(String desc) {
|
||||||
|
return EXCLUDED_DOI_DESCS.contains(trimToEmpty(desc));
|
||||||
|
}
|
||||||
|
|
||||||
private boolean isBlank(String value) {
|
private boolean isBlank(String value) {
|
||||||
return value == null || value.trim().isEmpty();
|
return value == null || value.trim().isEmpty();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,8 +5,10 @@ import com.njcn.common.pojo.enums.common.LogEnum;
|
|||||||
import com.njcn.common.pojo.enums.response.CommonResponseEnum;
|
import com.njcn.common.pojo.enums.response.CommonResponseEnum;
|
||||||
import com.njcn.common.pojo.response.HttpResult;
|
import com.njcn.common.pojo.response.HttpResult;
|
||||||
import com.njcn.common.utils.LogUtil;
|
import com.njcn.common.utils.LogUtil;
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
import com.njcn.gather.icd.mapping.pojo.param.CsIcdPathParam;
|
import com.njcn.gather.icd.mapping.pojo.param.CsIcdPathParam;
|
||||||
import com.njcn.gather.icd.mapping.pojo.param.IcdCheckResultSaveParam;
|
import com.njcn.gather.icd.mapping.pojo.param.IcdCheckResultSaveParam;
|
||||||
|
import com.njcn.gather.icd.mapping.pojo.vo.CsIcdPathDetailVO;
|
||||||
import com.njcn.gather.icd.mapping.pojo.vo.CsIcdPathVO;
|
import com.njcn.gather.icd.mapping.pojo.vo.CsIcdPathVO;
|
||||||
import com.njcn.gather.icd.mapping.service.CsIcdPathService;
|
import com.njcn.gather.icd.mapping.service.CsIcdPathService;
|
||||||
import com.njcn.web.controller.BaseController;
|
import com.njcn.web.controller.BaseController;
|
||||||
@@ -50,6 +52,16 @@ public class CsIcdPathController extends BaseController {
|
|||||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe);
|
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
|
||||||
|
@ApiOperation("查询参照ICD列表")
|
||||||
|
@PostMapping("/reference-list")
|
||||||
|
public HttpResult<List<CsIcdPathVO>> referenceList() {
|
||||||
|
String methodDescribe = getMethodDescribe("referenceList");
|
||||||
|
LogUtil.njcnDebug(log, "{},开始查询参照ICD列表", methodDescribe);
|
||||||
|
List<CsIcdPathVO> result = csIcdPathService.listReferenceIcdPaths();
|
||||||
|
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe);
|
||||||
|
}
|
||||||
|
|
||||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
|
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
|
||||||
@ApiOperation("新增ICD存储记录")
|
@ApiOperation("新增ICD存储记录")
|
||||||
@PostMapping(value = "/add", consumes = {"application/json"})
|
@PostMapping(value = "/add", consumes = {"application/json"})
|
||||||
@@ -115,6 +127,28 @@ public class CsIcdPathController extends BaseController {
|
|||||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe);
|
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
|
||||||
|
@ApiOperation("查询ICD校验结果详情")
|
||||||
|
@ApiImplicitParam(name = "id", value = "ICD记录ID", required = true)
|
||||||
|
@PostMapping("/{id}/icd-check-msg")
|
||||||
|
public HttpResult<JsonNode> getIcdCheckMsg(@PathVariable("id") String id) {
|
||||||
|
String methodDescribe = getMethodDescribe("getIcdCheckMsg");
|
||||||
|
LogUtil.njcnDebug(log, "{},开始查询ICD校验结果详情,icdId={}", methodDescribe, id);
|
||||||
|
JsonNode result = csIcdPathService.getIcdCheckMsg(id);
|
||||||
|
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe);
|
||||||
|
}
|
||||||
|
|
||||||
|
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
|
||||||
|
@ApiOperation("查询ICD映射文件详情")
|
||||||
|
@ApiImplicitParam(name = "id", value = "ICD记录ID", required = true)
|
||||||
|
@PostMapping("/{id}/mapping-detail")
|
||||||
|
public HttpResult<CsIcdPathDetailVO> getMappingDetail(@PathVariable("id") String id) {
|
||||||
|
String methodDescribe = getMethodDescribe("getMappingDetail");
|
||||||
|
LogUtil.njcnDebug(log, "{},开始查询ICD映射文件详情,icdId={}", methodDescribe, id);
|
||||||
|
CsIcdPathDetailVO result = csIcdPathService.getMappingDetail(id);
|
||||||
|
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe);
|
||||||
|
}
|
||||||
|
|
||||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
|
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
|
||||||
@ApiOperation("保存ICD唯一性校验结果")
|
@ApiOperation("保存ICD唯一性校验结果")
|
||||||
@ApiImplicitParam(name = "id", value = "ICD记录ID", required = true)
|
@ApiImplicitParam(name = "id", value = "ICD记录ID", required = true)
|
||||||
@@ -147,9 +181,6 @@ public class CsIcdPathController extends BaseController {
|
|||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
param.setIcdContent(icdFile.getBytes());
|
param.setIcdContent(icdFile.getBytes());
|
||||||
if (param.getPath() == null || param.getPath().trim().isEmpty()) {
|
|
||||||
param.setPath(resolveFileName(icdFile));
|
|
||||||
}
|
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
throw new IllegalArgumentException("读取ICD文件失败:" + ex.getMessage(), ex);
|
throw new IllegalArgumentException("读取ICD文件失败:" + ex.getMessage(), ex);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package com.njcn.gather.icd.mapping.mapper;
|
|||||||
|
|
||||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
import com.njcn.gather.icd.mapping.pojo.po.CsIcdPathPO;
|
import com.njcn.gather.icd.mapping.pojo.po.CsIcdPathPO;
|
||||||
|
import com.njcn.gather.icd.mapping.pojo.vo.CsIcdPathDetailVO;
|
||||||
import com.njcn.gather.icd.mapping.pojo.vo.CsIcdPathVO;
|
import com.njcn.gather.icd.mapping.pojo.vo.CsIcdPathVO;
|
||||||
import org.apache.ibatis.annotations.Param;
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
@@ -15,4 +16,10 @@ public interface CsIcdPathMapper extends BaseMapper<CsIcdPathPO> {
|
|||||||
List<CsIcdPathVO> selectIcdPathList(@Param("keyword") String keyword,
|
List<CsIcdPathVO> selectIcdPathList(@Param("keyword") String keyword,
|
||||||
@Param("type") Integer type,
|
@Param("type") Integer type,
|
||||||
@Param("result") Integer result);
|
@Param("result") Integer result);
|
||||||
|
|
||||||
|
List<CsIcdPathVO> selectReferenceIcdPathList();
|
||||||
|
|
||||||
|
CsIcdPathVO selectIcdCheckMsgById(@Param("id") String id);
|
||||||
|
|
||||||
|
CsIcdPathDetailVO selectIcdPathDetailById(@Param("id") String id);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,12 +7,9 @@
|
|||||||
type="com.njcn.gather.icd.mapping.pojo.vo.CsIcdPathVO">
|
type="com.njcn.gather.icd.mapping.pojo.vo.CsIcdPathVO">
|
||||||
<id column="id" property="id"/>
|
<id column="id" property="id"/>
|
||||||
<result column="name" property="name"/>
|
<result column="name" property="name"/>
|
||||||
<result column="path" property="path"/>
|
|
||||||
<result column="angle" property="angle"/>
|
<result column="angle" property="angle"/>
|
||||||
<result column="usePhaseIndex" property="usePhaseIndex"/>
|
<result column="usePhaseIndex" property="usePhaseIndex"/>
|
||||||
<result column="state" property="state"/>
|
<result column="state" property="state"/>
|
||||||
<result column="jsonStr" property="jsonStr"/>
|
|
||||||
<result column="xmlStr" property="xmlStr"/>
|
|
||||||
<result column="result" property="result"/>
|
<result column="result" property="result"/>
|
||||||
<result column="msg" property="msg"
|
<result column="msg" property="msg"
|
||||||
typeHandler="com.njcn.gather.icd.mapping.typehandler.JsonNodeTypeHandler"/>
|
typeHandler="com.njcn.gather.icd.mapping.typehandler.JsonNodeTypeHandler"/>
|
||||||
@@ -29,12 +26,9 @@
|
|||||||
SELECT
|
SELECT
|
||||||
ID AS id,
|
ID AS id,
|
||||||
Name AS name,
|
Name AS name,
|
||||||
Path AS path,
|
|
||||||
Angle AS angle,
|
Angle AS angle,
|
||||||
Use_Phase_Index AS usePhaseIndex,
|
Use_Phase_Index AS usePhaseIndex,
|
||||||
State AS state,
|
State AS state,
|
||||||
Json_Str AS jsonStr,
|
|
||||||
Xml_Str AS xmlStr,
|
|
||||||
Result AS result,
|
Result AS result,
|
||||||
Msg AS msg,
|
Msg AS msg,
|
||||||
Type AS type,
|
Type AS type,
|
||||||
@@ -46,8 +40,7 @@
|
|||||||
FROM cs_icd_path
|
FROM cs_icd_path
|
||||||
WHERE State = 1
|
WHERE State = 1
|
||||||
<if test="keyword != null and keyword != ''">
|
<if test="keyword != null and keyword != ''">
|
||||||
AND (Name LIKE CONCAT('%', #{keyword}, '%')
|
AND Name LIKE CONCAT('%', #{keyword}, '%')
|
||||||
OR Path LIKE CONCAT('%', #{keyword}, '%'))
|
|
||||||
</if>
|
</if>
|
||||||
<if test="type != null">
|
<if test="type != null">
|
||||||
AND Type = #{type}
|
AND Type = #{type}
|
||||||
@@ -58,4 +51,47 @@
|
|||||||
ORDER BY Update_Time DESC, Create_Time DESC
|
ORDER BY Update_Time DESC, Create_Time DESC
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
|
<select id="selectReferenceIcdPathList"
|
||||||
|
resultMap="CsIcdPathVOResultMap">
|
||||||
|
SELECT
|
||||||
|
ID AS id,
|
||||||
|
Name AS name,
|
||||||
|
Angle AS angle,
|
||||||
|
Use_Phase_Index AS usePhaseIndex,
|
||||||
|
State AS state,
|
||||||
|
Result AS result,
|
||||||
|
Msg AS msg,
|
||||||
|
Type AS type,
|
||||||
|
Reference_Icd_Id AS referenceIcdId,
|
||||||
|
Create_By AS createBy,
|
||||||
|
Create_Time AS createTime,
|
||||||
|
Update_By AS updateBy,
|
||||||
|
Update_Time AS updateTime
|
||||||
|
FROM cs_icd_path
|
||||||
|
WHERE State = 1
|
||||||
|
AND Type IN (1, 3)
|
||||||
|
ORDER BY Update_Time DESC, Create_Time DESC
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<select id="selectIcdCheckMsgById"
|
||||||
|
resultMap="CsIcdPathVOResultMap">
|
||||||
|
SELECT Msg AS msg
|
||||||
|
FROM cs_icd_path
|
||||||
|
WHERE ID = #{id}
|
||||||
|
AND State = 1
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<select id="selectIcdPathDetailById"
|
||||||
|
resultType="com.njcn.gather.icd.mapping.pojo.vo.CsIcdPathDetailVO">
|
||||||
|
SELECT
|
||||||
|
ID AS id,
|
||||||
|
Name AS name,
|
||||||
|
Json_Str AS jsonStr,
|
||||||
|
Xml_Str AS xmlStr,
|
||||||
|
Icd AS icdContent
|
||||||
|
FROM cs_icd_path
|
||||||
|
WHERE ID = #{id}
|
||||||
|
AND State = 1
|
||||||
|
</select>
|
||||||
|
|
||||||
</mapper>
|
</mapper>
|
||||||
|
|||||||
@@ -18,9 +18,6 @@ public class CsIcdPathParam {
|
|||||||
@NotBlank(message = "ICD名称不能为空")
|
@NotBlank(message = "ICD名称不能为空")
|
||||||
private String name;
|
private String name;
|
||||||
|
|
||||||
@ApiModelProperty("ICD存储路径")
|
|
||||||
private String path;
|
|
||||||
|
|
||||||
@ApiModelProperty("ICD文件二进制内容")
|
@ApiModelProperty("ICD文件二进制内容")
|
||||||
private byte[] icdContent;
|
private byte[] icdContent;
|
||||||
|
|
||||||
@@ -30,7 +27,7 @@ public class CsIcdPathParam {
|
|||||||
@ApiModelProperty("是否使用相位索引")
|
@ApiModelProperty("是否使用相位索引")
|
||||||
private Integer usePhaseIndex;
|
private Integer usePhaseIndex;
|
||||||
|
|
||||||
@ApiModelProperty("ICD类型,1-标准ICD")
|
@ApiModelProperty("ICD类型,1-手动录入的标准ICD,2-手动录入的非标准ICD,3-上游解析传递的标准ICD,4-上游解析传递的非标准ICD")
|
||||||
private Integer type;
|
private Integer type;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -53,10 +50,10 @@ public class CsIcdPathParam {
|
|||||||
@ApiModel("ICD存储记录列表查询参数")
|
@ApiModel("ICD存储记录列表查询参数")
|
||||||
public static class ListParam {
|
public static class ListParam {
|
||||||
|
|
||||||
@ApiModelProperty("关键字,匹配ICD名称或路径")
|
@ApiModelProperty("关键字,匹配ICD名称")
|
||||||
private String keyword;
|
private String keyword;
|
||||||
|
|
||||||
@ApiModelProperty("ICD类型")
|
@ApiModelProperty("ICD类型,1-手动录入的标准ICD,2-手动录入的非标准ICD,3-上游解析传递的标准ICD,4-上游解析传递的非标准ICD")
|
||||||
private Integer type;
|
private Integer type;
|
||||||
|
|
||||||
@ApiModelProperty("ICD校验结果,0-否,1-是")
|
@ApiModelProperty("ICD校验结果,0-否,1-是")
|
||||||
|
|||||||
@@ -25,9 +25,6 @@ public class CsIcdPathPO implements Serializable {
|
|||||||
@TableField("Name")
|
@TableField("Name")
|
||||||
private String name;
|
private String name;
|
||||||
|
|
||||||
@TableField("Path")
|
|
||||||
private String path;
|
|
||||||
|
|
||||||
@TableField("Icd")
|
@TableField("Icd")
|
||||||
private byte[] icdContent;
|
private byte[] icdContent;
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
package com.njcn.gather.icd.mapping.pojo.vo;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
|
import io.swagger.annotations.ApiModel;
|
||||||
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ICD 映射文件详情。
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@ApiModel("ICD映射文件详情")
|
||||||
|
public class CsIcdPathDetailVO {
|
||||||
|
|
||||||
|
@ApiModelProperty("ICD记录ID")
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
@ApiModelProperty("ICD名称")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@ApiModelProperty("MMS映射JSON")
|
||||||
|
private String jsonStr;
|
||||||
|
|
||||||
|
@ApiModelProperty("MMS映射XML")
|
||||||
|
private String xmlStr;
|
||||||
|
|
||||||
|
@ApiModelProperty("ICD源文件文本")
|
||||||
|
private String icdText;
|
||||||
|
|
||||||
|
@JsonIgnore
|
||||||
|
private byte[] icdContent;
|
||||||
|
}
|
||||||
@@ -20,9 +20,6 @@ public class CsIcdPathVO {
|
|||||||
@ApiModelProperty("ICD名称")
|
@ApiModelProperty("ICD名称")
|
||||||
private String name;
|
private String name;
|
||||||
|
|
||||||
@ApiModelProperty("ICD存储路径")
|
|
||||||
private String path;
|
|
||||||
|
|
||||||
@ApiModelProperty("角度")
|
@ApiModelProperty("角度")
|
||||||
private Integer angle;
|
private Integer angle;
|
||||||
|
|
||||||
@@ -32,19 +29,13 @@ public class CsIcdPathVO {
|
|||||||
@ApiModelProperty("状态,1-正常,0-删除")
|
@ApiModelProperty("状态,1-正常,0-删除")
|
||||||
private Integer state;
|
private Integer state;
|
||||||
|
|
||||||
@ApiModelProperty("MMS映射JSON")
|
|
||||||
private String jsonStr;
|
|
||||||
|
|
||||||
@ApiModelProperty("MMS映射XML")
|
|
||||||
private String xmlStr;
|
|
||||||
|
|
||||||
@ApiModelProperty("校验结论,0-否,1-是")
|
@ApiModelProperty("校验结论,0-否,1-是")
|
||||||
private Integer result;
|
private Integer result;
|
||||||
|
|
||||||
@ApiModelProperty("校验结论详情JSON")
|
@ApiModelProperty("校验结论详情JSON")
|
||||||
private JsonNode msg;
|
private JsonNode msg;
|
||||||
|
|
||||||
@ApiModelProperty("ICD类型,1-标准ICD")
|
@ApiModelProperty("ICD类型,1-手动录入的标准ICD,2-手动录入的非标准ICD,3-上游解析传递的标准ICD,4-上游解析传递的非标准ICD")
|
||||||
private Integer type;
|
private Integer type;
|
||||||
|
|
||||||
@ApiModelProperty("标准ICD引用ID")
|
@ApiModelProperty("标准ICD引用ID")
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
package com.njcn.gather.icd.mapping.service;
|
package com.njcn.gather.icd.mapping.service;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
import com.njcn.gather.icd.mapping.pojo.param.CsIcdPathParam;
|
import com.njcn.gather.icd.mapping.pojo.param.CsIcdPathParam;
|
||||||
import com.njcn.gather.icd.mapping.pojo.param.IcdCheckResultSaveParam;
|
import com.njcn.gather.icd.mapping.pojo.param.IcdCheckResultSaveParam;
|
||||||
|
import com.njcn.gather.icd.mapping.pojo.vo.CsIcdPathDetailVO;
|
||||||
import com.njcn.gather.icd.mapping.pojo.vo.CsIcdPathVO;
|
import com.njcn.gather.icd.mapping.pojo.vo.CsIcdPathVO;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -13,6 +15,12 @@ public interface CsIcdPathService {
|
|||||||
|
|
||||||
List<CsIcdPathVO> listIcdPaths(CsIcdPathParam.ListParam param);
|
List<CsIcdPathVO> listIcdPaths(CsIcdPathParam.ListParam param);
|
||||||
|
|
||||||
|
List<CsIcdPathVO> listReferenceIcdPaths();
|
||||||
|
|
||||||
|
JsonNode getIcdCheckMsg(String icdId);
|
||||||
|
|
||||||
|
CsIcdPathDetailVO getMappingDetail(String icdId);
|
||||||
|
|
||||||
boolean addIcdPath(CsIcdPathParam param);
|
boolean addIcdPath(CsIcdPathParam param);
|
||||||
|
|
||||||
boolean updateIcdPath(CsIcdPathParam.UpdateParam param);
|
boolean updateIcdPath(CsIcdPathParam.UpdateParam param);
|
||||||
|
|||||||
@@ -1,13 +1,14 @@
|
|||||||
package com.njcn.gather.icd.mapping.service.impl;
|
package com.njcn.gather.icd.mapping.service.impl;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
||||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import com.njcn.gather.icd.mapping.mapper.CsIcdPathMapper;
|
import com.njcn.gather.icd.mapping.mapper.CsIcdPathMapper;
|
||||||
import com.njcn.gather.icd.mapping.pojo.param.CsIcdPathParam;
|
import com.njcn.gather.icd.mapping.pojo.param.CsIcdPathParam;
|
||||||
import com.njcn.gather.icd.mapping.pojo.param.IcdCheckResultSaveParam;
|
import com.njcn.gather.icd.mapping.pojo.param.IcdCheckResultSaveParam;
|
||||||
import com.njcn.gather.icd.mapping.pojo.po.CsIcdPathPO;
|
import com.njcn.gather.icd.mapping.pojo.po.CsIcdPathPO;
|
||||||
|
import com.njcn.gather.icd.mapping.pojo.vo.CsIcdPathDetailVO;
|
||||||
import com.njcn.gather.icd.mapping.pojo.vo.CsIcdPathVO;
|
import com.njcn.gather.icd.mapping.pojo.vo.CsIcdPathVO;
|
||||||
import com.njcn.gather.icd.mapping.service.CsIcdPathService;
|
import com.njcn.gather.icd.mapping.service.CsIcdPathService;
|
||||||
import com.njcn.web.utils.RequestUtil;
|
import com.njcn.web.utils.RequestUtil;
|
||||||
@@ -15,6 +16,7 @@ import lombok.RequiredArgsConstructor;
|
|||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
@@ -30,7 +32,13 @@ public class CsIcdPathServiceImpl implements CsIcdPathService {
|
|||||||
|
|
||||||
private static final int STATE_DELETED = 0;
|
private static final int STATE_DELETED = 0;
|
||||||
|
|
||||||
private static final int ICD_TYPE_STANDARD = 1;
|
private static final int ICD_TYPE_MANUAL_STANDARD = 1;
|
||||||
|
|
||||||
|
private static final int ICD_TYPE_MANUAL_NON_STANDARD = 2;
|
||||||
|
|
||||||
|
private static final int ICD_TYPE_UPSTREAM_STANDARD = 3;
|
||||||
|
|
||||||
|
private static final int ICD_TYPE_UPSTREAM_NON_STANDARD = 4;
|
||||||
|
|
||||||
private final CsIcdPathMapper csIcdPathMapper;
|
private final CsIcdPathMapper csIcdPathMapper;
|
||||||
|
|
||||||
@@ -45,12 +53,38 @@ public class CsIcdPathServiceImpl implements CsIcdPathService {
|
|||||||
checkedParam.getResult());
|
checkedParam.getResult());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<CsIcdPathVO> listReferenceIcdPaths() {
|
||||||
|
return csIcdPathMapper.selectReferenceIcdPathList();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JsonNode getIcdCheckMsg(String icdId) {
|
||||||
|
String id = requireText(icdId, "ICD璁板綍ID涓嶈兘涓虹┖");
|
||||||
|
CsIcdPathVO icdPath = csIcdPathMapper.selectIcdCheckMsgById(id);
|
||||||
|
return icdPath == null ? null : icdPath.getMsg();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CsIcdPathDetailVO getMappingDetail(String icdId) {
|
||||||
|
String id = requireText(icdId, "ICD记录ID不能为空");
|
||||||
|
CsIcdPathDetailVO detail = csIcdPathMapper.selectIcdPathDetailById(id);
|
||||||
|
if (detail == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
byte[] icdContent = detail.getIcdContent();
|
||||||
|
if (icdContent != null && icdContent.length > 0) {
|
||||||
|
detail.setIcdText(new String(icdContent, StandardCharsets.UTF_8));
|
||||||
|
}
|
||||||
|
return detail;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional
|
@Transactional
|
||||||
public boolean addIcdPath(CsIcdPathParam param) {
|
public boolean addIcdPath(CsIcdPathParam param) {
|
||||||
CsIcdPathParam checkedParam = requireParam(param);
|
CsIcdPathParam checkedParam = requireParam(param);
|
||||||
LocalDateTime now = LocalDateTime.now();
|
LocalDateTime now = LocalDateTime.now();
|
||||||
CsIcdPathPO icdPath = buildIcdPath(checkedParam);
|
CsIcdPathPO icdPath = buildIcdPath(checkedParam, true);
|
||||||
icdPath.setId(UUID.randomUUID().toString().replace("-", ""));
|
icdPath.setId(UUID.randomUUID().toString().replace("-", ""));
|
||||||
icdPath.setState(STATE_NORMAL);
|
icdPath.setState(STATE_NORMAL);
|
||||||
icdPath.setCreateBy(currentUserId());
|
icdPath.setCreateBy(currentUserId());
|
||||||
@@ -65,7 +99,7 @@ public class CsIcdPathServiceImpl implements CsIcdPathService {
|
|||||||
public boolean updateIcdPath(CsIcdPathParam.UpdateParam param) {
|
public boolean updateIcdPath(CsIcdPathParam.UpdateParam param) {
|
||||||
CsIcdPathParam.UpdateParam checkedParam = requireUpdateParam(param);
|
CsIcdPathParam.UpdateParam checkedParam = requireUpdateParam(param);
|
||||||
requireIcdPath(checkedParam.getId());
|
requireIcdPath(checkedParam.getId());
|
||||||
CsIcdPathPO icdPath = buildIcdPath(checkedParam);
|
CsIcdPathPO icdPath = buildIcdPath(checkedParam, false);
|
||||||
icdPath.setId(checkedParam.getId());
|
icdPath.setId(checkedParam.getId());
|
||||||
icdPath.setUpdateBy(currentUserId());
|
icdPath.setUpdateBy(currentUserId());
|
||||||
icdPath.setUpdateTime(LocalDateTime.now());
|
icdPath.setUpdateTime(LocalDateTime.now());
|
||||||
@@ -80,13 +114,21 @@ public class CsIcdPathServiceImpl implements CsIcdPathService {
|
|||||||
String currentUserId = currentUserId();
|
String currentUserId = currentUserId();
|
||||||
|
|
||||||
csIcdPathMapper.update(null, new LambdaUpdateWrapper<CsIcdPathPO>()
|
csIcdPathMapper.update(null, new LambdaUpdateWrapper<CsIcdPathPO>()
|
||||||
.set(CsIcdPathPO::getType, null)
|
.set(CsIcdPathPO::getType, ICD_TYPE_MANUAL_NON_STANDARD)
|
||||||
.set(CsIcdPathPO::getUpdateBy, currentUserId)
|
.set(CsIcdPathPO::getUpdateBy, currentUserId)
|
||||||
.set(CsIcdPathPO::getUpdateTime, now)
|
.set(CsIcdPathPO::getUpdateTime, now)
|
||||||
.eq(CsIcdPathPO::getState, STATE_NORMAL));
|
.eq(CsIcdPathPO::getState, STATE_NORMAL)
|
||||||
|
.eq(CsIcdPathPO::getType, ICD_TYPE_MANUAL_STANDARD));
|
||||||
|
|
||||||
|
csIcdPathMapper.update(null, new LambdaUpdateWrapper<CsIcdPathPO>()
|
||||||
|
.set(CsIcdPathPO::getType, ICD_TYPE_UPSTREAM_NON_STANDARD)
|
||||||
|
.set(CsIcdPathPO::getUpdateBy, currentUserId)
|
||||||
|
.set(CsIcdPathPO::getUpdateTime, now)
|
||||||
|
.eq(CsIcdPathPO::getState, STATE_NORMAL)
|
||||||
|
.eq(CsIcdPathPO::getType, ICD_TYPE_UPSTREAM_STANDARD));
|
||||||
|
|
||||||
CsIcdPathPO activeIcdPath = new CsIcdPathPO();
|
CsIcdPathPO activeIcdPath = new CsIcdPathPO();
|
||||||
activeIcdPath.setType(ICD_TYPE_STANDARD);
|
activeIcdPath.setType(resolveStandardType(targetIcdPath.getType()));
|
||||||
activeIcdPath.setUpdateBy(currentUserId);
|
activeIcdPath.setUpdateBy(currentUserId);
|
||||||
activeIcdPath.setUpdateTime(now);
|
activeIcdPath.setUpdateTime(now);
|
||||||
return csIcdPathMapper.update(activeIcdPath, new LambdaUpdateWrapper<CsIcdPathPO>()
|
return csIcdPathMapper.update(activeIcdPath, new LambdaUpdateWrapper<CsIcdPathPO>()
|
||||||
@@ -116,7 +158,7 @@ public class CsIcdPathServiceImpl implements CsIcdPathService {
|
|||||||
throw new IllegalArgumentException("ICD校验结果不能为空");
|
throw new IllegalArgumentException("ICD校验结果不能为空");
|
||||||
}
|
}
|
||||||
CsIcdPathPO icdPath = requireIcdPath(icdId);
|
CsIcdPathPO icdPath = requireIcdPath(icdId);
|
||||||
CsIcdPathPO referenceIcd = requireUniqueReferenceIcd();
|
CsIcdPathPO referenceIcd = requireReferenceIcd(icdPath.getReferenceIcdId());
|
||||||
icdPath.setJsonStr(trimToNull(param.getMappingJson()));
|
icdPath.setJsonStr(trimToNull(param.getMappingJson()));
|
||||||
icdPath.setXmlStr(trimToNull(param.getXml()));
|
icdPath.setXmlStr(trimToNull(param.getXml()));
|
||||||
icdPath.setResult(normalizeResult(param.getResult()));
|
icdPath.setResult(normalizeResult(param.getResult()));
|
||||||
@@ -140,17 +182,34 @@ public class CsIcdPathServiceImpl implements CsIcdPathService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private CsIcdPathPO buildIcdPath(CsIcdPathParam param) {
|
private CsIcdPathPO buildIcdPath(CsIcdPathParam param, boolean useDefaultType) {
|
||||||
CsIcdPathPO icdPath = new CsIcdPathPO();
|
CsIcdPathPO icdPath = new CsIcdPathPO();
|
||||||
icdPath.setName(requireText(param.getName(), "ICD名称不能为空"));
|
icdPath.setName(requireText(param.getName(), "ICD名称不能为空"));
|
||||||
icdPath.setPath(requireText(param.getPath(), "ICD存储路径不能为空"));
|
|
||||||
icdPath.setIcdContent(param.getIcdContent());
|
icdPath.setIcdContent(param.getIcdContent());
|
||||||
icdPath.setAngle(param.getAngle());
|
icdPath.setAngle(param.getAngle());
|
||||||
icdPath.setUsePhaseIndex(param.getUsePhaseIndex());
|
icdPath.setUsePhaseIndex(param.getUsePhaseIndex());
|
||||||
icdPath.setType(param.getType());
|
icdPath.setType(useDefaultType ? resolveIcdType(param.getType()) : param.getType());
|
||||||
return icdPath;
|
return icdPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新增 ICD 记录未显式传类型时,默认归类为手动录入的非标准 ICD。
|
||||||
|
*/
|
||||||
|
private Integer resolveIcdType(Integer type) {
|
||||||
|
return type == null ? ICD_TYPE_MANUAL_NON_STANDARD : type;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 激活标准 ICD 时保留记录来源:手动录入升为 1,上游解析传递升为 3。
|
||||||
|
*/
|
||||||
|
private Integer resolveStandardType(Integer type) {
|
||||||
|
if (Integer.valueOf(ICD_TYPE_UPSTREAM_STANDARD).equals(type)
|
||||||
|
|| Integer.valueOf(ICD_TYPE_UPSTREAM_NON_STANDARD).equals(type)) {
|
||||||
|
return ICD_TYPE_UPSTREAM_STANDARD;
|
||||||
|
}
|
||||||
|
return ICD_TYPE_MANUAL_STANDARD;
|
||||||
|
}
|
||||||
|
|
||||||
private CsIcdPathParam requireParam(CsIcdPathParam param) {
|
private CsIcdPathParam requireParam(CsIcdPathParam param) {
|
||||||
if (param == null) {
|
if (param == null) {
|
||||||
throw new IllegalArgumentException("ICD记录参数不能为空");
|
throw new IllegalArgumentException("ICD记录参数不能为空");
|
||||||
@@ -176,19 +235,15 @@ public class CsIcdPathServiceImpl implements CsIcdPathService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 全系统只允许一个正常状态的标准 ICD 作为唯一参照。
|
* ICD 校验保存时以当前记录绑定的 Reference_Icd_Id 作为参照来源。
|
||||||
*/
|
*/
|
||||||
private CsIcdPathPO requireUniqueReferenceIcd() {
|
private CsIcdPathPO requireReferenceIcd(String referenceIcdId) {
|
||||||
List<CsIcdPathPO> referenceIcdList = csIcdPathMapper.selectList(new LambdaQueryWrapper<CsIcdPathPO>()
|
String id = requireText(referenceIcdId, "未配置参照ICD,无法保存校验结果");
|
||||||
.eq(CsIcdPathPO::getState, STATE_NORMAL)
|
CsIcdPathPO referenceIcd = csIcdPathMapper.selectById(id);
|
||||||
.eq(CsIcdPathPO::getType, ICD_TYPE_STANDARD));
|
if (referenceIcd == null || !Integer.valueOf(STATE_NORMAL).equals(referenceIcd.getState())) {
|
||||||
if (referenceIcdList == null || referenceIcdList.isEmpty()) {
|
throw new IllegalArgumentException("参照ICD不存在或已删除,无法保存校验结果");
|
||||||
throw new IllegalArgumentException("未配置标准ICD,无法执行唯一性校验");
|
|
||||||
}
|
}
|
||||||
if (referenceIcdList.size() > 1) {
|
return referenceIcd;
|
||||||
throw new IllegalArgumentException("存在多个标准ICD,无法确定唯一参照");
|
|
||||||
}
|
|
||||||
return referenceIcdList.get(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Integer normalizeResult(Integer result) {
|
private Integer normalizeResult(Integer result) {
|
||||||
|
|||||||
@@ -36,20 +36,62 @@ class IcdConsistencyCheckServiceTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void checkShouldOnlyReturnCorrectedJsonForRtFreSelfMappingRule() {
|
void checkShouldReturnPassAndCorrectedJsonForRtFreSelfMappingRule() {
|
||||||
IcdConsistencyCheckRequest request = new IcdConsistencyCheckRequest();
|
IcdConsistencyCheckRequest request = new IcdConsistencyCheckRequest();
|
||||||
request.setStandardJson(buildRtFreStandardJson());
|
request.setStandardJson(buildRtFreStandardJson());
|
||||||
request.setCheckedJson(buildRtFreCheckedJson());
|
request.setCheckedJson(buildRtFreCheckedJson());
|
||||||
|
|
||||||
IcdConsistencyCheckResponse response = service.check(request);
|
IcdConsistencyCheckResponse response = service.check(request);
|
||||||
|
|
||||||
Assertions.assertEquals(0, response.getResult());
|
Assertions.assertEquals(1, response.getResult());
|
||||||
|
Assertions.assertTrue(response.getIssues().isEmpty());
|
||||||
Assertions.assertTrue(response.getCorrectedJson().contains("\"rptID\" : \"demoRtFre\""));
|
Assertions.assertTrue(response.getCorrectedJson().contains("\"rptID\" : \"demoRtFre\""));
|
||||||
Assertions.assertTrue(response.getCorrectedJson().contains("\"FlickerFlag\" : \"1\""));
|
Assertions.assertTrue(response.getCorrectedJson().contains("\"FlickerFlag\" : \"1\""));
|
||||||
Assertions.assertTrue(response.getCorrectedJson().contains("\"reportCount\" : 0"));
|
Assertions.assertTrue(response.getCorrectedJson().contains("\"reportCount\" : 0"));
|
||||||
Assertions.assertTrue(response.getCorrectedJson().contains("\"reportCount\" : 1"));
|
Assertions.assertTrue(response.getCorrectedJson().contains("\"reportCount\" : 1"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void checkShouldNotCorrectRtFreWhenReportMapAlreadyMatchesStandard() {
|
||||||
|
IcdConsistencyCheckRequest request = new IcdConsistencyCheckRequest();
|
||||||
|
request.setStandardJson(buildRtFreStandardJson());
|
||||||
|
request.setCheckedJson(buildRtFreStandardJson());
|
||||||
|
|
||||||
|
IcdConsistencyCheckResponse response = service.check(request);
|
||||||
|
|
||||||
|
Assertions.assertEquals(1, response.getResult());
|
||||||
|
Assertions.assertNull(response.getCorrectedJson());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void checkShouldKeepStandardDoiWhenSameDoiListContainsDuplicateDesc() {
|
||||||
|
IcdConsistencyCheckRequest request = new IcdConsistencyCheckRequest();
|
||||||
|
request.setStandardJson(buildStandardJson());
|
||||||
|
request.setCheckedJson(buildDuplicateDoiDescJson());
|
||||||
|
|
||||||
|
IcdConsistencyCheckResponse response = service.check(request);
|
||||||
|
|
||||||
|
Assertions.assertEquals(0, response.getResult());
|
||||||
|
Assertions.assertTrue(response.getIssuesJson().contains("同一个 doiList 下存在 desc 相同的指标"));
|
||||||
|
Assertions.assertTrue(response.getIssuesJson().contains("频率+Hz 在标准映射中存在"));
|
||||||
|
Assertions.assertTrue(response.getIssuesJson().contains("频率+Hz2 不在标准映射中"));
|
||||||
|
Assertions.assertNotNull(response.getCorrectedJson());
|
||||||
|
Assertions.assertTrue(response.getCorrectedJson().contains("\"name\" : \"Hz\""));
|
||||||
|
Assertions.assertFalse(response.getCorrectedJson().contains("\"name\" : \"Hz2\""));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void checkShouldIgnoreVoltageStartMetricsDuringDoiConsistency() {
|
||||||
|
IcdConsistencyCheckRequest request = new IcdConsistencyCheckRequest();
|
||||||
|
request.setStandardJson(buildVoltageStartMetricsStandardJson());
|
||||||
|
request.setCheckedJson(buildStandardJson());
|
||||||
|
|
||||||
|
IcdConsistencyCheckResponse response = service.check(request);
|
||||||
|
|
||||||
|
Assertions.assertEquals(1, response.getResult());
|
||||||
|
Assertions.assertTrue(response.getIssues().isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void checkShouldReportEmptySdiListAsTypeListProblem() {
|
void checkShouldReportEmptySdiListAsTypeListProblem() {
|
||||||
IcdConsistencyCheckRequest request = new IcdConsistencyCheckRequest();
|
IcdConsistencyCheckRequest request = new IcdConsistencyCheckRequest();
|
||||||
@@ -63,6 +105,18 @@ class IcdConsistencyCheckServiceTest {
|
|||||||
Assertions.assertFalse(response.getIssuesJson().contains("sdiList 不能为空"));
|
Assertions.assertFalse(response.getIssuesJson().contains("sdiList 不能为空"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void checkShouldIgnoreVoltageStartMetricsDuringSelfFormatTypeListValidation() {
|
||||||
|
IcdConsistencyCheckRequest request = new IcdConsistencyCheckRequest();
|
||||||
|
request.setStandardJson(buildVoltageStartMetricsWithoutSdiJson());
|
||||||
|
request.setCheckedJson(buildVoltageStartMetricsWithoutSdiJson());
|
||||||
|
|
||||||
|
IcdConsistencyCheckResponse response = service.check(request);
|
||||||
|
|
||||||
|
Assertions.assertEquals(1, response.getResult());
|
||||||
|
Assertions.assertTrue(response.getIssues().isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void checkShouldReturnPassWhenCheckedJsonMatchesStandardJson() {
|
void checkShouldReturnPassWhenCheckedJsonMatchesStandardJson() {
|
||||||
IcdConsistencyCheckRequest request = new IcdConsistencyCheckRequest();
|
IcdConsistencyCheckRequest request = new IcdConsistencyCheckRequest();
|
||||||
@@ -161,6 +215,69 @@ class IcdConsistencyCheckServiceTest {
|
|||||||
"}";
|
"}";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String buildDuplicateDoiDescJson() {
|
||||||
|
return "{\n" +
|
||||||
|
" \"IED\":\"IED1\",\n" +
|
||||||
|
" \"LD\":\"LD0\",\n" +
|
||||||
|
" \"DataType\":\"1\",\n" +
|
||||||
|
" \"unit\":\"s\",\n" +
|
||||||
|
" \"ReportMap\":[\n" +
|
||||||
|
" {\"desc\":\"统计数据\",\"reportCount\":2,\"rptID\":\"rpt-stat\",\"name\":\"brcbStat\",\"buffered\":\"BR\",\"inst\":\"01\",\"FlickerFlag\":\"0\",\"Select\":\"all\",\"TrgOps\":\"dchg\"},\n" +
|
||||||
|
" {\"desc\":\"波动闪变\",\"reportCount\":1,\"rptID\":\"rpt-flk\",\"name\":\"brcbFlk\",\"buffered\":\"BR\",\"inst\":\"02\",\"FlickerFlag\":\"0\",\"Select\":\"all\",\"TrgOps\":\"dchg\"},\n" +
|
||||||
|
" {\"desc\":\"实时数据\",\"reportCount\":1,\"rptID\":\"rpt-rt\",\"name\":\"brcbRt\",\"buffered\":\"RP\",\"inst\":\"03\",\"FlickerFlag\":\"0\",\"Select\":\"all\",\"TrgOps\":\"dchg\"},\n" +
|
||||||
|
" {\"desc\":\"暂态事件\",\"reportCount\":1,\"rptID\":\"rpt-tran\",\"name\":\"brcbTran\",\"buffered\":\"BR\",\"inst\":\"04\",\"FlickerFlag\":\"0\",\"Select\":\"all\",\"TrgOps\":\"dchg\"}\n" +
|
||||||
|
" ],\n" +
|
||||||
|
" \"DataSetList\":[\n" +
|
||||||
|
buildDataSetWithDuplicateDoiDesc("MMXU", "统计数据", "1", "A相") + ",\n" +
|
||||||
|
buildDataSet("MSQI", "实时数据", "1", "A相", "A", "电流", 1, 2, "A") + ",\n" +
|
||||||
|
buildDataSet("MHAI", "谐波数据", "1", "A相", "Har", "谐波", 1, 2, "%") + ",\n" +
|
||||||
|
buildDataSet("MFLK", "波动闪变", "1", "A相", "Flk", "闪变", 1, 2, "pu") + "\n" +
|
||||||
|
" ]\n" +
|
||||||
|
"}";
|
||||||
|
}
|
||||||
|
|
||||||
|
private String buildVoltageStartMetricsStandardJson() {
|
||||||
|
return "{\n" +
|
||||||
|
" \"IED\":\"IED1\",\n" +
|
||||||
|
" \"LD\":\"LD0\",\n" +
|
||||||
|
" \"DataType\":\"1\",\n" +
|
||||||
|
" \"unit\":\"s\",\n" +
|
||||||
|
" \"ReportMap\":[\n" +
|
||||||
|
" {\"desc\":\"统计数据\",\"reportCount\":2,\"rptID\":\"rpt-stat\",\"name\":\"brcbStat\",\"buffered\":\"BR\",\"inst\":\"01\",\"FlickerFlag\":\"0\",\"Select\":\"all\",\"TrgOps\":\"dchg\"},\n" +
|
||||||
|
" {\"desc\":\"波动闪变\",\"reportCount\":1,\"rptID\":\"rpt-flk\",\"name\":\"brcbFlk\",\"buffered\":\"BR\",\"inst\":\"02\",\"FlickerFlag\":\"0\",\"Select\":\"all\",\"TrgOps\":\"dchg\"},\n" +
|
||||||
|
" {\"desc\":\"实时数据\",\"reportCount\":1,\"rptID\":\"rpt-rt\",\"name\":\"brcbRt\",\"buffered\":\"RP\",\"inst\":\"03\",\"FlickerFlag\":\"0\",\"Select\":\"all\",\"TrgOps\":\"dchg\"},\n" +
|
||||||
|
" {\"desc\":\"暂态事件\",\"reportCount\":1,\"rptID\":\"rpt-tran\",\"name\":\"brcbTran\",\"buffered\":\"BR\",\"inst\":\"04\",\"FlickerFlag\":\"0\",\"Select\":\"all\",\"TrgOps\":\"dchg\"}\n" +
|
||||||
|
" ],\n" +
|
||||||
|
" \"DataSetList\":[\n" +
|
||||||
|
buildDataSetWithVoltageStartMetrics("MMXU", "统计数据", "1", "A相") + ",\n" +
|
||||||
|
buildDataSet("MSQI", "实时数据", "1", "A相", "A", "电流", 1, 2, "A") + ",\n" +
|
||||||
|
buildDataSet("MHAI", "谐波数据", "1", "A相", "Har", "谐波", 1, 2, "%") + ",\n" +
|
||||||
|
buildDataSet("MFLK", "波动闪变", "1", "A相", "Flk", "闪变", 1, 2, "pu") + "\n" +
|
||||||
|
" ]\n" +
|
||||||
|
"}";
|
||||||
|
}
|
||||||
|
|
||||||
|
private String buildVoltageStartMetricsWithoutSdiJson() {
|
||||||
|
return "{\n" +
|
||||||
|
" \"IED\":\"IED1\",\n" +
|
||||||
|
" \"LD\":\"LD0\",\n" +
|
||||||
|
" \"DataType\":\"1\",\n" +
|
||||||
|
" \"unit\":\"s\",\n" +
|
||||||
|
" \"ReportMap\":[\n" +
|
||||||
|
" {\"desc\":\"统计数据\",\"reportCount\":2,\"rptID\":\"rpt-stat\",\"name\":\"brcbStat\",\"buffered\":\"BR\",\"inst\":\"01\",\"FlickerFlag\":\"0\",\"Select\":\"all\",\"TrgOps\":\"dchg\"},\n" +
|
||||||
|
" {\"desc\":\"波动闪变\",\"reportCount\":1,\"rptID\":\"rpt-flk\",\"name\":\"brcbFlk\",\"buffered\":\"BR\",\"inst\":\"02\",\"FlickerFlag\":\"0\",\"Select\":\"all\",\"TrgOps\":\"dchg\"},\n" +
|
||||||
|
" {\"desc\":\"实时数据\",\"reportCount\":1,\"rptID\":\"rpt-rt\",\"name\":\"brcbRt\",\"buffered\":\"RP\",\"inst\":\"03\",\"FlickerFlag\":\"0\",\"Select\":\"all\",\"TrgOps\":\"dchg\"},\n" +
|
||||||
|
" {\"desc\":\"暂态事件\",\"reportCount\":1,\"rptID\":\"rpt-tran\",\"name\":\"brcbTran\",\"buffered\":\"BR\",\"inst\":\"04\",\"FlickerFlag\":\"0\",\"Select\":\"all\",\"TrgOps\":\"dchg\"}\n" +
|
||||||
|
" ],\n" +
|
||||||
|
" \"DataSetList\":[\n" +
|
||||||
|
buildDataSetWithVoltageStartMetricsWithoutSdi("MMXU", "统计数据", "1", "A相") + ",\n" +
|
||||||
|
buildDataSet("MSQI", "实时数据", "1", "A相", "A", "电流", 1, 2, "A") + ",\n" +
|
||||||
|
buildDataSet("MHAI", "谐波数据", "1", "A相", "Har", "谐波", 1, 2, "%") + ",\n" +
|
||||||
|
buildDataSet("MFLK", "波动闪变", "1", "A相", "Flk", "闪变", 1, 2, "pu") + "\n" +
|
||||||
|
" ]\n" +
|
||||||
|
"}";
|
||||||
|
}
|
||||||
|
|
||||||
private String buildEmptySdiListJson() {
|
private String buildEmptySdiListJson() {
|
||||||
return "{\n" +
|
return "{\n" +
|
||||||
" \"IED\":\"IED1\",\n" +
|
" \"IED\":\"IED1\",\n" +
|
||||||
@@ -196,4 +313,51 @@ class IcdConsistencyCheckServiceTest {
|
|||||||
"\",\"desc\":\"" + instDesc + "\",\"doiList\":[{\"name\":\"" + doiName + "\",\"desc\":\"" + doiDesc +
|
"\",\"desc\":\"" + instDesc + "\",\"doiList\":[{\"name\":\"" + doiName + "\",\"desc\":\"" + doiDesc +
|
||||||
"\",\"start\":1,\"end\":4,\"unit\":\"Hz\",\"coefficient\":1.0,\"baseflag\":1,\"basecount\":1,\"icdcout\":10,\"sdiList\":[]}]}]}";
|
"\",\"start\":1,\"end\":4,\"unit\":\"Hz\",\"coefficient\":1.0,\"baseflag\":1,\"basecount\":1,\"icdcout\":10,\"sdiList\":[]}]}]}";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String buildDataSetWithDuplicateDoiDesc(String lnClass, String groupDesc, String inst, String instDesc) {
|
||||||
|
return " {\"desc\":\"" + groupDesc + "\",\"lnClass\":\"" + lnClass + "\",\"instList\":[{\"inst\":\"" + inst +
|
||||||
|
"\",\"desc\":\"" + instDesc + "\",\"doiList\":[" +
|
||||||
|
buildDoi("Hz2", "频率", 5, 8, "Hz") + "," +
|
||||||
|
buildDoi("Hz", "频率", 1, 4, "Hz") +
|
||||||
|
"]}]}";
|
||||||
|
}
|
||||||
|
|
||||||
|
private String buildDataSetWithVoltageStartMetrics(String lnClass, String groupDesc, String inst, String instDesc) {
|
||||||
|
return " {\"desc\":\"" + groupDesc + "\",\"lnClass\":\"" + lnClass + "\",\"instList\":[{\"inst\":\"" + inst +
|
||||||
|
"\",\"desc\":\"" + instDesc + "\",\"doiList\":[" +
|
||||||
|
buildDoi("Hz", "频率", 1, 4, "Hz") + "," +
|
||||||
|
buildDoi("VolDistStr", "电压扰动事件启动", 5, 6, "") + "," +
|
||||||
|
buildDoi("VolDipStr", "电压暂降事件启动", 7, 8, "") + "," +
|
||||||
|
buildDoi("VolSwellStr", "电压暂升事件启动", 9, 10, "") + "," +
|
||||||
|
buildDoi("VolInterStr", "电压中断事件启动", 11, 12, "") + "," +
|
||||||
|
buildDoi("VolDipSet", "电压暂降启动定值", 13, 14, "V") + "," +
|
||||||
|
buildDoi("VolSwellSet", "电压暂升启动定值", 15, 16, "V") + "," +
|
||||||
|
buildDoi("VolInterSet", "电压中断启动定值", 17, 18, "V") +
|
||||||
|
"]}]}";
|
||||||
|
}
|
||||||
|
|
||||||
|
private String buildDataSetWithVoltageStartMetricsWithoutSdi(String lnClass, String groupDesc, String inst, String instDesc) {
|
||||||
|
return " {\"desc\":\"" + groupDesc + "\",\"lnClass\":\"" + lnClass + "\",\"instList\":[{\"inst\":\"" + inst +
|
||||||
|
"\",\"desc\":\"" + instDesc + "\",\"doiList\":[" +
|
||||||
|
buildDoiWithoutSdi("VolDistStr", "电压扰动事件启动", 5, 6, "") + "," +
|
||||||
|
buildDoiWithoutSdi("VolDipStr", "电压暂降事件启动", 7, 8, "") + "," +
|
||||||
|
buildDoiWithoutSdi("VolSwellStr", "电压暂升事件启动", 9, 10, "") + "," +
|
||||||
|
buildDoiWithoutSdi("VolInterStr", "电压中断事件启动", 11, 12, "") + "," +
|
||||||
|
buildDoiWithoutSdi("VolDipSet", "电压暂降启动定值", 13, 14, "V") + "," +
|
||||||
|
buildDoiWithoutSdi("VolSwellSet", "电压暂升启动定值", 15, 16, "V") + "," +
|
||||||
|
buildDoiWithoutSdi("VolInterSet", "电压中断启动定值", 17, 18, "V") +
|
||||||
|
"]}]}";
|
||||||
|
}
|
||||||
|
|
||||||
|
private String buildDoi(String doiName, String doiDesc, int start, int end, String unit) {
|
||||||
|
return "{\"name\":\"" + doiName + "\",\"desc\":\"" + doiDesc +
|
||||||
|
"\",\"start\":" + start + ",\"end\":" + end + ",\"unit\":\"" + unit +
|
||||||
|
"\",\"coefficient\":1.0,\"baseflag\":1,\"basecount\":1,\"icdcout\":10,\"sdiList\":[{\"name\":\"mag\",\"desc\":\"幅值\",\"typeList\":[{\"name\":\"f\",\"desc\":\"浮点\"}]}]}";
|
||||||
|
}
|
||||||
|
|
||||||
|
private String buildDoiWithoutSdi(String doiName, String doiDesc, int start, int end, String unit) {
|
||||||
|
return "{\"name\":\"" + doiName + "\",\"desc\":\"" + doiDesc +
|
||||||
|
"\",\"start\":" + start + ",\"end\":" + end + ",\"unit\":\"" + unit +
|
||||||
|
"\",\"coefficient\":1.0,\"baseflag\":1,\"basecount\":1,\"icdcout\":10,\"sdiList\":[]}";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ import com.njcn.gather.icd.mapping.pojo.bo.icd.IcdDocument;
|
|||||||
import com.njcn.gather.icd.mapping.pojo.param.CsIcdPathParam;
|
import com.njcn.gather.icd.mapping.pojo.param.CsIcdPathParam;
|
||||||
import com.njcn.gather.icd.mapping.pojo.param.IcdCheckResultSaveParam;
|
import com.njcn.gather.icd.mapping.pojo.param.IcdCheckResultSaveParam;
|
||||||
import com.njcn.gather.icd.mapping.pojo.po.CsIcdPathPO;
|
import com.njcn.gather.icd.mapping.pojo.po.CsIcdPathPO;
|
||||||
|
import com.njcn.gather.icd.mapping.pojo.vo.CsIcdPathDetailVO;
|
||||||
|
import com.njcn.gather.icd.mapping.pojo.vo.CsIcdPathVO;
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import org.junit.jupiter.api.Assertions;
|
import org.junit.jupiter.api.Assertions;
|
||||||
@@ -47,6 +49,26 @@ class CsIcdPathServiceImplTest {
|
|||||||
verify(csIcdPathMapper).selectIcdPathList(eq("standard"), eq(null), eq(null));
|
verify(csIcdPathMapper).selectIcdPathList(eq("standard"), eq(null), eq(null));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void listReferenceIcdPathsShouldQueryStandardIcdTypes() {
|
||||||
|
service.listReferenceIcdPaths();
|
||||||
|
|
||||||
|
verify(csIcdPathMapper).selectReferenceIcdPathList();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getIcdCheckMsgShouldQueryMsgByTrimmedId() {
|
||||||
|
JsonNode msg = objectMapper.createObjectNode().put("summary", "通过");
|
||||||
|
CsIcdPathVO vo = new CsIcdPathVO();
|
||||||
|
vo.setMsg(msg);
|
||||||
|
when(csIcdPathMapper.selectIcdCheckMsgById(eq("icd-001"))).thenReturn(vo);
|
||||||
|
|
||||||
|
JsonNode result = service.getIcdCheckMsg(" icd-001 ");
|
||||||
|
|
||||||
|
Assertions.assertSame(msg, result);
|
||||||
|
verify(csIcdPathMapper).selectIcdCheckMsgById(eq("icd-001"));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void addIcdPathShouldInsertEnabledRecord() {
|
void addIcdPathShouldInsertEnabledRecord() {
|
||||||
CsIcdPathParam param = buildParam("标准ICD");
|
CsIcdPathParam param = buildParam("标准ICD");
|
||||||
@@ -58,7 +80,6 @@ class CsIcdPathServiceImplTest {
|
|||||||
verify(csIcdPathMapper).insert(captor.capture());
|
verify(csIcdPathMapper).insert(captor.capture());
|
||||||
Assertions.assertTrue(result);
|
Assertions.assertTrue(result);
|
||||||
Assertions.assertEquals("标准ICD", captor.getValue().getName());
|
Assertions.assertEquals("标准ICD", captor.getValue().getName());
|
||||||
Assertions.assertEquals("D:/icd/standard.icd", captor.getValue().getPath());
|
|
||||||
Assertions.assertEquals(1, captor.getValue().getState());
|
Assertions.assertEquals(1, captor.getValue().getState());
|
||||||
Assertions.assertNotNull(captor.getValue().getId());
|
Assertions.assertNotNull(captor.getValue().getId());
|
||||||
Assertions.assertNotNull(captor.getValue().getCreateTime());
|
Assertions.assertNotNull(captor.getValue().getCreateTime());
|
||||||
@@ -79,12 +100,25 @@ class CsIcdPathServiceImplTest {
|
|||||||
Assertions.assertArrayEquals(fileContent, captor.getValue().getIcdContent());
|
Assertions.assertArrayEquals(fileContent, captor.getValue().getIcdContent());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void addIcdPathShouldDefaultTypeToManualNonStandard() {
|
||||||
|
CsIcdPathParam param = buildParam("手动录入非标准ICD");
|
||||||
|
param.setType(null);
|
||||||
|
when(csIcdPathMapper.insert(any(CsIcdPathPO.class))).thenReturn(1);
|
||||||
|
|
||||||
|
boolean result = service.addIcdPath(param);
|
||||||
|
|
||||||
|
ArgumentCaptor<CsIcdPathPO> captor = ArgumentCaptor.forClass(CsIcdPathPO.class);
|
||||||
|
verify(csIcdPathMapper).insert(captor.capture());
|
||||||
|
Assertions.assertTrue(result);
|
||||||
|
Assertions.assertEquals(2, captor.getValue().getType());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void updateIcdPathShouldRejectDeletedRecord() {
|
void updateIcdPathShouldRejectDeletedRecord() {
|
||||||
CsIcdPathParam.UpdateParam param = new CsIcdPathParam.UpdateParam();
|
CsIcdPathParam.UpdateParam param = new CsIcdPathParam.UpdateParam();
|
||||||
param.setId("icd-001");
|
param.setId("icd-001");
|
||||||
param.setName("标准ICD");
|
param.setName("标准ICD");
|
||||||
param.setPath("D:/icd/standard.icd");
|
|
||||||
|
|
||||||
CsIcdPathPO deleted = new CsIcdPathPO();
|
CsIcdPathPO deleted = new CsIcdPathPO();
|
||||||
deleted.setId("icd-001");
|
deleted.setId("icd-001");
|
||||||
@@ -103,7 +137,6 @@ class CsIcdPathServiceImplTest {
|
|||||||
byte[] fileContent = "<SCL version=\"1\"></SCL>".getBytes();
|
byte[] fileContent = "<SCL version=\"1\"></SCL>".getBytes();
|
||||||
param.setId("icd-001");
|
param.setId("icd-001");
|
||||||
param.setName("标准ICD");
|
param.setName("标准ICD");
|
||||||
param.setPath("standard.icd");
|
|
||||||
param.setIcdContent(fileContent);
|
param.setIcdContent(fileContent);
|
||||||
|
|
||||||
CsIcdPathPO existed = new CsIcdPathPO();
|
CsIcdPathPO existed = new CsIcdPathPO();
|
||||||
@@ -134,21 +167,40 @@ class CsIcdPathServiceImplTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void activateIcdPathShouldOnlyKeepTargetAsStandardIcd() {
|
void activateIcdPathShouldOnlyKeepTargetAsManualStandardIcd() {
|
||||||
CsIcdPathPO icdPath = new CsIcdPathPO();
|
CsIcdPathPO icdPath = new CsIcdPathPO();
|
||||||
icdPath.setId("icd-001");
|
icdPath.setId("icd-001");
|
||||||
icdPath.setState(1);
|
icdPath.setState(1);
|
||||||
|
icdPath.setType(2);
|
||||||
when(csIcdPathMapper.selectById(eq("icd-001"))).thenReturn(icdPath);
|
when(csIcdPathMapper.selectById(eq("icd-001"))).thenReturn(icdPath);
|
||||||
when(csIcdPathMapper.update(any(CsIcdPathPO.class), any())).thenReturn(1);
|
when(csIcdPathMapper.update(any(CsIcdPathPO.class), any())).thenReturn(1);
|
||||||
|
|
||||||
boolean result = service.activateIcdPath("icd-001");
|
boolean result = service.activateIcdPath("icd-001");
|
||||||
|
|
||||||
ArgumentCaptor<CsIcdPathPO> captor = ArgumentCaptor.forClass(CsIcdPathPO.class);
|
ArgumentCaptor<CsIcdPathPO> captor = ArgumentCaptor.forClass(CsIcdPathPO.class);
|
||||||
verify(csIcdPathMapper, times(2)).update(captor.capture(), any());
|
verify(csIcdPathMapper, times(3)).update(captor.capture(), any());
|
||||||
Assertions.assertTrue(result);
|
Assertions.assertTrue(result);
|
||||||
Assertions.assertNull(captor.getAllValues().get(0));
|
Assertions.assertNull(captor.getAllValues().get(0));
|
||||||
Assertions.assertEquals(1, captor.getAllValues().get(1).getType());
|
Assertions.assertNull(captor.getAllValues().get(1));
|
||||||
Assertions.assertNotNull(captor.getAllValues().get(1).getUpdateTime());
|
Assertions.assertEquals(1, captor.getAllValues().get(2).getType());
|
||||||
|
Assertions.assertNotNull(captor.getAllValues().get(2).getUpdateTime());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void activateIcdPathShouldKeepUpstreamSourceWhenTargetIsUpstreamIcd() {
|
||||||
|
CsIcdPathPO icdPath = new CsIcdPathPO();
|
||||||
|
icdPath.setId("icd-001");
|
||||||
|
icdPath.setState(1);
|
||||||
|
icdPath.setType(4);
|
||||||
|
when(csIcdPathMapper.selectById(eq("icd-001"))).thenReturn(icdPath);
|
||||||
|
when(csIcdPathMapper.update(any(CsIcdPathPO.class), any())).thenReturn(1);
|
||||||
|
|
||||||
|
boolean result = service.activateIcdPath("icd-001");
|
||||||
|
|
||||||
|
ArgumentCaptor<CsIcdPathPO> captor = ArgumentCaptor.forClass(CsIcdPathPO.class);
|
||||||
|
verify(csIcdPathMapper, times(3)).update(captor.capture(), any());
|
||||||
|
Assertions.assertTrue(result);
|
||||||
|
Assertions.assertEquals(3, captor.getAllValues().get(2).getType());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -235,10 +287,36 @@ class CsIcdPathServiceImplTest {
|
|||||||
Assertions.assertArrayEquals(objectMapper.writeValueAsBytes(icdDocument), captor.getValue().getIcdContent());
|
Assertions.assertArrayEquals(objectMapper.writeValueAsBytes(icdDocument), captor.getValue().getIcdContent());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getMappingDetailShouldReturnJsonXmlAndUtf8IcdText() {
|
||||||
|
CsIcdPathDetailVO detail = new CsIcdPathDetailVO();
|
||||||
|
detail.setId("icd-001");
|
||||||
|
detail.setName("标准ICD");
|
||||||
|
detail.setJsonStr("{\"ied\":\"IED1\"}");
|
||||||
|
detail.setXmlStr("<Root></Root>");
|
||||||
|
detail.setIcdContent("<SCL name=\"demo\"></SCL>".getBytes(java.nio.charset.StandardCharsets.UTF_8));
|
||||||
|
when(csIcdPathMapper.selectIcdPathDetailById(eq("icd-001"))).thenReturn(detail);
|
||||||
|
|
||||||
|
CsIcdPathDetailVO result = service.getMappingDetail(" icd-001 ");
|
||||||
|
|
||||||
|
Assertions.assertEquals("{\"ied\":\"IED1\"}", result.getJsonStr());
|
||||||
|
Assertions.assertEquals("<Root></Root>", result.getXmlStr());
|
||||||
|
Assertions.assertEquals("<SCL name=\"demo\"></SCL>", result.getIcdText());
|
||||||
|
verify(csIcdPathMapper).selectIcdPathDetailById(eq("icd-001"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getMappingDetailShouldReturnNullWhenRecordMissing() {
|
||||||
|
when(csIcdPathMapper.selectIcdPathDetailById(eq("missing"))).thenReturn(null);
|
||||||
|
|
||||||
|
CsIcdPathDetailVO result = service.getMappingDetail("missing");
|
||||||
|
|
||||||
|
Assertions.assertNull(result);
|
||||||
|
}
|
||||||
|
|
||||||
private CsIcdPathParam buildParam(String name) {
|
private CsIcdPathParam buildParam(String name) {
|
||||||
CsIcdPathParam param = new CsIcdPathParam();
|
CsIcdPathParam param = new CsIcdPathParam();
|
||||||
param.setName(name);
|
param.setName(name);
|
||||||
param.setPath("D:/icd/standard.icd");
|
|
||||||
param.setAngle(0);
|
param.setAngle(0);
|
||||||
param.setUsePhaseIndex(1);
|
param.setUsePhaseIndex(1);
|
||||||
param.setType(1);
|
param.setType(1);
|
||||||
|
|||||||
@@ -20,6 +20,12 @@
|
|||||||
<version>0.0.1</version>
|
<version>0.0.1</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.njcn</groupId>
|
||||||
|
<artifactId>mybatis-plus</artifactId>
|
||||||
|
<version>0.0.1</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.njcn</groupId>
|
<groupId>com.njcn</groupId>
|
||||||
<artifactId>spingboot2.3.12</artifactId>
|
<artifactId>spingboot2.3.12</artifactId>
|
||||||
@@ -74,6 +80,14 @@
|
|||||||
<exclude>pqdif-samples/**</exclude>
|
<exclude>pqdif-samples/**</exclude>
|
||||||
</excludes>
|
</excludes>
|
||||||
</resource>
|
</resource>
|
||||||
|
|
||||||
|
<!-- Mapper XML 按当前项目约定放在 Java 包路径下,需要显式复制到 classpath。 -->
|
||||||
|
<resource>
|
||||||
|
<directory>src/main/java</directory>
|
||||||
|
<includes>
|
||||||
|
<include>**/*.xml</include>
|
||||||
|
</includes>
|
||||||
|
</resource>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
||||||
<plugins>
|
<plugins>
|
||||||
|
|||||||
@@ -0,0 +1,162 @@
|
|||||||
|
package com.njcn.gather.tool.parsepqdif.controller;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
import com.njcn.common.pojo.annotation.OperateInfo;
|
||||||
|
import com.njcn.common.pojo.enums.common.LogEnum;
|
||||||
|
import com.njcn.common.pojo.enums.response.CommonResponseEnum;
|
||||||
|
import com.njcn.common.pojo.response.HttpResult;
|
||||||
|
import com.njcn.common.utils.LogUtil;
|
||||||
|
import com.njcn.gather.tool.parsepqdif.pojo.param.CsPqdifPathParam;
|
||||||
|
import com.njcn.gather.tool.parsepqdif.pojo.param.PqdifParseResultSaveParam;
|
||||||
|
import com.njcn.gather.tool.parsepqdif.pojo.vo.CsPqdifPathDetailVO;
|
||||||
|
import com.njcn.gather.tool.parsepqdif.pojo.vo.CsPqdifPathVO;
|
||||||
|
import com.njcn.gather.tool.parsepqdif.service.CsPqdifPathService;
|
||||||
|
import com.njcn.web.controller.BaseController;
|
||||||
|
import com.njcn.web.utils.HttpResultUtil;
|
||||||
|
import io.swagger.annotations.Api;
|
||||||
|
import io.swagger.annotations.ApiImplicitParam;
|
||||||
|
import io.swagger.annotations.ApiOperation;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.validation.annotation.Validated;
|
||||||
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestPart;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PQDIF 存储记录维护入口。
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Api(tags = "PQDIF存储记录管理")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/parse-pqdif/pqdif-paths")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class CsPqdifPathController extends BaseController {
|
||||||
|
|
||||||
|
private final CsPqdifPathService csPqdifPathService;
|
||||||
|
|
||||||
|
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
|
||||||
|
@ApiOperation("查询PQDIF存储记录列表")
|
||||||
|
@PostMapping("/list")
|
||||||
|
public HttpResult<List<CsPqdifPathVO>> list(@RequestBody(required = false) CsPqdifPathParam.ListParam param) {
|
||||||
|
String methodDescribe = getMethodDescribe("list");
|
||||||
|
LogUtil.njcnDebug(log, "{},开始查询PQDIF存储记录列表", methodDescribe);
|
||||||
|
List<CsPqdifPathVO> result = csPqdifPathService.listPqdifPaths(param);
|
||||||
|
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe);
|
||||||
|
}
|
||||||
|
|
||||||
|
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
|
||||||
|
@ApiOperation("新增PQDIF存储记录")
|
||||||
|
@PostMapping(value = "/add", consumes = {"application/json"})
|
||||||
|
public HttpResult<Boolean> add(@RequestBody @Validated CsPqdifPathParam param) {
|
||||||
|
String methodDescribe = getMethodDescribe("add");
|
||||||
|
LogUtil.njcnDebug(log, "{},开始新增PQDIF存储记录", methodDescribe);
|
||||||
|
boolean result = csPqdifPathService.addPqdifPath(param);
|
||||||
|
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe);
|
||||||
|
}
|
||||||
|
|
||||||
|
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
|
||||||
|
@ApiOperation("上传并新增PQDIF存储记录")
|
||||||
|
@PostMapping(value = "/add", consumes = {"multipart/form-data"})
|
||||||
|
public HttpResult<Boolean> addWithFile(@RequestPart("pqdifFile") MultipartFile pqdifFile,
|
||||||
|
@RequestPart("request") @Validated CsPqdifPathParam param) {
|
||||||
|
String methodDescribe = getMethodDescribe("addWithFile");
|
||||||
|
LogUtil.njcnDebug(log, "{},开始上传并新增PQDIF存储记录,fileName={}", methodDescribe, resolveFileName(pqdifFile));
|
||||||
|
fillPqdifFile(param, pqdifFile);
|
||||||
|
boolean result = csPqdifPathService.addPqdifPath(param);
|
||||||
|
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe);
|
||||||
|
}
|
||||||
|
|
||||||
|
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
|
||||||
|
@ApiOperation("编辑PQDIF存储记录")
|
||||||
|
@PostMapping(value = "/update", consumes = {"application/json"})
|
||||||
|
public HttpResult<Boolean> update(@RequestBody @Validated CsPqdifPathParam.UpdateParam param) {
|
||||||
|
String methodDescribe = getMethodDescribe("update");
|
||||||
|
LogUtil.njcnDebug(log, "{},开始编辑PQDIF存储记录,pqdifId={}", methodDescribe, param.getId());
|
||||||
|
boolean result = csPqdifPathService.updatePqdifPath(param);
|
||||||
|
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe);
|
||||||
|
}
|
||||||
|
|
||||||
|
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
|
||||||
|
@ApiOperation("上传并编辑PQDIF存储记录")
|
||||||
|
@PostMapping(value = "/update", consumes = {"multipart/form-data"})
|
||||||
|
public HttpResult<Boolean> updateWithFile(@RequestPart("pqdifFile") MultipartFile pqdifFile,
|
||||||
|
@RequestPart("request") @Validated CsPqdifPathParam.UpdateParam param) {
|
||||||
|
String methodDescribe = getMethodDescribe("updateWithFile");
|
||||||
|
LogUtil.njcnDebug(log, "{},开始上传并编辑PQDIF存储记录,pqdifId={},fileName={}",
|
||||||
|
methodDescribe, param.getId(), resolveFileName(pqdifFile));
|
||||||
|
fillPqdifFile(param, pqdifFile);
|
||||||
|
boolean result = csPqdifPathService.updatePqdifPath(param);
|
||||||
|
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe);
|
||||||
|
}
|
||||||
|
|
||||||
|
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
|
||||||
|
@ApiOperation("删除PQDIF存储记录")
|
||||||
|
@PostMapping("/delete")
|
||||||
|
public HttpResult<Boolean> delete(@RequestBody List<String> ids) {
|
||||||
|
String methodDescribe = getMethodDescribe("delete");
|
||||||
|
LogUtil.njcnDebug(log, "{},开始删除PQDIF存储记录,ids={}", methodDescribe, ids);
|
||||||
|
boolean result = csPqdifPathService.deletePqdifPath(ids);
|
||||||
|
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe);
|
||||||
|
}
|
||||||
|
|
||||||
|
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
|
||||||
|
@ApiOperation("查询PQDIF解析结果详情")
|
||||||
|
@ApiImplicitParam(name = "id", value = "PQDIF记录ID", required = true)
|
||||||
|
@PostMapping("/{id}/parse-msg")
|
||||||
|
public HttpResult<JsonNode> getPqdifParseMsg(@PathVariable("id") String id) {
|
||||||
|
String methodDescribe = getMethodDescribe("getPqdifParseMsg");
|
||||||
|
LogUtil.njcnDebug(log, "{},开始查询PQDIF解析结果详情,pqdifId={}", methodDescribe, id);
|
||||||
|
JsonNode result = csPqdifPathService.getPqdifParseMsg(id);
|
||||||
|
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe);
|
||||||
|
}
|
||||||
|
|
||||||
|
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
|
||||||
|
@ApiOperation("查询PQDIF文件和解析结果详情")
|
||||||
|
@ApiImplicitParam(name = "id", value = "PQDIF记录ID", required = true)
|
||||||
|
@PostMapping("/{id}/parse-detail")
|
||||||
|
public HttpResult<CsPqdifPathDetailVO> getPqdifParseDetail(@PathVariable("id") String id) {
|
||||||
|
String methodDescribe = getMethodDescribe("getPqdifParseDetail");
|
||||||
|
LogUtil.njcnDebug(log, "{},开始查询PQDIF文件和解析结果详情,pqdifId={}", methodDescribe, id);
|
||||||
|
CsPqdifPathDetailVO result = csPqdifPathService.getPqdifParseDetail(id);
|
||||||
|
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe);
|
||||||
|
}
|
||||||
|
|
||||||
|
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
|
||||||
|
@ApiOperation("保存PQDIF解析结果")
|
||||||
|
@ApiImplicitParam(name = "id", value = "PQDIF记录ID", required = true)
|
||||||
|
@PostMapping(value = "/{id}/parse-result", consumes = {"application/json"})
|
||||||
|
public HttpResult<Boolean> savePqdifParseResult(@PathVariable("id") String id,
|
||||||
|
@RequestBody PqdifParseResultSaveParam param) {
|
||||||
|
String methodDescribe = getMethodDescribe("savePqdifParseResult");
|
||||||
|
LogUtil.njcnDebug(log, "{},开始保存PQDIF解析结果,pqdifId={}", methodDescribe, id);
|
||||||
|
boolean result = csPqdifPathService.savePqdifParseResult(id, param);
|
||||||
|
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fillPqdifFile(CsPqdifPathParam param, MultipartFile pqdifFile) {
|
||||||
|
if (pqdifFile == null || pqdifFile.isEmpty()) {
|
||||||
|
throw new IllegalArgumentException("PQDIF文件不能为空");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
param.setPqdifContent(pqdifFile.getBytes());
|
||||||
|
} catch (IOException ex) {
|
||||||
|
throw new IllegalArgumentException("读取PQDIF文件失败:" + ex.getMessage(), ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String resolveFileName(MultipartFile pqdifFile) {
|
||||||
|
if (pqdifFile == null || pqdifFile.getOriginalFilename() == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String fileName = pqdifFile.getOriginalFilename().trim();
|
||||||
|
return fileName.isEmpty() ? null : fileName;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -28,7 +28,7 @@ import org.springframework.web.multipart.MultipartFile;
|
|||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/api/parse-pqdif")
|
@RequestMapping("/api/parse-pqdif")
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class ParsePqdifController extends BaseController {
|
public class ParsePqdifController extends BaseController {
|
||||||
|
|
||||||
private final ParsePqdifService parsePqdifService;
|
private final ParsePqdifService parsePqdifService;
|
||||||
|
|
||||||
@@ -38,7 +38,7 @@ public class ParsePqdifController extends BaseController {
|
|||||||
@PostMapping(value = "/parse", consumes = {"multipart/form-data"})
|
@PostMapping(value = "/parse", consumes = {"multipart/form-data"})
|
||||||
public HttpResult<PqdifParseResponse> parse(@RequestPart("pqdifFile") MultipartFile pqdifFile) {
|
public HttpResult<PqdifParseResponse> parse(@RequestPart("pqdifFile") MultipartFile pqdifFile) {
|
||||||
String methodDescribe = getMethodDescribe("parse");
|
String methodDescribe = getMethodDescribe("parse");
|
||||||
LogUtil.njcnDebug(log, "{},PQDIF解析预留入口,fileName={}",
|
LogUtil.njcnDebug(log, "{},PQDIF解析入口,fileName={}",
|
||||||
methodDescribe, pqdifFile == null ? null : pqdifFile.getOriginalFilename());
|
methodDescribe, pqdifFile == null ? null : pqdifFile.getOriginalFilename());
|
||||||
PqdifParseResponse result = parsePqdifService.parse(pqdifFile);
|
PqdifParseResponse result = parsePqdifService.parse(pqdifFile);
|
||||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe);
|
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe);
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package com.njcn.gather.tool.parsepqdif.mapper;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import com.njcn.gather.tool.parsepqdif.pojo.po.CsPqdifPathPO;
|
||||||
|
import com.njcn.gather.tool.parsepqdif.pojo.vo.CsPqdifPathDetailVO;
|
||||||
|
import com.njcn.gather.tool.parsepqdif.pojo.vo.CsPqdifPathVO;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public interface CsPqdifPathMapper extends BaseMapper<CsPqdifPathPO> {
|
||||||
|
|
||||||
|
List<CsPqdifPathVO> selectPqdifPathList(@Param("keyword") String keyword,
|
||||||
|
@Param("result") Integer result);
|
||||||
|
|
||||||
|
CsPqdifPathVO selectPqdifParseMsgById(@Param("id") String id);
|
||||||
|
|
||||||
|
CsPqdifPathDetailVO selectPqdifPathDetailById(@Param("id") String id);
|
||||||
|
}
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||||
|
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||||
|
<mapper namespace="com.njcn.gather.tool.parsepqdif.mapper.CsPqdifPathMapper">
|
||||||
|
|
||||||
|
<resultMap id="CsPqdifPathVOResultMap"
|
||||||
|
type="com.njcn.gather.tool.parsepqdif.pojo.vo.CsPqdifPathVO">
|
||||||
|
<id column="id" property="id"/>
|
||||||
|
<result column="name" property="name"/>
|
||||||
|
<result column="nativeVersion" property="nativeVersion"/>
|
||||||
|
<result column="recordCount" property="recordCount"/>
|
||||||
|
<result column="observationCount" property="observationCount"/>
|
||||||
|
<result column="sampleValueCount" property="sampleValueCount"/>
|
||||||
|
<result column="state" property="state"/>
|
||||||
|
<result column="result" property="result"/>
|
||||||
|
<result column="msg" property="msg"
|
||||||
|
typeHandler="com.njcn.gather.tool.parsepqdif.typehandler.JsonNodeTypeHandler"/>
|
||||||
|
<result column="createBy" property="createBy"/>
|
||||||
|
<result column="createTime" property="createTime"/>
|
||||||
|
<result column="updateBy" property="updateBy"/>
|
||||||
|
<result column="updateTime" property="updateTime"/>
|
||||||
|
</resultMap>
|
||||||
|
|
||||||
|
<select id="selectPqdifPathList"
|
||||||
|
resultMap="CsPqdifPathVOResultMap">
|
||||||
|
SELECT
|
||||||
|
ID AS id,
|
||||||
|
Name AS name,
|
||||||
|
Native_Version AS nativeVersion,
|
||||||
|
Record_Count AS recordCount,
|
||||||
|
Observation_Count AS observationCount,
|
||||||
|
Sample_Value_Count AS sampleValueCount,
|
||||||
|
State AS state,
|
||||||
|
Result AS result,
|
||||||
|
Msg AS msg,
|
||||||
|
Create_By AS createBy,
|
||||||
|
Create_Time AS createTime,
|
||||||
|
Update_By AS updateBy,
|
||||||
|
Update_Time AS updateTime
|
||||||
|
FROM cs_pqdif_path
|
||||||
|
WHERE State = 1
|
||||||
|
<if test="keyword != null and keyword != ''">
|
||||||
|
AND Name LIKE CONCAT('%', #{keyword}, '%')
|
||||||
|
</if>
|
||||||
|
<if test="result != null">
|
||||||
|
AND Result = #{result}
|
||||||
|
</if>
|
||||||
|
ORDER BY Update_Time DESC, Create_Time DESC
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<select id="selectPqdifParseMsgById"
|
||||||
|
resultMap="CsPqdifPathVOResultMap">
|
||||||
|
SELECT Msg AS msg
|
||||||
|
FROM cs_pqdif_path
|
||||||
|
WHERE ID = #{id}
|
||||||
|
AND State = 1
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<select id="selectPqdifPathDetailById"
|
||||||
|
resultType="com.njcn.gather.tool.parsepqdif.pojo.vo.CsPqdifPathDetailVO">
|
||||||
|
SELECT
|
||||||
|
ID AS id,
|
||||||
|
Name AS name,
|
||||||
|
Json_Str AS jsonStr,
|
||||||
|
Pqdif AS pqdifContent
|
||||||
|
FROM cs_pqdif_path
|
||||||
|
WHERE ID = #{id}
|
||||||
|
AND State = 1
|
||||||
|
</select>
|
||||||
|
|
||||||
|
</mapper>
|
||||||
@@ -1,12 +1,17 @@
|
|||||||
package com.njcn.gather.tool.parsepqdif.nativebridge;
|
package com.njcn.gather.tool.parsepqdif.nativebridge;
|
||||||
|
|
||||||
import com.sun.jna.NativeLibrary;
|
import com.sun.jna.NativeLibrary;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.nio.file.*;
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.nio.file.StandardCopyOption;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
public final class PqdifNativeLibraryLoader {
|
public final class PqdifNativeLibraryLoader {
|
||||||
|
|
||||||
private static final String RESOURCE_DLL = "/pqdif-native/win-x64/pqdifbasic.dll";
|
private static final String RESOURCE_DLL = "/pqdif-native/win-x64/pqdifbasic.dll";
|
||||||
@@ -41,8 +46,8 @@ public final class PqdifNativeLibraryLoader {
|
|||||||
NativeLibrary.addSearchPath("pqdifbasic", nativeDir.toAbsolutePath().toString());
|
NativeLibrary.addSearchPath("pqdifbasic", nativeDir.toAbsolutePath().toString());
|
||||||
NativeLibrary.addSearchPath("pqdifbasic.dll", nativeDir.toAbsolutePath().toString());
|
NativeLibrary.addSearchPath("pqdifbasic.dll", nativeDir.toAbsolutePath().toString());
|
||||||
|
|
||||||
System.out.println("PQDIF native dir = " + nativeDir.toAbsolutePath());
|
log.info("PQDIF native dir = {}", nativeDir.toAbsolutePath());
|
||||||
System.out.println("PQDIF native dll = " + dllPath.toAbsolutePath());
|
log.info("PQDIF native dll = {}", dllPath.toAbsolutePath());
|
||||||
|
|
||||||
preparedNativeDir = nativeDir;
|
preparedNativeDir = nativeDir;
|
||||||
prepared = true;
|
prepared = true;
|
||||||
|
|||||||
@@ -0,0 +1,50 @@
|
|||||||
|
package com.njcn.gather.tool.parsepqdif.pojo.param;
|
||||||
|
|
||||||
|
import io.swagger.annotations.ApiModel;
|
||||||
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
import javax.validation.constraints.NotBlank;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PQDIF 存储记录保存参数。
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@ApiModel("PQDIF存储记录保存参数")
|
||||||
|
public class CsPqdifPathParam {
|
||||||
|
|
||||||
|
@ApiModelProperty("PQDIF名称")
|
||||||
|
@NotBlank(message = "PQDIF名称不能为空")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@ApiModelProperty("PQDIF文件二进制内容")
|
||||||
|
private byte[] pqdifContent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PQDIF 存储记录编辑参数。
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@ApiModel("PQDIF存储记录编辑参数")
|
||||||
|
public static class UpdateParam extends CsPqdifPathParam {
|
||||||
|
|
||||||
|
@ApiModelProperty("PQDIF记录ID")
|
||||||
|
@NotBlank(message = "PQDIF记录ID不能为空")
|
||||||
|
private String id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PQDIF 存储记录列表查询参数。
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@ApiModel("PQDIF存储记录列表查询参数")
|
||||||
|
public static class ListParam {
|
||||||
|
|
||||||
|
@ApiModelProperty("关键字,匹配PQDIF名称")
|
||||||
|
private String keyword;
|
||||||
|
|
||||||
|
@ApiModelProperty("解析结果:1-成功,0-失败")
|
||||||
|
private Integer result;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
package com.njcn.gather.tool.parsepqdif.pojo.param;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
import io.swagger.annotations.ApiModel;
|
||||||
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PQDIF 解析结果保存参数。
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@ApiModel("PQDIF解析结果保存参数")
|
||||||
|
public class PqdifParseResultSaveParam {
|
||||||
|
|
||||||
|
@ApiModelProperty("native解析库版本")
|
||||||
|
private String nativeVersion;
|
||||||
|
|
||||||
|
@ApiModelProperty("Record总数")
|
||||||
|
private Long recordCount;
|
||||||
|
|
||||||
|
@ApiModelProperty("Observation Record总数")
|
||||||
|
private Long observationCount;
|
||||||
|
|
||||||
|
@ApiModelProperty("每个Series返回的样例采样值数量")
|
||||||
|
private Integer sampleValueCount;
|
||||||
|
|
||||||
|
@ApiModelProperty("解析结果:1-成功,0-失败")
|
||||||
|
private Integer result;
|
||||||
|
|
||||||
|
@ApiModelProperty("解析提示、失败原因或解析结论JSON")
|
||||||
|
private JsonNode msg;
|
||||||
|
|
||||||
|
@ApiModelProperty("完整PQDIF解析结果JSON")
|
||||||
|
private String jsonStr;
|
||||||
|
}
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
package com.njcn.gather.tool.parsepqdif.pojo.po;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableField;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
import com.njcn.gather.tool.parsepqdif.typehandler.JsonNodeTypeHandler;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PQDIF 文件存储和解析结果记录。
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@TableName(value = "cs_pqdif_path", autoResultMap = true)
|
||||||
|
public class CsPqdifPathPO implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@TableId("ID")
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
@TableField("Name")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@TableField("Pqdif")
|
||||||
|
private byte[] pqdifContent;
|
||||||
|
|
||||||
|
@TableField("Native_Version")
|
||||||
|
private String nativeVersion;
|
||||||
|
|
||||||
|
@TableField("Record_Count")
|
||||||
|
private Long recordCount;
|
||||||
|
|
||||||
|
@TableField("Observation_Count")
|
||||||
|
private Long observationCount;
|
||||||
|
|
||||||
|
@TableField("Sample_Value_Count")
|
||||||
|
private Integer sampleValueCount;
|
||||||
|
|
||||||
|
@TableField("Result")
|
||||||
|
private Integer result;
|
||||||
|
|
||||||
|
@TableField(value = "Msg", typeHandler = JsonNodeTypeHandler.class)
|
||||||
|
private JsonNode msg;
|
||||||
|
|
||||||
|
@TableField("Json_Str")
|
||||||
|
private String jsonStr;
|
||||||
|
|
||||||
|
@TableField("State")
|
||||||
|
private Integer state;
|
||||||
|
|
||||||
|
@TableField("Create_By")
|
||||||
|
private String createBy;
|
||||||
|
|
||||||
|
@TableField("Create_Time")
|
||||||
|
private LocalDateTime createTime;
|
||||||
|
|
||||||
|
@TableField("Update_By")
|
||||||
|
private String updateBy;
|
||||||
|
|
||||||
|
@TableField("Update_Time")
|
||||||
|
private LocalDateTime updateTime;
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
package com.njcn.gather.tool.parsepqdif.pojo.vo;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
|
import io.swagger.annotations.ApiModel;
|
||||||
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PQDIF 文件和解析结果详情。
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@ApiModel("PQDIF文件和解析结果详情")
|
||||||
|
public class CsPqdifPathDetailVO {
|
||||||
|
|
||||||
|
@ApiModelProperty("PQDIF记录ID")
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
@ApiModelProperty("PQDIF名称")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@ApiModelProperty("完整PQDIF解析结果JSON")
|
||||||
|
private String jsonStr;
|
||||||
|
|
||||||
|
@JsonIgnore
|
||||||
|
private byte[] pqdifContent;
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
package com.njcn.gather.tool.parsepqdif.pojo.vo;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
import io.swagger.annotations.ApiModel;
|
||||||
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PQDIF 存储记录列表项。
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@ApiModel("PQDIF存储记录列表项")
|
||||||
|
public class CsPqdifPathVO {
|
||||||
|
|
||||||
|
@ApiModelProperty("PQDIF记录ID")
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
@ApiModelProperty("PQDIF名称")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@ApiModelProperty("native解析库版本")
|
||||||
|
private String nativeVersion;
|
||||||
|
|
||||||
|
@ApiModelProperty("Record总数")
|
||||||
|
private Long recordCount;
|
||||||
|
|
||||||
|
@ApiModelProperty("Observation Record总数")
|
||||||
|
private Long observationCount;
|
||||||
|
|
||||||
|
@ApiModelProperty("每个Series返回的样例采样值数量")
|
||||||
|
private Integer sampleValueCount;
|
||||||
|
|
||||||
|
@ApiModelProperty("状态,1-正常,0-删除")
|
||||||
|
private Integer state;
|
||||||
|
|
||||||
|
@ApiModelProperty("解析结果:1-成功,0-失败")
|
||||||
|
private Integer result;
|
||||||
|
|
||||||
|
@ApiModelProperty("解析提示、失败原因或解析结论JSON")
|
||||||
|
private JsonNode msg;
|
||||||
|
|
||||||
|
@ApiModelProperty("创建人")
|
||||||
|
private String createBy;
|
||||||
|
|
||||||
|
@ApiModelProperty("创建时间")
|
||||||
|
private LocalDateTime createTime;
|
||||||
|
|
||||||
|
@ApiModelProperty("更新人")
|
||||||
|
private String updateBy;
|
||||||
|
|
||||||
|
@ApiModelProperty("更新时间")
|
||||||
|
private LocalDateTime updateTime;
|
||||||
|
}
|
||||||
@@ -4,15 +4,20 @@ import com.njcn.gather.tool.parsepqdif.nativebridge.PqdifNativeLibraryLoader;
|
|||||||
import com.njcn.gather.tool.parsepqdif.pojo.vo.PqdifParseResponse;
|
import com.njcn.gather.tool.parsepqdif.pojo.vo.PqdifParseResponse;
|
||||||
import com.njcn.pqdif.nativebridge.PqdifBasicNative;
|
import com.njcn.pqdif.nativebridge.PqdifBasicNative;
|
||||||
import com.njcn.pqdif.nativebridge.PqdifNativeSession;
|
import com.njcn.pqdif.nativebridge.PqdifNativeSession;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.util.*;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
@Component
|
||||||
public class PqdifNativeReader {
|
public class PqdifNativeReader {
|
||||||
|
|
||||||
private static final String STATUS_SUCCESS = "SUCCESS";
|
private static final String STATUS_SUCCESS = "SUCCESS";
|
||||||
|
private static final String DATA_SUCCESS = "DATA_SUCCESS";
|
||||||
|
private static final String DATA_FAILED = "DATA_FAILED";
|
||||||
private static final int DEFAULT_SAMPLE_VALUE_COUNT = 5;
|
private static final int DEFAULT_SAMPLE_VALUE_COUNT = 5;
|
||||||
|
|
||||||
public PqdifParseResponse read(Path pqdifPath, String fileName) {
|
public PqdifParseResponse read(Path pqdifPath, String fileName) {
|
||||||
@@ -40,10 +45,11 @@ public class PqdifNativeReader {
|
|||||||
recordVO.setRecordIndex(recordIndex);
|
recordVO.setRecordIndex(recordIndex);
|
||||||
recordVO.setTypeGuid(recordInfo.typeGuid);
|
recordVO.setTypeGuid(recordInfo.typeGuid);
|
||||||
recordVO.setTypeName(recordInfo.typeName);
|
recordVO.setTypeName(recordInfo.typeName);
|
||||||
recordVO.setObservation(isObservation(recordInfo));
|
boolean observationRecord = isObservation(recordInfo);
|
||||||
|
recordVO.setObservation(observationRecord);
|
||||||
response.getRecords().add(recordVO);
|
response.getRecords().add(recordVO);
|
||||||
|
|
||||||
if (!isObservation(recordInfo)) {
|
if (!observationRecord) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,7 +126,7 @@ public class PqdifNativeReader {
|
|||||||
vo.setScale(seriesInfo.scale);
|
vo.setScale(seriesInfo.scale);
|
||||||
vo.setOffset(seriesInfo.offset);
|
vo.setOffset(seriesInfo.offset);
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
vo.setDataStatus("DATA_FAILED");
|
vo.setDataStatus(DATA_FAILED);
|
||||||
vo.setDataMessage("getSeriesInfo failed, channel=" + channelIndex
|
vo.setDataMessage("getSeriesInfo failed, channel=" + channelIndex
|
||||||
+ ", series=" + seriesIndex
|
+ ", series=" + seriesIndex
|
||||||
+ ", error=" + e.getMessage());
|
+ ", error=" + e.getMessage());
|
||||||
@@ -132,12 +138,12 @@ public class PqdifNativeReader {
|
|||||||
try {
|
try {
|
||||||
double[] values = observation.getSeriesData(channelIndex, seriesIndex);
|
double[] values = observation.getSeriesData(channelIndex, seriesIndex);
|
||||||
|
|
||||||
vo.setDataStatus("DATA_SUCCESS");
|
vo.setDataStatus(DATA_SUCCESS);
|
||||||
vo.setDataMessage(null);
|
vo.setDataMessage(null);
|
||||||
vo.setValueCount(values == null ? 0 : values.length);
|
vo.setValueCount(values == null ? 0 : values.length);
|
||||||
vo.setFirstValues(firstValues(values, DEFAULT_SAMPLE_VALUE_COUNT));
|
vo.setFirstValues(firstValues(values, DEFAULT_SAMPLE_VALUE_COUNT));
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
vo.setDataStatus("DATA_FAILED");
|
vo.setDataStatus(DATA_FAILED);
|
||||||
vo.setDataMessage("getSeriesData failed, channel=" + channelIndex
|
vo.setDataMessage("getSeriesData failed, channel=" + channelIndex
|
||||||
+ ", series=" + seriesIndex
|
+ ", series=" + seriesIndex
|
||||||
+ ", seriesBaseType=" + vo.getSeriesBaseType()
|
+ ", seriesBaseType=" + vo.getSeriesBaseType()
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package com.njcn.gather.tool.parsepqdif.service;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
import com.njcn.gather.tool.parsepqdif.pojo.param.CsPqdifPathParam;
|
||||||
|
import com.njcn.gather.tool.parsepqdif.pojo.param.PqdifParseResultSaveParam;
|
||||||
|
import com.njcn.gather.tool.parsepqdif.pojo.vo.CsPqdifPathDetailVO;
|
||||||
|
import com.njcn.gather.tool.parsepqdif.pojo.vo.CsPqdifPathVO;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PQDIF 存储记录服务。
|
||||||
|
*/
|
||||||
|
public interface CsPqdifPathService {
|
||||||
|
|
||||||
|
List<CsPqdifPathVO> listPqdifPaths(CsPqdifPathParam.ListParam param);
|
||||||
|
|
||||||
|
JsonNode getPqdifParseMsg(String pqdifId);
|
||||||
|
|
||||||
|
CsPqdifPathDetailVO getPqdifParseDetail(String pqdifId);
|
||||||
|
|
||||||
|
boolean addPqdifPath(CsPqdifPathParam param);
|
||||||
|
|
||||||
|
boolean updatePqdifPath(CsPqdifPathParam.UpdateParam param);
|
||||||
|
|
||||||
|
boolean deletePqdifPath(List<String> ids);
|
||||||
|
|
||||||
|
boolean savePqdifParseResult(String pqdifId, PqdifParseResultSaveParam param);
|
||||||
|
}
|
||||||
@@ -0,0 +1,185 @@
|
|||||||
|
package com.njcn.gather.tool.parsepqdif.service.impl;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
import com.njcn.gather.tool.parsepqdif.mapper.CsPqdifPathMapper;
|
||||||
|
import com.njcn.gather.tool.parsepqdif.pojo.param.CsPqdifPathParam;
|
||||||
|
import com.njcn.gather.tool.parsepqdif.pojo.param.PqdifParseResultSaveParam;
|
||||||
|
import com.njcn.gather.tool.parsepqdif.pojo.po.CsPqdifPathPO;
|
||||||
|
import com.njcn.gather.tool.parsepqdif.pojo.vo.CsPqdifPathDetailVO;
|
||||||
|
import com.njcn.gather.tool.parsepqdif.pojo.vo.CsPqdifPathVO;
|
||||||
|
import com.njcn.gather.tool.parsepqdif.service.CsPqdifPathService;
|
||||||
|
import com.njcn.web.utils.RequestUtil;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PQDIF 存储记录服务实现。
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class CsPqdifPathServiceImpl implements CsPqdifPathService {
|
||||||
|
|
||||||
|
private static final int STATE_NORMAL = 1;
|
||||||
|
|
||||||
|
private static final int STATE_DELETED = 0;
|
||||||
|
|
||||||
|
private final CsPqdifPathMapper csPqdifPathMapper;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<CsPqdifPathVO> listPqdifPaths(CsPqdifPathParam.ListParam param) {
|
||||||
|
CsPqdifPathParam.ListParam checkedParam = param == null ? new CsPqdifPathParam.ListParam() : param;
|
||||||
|
return csPqdifPathMapper.selectPqdifPathList(
|
||||||
|
trimToNull(checkedParam.getKeyword()),
|
||||||
|
checkedParam.getResult());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JsonNode getPqdifParseMsg(String pqdifId) {
|
||||||
|
String id = requireText(pqdifId, "PQDIF记录ID不能为空");
|
||||||
|
CsPqdifPathVO pqdifPath = csPqdifPathMapper.selectPqdifParseMsgById(id);
|
||||||
|
return pqdifPath == null ? null : pqdifPath.getMsg();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CsPqdifPathDetailVO getPqdifParseDetail(String pqdifId) {
|
||||||
|
String id = requireText(pqdifId, "PQDIF记录ID不能为空");
|
||||||
|
return csPqdifPathMapper.selectPqdifPathDetailById(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional
|
||||||
|
public boolean addPqdifPath(CsPqdifPathParam param) {
|
||||||
|
CsPqdifPathParam checkedParam = requireParam(param);
|
||||||
|
LocalDateTime now = LocalDateTime.now();
|
||||||
|
CsPqdifPathPO pqdifPath = buildPqdifPath(checkedParam);
|
||||||
|
pqdifPath.setId(UUID.randomUUID().toString().replace("-", ""));
|
||||||
|
pqdifPath.setState(STATE_NORMAL);
|
||||||
|
pqdifPath.setCreateBy(currentUserId());
|
||||||
|
pqdifPath.setCreateTime(now);
|
||||||
|
pqdifPath.setUpdateBy(currentUserId());
|
||||||
|
pqdifPath.setUpdateTime(now);
|
||||||
|
return csPqdifPathMapper.insert(pqdifPath) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional
|
||||||
|
public boolean updatePqdifPath(CsPqdifPathParam.UpdateParam param) {
|
||||||
|
CsPqdifPathParam.UpdateParam checkedParam = requireUpdateParam(param);
|
||||||
|
requirePqdifPath(checkedParam.getId());
|
||||||
|
CsPqdifPathPO pqdifPath = buildPqdifPath(checkedParam);
|
||||||
|
pqdifPath.setId(checkedParam.getId());
|
||||||
|
pqdifPath.setUpdateBy(currentUserId());
|
||||||
|
pqdifPath.setUpdateTime(LocalDateTime.now());
|
||||||
|
return csPqdifPathMapper.updateById(pqdifPath) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional
|
||||||
|
public boolean deletePqdifPath(List<String> ids) {
|
||||||
|
if (ids == null || ids.isEmpty()) {
|
||||||
|
throw new IllegalArgumentException("PQDIF记录ID不能为空");
|
||||||
|
}
|
||||||
|
CsPqdifPathPO pqdifPath = new CsPqdifPathPO();
|
||||||
|
pqdifPath.setState(STATE_DELETED);
|
||||||
|
pqdifPath.setUpdateBy(currentUserId());
|
||||||
|
pqdifPath.setUpdateTime(LocalDateTime.now());
|
||||||
|
return csPqdifPathMapper.update(pqdifPath, new LambdaUpdateWrapper<CsPqdifPathPO>()
|
||||||
|
.in(CsPqdifPathPO::getId, ids)
|
||||||
|
.eq(CsPqdifPathPO::getState, STATE_NORMAL)) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional
|
||||||
|
public boolean savePqdifParseResult(String pqdifId, PqdifParseResultSaveParam param) {
|
||||||
|
if (param == null) {
|
||||||
|
throw new IllegalArgumentException("PQDIF解析结果不能为空");
|
||||||
|
}
|
||||||
|
CsPqdifPathPO pqdifPath = requirePqdifPath(pqdifId);
|
||||||
|
pqdifPath.setNativeVersion(trimToNull(param.getNativeVersion()));
|
||||||
|
pqdifPath.setRecordCount(param.getRecordCount());
|
||||||
|
pqdifPath.setObservationCount(param.getObservationCount());
|
||||||
|
pqdifPath.setSampleValueCount(param.getSampleValueCount());
|
||||||
|
pqdifPath.setResult(normalizeResult(param.getResult()));
|
||||||
|
pqdifPath.setMsg(param.getMsg());
|
||||||
|
pqdifPath.setJsonStr(trimToNull(param.getJsonStr()));
|
||||||
|
pqdifPath.setUpdateBy(currentUserId());
|
||||||
|
pqdifPath.setUpdateTime(LocalDateTime.now());
|
||||||
|
return csPqdifPathMapper.updateById(pqdifPath) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private CsPqdifPathPO buildPqdifPath(CsPqdifPathParam param) {
|
||||||
|
CsPqdifPathPO pqdifPath = new CsPqdifPathPO();
|
||||||
|
pqdifPath.setName(requireText(param.getName(), "PQDIF名称不能为空"));
|
||||||
|
pqdifPath.setPqdifContent(param.getPqdifContent());
|
||||||
|
return pqdifPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
private CsPqdifPathParam requireParam(CsPqdifPathParam param) {
|
||||||
|
if (param == null) {
|
||||||
|
throw new IllegalArgumentException("PQDIF记录参数不能为空");
|
||||||
|
}
|
||||||
|
return param;
|
||||||
|
}
|
||||||
|
|
||||||
|
private CsPqdifPathParam.UpdateParam requireUpdateParam(CsPqdifPathParam.UpdateParam param) {
|
||||||
|
if (param == null) {
|
||||||
|
throw new IllegalArgumentException("PQDIF记录参数不能为空");
|
||||||
|
}
|
||||||
|
requireText(param.getId(), "PQDIF记录ID不能为空");
|
||||||
|
return param;
|
||||||
|
}
|
||||||
|
|
||||||
|
private CsPqdifPathPO requirePqdifPath(String pqdifId) {
|
||||||
|
String id = requireText(pqdifId, "PQDIF记录ID不能为空");
|
||||||
|
CsPqdifPathPO pqdifPath = csPqdifPathMapper.selectById(id);
|
||||||
|
if (pqdifPath == null || !Integer.valueOf(STATE_NORMAL).equals(pqdifPath.getState())) {
|
||||||
|
throw new IllegalArgumentException("PQDIF记录不存在或已删除");
|
||||||
|
}
|
||||||
|
return pqdifPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Integer normalizeResult(Integer result) {
|
||||||
|
if (result == null) {
|
||||||
|
throw new IllegalArgumentException("解析结果不能为空");
|
||||||
|
}
|
||||||
|
if (result != 0 && result != 1) {
|
||||||
|
throw new IllegalArgumentException("解析结果只能是0或1");
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String requireText(String value, String message) {
|
||||||
|
String text = trimToNull(value);
|
||||||
|
if (text == null) {
|
||||||
|
throw new IllegalArgumentException(message);
|
||||||
|
}
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String trimToNull(String value) {
|
||||||
|
if (value == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String text = value.trim();
|
||||||
|
return text.isEmpty() ? null : text;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isBlank(String value) {
|
||||||
|
return trimToNull(value) == null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String currentUserId() {
|
||||||
|
try {
|
||||||
|
String userId = RequestUtil.getUserId();
|
||||||
|
return isBlank(userId) ? "未知用户" : userId;
|
||||||
|
} catch (Exception ex) {
|
||||||
|
return "未知用户";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,87 +3,115 @@ package com.njcn.gather.tool.parsepqdif.service.impl;
|
|||||||
import com.njcn.gather.tool.parsepqdif.pojo.vo.PqdifParseResponse;
|
import com.njcn.gather.tool.parsepqdif.pojo.vo.PqdifParseResponse;
|
||||||
import com.njcn.gather.tool.parsepqdif.reader.PqdifNativeReader;
|
import com.njcn.gather.tool.parsepqdif.reader.PqdifNativeReader;
|
||||||
import com.njcn.gather.tool.parsepqdif.service.ParsePqdifService;
|
import com.njcn.gather.tool.parsepqdif.service.ParsePqdifService;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.nio.file.*;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.nio.file.StandardCopyOption;
|
import java.nio.file.StandardCopyOption;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Service
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
public class ParsePqdifServiceImpl implements ParsePqdifService {
|
public class ParsePqdifServiceImpl implements ParsePqdifService {
|
||||||
|
|
||||||
private static final String STATUS_FAILED = "FAILED";
|
private static final String STATUS_FAILED = "FAILED";
|
||||||
|
private static final String DEFAULT_SUFFIX = ".pqd";
|
||||||
|
private static final String PQDIF_SUFFIX = ".pqdif";
|
||||||
|
private static final String TEMP_DIR_NAME = "cn-tool-pqdif-upload";
|
||||||
|
private static final String EMPTY_FILE_MESSAGE = "PQDIF文件不能为空";
|
||||||
|
private static final String UNSUPPORTED_FILE_MESSAGE = "仅支持 .pqd 或 .pqdif 格式文件";
|
||||||
|
private static final String DEFAULT_FAILED_MESSAGE = "PQDIF解析失败";
|
||||||
|
private static final String UNKNOWN_FAILED_REASON = "请检查文件内容或原生解析库状态";
|
||||||
|
|
||||||
private final PqdifNativeReader pqdifNativeReader = new PqdifNativeReader();
|
private final PqdifNativeReader pqdifNativeReader;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PqdifParseResponse parse(MultipartFile pqdifFile) {
|
public PqdifParseResponse parse(MultipartFile pqdifFile) {
|
||||||
if (pqdifFile == null || pqdifFile.isEmpty()) {
|
if (pqdifFile == null || pqdifFile.isEmpty()) {
|
||||||
return failed(null, "PQDIF文件不能为空");
|
return failed(null, EMPTY_FILE_MESSAGE);
|
||||||
|
}
|
||||||
|
|
||||||
|
String originalFilename = pqdifFile.getOriginalFilename();
|
||||||
|
String suffix = getSupportedSuffix(originalFilename);
|
||||||
|
if (suffix == null) {
|
||||||
|
return failed(originalFilename, UNSUPPORTED_FILE_MESSAGE);
|
||||||
}
|
}
|
||||||
|
|
||||||
Path tempFile = null;
|
Path tempFile = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
tempFile = createTempPqdifFile(pqdifFile);
|
tempFile = createTempPqdifFile(pqdifFile, suffix);
|
||||||
return pqdifNativeReader.read(tempFile, pqdifFile.getOriginalFilename());
|
return pqdifNativeReader.read(tempFile, originalFilename);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("PQDIF解析失败,fileName={}", pqdifFile.getOriginalFilename(), e);
|
log.error("PQDIF解析失败,fileName={}", originalFilename, e);
|
||||||
return failed(pqdifFile.getOriginalFilename(), e.getMessage());
|
return failed(originalFilename, buildFailedMessage(e));
|
||||||
} finally {
|
} finally {
|
||||||
if (tempFile != null) {
|
deleteTempFile(tempFile);
|
||||||
try {
|
|
||||||
Files.deleteIfExists(tempFile);
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.warn("删除PQDIF临时文件失败,path={}", tempFile, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Path createTempPqdifFile(MultipartFile pqdifFile) throws Exception {
|
private Path createTempPqdifFile(MultipartFile pqdifFile, String suffix) throws Exception {
|
||||||
String originalFilename = pqdifFile.getOriginalFilename();
|
// 原生解析库只接收文件路径,因此上传内容需先落到系统临时目录。
|
||||||
String suffix = getSuffix(originalFilename);
|
Path uploadDir = Paths.get(System.getProperty("java.io.tmpdir"), TEMP_DIR_NAME);
|
||||||
|
|
||||||
Path uploadDir = Paths.get("D:", "CN_Tool_Runtime", "pqdif-upload");
|
|
||||||
Files.createDirectories(uploadDir);
|
Files.createDirectories(uploadDir);
|
||||||
|
|
||||||
String safeFileName = "parse-pqdif-" + System.currentTimeMillis() + "-" +
|
Path tempFile = Files.createTempFile(uploadDir, "parse-pqdif-", suffix);
|
||||||
java.util.UUID.randomUUID().toString().replace("-", "") + suffix;
|
|
||||||
|
|
||||||
Path tempFile = uploadDir.resolve(safeFileName);
|
|
||||||
|
|
||||||
try (InputStream inputStream = pqdifFile.getInputStream()) {
|
try (InputStream inputStream = pqdifFile.getInputStream()) {
|
||||||
Files.copy(inputStream, tempFile, StandardCopyOption.REPLACE_EXISTING);
|
Files.copy(inputStream, tempFile, StandardCopyOption.REPLACE_EXISTING);
|
||||||
}
|
}
|
||||||
|
|
||||||
return tempFile;
|
return tempFile;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getSuffix(String originalFilename) {
|
private void deleteTempFile(Path tempFile) {
|
||||||
if (originalFilename == null) {
|
if (tempFile == null) {
|
||||||
return ".pqd";
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
Files.deleteIfExists(tempFile);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warn("删除PQDIF临时文件失败,path={}", tempFile, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getSupportedSuffix(String originalFilename) {
|
||||||
|
if (originalFilename == null || originalFilename.trim().isEmpty()) {
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
int index = originalFilename.lastIndexOf('.');
|
int index = originalFilename.lastIndexOf('.');
|
||||||
if (index < 0 || index == originalFilename.length() - 1) {
|
if (index < 0 || index == originalFilename.length() - 1) {
|
||||||
return ".pqd";
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return originalFilename.substring(index);
|
String suffix = originalFilename.substring(index).toLowerCase(Locale.ROOT);
|
||||||
|
if (DEFAULT_SUFFIX.equals(suffix) || PQDIF_SUFFIX.equals(suffix)) {
|
||||||
|
return suffix;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String resolveErrorMessage(Exception e) {
|
||||||
|
String message = e.getMessage();
|
||||||
|
if (message == null || message.trim().isEmpty()) {
|
||||||
|
return UNKNOWN_FAILED_REASON;
|
||||||
|
}
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String buildFailedMessage(Exception e) {
|
||||||
|
return DEFAULT_FAILED_MESSAGE + ":" + resolveErrorMessage(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
private PqdifParseResponse failed(String fileName, String message) {
|
private PqdifParseResponse failed(String fileName, String message) {
|
||||||
PqdifParseResponse response = new PqdifParseResponse();
|
PqdifParseResponse response = new PqdifParseResponse();
|
||||||
response.setStatus(STATUS_FAILED);
|
response.setStatus(STATUS_FAILED);
|
||||||
response.setMessage(message == null ? "PQDIF解析失败" : message);
|
response.setMessage(message == null ? DEFAULT_FAILED_MESSAGE : message);
|
||||||
response.setFileName(fileName);
|
response.setFileName(fileName);
|
||||||
response.setRecordCount(0L);
|
response.setRecordCount(0L);
|
||||||
response.setObservationCount(0L);
|
response.setObservationCount(0L);
|
||||||
|
|||||||
@@ -0,0 +1,51 @@
|
|||||||
|
package com.njcn.gather.tool.parsepqdif.typehandler;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import org.apache.ibatis.type.BaseTypeHandler;
|
||||||
|
import org.apache.ibatis.type.JdbcType;
|
||||||
|
|
||||||
|
import java.sql.CallableStatement;
|
||||||
|
import java.sql.PreparedStatement;
|
||||||
|
import java.sql.ResultSet;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将数据库 JSON 文本映射为 Jackson JsonNode,便于接口直接返回结构化解析结果。
|
||||||
|
*/
|
||||||
|
public class JsonNodeTypeHandler extends BaseTypeHandler<JsonNode> {
|
||||||
|
|
||||||
|
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setNonNullParameter(PreparedStatement ps, int i, JsonNode parameter, JdbcType jdbcType)
|
||||||
|
throws SQLException {
|
||||||
|
ps.setString(i, parameter.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JsonNode getNullableResult(ResultSet rs, String columnName) throws SQLException {
|
||||||
|
return parse(rs.getString(columnName));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JsonNode getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
|
||||||
|
return parse(rs.getString(columnIndex));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JsonNode getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
|
||||||
|
return parse(cs.getString(columnIndex));
|
||||||
|
}
|
||||||
|
|
||||||
|
private JsonNode parse(String value) throws SQLException {
|
||||||
|
if (value == null || value.trim().isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return OBJECT_MAPPER.readTree(value);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
throw new SQLException("解析JSON字段失败", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -36,7 +36,10 @@ public class AuthGlobalFilter implements Filter, Ordered {
|
|||||||
"/admin/login",
|
"/admin/login",
|
||||||
"/admin/getPublicKey",
|
"/admin/getPublicKey",
|
||||||
"/event/list/transient/page",
|
"/event/list/transient/page",
|
||||||
"/event/list/transient/page/debug"
|
"/event/list/transient/page/debug",
|
||||||
|
"/steady/checksquare/create",
|
||||||
|
"/steady/checksquare/detail",
|
||||||
|
"/steady/checksquare/item-detail"
|
||||||
);
|
);
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
|
|||||||
Reference in New Issue
Block a user