feat(mms-mapping): 添加ICD一致性校验功能并重构设备类型管理
- 在MappingController中新增ICD一致性校验接口checkIcdJsonConsistency - 添加IcdConsistencyCheckService服务实现ICD映射JSON一致性校验逻辑 - 添加IcdConsistencyCheckRequest和IcdConsistencyCheckResponse相关数据传输对象 - 在CsIcdPathPO中新增icdContent字段存储ICD内容字节数组 - 在CsIcdPathMapper中新增selectIcdPathList方法支持关键词搜索 - 移除设备类型相关的控制器、服务接口及实现类(MmsDeviceTypeController等) - 更新.gitignore文件排除特定jar包路径 - 在pom.xml中添加device-types模块依赖和JNA库依赖 - 更新README.md文档添加device-types模块说明 - 重命名steady-DataView为steady-dataView模块名统一格式
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -23,6 +23,7 @@ docs/
|
|||||||
*.ear
|
*.ear
|
||||||
*.tar.gz
|
*.tar.gz
|
||||||
*.rar
|
*.rar
|
||||||
|
!tools/parse-pqdif/lib/pqdif-native-basic-bridge-1.0.0-jar-with-dependencies.jar
|
||||||
|
|
||||||
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
|
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
|
||||||
hs_err_pid*
|
hs_err_pid*
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ CN_Tool 是一个基于 Spring Boot 的多模块后端聚合工程,当前仓
|
|||||||
- `activate-tool`
|
- `activate-tool`
|
||||||
- `add-data`
|
- `add-data`
|
||||||
- `add-ledger`
|
- `add-ledger`
|
||||||
|
- `device-types`
|
||||||
- `mms-mapping`
|
- `mms-mapping`
|
||||||
- `parse-pqdif`
|
- `parse-pqdif`
|
||||||
- `wave-tool`
|
- `wave-tool`
|
||||||
@@ -44,7 +45,7 @@ CN_Tool 是一个基于 Spring Boot 的多模块后端聚合工程,当前仓
|
|||||||
|
|
||||||
- `entrance/src/main/java/com/njcn/gather/EntranceApplication.java`
|
- `entrance/src/main/java/com/njcn/gather/EntranceApplication.java`
|
||||||
|
|
||||||
`entrance` 模块聚合了 `system`、`disk-monitor`、`dbms`、`deploy`、`user`、`detection`、`activate-tool`、`add-data`、`add-ledger`、`wave-tool`、`mms-mapping`、`parse-pqdif`,是当前运行时主入口。
|
`entrance` 模块聚合了 `system`、`disk-monitor`、`dbms`、`deploy`、`user`、`detection`、`activate-tool`、`add-data`、`add-ledger`、`wave-tool`、`device-types`、`mms-mapping`、`parse-pqdif`,是当前运行时主入口。
|
||||||
|
|
||||||
## 技术基线
|
## 技术基线
|
||||||
|
|
||||||
@@ -99,6 +100,8 @@ P0 已补齐基线文档,建议按以下顺序阅读:
|
|||||||
- 当前提供电能质量 13 张表批量补数、任务状态查询和模板规则查询能力
|
- 当前提供电能质量 13 张表批量补数、任务状态查询和模板规则查询能力
|
||||||
- `tools/add-ledger`
|
- `tools/add-ledger`
|
||||||
- 当前为数据台账工具预留空模块
|
- 当前为数据台账工具预留空模块
|
||||||
|
- `tools/device-types`
|
||||||
|
- 负责设备类型维护、ICD 校验结果保存和 PQDIF 校验预留入口
|
||||||
- `tools/mms-mapping`
|
- `tools/mms-mapping`
|
||||||
- 负责 ICD 文件解析与 MMS 映射数据生成能力
|
- 负责 ICD 文件解析与 MMS 映射数据生成能力
|
||||||
- `tools/parse-pqdif`
|
- `tools/parse-pqdif`
|
||||||
|
|||||||
@@ -53,6 +53,11 @@
|
|||||||
<artifactId>wave-tool</artifactId>
|
<artifactId>wave-tool</artifactId>
|
||||||
<version>1.0.0</version>
|
<version>1.0.0</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.njcn.gather</groupId>
|
||||||
|
<artifactId>device-types</artifactId>
|
||||||
|
<version>1.0.0</version>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.njcn.gather</groupId>
|
<groupId>com.njcn.gather</groupId>
|
||||||
<artifactId>mms-mapping</artifactId>
|
<artifactId>mms-mapping</artifactId>
|
||||||
@@ -80,7 +85,12 @@
|
|||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.njcn.gather</groupId>
|
<groupId>com.njcn.gather</groupId>
|
||||||
<artifactId>steady-DataView</artifactId>
|
<artifactId>steady-dataView</artifactId>
|
||||||
|
<version>1.0.0</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.njcn.gather</groupId>
|
||||||
|
<artifactId>check-square</artifactId>
|
||||||
<version>1.0.0</version>
|
<version>1.0.0</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|||||||
45
steady/check-square/pom.xml
Normal file
45
steady/check-square/pom.xml
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<parent>
|
||||||
|
<groupId>com.njcn.gather</groupId>
|
||||||
|
<artifactId>steady</artifactId>
|
||||||
|
<version>1.0.0</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<artifactId>check-square</artifactId>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.njcn.gather</groupId>
|
||||||
|
<artifactId>steady-dataView</artifactId>
|
||||||
|
<version>1.0.0</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.njcn</groupId>
|
||||||
|
<artifactId>njcn-common</artifactId>
|
||||||
|
<version>0.0.1</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.njcn</groupId>
|
||||||
|
<artifactId>mybatis-plus</artifactId>
|
||||||
|
<version>0.0.1</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.njcn</groupId>
|
||||||
|
<artifactId>spingboot2.3.12</artifactId>
|
||||||
|
<version>2.3.12</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-test</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</project>
|
||||||
@@ -41,6 +41,7 @@ public class SteadyChecksquareInfluxQueryComponent {
|
|||||||
|
|
||||||
private static final DateTimeFormatter INFLUX_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss'Z'");
|
private static final DateTimeFormatter INFLUX_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss'Z'");
|
||||||
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
|
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
|
||||||
|
private static final int QUERY_WINDOW_DAYS = 1;
|
||||||
private static final ThreadLocal<Map<String, List<SteadyChecksquareValuePointBO>>> REQUEST_VALUE_CACHE =
|
private static final ThreadLocal<Map<String, List<SteadyChecksquareValuePointBO>>> REQUEST_VALUE_CACHE =
|
||||||
new ThreadLocal<Map<String, List<SteadyChecksquareValuePointBO>>>();
|
new ThreadLocal<Map<String, List<SteadyChecksquareValuePointBO>>>();
|
||||||
|
|
||||||
@@ -79,8 +80,7 @@ public class SteadyChecksquareInfluxQueryComponent {
|
|||||||
log.info("数据校验指标值 InfluxDB 查询开始,measurement={},field={},lineId={},phase={},statType={},query={}",
|
log.info("数据校验指标值 InfluxDB 查询开始,measurement={},field={},lineId={},phase={},statType={},query={}",
|
||||||
field.getMeasurement(), field.getField(), field.getLineId(), field.getPhase(), field.getStatType(), query);
|
field.getMeasurement(), field.getField(), field.getLineId(), field.getPhase(), field.getStatType(), query);
|
||||||
try {
|
try {
|
||||||
String body = executeQuery(query);
|
List<SteadyChecksquareValuePointBO> points = queryValuePointsByWindow(field, startTime, endTime, intervalMinutes);
|
||||||
List<SteadyChecksquareValuePointBO> points = parseValuePoints(body, intervalMinutes);
|
|
||||||
if (cache != null) {
|
if (cache != null) {
|
||||||
cache.put(cacheKey, new ArrayList<SteadyChecksquareValuePointBO>(points));
|
cache.put(cacheKey, new ArrayList<SteadyChecksquareValuePointBO>(points));
|
||||||
}
|
}
|
||||||
@@ -124,7 +124,8 @@ public class SteadyChecksquareInfluxQueryComponent {
|
|||||||
log.info("数据校验指标值 InfluxDB 批量查询开始,measurement={},fieldCount={},lineId={},phase={},statType={},query={}",
|
log.info("数据校验指标值 InfluxDB 批量查询开始,measurement={},fieldCount={},lineId={},phase={},statType={},query={}",
|
||||||
first.getMeasurement(), missingFields.size(), first.getLineId(), first.getPhase(), first.getStatType(), query);
|
first.getMeasurement(), missingFields.size(), first.getLineId(), first.getPhase(), first.getStatType(), query);
|
||||||
try {
|
try {
|
||||||
Map<String, List<SteadyChecksquareValuePointBO>> queried = parseBatchValuePoints(executeQuery(query), intervalMinutes);
|
Map<String, List<SteadyChecksquareValuePointBO>> queried =
|
||||||
|
queryBatchValuePointsByWindow(missingFields, startTime, endTime, intervalMinutes);
|
||||||
for (SteadyTrendResolvedFieldBO field : missingFields) {
|
for (SteadyTrendResolvedFieldBO field : missingFields) {
|
||||||
List<SteadyChecksquareValuePointBO> points = queried.get(field.getField());
|
List<SteadyChecksquareValuePointBO> points = queried.get(field.getField());
|
||||||
if (points == null) {
|
if (points == null) {
|
||||||
@@ -155,6 +156,51 @@ public class SteadyChecksquareInfluxQueryComponent {
|
|||||||
return query + "|intervalMinutes=" + intervalMinutes;
|
return query + "|intervalMinutes=" + intervalMinutes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private List<SteadyChecksquareValuePointBO> queryValuePointsByWindow(SteadyTrendResolvedFieldBO field,
|
||||||
|
LocalDateTime startTime,
|
||||||
|
LocalDateTime endTime,
|
||||||
|
int intervalMinutes) {
|
||||||
|
List<SteadyChecksquareValuePointBO> result = new ArrayList<SteadyChecksquareValuePointBO>();
|
||||||
|
LocalDateTime windowStart = startTime;
|
||||||
|
while (!windowStart.isAfter(endTime)) {
|
||||||
|
LocalDateTime windowEnd = min(windowStart.plusDays(QUERY_WINDOW_DAYS).minusNanos(1), endTime);
|
||||||
|
result.addAll(parseValuePoints(executeQuery(buildValuePointQuery(field, windowStart, windowEnd)), intervalMinutes));
|
||||||
|
windowStart = windowEnd.plusNanos(1);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<String, List<SteadyChecksquareValuePointBO>> queryBatchValuePointsByWindow(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(field.getField(), 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 =
|
||||||
|
parseBatchValuePoints(executeQuery(buildBatchValuePointQuery(fields, windowStart, windowEnd)), intervalMinutes);
|
||||||
|
for (Map.Entry<String, List<SteadyChecksquareValuePointBO>> entry : windowResult.entrySet()) {
|
||||||
|
List<SteadyChecksquareValuePointBO> points = result.get(entry.getKey());
|
||||||
|
if (points == null) {
|
||||||
|
points = new ArrayList<SteadyChecksquareValuePointBO>();
|
||||||
|
result.put(entry.getKey(), points);
|
||||||
|
}
|
||||||
|
points.addAll(entry.getValue());
|
||||||
|
}
|
||||||
|
windowStart = windowEnd.plusNanos(1);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private LocalDateTime min(LocalDateTime first, LocalDateTime second) {
|
||||||
|
return first.isAfter(second) ? second : first;
|
||||||
|
}
|
||||||
|
|
||||||
public String buildValuePointQuery(SteadyTrendResolvedFieldBO field, LocalDateTime startTime, LocalDateTime endTime) {
|
public String buildValuePointQuery(SteadyTrendResolvedFieldBO field, LocalDateTime startTime, LocalDateTime endTime) {
|
||||||
StringBuilder sql = new StringBuilder();
|
StringBuilder sql = new StringBuilder();
|
||||||
sql.append("SELECT \"").append(field.getField()).append("\" AS \"value\"");
|
sql.append("SELECT \"").append(field.getField()).append("\" AS \"value\"");
|
||||||
@@ -9,7 +9,6 @@ import com.njcn.common.pojo.response.HttpResult;
|
|||||||
import com.njcn.common.utils.LogUtil;
|
import com.njcn.common.utils.LogUtil;
|
||||||
import com.njcn.gather.steady.checksquare.pojo.param.SteadyChecksquareHistoryQueryParam;
|
import com.njcn.gather.steady.checksquare.pojo.param.SteadyChecksquareHistoryQueryParam;
|
||||||
import com.njcn.gather.steady.checksquare.pojo.param.SteadyChecksquareQueryParam;
|
import com.njcn.gather.steady.checksquare.pojo.param.SteadyChecksquareQueryParam;
|
||||||
import com.njcn.gather.steady.checksquare.pojo.vo.SteadyChecksquareCreateVO;
|
|
||||||
import com.njcn.gather.steady.checksquare.pojo.vo.SteadyChecksquareItemDetailVO;
|
import com.njcn.gather.steady.checksquare.pojo.vo.SteadyChecksquareItemDetailVO;
|
||||||
import com.njcn.gather.steady.checksquare.pojo.vo.SteadyChecksquareQueryVO;
|
import com.njcn.gather.steady.checksquare.pojo.vo.SteadyChecksquareQueryVO;
|
||||||
import com.njcn.gather.steady.checksquare.pojo.vo.SteadyChecksquareTaskVO;
|
import com.njcn.gather.steady.checksquare.pojo.vo.SteadyChecksquareTaskVO;
|
||||||
@@ -36,7 +35,7 @@ import java.util.List;
|
|||||||
@Slf4j
|
@Slf4j
|
||||||
@Api(tags = "数据校验")
|
@Api(tags = "数据校验")
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/steady/data-view/checksquare")
|
@RequestMapping("/steady/checksquare")
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class SteadyChecksquareController extends BaseController {
|
public class SteadyChecksquareController extends BaseController {
|
||||||
|
|
||||||
@@ -55,10 +54,10 @@ public class SteadyChecksquareController extends BaseController {
|
|||||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON, operateType = OperateType.ADD)
|
@OperateInfo(info = LogEnum.BUSINESS_COMMON, operateType = OperateType.ADD)
|
||||||
@ApiOperation("新增数据校验记录")
|
@ApiOperation("新增数据校验记录")
|
||||||
@PostMapping("/create")
|
@PostMapping("/create")
|
||||||
public HttpResult<SteadyChecksquareCreateVO> create(@RequestBody @Validated SteadyChecksquareQueryParam param) {
|
public HttpResult<SteadyChecksquareTaskVO> create(@RequestBody @Validated SteadyChecksquareQueryParam param) {
|
||||||
String methodDescribe = getMethodDescribe("create");
|
String methodDescribe = getMethodDescribe("create");
|
||||||
LogUtil.njcnDebug(log, "{},开始新增数据校验记录,param={}", methodDescribe, param);
|
LogUtil.njcnDebug(log, "{},开始新增数据校验记录,param={}", methodDescribe, param);
|
||||||
SteadyChecksquareCreateVO result = checksquareService.create(param);
|
SteadyChecksquareTaskVO result = checksquareService.create(param);
|
||||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe);
|
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -8,7 +8,9 @@ public final class SteadyChecksquareConst {
|
|||||||
public static final int STATE_DELETED = 0;
|
public static final int STATE_DELETED = 0;
|
||||||
public static final int STATE_ENABLED = 1;
|
public static final int STATE_ENABLED = 1;
|
||||||
|
|
||||||
|
public static final String TASK_STATUS_RUNNING = "RUNNING";
|
||||||
public static final String TASK_STATUS_SUCCESS = "SUCCESS";
|
public static final String TASK_STATUS_SUCCESS = "SUCCESS";
|
||||||
|
public static final String TASK_STATUS_FAIL = "FAIL";
|
||||||
public static final String DETAIL_TYPE_SEGMENT = "SEGMENT";
|
public static final String DETAIL_TYPE_SEGMENT = "SEGMENT";
|
||||||
public static final String DETAIL_TYPE_VALUE_ORDER = "VALUE_ORDER";
|
public static final String DETAIL_TYPE_VALUE_ORDER = "VALUE_ORDER";
|
||||||
public static final String DETAIL_TYPE_HARMONIC_PARITY = "HARMONIC_PARITY";
|
public static final String DETAIL_TYPE_HARMONIC_PARITY = "HARMONIC_PARITY";
|
||||||
@@ -3,7 +3,6 @@ package com.njcn.gather.steady.checksquare.service;
|
|||||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
import com.njcn.gather.steady.checksquare.pojo.param.SteadyChecksquareHistoryQueryParam;
|
import com.njcn.gather.steady.checksquare.pojo.param.SteadyChecksquareHistoryQueryParam;
|
||||||
import com.njcn.gather.steady.checksquare.pojo.param.SteadyChecksquareQueryParam;
|
import com.njcn.gather.steady.checksquare.pojo.param.SteadyChecksquareQueryParam;
|
||||||
import com.njcn.gather.steady.checksquare.pojo.vo.SteadyChecksquareCreateVO;
|
|
||||||
import com.njcn.gather.steady.checksquare.pojo.vo.SteadyChecksquareItemDetailVO;
|
import com.njcn.gather.steady.checksquare.pojo.vo.SteadyChecksquareItemDetailVO;
|
||||||
import com.njcn.gather.steady.checksquare.pojo.vo.SteadyChecksquareQueryVO;
|
import com.njcn.gather.steady.checksquare.pojo.vo.SteadyChecksquareQueryVO;
|
||||||
import com.njcn.gather.steady.checksquare.pojo.vo.SteadyChecksquareTaskVO;
|
import com.njcn.gather.steady.checksquare.pojo.vo.SteadyChecksquareTaskVO;
|
||||||
@@ -17,7 +16,7 @@ public interface SteadyChecksquareService {
|
|||||||
|
|
||||||
Page<SteadyChecksquareTaskVO> query(SteadyChecksquareHistoryQueryParam param);
|
Page<SteadyChecksquareTaskVO> query(SteadyChecksquareHistoryQueryParam param);
|
||||||
|
|
||||||
SteadyChecksquareCreateVO create(SteadyChecksquareQueryParam param);
|
SteadyChecksquareTaskVO create(SteadyChecksquareQueryParam param);
|
||||||
|
|
||||||
boolean delete(List<String> taskIds);
|
boolean delete(List<String> taskIds);
|
||||||
|
|
||||||
@@ -17,7 +17,6 @@ import com.njcn.gather.steady.checksquare.pojo.po.SteadyChecksquareDetailPO;
|
|||||||
import com.njcn.gather.steady.checksquare.pojo.po.SteadyChecksquareItemPO;
|
import com.njcn.gather.steady.checksquare.pojo.po.SteadyChecksquareItemPO;
|
||||||
import com.njcn.gather.steady.checksquare.pojo.po.SteadyChecksquareStatSummaryPO;
|
import com.njcn.gather.steady.checksquare.pojo.po.SteadyChecksquareStatSummaryPO;
|
||||||
import com.njcn.gather.steady.checksquare.pojo.po.SteadyChecksquareTaskPO;
|
import com.njcn.gather.steady.checksquare.pojo.po.SteadyChecksquareTaskPO;
|
||||||
import com.njcn.gather.steady.checksquare.pojo.vo.SteadyChecksquareCreateVO;
|
|
||||||
import com.njcn.gather.steady.checksquare.pojo.vo.SteadyChecksquareHarmonicParityDetailVO;
|
import com.njcn.gather.steady.checksquare.pojo.vo.SteadyChecksquareHarmonicParityDetailVO;
|
||||||
import com.njcn.gather.steady.checksquare.pojo.vo.SteadyChecksquareHarmonicParityRuleVO;
|
import com.njcn.gather.steady.checksquare.pojo.vo.SteadyChecksquareHarmonicParityRuleVO;
|
||||||
import com.njcn.gather.steady.checksquare.pojo.vo.SteadyChecksquareItemDetailVO;
|
import com.njcn.gather.steady.checksquare.pojo.vo.SteadyChecksquareItemDetailVO;
|
||||||
@@ -52,6 +51,7 @@ import org.springframework.transaction.support.TransactionTemplate;
|
|||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.math.RoundingMode;
|
import java.math.RoundingMode;
|
||||||
|
import java.time.Duration;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeFormatter;
|
||||||
import java.time.format.DateTimeParseException;
|
import java.time.format.DateTimeParseException;
|
||||||
@@ -67,8 +67,7 @@ import java.util.Set;
|
|||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 数据校验服务实现。
|
* 数据校验服务实现。 */
|
||||||
*/
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Service
|
@Service
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
@@ -80,6 +79,8 @@ public class SteadyChecksquareServiceImpl implements SteadyChecksquareService {
|
|||||||
private static final int FLICKER_LONG_INTERVAL_MINUTES = 120;
|
private static final int FLICKER_LONG_INTERVAL_MINUTES = 120;
|
||||||
private static final int HARMONIC_AGGREGATE_ORDER_START = 2;
|
private static final int HARMONIC_AGGREGATE_ORDER_START = 2;
|
||||||
private static final int HARMONIC_AGGREGATE_ORDER_END = 50;
|
private static final int HARMONIC_AGGREGATE_ORDER_END = 50;
|
||||||
|
private static final long MAX_CREATE_RANGE_DAYS = 7L;
|
||||||
|
private static final int DETAIL_SAVE_BATCH_SIZE = 1000;
|
||||||
|
|
||||||
private final SteadyTrendIndicatorCatalog indicatorCatalog;
|
private final SteadyTrendIndicatorCatalog indicatorCatalog;
|
||||||
private final SteadyChecksquareInfluxQueryComponent influxQueryComponent;
|
private final SteadyChecksquareInfluxQueryComponent influxQueryComponent;
|
||||||
@@ -116,18 +117,25 @@ public class SteadyChecksquareServiceImpl implements SteadyChecksquareService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SteadyChecksquareCreateVO create(SteadyChecksquareQueryParam param) {
|
public SteadyChecksquareTaskVO create(SteadyChecksquareQueryParam param) {
|
||||||
|
validateCreateBaseParam(param);
|
||||||
|
String lineId = trimToNull(param.getLineId());
|
||||||
|
LocalDateTime startTime = parseRequiredTime(param.getTimeStart(), "开始时间不能为空");
|
||||||
|
LocalDateTime endTime = parseRequiredTime(param.getTimeEnd(), "结束时间不能为空");
|
||||||
|
SteadyChecksquareTaskPO existedTask = findExistingTask(lineId, startTime, endTime);
|
||||||
|
if (existedTask != null) {
|
||||||
|
return toTaskVO(existedTask);
|
||||||
|
}
|
||||||
|
prepareCreateContext(param);
|
||||||
influxQueryComponent.enableRequestCache();
|
influxQueryComponent.enableRequestCache();
|
||||||
SteadyChecksquareQueryVO result;
|
|
||||||
try {
|
try {
|
||||||
result = calculate(param);
|
SteadyChecksquareQueryVO result = calculate(param);
|
||||||
|
SteadyChecksquareTaskPO task = saveResultInTransaction(param, result);
|
||||||
|
return toTaskVO(task);
|
||||||
} finally {
|
} finally {
|
||||||
influxQueryComponent.clearRequestCache();
|
influxQueryComponent.clearRequestCache();
|
||||||
}
|
}
|
||||||
SteadyChecksquareTaskPO task = saveResultInTransaction(param, result);
|
|
||||||
return toCreateVO(task);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean delete(List<String> taskIds) {
|
public boolean delete(List<String> taskIds) {
|
||||||
List<String> ids = normalizeTextList(taskIds);
|
List<String> ids = normalizeTextList(taskIds);
|
||||||
@@ -224,7 +232,6 @@ public class SteadyChecksquareServiceImpl implements SteadyChecksquareService {
|
|||||||
.in(SteadyChecksquareTaskPO::getId, taskIds)
|
.in(SteadyChecksquareTaskPO::getId, taskIds)
|
||||||
.eq(SteadyChecksquareTaskPO::getState, SteadyChecksquareConst.STATE_ENABLED);
|
.eq(SteadyChecksquareTaskPO::getState, SteadyChecksquareConst.STATE_ENABLED);
|
||||||
boolean taskResult = taskService.update(taskWrapper);
|
boolean taskResult = taskService.update(taskWrapper);
|
||||||
// 检测项同步置为删除态,避免已删任务下的 item-detail 被继续访问。
|
|
||||||
LambdaUpdateWrapper<SteadyChecksquareItemPO> itemWrapper = new LambdaUpdateWrapper<SteadyChecksquareItemPO>()
|
LambdaUpdateWrapper<SteadyChecksquareItemPO> itemWrapper = new LambdaUpdateWrapper<SteadyChecksquareItemPO>()
|
||||||
.set(SteadyChecksquareItemPO::getState, SteadyChecksquareConst.STATE_DELETED)
|
.set(SteadyChecksquareItemPO::getState, SteadyChecksquareConst.STATE_DELETED)
|
||||||
.in(SteadyChecksquareItemPO::getTaskId, taskIds)
|
.in(SteadyChecksquareItemPO::getTaskId, taskIds)
|
||||||
@@ -233,6 +240,51 @@ public class SteadyChecksquareServiceImpl implements SteadyChecksquareService {
|
|||||||
return taskResult;
|
return taskResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private CreateContext prepareCreateContext(SteadyChecksquareQueryParam param) {
|
||||||
|
validateParam(param);
|
||||||
|
String lineId = trimToNull(param.getLineId());
|
||||||
|
LocalDateTime startTime = parseRequiredTime(param.getTimeStart(), "开始时间不能为空");
|
||||||
|
LocalDateTime endTime = parseRequiredTime(param.getTimeEnd(), "结束时间不能为空");
|
||||||
|
if (startTime.isAfter(endTime)) {
|
||||||
|
throw fail("开始时间不能大于结束时间");
|
||||||
|
}
|
||||||
|
AddLedgerLinePathVO linePath = requireLinePath(lineId);
|
||||||
|
int intervalMinutes = resolveIntervalMinutes(linePath);
|
||||||
|
List<String> indicatorCodes = normalizeTextList(param.getIndicatorCodes());
|
||||||
|
for (String indicatorCode : indicatorCodes) {
|
||||||
|
requireIndicator(indicatorCode);
|
||||||
|
}
|
||||||
|
validateCreateTimeRange(startTime, endTime);
|
||||||
|
return new CreateContext(lineId, linePath, startTime, endTime, intervalMinutes, indicatorCodes);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validateCreateTimeRange(LocalDateTime startTime, LocalDateTime endTime) {
|
||||||
|
if (Duration.between(startTime, endTime).compareTo(Duration.ofDays(MAX_CREATE_RANGE_DAYS)) > 0) {
|
||||||
|
throw fail("数据校验时间范围不能超过7天");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private SteadyChecksquareTaskPO findExistingTask(String lineId, LocalDateTime startTime, LocalDateTime endTime) {
|
||||||
|
List<SteadyChecksquareTaskPO> tasks = taskService.lambdaQuery()
|
||||||
|
.eq(SteadyChecksquareTaskPO::getState, SteadyChecksquareConst.STATE_ENABLED)
|
||||||
|
.eq(SteadyChecksquareTaskPO::getLineId, lineId)
|
||||||
|
.eq(SteadyChecksquareTaskPO::getTimeStart, startTime)
|
||||||
|
.eq(SteadyChecksquareTaskPO::getTimeEnd, endTime)
|
||||||
|
.orderByDesc(SteadyChecksquareTaskPO::getCreateTime)
|
||||||
|
.list();
|
||||||
|
return tasks == null || tasks.isEmpty() ? null : tasks.get(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void markTaskFail(String taskId, String message) {
|
||||||
|
LambdaUpdateWrapper<SteadyChecksquareTaskPO> wrapper = new LambdaUpdateWrapper<SteadyChecksquareTaskPO>()
|
||||||
|
.set(SteadyChecksquareTaskPO::getTaskStatus, SteadyChecksquareConst.TASK_STATUS_FAIL)
|
||||||
|
.set(SteadyChecksquareTaskPO::getResultMessage, limitMessage(message))
|
||||||
|
.set(SteadyChecksquareTaskPO::getUpdateTime, LocalDateTime.now())
|
||||||
|
.eq(SteadyChecksquareTaskPO::getId, taskId)
|
||||||
|
.eq(SteadyChecksquareTaskPO::getState, SteadyChecksquareConst.STATE_ENABLED);
|
||||||
|
taskService.update(wrapper);
|
||||||
|
}
|
||||||
|
|
||||||
private SteadyChecksquareQueryVO calculate(SteadyChecksquareQueryParam param) {
|
private SteadyChecksquareQueryVO calculate(SteadyChecksquareQueryParam param) {
|
||||||
validateParam(param);
|
validateParam(param);
|
||||||
String lineId = trimToNull(param.getLineId());
|
String lineId = trimToNull(param.getLineId());
|
||||||
@@ -275,10 +327,21 @@ public class SteadyChecksquareServiceImpl implements SteadyChecksquareService {
|
|||||||
return transactionTemplate.execute(status -> saveResult(param, result));
|
return transactionTemplate.execute(status -> saveResult(param, result));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private SteadyChecksquareTaskPO saveResultInTransaction(String taskId, SteadyChecksquareQueryParam param, SteadyChecksquareQueryVO result) {
|
||||||
|
if (transactionTemplate == null) {
|
||||||
|
return saveResult(taskId, param, result);
|
||||||
|
}
|
||||||
|
return transactionTemplate.execute(status -> saveResult(taskId, param, result));
|
||||||
|
}
|
||||||
|
|
||||||
private SteadyChecksquareTaskPO saveResult(SteadyChecksquareQueryParam param, SteadyChecksquareQueryVO result) {
|
private SteadyChecksquareTaskPO saveResult(SteadyChecksquareQueryParam param, SteadyChecksquareQueryVO result) {
|
||||||
|
return saveResult(null, param, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
private SteadyChecksquareTaskPO saveResult(String taskId, SteadyChecksquareQueryParam param, SteadyChecksquareQueryVO result) {
|
||||||
LocalDateTime now = LocalDateTime.now();
|
LocalDateTime now = LocalDateTime.now();
|
||||||
SteadyChecksquareTaskPO task = new SteadyChecksquareTaskPO();
|
SteadyChecksquareTaskPO task = new SteadyChecksquareTaskPO();
|
||||||
task.setId(SteadyChecksquareIdUtil.uuid());
|
task.setId(trimToNull(taskId) == null ? SteadyChecksquareIdUtil.uuid() : taskId);
|
||||||
task.setTaskNo(SteadyChecksquareIdUtil.taskNo());
|
task.setTaskNo(SteadyChecksquareIdUtil.taskNo());
|
||||||
task.setLineId(result.getLineId());
|
task.setLineId(result.getLineId());
|
||||||
task.setLineName(result.getLineName());
|
task.setLineName(result.getLineName());
|
||||||
@@ -296,7 +359,11 @@ public class SteadyChecksquareServiceImpl implements SteadyChecksquareService {
|
|||||||
task.setState(SteadyChecksquareConst.STATE_ENABLED);
|
task.setState(SteadyChecksquareConst.STATE_ENABLED);
|
||||||
task.setCreateTime(now);
|
task.setCreateTime(now);
|
||||||
task.setUpdateTime(now);
|
task.setUpdateTime(now);
|
||||||
|
if (trimToNull(taskId) == null) {
|
||||||
taskService.save(task);
|
taskService.save(task);
|
||||||
|
} else {
|
||||||
|
updateCompletedTask(task);
|
||||||
|
}
|
||||||
|
|
||||||
List<SteadyChecksquareItemPO> itemPOs = new ArrayList<SteadyChecksquareItemPO>();
|
List<SteadyChecksquareItemPO> itemPOs = new ArrayList<SteadyChecksquareItemPO>();
|
||||||
List<SteadyChecksquareStatSummaryPO> summaryPOs = new ArrayList<SteadyChecksquareStatSummaryPO>();
|
List<SteadyChecksquareStatSummaryPO> summaryPOs = new ArrayList<SteadyChecksquareStatSummaryPO>();
|
||||||
@@ -315,11 +382,33 @@ public class SteadyChecksquareServiceImpl implements SteadyChecksquareService {
|
|||||||
statSummaryService.saveBatch(summaryPOs);
|
statSummaryService.saveBatch(summaryPOs);
|
||||||
}
|
}
|
||||||
if (!detailPOs.isEmpty()) {
|
if (!detailPOs.isEmpty()) {
|
||||||
detailService.saveBatch(detailPOs);
|
saveDetailBatchInChunks(detailPOs);
|
||||||
}
|
}
|
||||||
return task;
|
return task;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void updateCompletedTask(SteadyChecksquareTaskPO task) {
|
||||||
|
LambdaUpdateWrapper<SteadyChecksquareTaskPO> wrapper = new LambdaUpdateWrapper<SteadyChecksquareTaskPO>()
|
||||||
|
.set(SteadyChecksquareTaskPO::getLineName, task.getLineName())
|
||||||
|
.set(SteadyChecksquareTaskPO::getIntervalMinutes, task.getIntervalMinutes())
|
||||||
|
.set(SteadyChecksquareTaskPO::getTaskStatus, task.getTaskStatus())
|
||||||
|
.set(SteadyChecksquareTaskPO::getItemCount, task.getItemCount())
|
||||||
|
.set(SteadyChecksquareTaskPO::getAbnormalItemCount, task.getAbnormalItemCount())
|
||||||
|
.set(SteadyChecksquareTaskPO::getMinDataIntegrity, task.getMinDataIntegrity())
|
||||||
|
.set(SteadyChecksquareTaskPO::getResultMessage, task.getResultMessage())
|
||||||
|
.set(SteadyChecksquareTaskPO::getUpdateTime, task.getUpdateTime())
|
||||||
|
.eq(SteadyChecksquareTaskPO::getId, task.getId())
|
||||||
|
.eq(SteadyChecksquareTaskPO::getState, SteadyChecksquareConst.STATE_ENABLED);
|
||||||
|
taskService.update(wrapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void saveDetailBatchInChunks(List<SteadyChecksquareDetailPO> detailPOs) {
|
||||||
|
for (int start = 0; start < detailPOs.size(); start += DETAIL_SAVE_BATCH_SIZE) {
|
||||||
|
int end = Math.min(start + DETAIL_SAVE_BATCH_SIZE, detailPOs.size());
|
||||||
|
detailService.saveBatch(detailPOs.subList(start, end));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private SteadyChecksquareItemPO buildItemPO(String taskId, SteadyChecksquareItemVO item, LocalDateTime now) {
|
private SteadyChecksquareItemPO buildItemPO(String taskId, SteadyChecksquareItemVO item, LocalDateTime now) {
|
||||||
SteadyChecksquareItemPO po = new SteadyChecksquareItemPO();
|
SteadyChecksquareItemPO po = new SteadyChecksquareItemPO();
|
||||||
po.setId(SteadyChecksquareIdUtil.uuid());
|
po.setId(SteadyChecksquareIdUtil.uuid());
|
||||||
@@ -444,7 +533,6 @@ public class SteadyChecksquareServiceImpl implements SteadyChecksquareService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (Map.Entry<String, List<SteadyTrendResolvedFieldBO>> entry : fieldMap.entrySet()) {
|
for (Map.Entry<String, List<SteadyTrendResolvedFieldBO>> entry : fieldMap.entrySet()) {
|
||||||
// 预取只依赖请求级缓存;后续缺数和规则校验复用同一批 Influx 结果。
|
|
||||||
influxQueryComponent.queryValuePointMap(entry.getValue(), startTime, endTime, intervalMap.get(entry.getKey()));
|
influxQueryComponent.queryValuePointMap(entry.getValue(), startTime, endTime, intervalMap.get(entry.getKey()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -727,13 +815,28 @@ public class SteadyChecksquareServiceImpl implements SteadyChecksquareService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void validateParam(SteadyChecksquareQueryParam param) {
|
private void validateParam(SteadyChecksquareQueryParam param) {
|
||||||
|
validateParam(param, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validateCreateBaseParam(SteadyChecksquareQueryParam param) {
|
||||||
if (param == null) {
|
if (param == null) {
|
||||||
throw fail("数据校验参数不能为空");
|
throw fail("数据校验参数不能为空");
|
||||||
}
|
}
|
||||||
if (trimToNull(param.getLineId()) == null) {
|
if (trimToNull(param.getLineId()) == null) {
|
||||||
throw fail("监测点 ID 不能为空");
|
throw fail("监测点ID不能为空");
|
||||||
}
|
}
|
||||||
if (normalizeTextList(param.getIndicatorCodes()).isEmpty()) {
|
parseRequiredTime(param.getTimeStart(), "开始时间不能为空");
|
||||||
|
parseRequiredTime(param.getTimeEnd(), "结束时间不能为空");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validateParam(SteadyChecksquareQueryParam param, boolean allowEmptyIndicators) {
|
||||||
|
if (param == null) {
|
||||||
|
throw fail("数据校验参数不能为空");
|
||||||
|
}
|
||||||
|
if (trimToNull(param.getLineId()) == null) {
|
||||||
|
throw fail("监测点ID不能为空");
|
||||||
|
}
|
||||||
|
if (!allowEmptyIndicators && normalizeTextList(param.getIndicatorCodes()).isEmpty()) {
|
||||||
throw fail("指标不能为空");
|
throw fail("指标不能为空");
|
||||||
}
|
}
|
||||||
parseRequiredTime(param.getTimeStart(), "开始时间不能为空");
|
parseRequiredTime(param.getTimeStart(), "开始时间不能为空");
|
||||||
@@ -800,7 +903,7 @@ public class SteadyChecksquareServiceImpl implements SteadyChecksquareService {
|
|||||||
int start = Math.max(HARMONIC_AGGREGATE_ORDER_START, indicator.getHarmonicOrderStart());
|
int start = Math.max(HARMONIC_AGGREGATE_ORDER_START, indicator.getHarmonicOrderStart());
|
||||||
int end = Math.min(HARMONIC_AGGREGATE_ORDER_END, indicator.getHarmonicOrderEnd());
|
int end = Math.min(HARMONIC_AGGREGATE_ORDER_END, indicator.getHarmonicOrderEnd());
|
||||||
if (start > end) {
|
if (start > end) {
|
||||||
throw fail("谐波次数只能在 " + HARMONIC_AGGREGATE_ORDER_START + " 到 " + HARMONIC_AGGREGATE_ORDER_END + " 之间");
|
throw fail("谐波次数只能在" + HARMONIC_AGGREGATE_ORDER_START + "到" + HARMONIC_AGGREGATE_ORDER_END + "之间");
|
||||||
}
|
}
|
||||||
List<Integer> result = new ArrayList<Integer>();
|
List<Integer> result = new ArrayList<Integer>();
|
||||||
for (int order = start; order <= end; order++) {
|
for (int order = start; order <= end; order++) {
|
||||||
@@ -914,20 +1017,6 @@ public class SteadyChecksquareServiceImpl implements SteadyChecksquareService {
|
|||||||
return vo;
|
return vo;
|
||||||
}
|
}
|
||||||
|
|
||||||
private SteadyChecksquareCreateVO toCreateVO(SteadyChecksquareTaskPO task) {
|
|
||||||
SteadyChecksquareCreateVO vo = new SteadyChecksquareCreateVO();
|
|
||||||
vo.setTaskId(task.getId());
|
|
||||||
vo.setTaskNo(task.getTaskNo());
|
|
||||||
vo.setLineId(task.getLineId());
|
|
||||||
vo.setLineName(task.getLineName());
|
|
||||||
vo.setTimeStart(formatTime(task.getTimeStart()));
|
|
||||||
vo.setTimeEnd(formatTime(task.getTimeEnd()));
|
|
||||||
vo.setIntervalMinutes(task.getIntervalMinutes());
|
|
||||||
vo.setItemCount(task.getItemCount());
|
|
||||||
vo.setAbnormalItemCount(task.getAbnormalItemCount());
|
|
||||||
return vo;
|
|
||||||
}
|
|
||||||
|
|
||||||
private SteadyChecksquareItemVO toItemVO(SteadyChecksquareItemPO item) {
|
private SteadyChecksquareItemVO toItemVO(SteadyChecksquareItemPO item) {
|
||||||
SteadyChecksquareItemVO vo = new SteadyChecksquareItemVO();
|
SteadyChecksquareItemVO vo = new SteadyChecksquareItemVO();
|
||||||
vo.setItemId(item.getId());
|
vo.setItemId(item.getId());
|
||||||
@@ -1167,6 +1256,14 @@ public class SteadyChecksquareServiceImpl implements SteadyChecksquareService {
|
|||||||
return builder.toString();
|
return builder.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String limitMessage(String message) {
|
||||||
|
String text = trimToNull(message);
|
||||||
|
if (text == null) {
|
||||||
|
return "数据校验任务执行失败";
|
||||||
|
}
|
||||||
|
return text.length() > 2000 ? text.substring(0, 2000) : text;
|
||||||
|
}
|
||||||
|
|
||||||
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>();
|
||||||
@@ -1179,6 +1276,18 @@ public class SteadyChecksquareServiceImpl implements SteadyChecksquareService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private List<String> readStringList(String json) {
|
||||||
|
if (trimToNull(json) == null) {
|
||||||
|
return new ArrayList<String>();
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
String[] values = objectMapper.readValue(json, String[].class);
|
||||||
|
return normalizeTextList(Arrays.asList(values));
|
||||||
|
} catch (Exception exception) {
|
||||||
|
throw new BusinessException(CommonResponseEnum.JSON_CONVERT_EXCEPTION, exception.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private List<BigDecimal> readBigDecimalList(String json) {
|
private List<BigDecimal> readBigDecimalList(String json) {
|
||||||
if (trimToNull(json) == null) {
|
if (trimToNull(json) == null) {
|
||||||
return new ArrayList<BigDecimal>();
|
return new ArrayList<BigDecimal>();
|
||||||
@@ -1194,4 +1303,26 @@ public class SteadyChecksquareServiceImpl implements SteadyChecksquareService {
|
|||||||
private BusinessException fail(String message) {
|
private BusinessException fail(String message) {
|
||||||
return new BusinessException(CommonResponseEnum.FAIL, message);
|
return new BusinessException(CommonResponseEnum.FAIL, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static class CreateContext {
|
||||||
|
private final String lineId;
|
||||||
|
private final AddLedgerLinePathVO linePath;
|
||||||
|
private final LocalDateTime startTime;
|
||||||
|
private final LocalDateTime endTime;
|
||||||
|
private final int intervalMinutes;
|
||||||
|
private final List<String> indicatorCodes;
|
||||||
|
|
||||||
|
private CreateContext(String lineId, AddLedgerLinePathVO linePath, LocalDateTime startTime,
|
||||||
|
LocalDateTime endTime, int intervalMinutes, List<String> indicatorCodes) {
|
||||||
|
this.lineId = lineId;
|
||||||
|
this.linePath = linePath;
|
||||||
|
this.startTime = startTime;
|
||||||
|
this.endTime = endTime;
|
||||||
|
this.intervalMinutes = intervalMinutes;
|
||||||
|
this.indicatorCodes = indicatorCodes;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -8,7 +8,7 @@ CREATE TABLE IF NOT EXISTS `steady_checksquare_task` (
|
|||||||
`interval_minutes` INT NULL COMMENT '默认统计间隔,单位分钟',
|
`interval_minutes` INT NULL COMMENT '默认统计间隔,单位分钟',
|
||||||
`indicator_codes_json` JSON NULL COMMENT '请求指标编码列表',
|
`indicator_codes_json` JSON NULL COMMENT '请求指标编码列表',
|
||||||
`indicator_codes_text` VARCHAR(2000) NULL COMMENT '请求指标编码检索文本,格式 |code1|code2|',
|
`indicator_codes_text` VARCHAR(2000) NULL COMMENT '请求指标编码检索文本,格式 |code1|code2|',
|
||||||
`task_status` VARCHAR(32) NOT NULL DEFAULT 'SUCCESS' COMMENT '任务状态:SUCCESS/FAIL',
|
`task_status` VARCHAR(32) NOT NULL DEFAULT 'SUCCESS' COMMENT '任务状态:RUNNING/SUCCESS/FAIL',
|
||||||
`item_count` INT NOT NULL DEFAULT 0 COMMENT '检测项数量',
|
`item_count` INT NOT NULL DEFAULT 0 COMMENT '检测项数量',
|
||||||
`abnormal_item_count` INT NOT NULL DEFAULT 0 COMMENT '异常检测项数量',
|
`abnormal_item_count` INT NOT NULL DEFAULT 0 COMMENT '异常检测项数量',
|
||||||
`min_data_integrity` DECIMAL(12,6) NOT NULL DEFAULT 0.000000 COMMENT '最低数据完整性',
|
`min_data_integrity` DECIMAL(12,6) NOT NULL DEFAULT 0.000000 COMMENT '最低数据完整性',
|
||||||
@@ -10,6 +10,7 @@ import java.net.InetSocketAddress;
|
|||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
@@ -141,6 +142,38 @@ class SteadyChecksquareInfluxQueryComponentTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldSplitLongValuePointQueryByDay() throws Exception {
|
||||||
|
AtomicInteger requestCount = new AtomicInteger();
|
||||||
|
HttpServer server = HttpServer.create(new InetSocketAddress(0), 0);
|
||||||
|
server.createContext("/query", exchange -> {
|
||||||
|
int index = requestCount.incrementAndGet();
|
||||||
|
byte[] body = ("{\"results\":[{\"series\":[{\"values\":["
|
||||||
|
+ "[\"2026-05-0" + index + "T00:00:00Z\"," + index + "]"
|
||||||
|
+ "]}]}]}").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);
|
||||||
|
|
||||||
|
List<com.njcn.gather.steady.checksquare.pojo.bo.SteadyChecksquareValuePointBO> result =
|
||||||
|
component.queryValuePoints(buildField("h_2"),
|
||||||
|
LocalDateTime.of(2026, 5, 1, 0, 0, 0),
|
||||||
|
LocalDateTime.of(2026, 5, 3, 0, 0, 0), 1);
|
||||||
|
|
||||||
|
Assertions.assertEquals(3, requestCount.get());
|
||||||
|
Assertions.assertEquals(3, result.size());
|
||||||
|
} finally {
|
||||||
|
server.stop(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private SteadyTrendResolvedFieldBO buildField(String fieldName) {
|
private SteadyTrendResolvedFieldBO buildField(String fieldName) {
|
||||||
SteadyTrendResolvedFieldBO field = new SteadyTrendResolvedFieldBO();
|
SteadyTrendResolvedFieldBO field = new SteadyTrendResolvedFieldBO();
|
||||||
field.setMeasurement("data_harmonic");
|
field.setMeasurement("data_harmonic");
|
||||||
@@ -6,8 +6,9 @@ import org.springframework.web.bind.annotation.GetMapping;
|
|||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
|
import java.lang.reflect.ParameterizedType;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 数据校验接口契约测试。
|
* 数据校验接口契约测试。
|
||||||
@@ -17,7 +18,7 @@ class SteadyChecksquareControllerTest {
|
|||||||
@Test
|
@Test
|
||||||
void shouldExposeChecksquareQueryEndpointInSeparateController() throws Exception {
|
void shouldExposeChecksquareQueryEndpointInSeparateController() throws Exception {
|
||||||
RequestMapping requestMapping = SteadyChecksquareController.class.getAnnotation(RequestMapping.class);
|
RequestMapping requestMapping = SteadyChecksquareController.class.getAnnotation(RequestMapping.class);
|
||||||
Assertions.assertArrayEquals(new String[]{"/steady/data-view/checksquare"}, requestMapping.value());
|
Assertions.assertArrayEquals(new String[]{"/steady/checksquare"}, requestMapping.value());
|
||||||
|
|
||||||
Method queryMethod = SteadyChecksquareController.class.getDeclaredMethod("query", com.njcn.gather.steady.checksquare.pojo.param.SteadyChecksquareHistoryQueryParam.class);
|
Method queryMethod = SteadyChecksquareController.class.getDeclaredMethod("query", com.njcn.gather.steady.checksquare.pojo.param.SteadyChecksquareHistoryQueryParam.class);
|
||||||
PostMapping queryMapping = queryMethod.getAnnotation(PostMapping.class);
|
PostMapping queryMapping = queryMethod.getAnnotation(PostMapping.class);
|
||||||
@@ -40,4 +41,16 @@ class SteadyChecksquareControllerTest {
|
|||||||
PostMapping deleteMapping = deleteMethod.getAnnotation(PostMapping.class);
|
PostMapping deleteMapping = deleteMethod.getAnnotation(PostMapping.class);
|
||||||
Assertions.assertArrayEquals(new String[]{"/delete"}, deleteMapping.value());
|
Assertions.assertArrayEquals(new String[]{"/delete"}, deleteMapping.value());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldKeepCreateResponseAsTaskSummaryWithoutDetailItems() throws Exception {
|
||||||
|
Method createMethod = SteadyChecksquareController.class.getDeclaredMethod("create",
|
||||||
|
com.njcn.gather.steady.checksquare.pojo.param.SteadyChecksquareQueryParam.class);
|
||||||
|
ParameterizedType resultType = (ParameterizedType) createMethod.getGenericReturnType();
|
||||||
|
|
||||||
|
Assertions.assertEquals(com.njcn.gather.steady.checksquare.pojo.vo.SteadyChecksquareTaskVO.class,
|
||||||
|
resultType.getActualTypeArguments()[0]);
|
||||||
|
Assertions.assertThrows(NoSuchFieldException.class,
|
||||||
|
() -> com.njcn.gather.steady.checksquare.pojo.vo.SteadyChecksquareTaskVO.class.getDeclaredField("items"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -18,6 +18,7 @@ import com.njcn.gather.steady.checksquare.pojo.vo.SteadyChecksquareItemDetailVO;
|
|||||||
import com.njcn.gather.steady.checksquare.pojo.vo.SteadyChecksquareItemVO;
|
import com.njcn.gather.steady.checksquare.pojo.vo.SteadyChecksquareItemVO;
|
||||||
import com.njcn.gather.steady.checksquare.pojo.vo.SteadyChecksquareQueryVO;
|
import com.njcn.gather.steady.checksquare.pojo.vo.SteadyChecksquareQueryVO;
|
||||||
import com.njcn.gather.steady.checksquare.pojo.vo.SteadyChecksquareStatSummaryVO;
|
import com.njcn.gather.steady.checksquare.pojo.vo.SteadyChecksquareStatSummaryVO;
|
||||||
|
import com.njcn.gather.steady.checksquare.pojo.vo.SteadyChecksquareTaskVO;
|
||||||
import com.njcn.gather.steady.checksquare.pojo.vo.SteadyChecksquareValueOrderDetailVO;
|
import com.njcn.gather.steady.checksquare.pojo.vo.SteadyChecksquareValueOrderDetailVO;
|
||||||
import com.njcn.gather.steady.checksquare.pojo.vo.SteadyChecksquareValueOrderRuleVO;
|
import com.njcn.gather.steady.checksquare.pojo.vo.SteadyChecksquareValueOrderRuleVO;
|
||||||
import com.njcn.gather.steady.checksquare.service.SteadyChecksquareDetailService;
|
import com.njcn.gather.steady.checksquare.service.SteadyChecksquareDetailService;
|
||||||
@@ -48,13 +49,13 @@ import static org.mockito.ArgumentMatchers.any;
|
|||||||
import static org.mockito.ArgumentMatchers.anyInt;
|
import static org.mockito.ArgumentMatchers.anyInt;
|
||||||
import static org.mockito.ArgumentMatchers.eq;
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.never;
|
||||||
import static org.mockito.Mockito.times;
|
import static org.mockito.Mockito.times;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 数据校验服务测试。
|
* 鏁版嵁鏍¢獙鏈嶅姟娴嬭瘯銆? */
|
||||||
*/
|
|
||||||
class SteadyChecksquareServiceImplTest {
|
class SteadyChecksquareServiceImplTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -64,6 +65,177 @@ class SteadyChecksquareServiceImplTest {
|
|||||||
Assertions.assertNull(createMethod.getAnnotation(Transactional.class));
|
Assertions.assertNull(createMethod.getAnnotation(Transactional.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldRejectCreateWhenTimeRangeExceedsSevenDays() {
|
||||||
|
AddLedgerService addLedgerService = mock(AddLedgerService.class);
|
||||||
|
SteadyChecksquareTaskService taskService = mock(SteadyChecksquareTaskService.class);
|
||||||
|
LambdaQueryChainWrapper<SteadyChecksquareTaskPO> taskQuery = mock(LambdaQueryChainWrapper.class);
|
||||||
|
when(taskService.lambdaQuery()).thenReturn(taskQuery);
|
||||||
|
when(taskQuery.eq(any(), any())).thenReturn(taskQuery);
|
||||||
|
when(taskQuery.orderByDesc(any())).thenReturn(taskQuery);
|
||||||
|
when(taskQuery.list()).thenReturn(Collections.emptyList());
|
||||||
|
SteadyChecksquareServiceImpl service = new SteadyChecksquareServiceImpl(new SteadyTrendIndicatorCatalog(),
|
||||||
|
mock(SteadyChecksquareInfluxQueryComponent.class), new SteadyChecksquareCalculator(),
|
||||||
|
mock(SteadyChecksquareValueOrderRuleComponent.class), mock(SteadyChecksquareHarmonicParityRuleComponent.class),
|
||||||
|
new AddDataTimeSlotCalculator(), addLedgerService, taskService,
|
||||||
|
mock(SteadyChecksquareItemService.class), mock(SteadyChecksquareStatSummaryService.class),
|
||||||
|
mock(SteadyChecksquareDetailService.class), new ObjectMapper());
|
||||||
|
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));
|
||||||
|
SteadyChecksquareQueryParam param = new SteadyChecksquareQueryParam();
|
||||||
|
param.setLineId("line-001");
|
||||||
|
param.setIndicatorCodes(Collections.singletonList("V_RMS"));
|
||||||
|
param.setTimeStart("2026-05-01 00:00:00");
|
||||||
|
param.setTimeEnd("2026-05-08 00:01:00");
|
||||||
|
|
||||||
|
Assertions.assertThrows(RuntimeException.class, () -> service.create(param));
|
||||||
|
|
||||||
|
verify(taskService, never()).save(any());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldNotRejectCreateByIndicatorCountWithinSevenDays() {
|
||||||
|
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.orderByDesc(any())).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 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());
|
||||||
|
ArgumentCaptor<SteadyChecksquareTaskPO> taskCaptor = ArgumentCaptor.forClass(SteadyChecksquareTaskPO.class);
|
||||||
|
when(taskService.save(taskCaptor.capture())).thenReturn(true);
|
||||||
|
SteadyChecksquareQueryParam param = new SteadyChecksquareQueryParam();
|
||||||
|
param.setLineId("line-001");
|
||||||
|
param.setIndicatorCodes(Arrays.asList("V_RMS", "V_LINE_RMS", "FREQ", "I_RMS", "I_THD"));
|
||||||
|
param.setTimeStart("2026-05-01 00:00:00");
|
||||||
|
param.setTimeEnd("2026-05-03 23:59:00");
|
||||||
|
|
||||||
|
SteadyChecksquareTaskVO result = service.create(param);
|
||||||
|
|
||||||
|
Assertions.assertEquals(taskCaptor.getValue().getId(), result.getTaskId());
|
||||||
|
Assertions.assertEquals(Integer.valueOf(5), result.getItemCount());
|
||||||
|
verify(itemService).saveBatch(any());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldReturnExistingTaskSummaryWhenCreateMatchesLineAndTime() {
|
||||||
|
SteadyChecksquareTaskService taskService = mock(SteadyChecksquareTaskService.class);
|
||||||
|
LambdaQueryChainWrapper<SteadyChecksquareTaskPO> taskQuery = mock(LambdaQueryChainWrapper.class);
|
||||||
|
SteadyChecksquareTaskPO task = new SteadyChecksquareTaskPO();
|
||||||
|
task.setId("task-001");
|
||||||
|
task.setTaskNo("CS202605010001");
|
||||||
|
task.setLineId("line-001");
|
||||||
|
task.setLineName("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.setTaskStatus("SUCCESS");
|
||||||
|
task.setItemCount(1);
|
||||||
|
task.setAbnormalItemCount(0);
|
||||||
|
task.setMinDataIntegrity(BigDecimal.ONE.setScale(6));
|
||||||
|
task.setCreateTime(LocalDateTime.of(2026, 5, 1, 1, 0));
|
||||||
|
task.setState(1);
|
||||||
|
when(taskService.lambdaQuery()).thenReturn(taskQuery);
|
||||||
|
when(taskQuery.eq(any(), any())).thenReturn(taskQuery);
|
||||||
|
when(taskQuery.orderByDesc(any())).thenReturn(taskQuery);
|
||||||
|
when(taskQuery.list()).thenReturn(Collections.singletonList(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());
|
||||||
|
SteadyChecksquareQueryParam param = new SteadyChecksquareQueryParam();
|
||||||
|
param.setLineId("line-001");
|
||||||
|
param.setIndicatorCodes(Collections.singletonList("I_RMS"));
|
||||||
|
param.setTimeStart("2026-05-01 00:00:00");
|
||||||
|
param.setTimeEnd("2026-05-01 00:01:00");
|
||||||
|
|
||||||
|
SteadyChecksquareTaskVO result = service.create(param);
|
||||||
|
|
||||||
|
Assertions.assertEquals("task-001", result.getTaskId());
|
||||||
|
Assertions.assertEquals("CS202605010001", result.getTaskNo());
|
||||||
|
Assertions.assertEquals("SUCCESS", result.getTaskStatus());
|
||||||
|
verify(taskService, never()).save(any());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldCreateTaskSynchronouslyAndReturnTaskSummary() {
|
||||||
|
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.orderByDesc(any())).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 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());
|
||||||
|
ArgumentCaptor<SteadyChecksquareTaskPO> taskCaptor = ArgumentCaptor.forClass(SteadyChecksquareTaskPO.class);
|
||||||
|
when(taskService.save(taskCaptor.capture())).thenReturn(true);
|
||||||
|
SteadyChecksquareQueryParam param = new SteadyChecksquareQueryParam();
|
||||||
|
param.setLineId("line-001");
|
||||||
|
param.setIndicatorCodes(Collections.singletonList("V_RMS"));
|
||||||
|
param.setTimeStart("2026-05-01 00:00:00");
|
||||||
|
param.setTimeEnd("2026-05-01 00:01:00");
|
||||||
|
|
||||||
|
SteadyChecksquareTaskVO result = service.create(param);
|
||||||
|
|
||||||
|
Assertions.assertEquals(taskCaptor.getValue().getId(), result.getTaskId());
|
||||||
|
Assertions.assertEquals("SUCCESS", result.getTaskStatus());
|
||||||
|
Assertions.assertEquals(Integer.valueOf(1), result.getItemCount());
|
||||||
|
verify(itemService).saveBatch(any());
|
||||||
|
verify(statSummaryService).saveBatch(any());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void shouldUseFixedFlickerIntervalsPerIndicator() {
|
void shouldUseFixedFlickerIntervalsPerIndicator() {
|
||||||
SteadyChecksquareInfluxQueryComponent influxQueryComponent = mock(SteadyChecksquareInfluxQueryComponent.class);
|
SteadyChecksquareInfluxQueryComponent influxQueryComponent = mock(SteadyChecksquareInfluxQueryComponent.class);
|
||||||
@@ -81,7 +253,7 @@ class SteadyChecksquareServiceImplTest {
|
|||||||
.thenReturn(emptyHarmonicParityRuleResult());
|
.thenReturn(emptyHarmonicParityRuleResult());
|
||||||
AddLedgerLinePathVO linePath = new AddLedgerLinePathVO();
|
AddLedgerLinePathVO linePath = new AddLedgerLinePathVO();
|
||||||
linePath.setLineId("line-001");
|
linePath.setLineId("line-001");
|
||||||
linePath.setLineName("进线一");
|
linePath.setLineName("杩涚嚎涓€");
|
||||||
linePath.setLineInterval(1);
|
linePath.setLineInterval(1);
|
||||||
when(addLedgerService.listLinePathByLineIds(eq(Collections.singletonList("line-001"))))
|
when(addLedgerService.listLinePathByLineIds(eq(Collections.singletonList("line-001"))))
|
||||||
.thenReturn(Collections.singletonMap("line-001", linePath));
|
.thenReturn(Collections.singletonMap("line-001", linePath));
|
||||||
@@ -125,7 +297,7 @@ class SteadyChecksquareServiceImplTest {
|
|||||||
.thenReturn(emptyHarmonicParityRuleResult());
|
.thenReturn(emptyHarmonicParityRuleResult());
|
||||||
AddLedgerLinePathVO linePath = new AddLedgerLinePathVO();
|
AddLedgerLinePathVO linePath = new AddLedgerLinePathVO();
|
||||||
linePath.setLineId("line-001");
|
linePath.setLineId("line-001");
|
||||||
linePath.setLineName("进线一");
|
linePath.setLineName("杩涚嚎涓€");
|
||||||
linePath.setLineInterval(1);
|
linePath.setLineInterval(1);
|
||||||
when(addLedgerService.listLinePathByLineIds(eq(Collections.singletonList("line-001"))))
|
when(addLedgerService.listLinePathByLineIds(eq(Collections.singletonList("line-001"))))
|
||||||
.thenReturn(Collections.singletonMap("line-001", linePath));
|
.thenReturn(Collections.singletonMap("line-001", linePath));
|
||||||
@@ -164,7 +336,7 @@ class SteadyChecksquareServiceImplTest {
|
|||||||
.thenReturn(emptyHarmonicParityRuleResult());
|
.thenReturn(emptyHarmonicParityRuleResult());
|
||||||
AddLedgerLinePathVO linePath = new AddLedgerLinePathVO();
|
AddLedgerLinePathVO linePath = new AddLedgerLinePathVO();
|
||||||
linePath.setLineId("line-001");
|
linePath.setLineId("line-001");
|
||||||
linePath.setLineName("进线一");
|
linePath.setLineName("杩涚嚎涓€");
|
||||||
linePath.setLineInterval(1);
|
linePath.setLineInterval(1);
|
||||||
when(addLedgerService.listLinePathByLineIds(eq(Collections.singletonList("line-001"))))
|
when(addLedgerService.listLinePathByLineIds(eq(Collections.singletonList("line-001"))))
|
||||||
.thenReturn(Collections.singletonMap("line-001", linePath));
|
.thenReturn(Collections.singletonMap("line-001", linePath));
|
||||||
@@ -218,7 +390,7 @@ class SteadyChecksquareServiceImplTest {
|
|||||||
mock(SteadyChecksquareDetailService.class), new ObjectMapper());
|
mock(SteadyChecksquareDetailService.class), new ObjectMapper());
|
||||||
AddLedgerLinePathVO linePath = new AddLedgerLinePathVO();
|
AddLedgerLinePathVO linePath = new AddLedgerLinePathVO();
|
||||||
linePath.setLineId("line-001");
|
linePath.setLineId("line-001");
|
||||||
linePath.setLineName("进线一");
|
linePath.setLineName("杩涚嚎涓€");
|
||||||
linePath.setLineInterval(1);
|
linePath.setLineInterval(1);
|
||||||
when(addLedgerService.listLinePathByLineIds(eq(Collections.singletonList("line-001"))))
|
when(addLedgerService.listLinePathByLineIds(eq(Collections.singletonList("line-001"))))
|
||||||
.thenReturn(Collections.singletonMap("line-001", linePath));
|
.thenReturn(Collections.singletonMap("line-001", linePath));
|
||||||
@@ -267,7 +439,7 @@ class SteadyChecksquareServiceImplTest {
|
|||||||
.thenReturn(emptyHarmonicParityRuleResult());
|
.thenReturn(emptyHarmonicParityRuleResult());
|
||||||
AddLedgerLinePathVO linePath = new AddLedgerLinePathVO();
|
AddLedgerLinePathVO linePath = new AddLedgerLinePathVO();
|
||||||
linePath.setLineId("line-001");
|
linePath.setLineId("line-001");
|
||||||
linePath.setLineName("进线一");
|
linePath.setLineName("杩涚嚎涓€");
|
||||||
linePath.setLineInterval(1);
|
linePath.setLineInterval(1);
|
||||||
when(addLedgerService.listLinePathByLineIds(eq(Collections.singletonList("line-001"))))
|
when(addLedgerService.listLinePathByLineIds(eq(Collections.singletonList("line-001"))))
|
||||||
.thenReturn(Collections.singletonMap("line-001", linePath));
|
.thenReturn(Collections.singletonMap("line-001", linePath));
|
||||||
@@ -314,7 +486,7 @@ class SteadyChecksquareServiceImplTest {
|
|||||||
.thenReturn(emptyRuleResult());
|
.thenReturn(emptyRuleResult());
|
||||||
AddLedgerLinePathVO linePath = new AddLedgerLinePathVO();
|
AddLedgerLinePathVO linePath = new AddLedgerLinePathVO();
|
||||||
linePath.setLineId("line-001");
|
linePath.setLineId("line-001");
|
||||||
linePath.setLineName("进线一");
|
linePath.setLineName("杩涚嚎涓€");
|
||||||
linePath.setLineInterval(1);
|
linePath.setLineInterval(1);
|
||||||
when(addLedgerService.listLinePathByLineIds(eq(Collections.singletonList("line-001"))))
|
when(addLedgerService.listLinePathByLineIds(eq(Collections.singletonList("line-001"))))
|
||||||
.thenReturn(Collections.singletonMap("line-001", linePath));
|
.thenReturn(Collections.singletonMap("line-001", linePath));
|
||||||
@@ -376,7 +548,7 @@ class SteadyChecksquareServiceImplTest {
|
|||||||
task.setId("task-001");
|
task.setId("task-001");
|
||||||
task.setState(1);
|
task.setState(1);
|
||||||
task.setLineId("line-001");
|
task.setLineId("line-001");
|
||||||
task.setLineName("进线一");
|
task.setLineName("杩涚嚎涓€");
|
||||||
task.setTimeStart(LocalDateTime.of(2026, 5, 1, 0, 0));
|
task.setTimeStart(LocalDateTime.of(2026, 5, 1, 0, 0));
|
||||||
task.setTimeEnd(LocalDateTime.of(2026, 5, 1, 0, 1));
|
task.setTimeEnd(LocalDateTime.of(2026, 5, 1, 0, 1));
|
||||||
task.setIntervalMinutes(1);
|
task.setIntervalMinutes(1);
|
||||||
@@ -481,14 +653,14 @@ class SteadyChecksquareServiceImplTest {
|
|||||||
param.setIndicatorCodes(Collections.singletonList("V_RMS"));
|
param.setIndicatorCodes(Collections.singletonList("V_RMS"));
|
||||||
SteadyChecksquareQueryVO result = new SteadyChecksquareQueryVO();
|
SteadyChecksquareQueryVO result = new SteadyChecksquareQueryVO();
|
||||||
result.setLineId("line-001");
|
result.setLineId("line-001");
|
||||||
result.setLineName("进线一");
|
result.setLineName("杩涚嚎涓€");
|
||||||
result.setTimeStart("2026-05-01 00:00:00");
|
result.setTimeStart("2026-05-01 00:00:00");
|
||||||
result.setTimeEnd("2026-05-01 00:01:00");
|
result.setTimeEnd("2026-05-01 00:01:00");
|
||||||
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.setIndicatorCode("V_RMS");
|
item.setIndicatorCode("V_RMS");
|
||||||
item.setIndicatorName("相电压有效值");
|
item.setIndicatorName("鐩哥數鍘嬫湁鏁堝€?);
|
||||||
item.setIntervalMinutes(1);
|
item.setIntervalMinutes(1);
|
||||||
item.setHasData(true);
|
item.setHasData(true);
|
||||||
item.setExpectedPointCount(2);
|
item.setExpectedPointCount(2);
|
||||||
@@ -519,6 +691,41 @@ class SteadyChecksquareServiceImplTest {
|
|||||||
verify(statSummaryService).saveBatch(any());
|
verify(statSummaryService).saveBatch(any());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldSaveDetailResultsInChunks() {
|
||||||
|
SteadyChecksquareTaskService taskService = mock(SteadyChecksquareTaskService.class);
|
||||||
|
SteadyChecksquareItemService itemService = mock(SteadyChecksquareItemService.class);
|
||||||
|
SteadyChecksquareStatSummaryService statSummaryService = mock(SteadyChecksquareStatSummaryService.class);
|
||||||
|
SteadyChecksquareDetailService detailService = mock(SteadyChecksquareDetailService.class);
|
||||||
|
SteadyChecksquareServiceImpl service = new SteadyChecksquareServiceImpl(new SteadyTrendIndicatorCatalog(),
|
||||||
|
mock(SteadyChecksquareInfluxQueryComponent.class), new SteadyChecksquareCalculator(),
|
||||||
|
mock(SteadyChecksquareValueOrderRuleComponent.class), mock(SteadyChecksquareHarmonicParityRuleComponent.class),
|
||||||
|
new AddDataTimeSlotCalculator(), mock(AddLedgerService.class), taskService,
|
||||||
|
itemService, statSummaryService, detailService, new ObjectMapper());
|
||||||
|
SteadyChecksquareQueryParam param = new SteadyChecksquareQueryParam();
|
||||||
|
param.setIndicatorCodes(Collections.singletonList("V_RMS"));
|
||||||
|
SteadyChecksquareQueryVO result = new SteadyChecksquareQueryVO();
|
||||||
|
result.setLineId("line-001");
|
||||||
|
result.setLineName("line-001");
|
||||||
|
result.setTimeStart("2026-05-01 00:00:00");
|
||||||
|
result.setTimeEnd("2026-05-01 00:01:00");
|
||||||
|
result.setIntervalMinutes(1);
|
||||||
|
SteadyChecksquareItemVO item = buildOrderItem(true, BigDecimal.ONE.setScale(6));
|
||||||
|
item.setItemKey("line-001|V_RMS");
|
||||||
|
item.setIndicatorCode("V_RMS");
|
||||||
|
for (int i = 0; i < 1001; i++) {
|
||||||
|
SteadyChecksquareValueOrderDetailVO detail = new SteadyChecksquareValueOrderDetailVO();
|
||||||
|
detail.setTime("2026-05-01 00:00:00");
|
||||||
|
detail.setPhase("A");
|
||||||
|
item.getAbnormalDetails().add(detail);
|
||||||
|
}
|
||||||
|
result.getItems().add(item);
|
||||||
|
|
||||||
|
saveResult(service, param, result);
|
||||||
|
|
||||||
|
verify(detailService, times(2)).saveBatch(any());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void shouldCountNoDataItemAsAbnormalWhenSavingTask() {
|
void shouldCountNoDataItemAsAbnormalWhenSavingTask() {
|
||||||
SteadyChecksquareTaskService taskService = mock(SteadyChecksquareTaskService.class);
|
SteadyChecksquareTaskService taskService = mock(SteadyChecksquareTaskService.class);
|
||||||
@@ -532,7 +739,7 @@ class SteadyChecksquareServiceImplTest {
|
|||||||
param.setIndicatorCodes(Collections.singletonList("V_RMS"));
|
param.setIndicatorCodes(Collections.singletonList("V_RMS"));
|
||||||
SteadyChecksquareQueryVO result = new SteadyChecksquareQueryVO();
|
SteadyChecksquareQueryVO result = new SteadyChecksquareQueryVO();
|
||||||
result.setLineId("line-001");
|
result.setLineId("line-001");
|
||||||
result.setLineName("进线一");
|
result.setLineName("杩涚嚎涓€");
|
||||||
result.setTimeStart("2026-05-01 00:00:00");
|
result.setTimeStart("2026-05-01 00:00:00");
|
||||||
result.setTimeEnd("2026-05-01 00:01:00");
|
result.setTimeEnd("2026-05-01 00:01:00");
|
||||||
result.setIntervalMinutes(1);
|
result.setIntervalMinutes(1);
|
||||||
@@ -712,3 +919,5 @@ class SteadyChecksquareServiceImplTest {
|
|||||||
return new SteadyChecksquareHarmonicParityRuleVO();
|
return new SteadyChecksquareHarmonicParityRuleVO();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -13,7 +13,8 @@
|
|||||||
<packaging>pom</packaging>
|
<packaging>pom</packaging>
|
||||||
|
|
||||||
<modules>
|
<modules>
|
||||||
<module>steady-DataView</module>
|
<module>steady-dataView</module>
|
||||||
|
<module>check-square</module>
|
||||||
</modules>
|
</modules>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
<version>1.0.0</version>
|
<version>1.0.0</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>steady-DataView</artifactId>
|
<artifactId>steady-dataView</artifactId>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency>
|
<dependency>
|
||||||
|
|||||||
@@ -1,44 +0,0 @@
|
|||||||
package com.njcn.gather.steady.checksquare.pojo.vo;
|
|
||||||
|
|
||||||
import io.swagger.annotations.ApiModel;
|
|
||||||
import io.swagger.annotations.ApiModelProperty;
|
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 新增数据校验结果。
|
|
||||||
*/
|
|
||||||
@Data
|
|
||||||
@ApiModel("新增数据校验结果")
|
|
||||||
public class SteadyChecksquareCreateVO implements Serializable {
|
|
||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
|
|
||||||
@ApiModelProperty("任务 ID")
|
|
||||||
private String taskId;
|
|
||||||
|
|
||||||
@ApiModelProperty("任务编号")
|
|
||||||
private String taskNo;
|
|
||||||
|
|
||||||
@ApiModelProperty("监测点 ID")
|
|
||||||
private String lineId;
|
|
||||||
|
|
||||||
@ApiModelProperty("监测点名称")
|
|
||||||
private String lineName;
|
|
||||||
|
|
||||||
@ApiModelProperty("开始时间")
|
|
||||||
private String timeStart;
|
|
||||||
|
|
||||||
@ApiModelProperty("结束时间")
|
|
||||||
private String timeEnd;
|
|
||||||
|
|
||||||
@ApiModelProperty("统计间隔,单位分钟")
|
|
||||||
private Integer intervalMinutes;
|
|
||||||
|
|
||||||
@ApiModelProperty("检测项数量")
|
|
||||||
private Integer itemCount;
|
|
||||||
|
|
||||||
@ApiModelProperty("异常检测项数量")
|
|
||||||
private Integer abnormalItemCount;
|
|
||||||
}
|
|
||||||
@@ -1,522 +0,0 @@
|
|||||||
# 数据校验 API 调试文档
|
|
||||||
|
|
||||||
## 1. 基础信息
|
|
||||||
|
|
||||||
- 模块:`steady/steady-DataView`
|
|
||||||
- 控制器:`com.njcn.gather.steady.checksquare.controller.SteadyChecksquareController`
|
|
||||||
- 接口前缀:`/steady/data-view/checksquare`
|
|
||||||
- 本地默认地址:`http://localhost:18192`
|
|
||||||
- Content-Type:`application/json`
|
|
||||||
- 认证:除登录和 Swagger 资源外,请求需要携带登录后的 `Authorization` 请求头。
|
|
||||||
- 数据库结果表:`steady_checksquare_task`、`steady_checksquare_item`、`steady_checksquare_stat_summary`、`steady_checksquare_detail`
|
|
||||||
|
|
||||||
通用请求头:
|
|
||||||
|
|
||||||
```http
|
|
||||||
Authorization: Bearer <token>
|
|
||||||
Content-Type: application/json
|
|
||||||
```
|
|
||||||
|
|
||||||
通用返回结构:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"code": 200,
|
|
||||||
"message": "success",
|
|
||||||
"data": {}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
> 实际 `code`、`message` 字段以项目公共 `HttpResult` 封装为准,下面示例重点展示 `data` 内容。
|
|
||||||
|
|
||||||
## 2. 查询数据校验历史记录
|
|
||||||
|
|
||||||
- 方法:`POST`
|
|
||||||
- 路径:`/steady/data-view/checksquare/query`
|
|
||||||
- 返回:`HttpResult<Page<SteadyChecksquareTaskVO>>`
|
|
||||||
- 说明:分页查询已落库的数据校验任务,按创建时间倒序返回。
|
|
||||||
|
|
||||||
请求示例:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"pageNum": 1,
|
|
||||||
"pageSize": 10,
|
|
||||||
"lineId": "line-001",
|
|
||||||
"indicatorCode": "V_RMS",
|
|
||||||
"timeStart": "2026-05-01 00:00:00",
|
|
||||||
"timeEnd": "2026-05-01 23:59:59",
|
|
||||||
"hasAbnormal": true
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
请求字段:
|
|
||||||
|
|
||||||
| 字段 | 必填 | 说明 |
|
|
||||||
| --- | --- | --- |
|
|
||||||
| `pageNum` | 否 | 当前页码,继承自 `BaseParam` |
|
|
||||||
| `pageSize` | 否 | 每页数量,继承自 `BaseParam` |
|
|
||||||
| `lineId` | 否 | 监测点 ID,精确匹配 |
|
|
||||||
| `indicatorCode` | 否 | 指标编码,按任务指标集合匹配 |
|
|
||||||
| `timeStart` | 否 | 检测开始时间下限,格式 `yyyy-MM-dd HH:mm:ss` |
|
|
||||||
| `timeEnd` | 否 | 检测结束时间上限,格式 `yyyy-MM-dd HH:mm:ss` |
|
|
||||||
| `hasAbnormal` | 否 | 是否只查询存在异常项的任务;`true` 表示 `abnormalItemCount > 0` |
|
|
||||||
|
|
||||||
返回示例:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"records": [
|
|
||||||
{
|
|
||||||
"taskId": "8f7a4d6d1f3145a88b6f9d7a8e6c1001",
|
|
||||||
"taskNo": "CS202606101630001",
|
|
||||||
"lineId": "line-001",
|
|
||||||
"lineName": "进线一",
|
|
||||||
"timeStart": "2026-05-01 00:00:00",
|
|
||||||
"timeEnd": "2026-05-01 23:59:59",
|
|
||||||
"intervalMinutes": 1,
|
|
||||||
"taskStatus": "SUCCESS",
|
|
||||||
"itemCount": 2,
|
|
||||||
"abnormalItemCount": 1,
|
|
||||||
"minDataIntegrity": 0.999306,
|
|
||||||
"createTime": "2026-06-10 16:30:00"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"total": 1,
|
|
||||||
"size": 10,
|
|
||||||
"current": 1,
|
|
||||||
"pages": 1
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
cURL 示例:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
curl -X POST "http://localhost:18192/steady/data-view/checksquare/query" \
|
|
||||||
-H "Authorization: Bearer <token>" \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-d '{"pageNum":1,"pageSize":10,"lineId":"line-001","indicatorCode":"V_RMS","hasAbnormal":true}'
|
|
||||||
```
|
|
||||||
|
|
||||||
## 3. 新增数据校验记录
|
|
||||||
|
|
||||||
- 方法:`POST`
|
|
||||||
- 路径:`/steady/data-view/checksquare/create`
|
|
||||||
- 返回:`HttpResult<SteadyChecksquareCreateVO>`
|
|
||||||
- 说明:按监测点、指标和时间范围实时查询 InfluxDB,执行缺数校验、指标值大小关系校验、谐波奇偶关系校验,并将任务、检测项、统计摘要和明细写入 MySQL。
|
|
||||||
|
|
||||||
请求示例:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"lineId": "line-001",
|
|
||||||
"indicatorCodes": ["V_RMS", "FREQ", "V_HARMONIC"],
|
|
||||||
"timeStart": "2026-05-01 00:00:00",
|
|
||||||
"timeEnd": "2026-05-01 23:59:59"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
请求字段:
|
|
||||||
|
|
||||||
| 字段 | 必填 | 说明 |
|
|
||||||
| --- | --- | --- |
|
|
||||||
| `lineId` | 是 | 监测点 ID,需要能在台账中找到且处于可用状态 |
|
|
||||||
| `indicatorCodes` | 是 | 指标编码列表,来自 `/steady/data-view/indicator-tree` |
|
|
||||||
| `timeStart` | 是 | 检测开始时间,格式 `yyyy-MM-dd HH:mm:ss` |
|
|
||||||
| `timeEnd` | 是 | 检测结束时间,格式 `yyyy-MM-dd HH:mm:ss` |
|
|
||||||
|
|
||||||
返回示例:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"taskId": "8f7a4d6d1f3145a88b6f9d7a8e6c1001",
|
|
||||||
"taskNo": "CS202606101630001",
|
|
||||||
"lineId": "line-001",
|
|
||||||
"lineName": "进线一",
|
|
||||||
"timeStart": "2026-05-01 00:00:00",
|
|
||||||
"timeEnd": "2026-05-01 23:59:59",
|
|
||||||
"intervalMinutes": 1,
|
|
||||||
"itemCount": 3,
|
|
||||||
"abnormalItemCount": 1
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
cURL 示例:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
curl -X POST "http://localhost:18192/steady/data-view/checksquare/create" \
|
|
||||||
-H "Authorization: Bearer <token>" \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-d '{"lineId":"line-001","indicatorCodes":["V_RMS","FREQ","V_HARMONIC"],"timeStart":"2026-05-01 00:00:00","timeEnd":"2026-05-01 23:59:59"}'
|
|
||||||
```
|
|
||||||
|
|
||||||
调试建议:
|
|
||||||
|
|
||||||
- 新增接口会实际写入 MySQL,重复调用会生成新的任务记录。
|
|
||||||
- 时间范围越大、指标越多,InfluxDB 查询和明细落库耗时越高。
|
|
||||||
- 谐波类指标固定按 2-50 次聚合检测,不需要在请求体传 `harmonicOrders`。
|
|
||||||
|
|
||||||
## 4. 查询数据校验任务详情
|
|
||||||
|
|
||||||
- 方法:`GET`
|
|
||||||
- 路径:`/steady/data-view/checksquare/detail`
|
|
||||||
- 返回:`HttpResult<SteadyChecksquareQueryVO>`
|
|
||||||
- 说明:按任务 ID 查询任务基础信息、检测项列表和各检测项统计摘要。检测项的明细列表通过 `/item-detail` 按需查询。
|
|
||||||
|
|
||||||
请求参数:
|
|
||||||
|
|
||||||
| 参数 | 必填 | 说明 |
|
|
||||||
| --- | --- | --- |
|
|
||||||
| `taskId` | 是 | 数据校验任务 ID,即 `/query` 或 `/create` 返回的 `taskId` |
|
|
||||||
|
|
||||||
请求示例:
|
|
||||||
|
|
||||||
```http
|
|
||||||
GET /steady/data-view/checksquare/detail?taskId=8f7a4d6d1f3145a88b6f9d7a8e6c1001
|
|
||||||
```
|
|
||||||
|
|
||||||
返回示例:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"taskId": "8f7a4d6d1f3145a88b6f9d7a8e6c1001",
|
|
||||||
"taskNo": "CS202606101630001",
|
|
||||||
"lineId": "line-001",
|
|
||||||
"lineName": "进线一",
|
|
||||||
"timeStart": "2026-05-01 00:00:00",
|
|
||||||
"timeEnd": "2026-05-01 23:59:59",
|
|
||||||
"intervalMinutes": 1,
|
|
||||||
"items": [
|
|
||||||
{
|
|
||||||
"itemId": "0f4b6d6c3e9d400a902a2df101d10001",
|
|
||||||
"itemKey": "line-001|V_RMS",
|
|
||||||
"indicatorCode": "V_RMS",
|
|
||||||
"indicatorName": "相电压有效值",
|
|
||||||
"harmonicOrder": null,
|
|
||||||
"intervalMinutes": 1,
|
|
||||||
"hasData": true,
|
|
||||||
"expectedPointCount": 5760,
|
|
||||||
"actualPointCount": 5756,
|
|
||||||
"missingPointCount": 4,
|
|
||||||
"dataIntegrity": 0.999306,
|
|
||||||
"dataIntegrityText": "99.93%",
|
|
||||||
"abnormal": true,
|
|
||||||
"abnormalPointCount": 2,
|
|
||||||
"harmonicParityAbnormal": false,
|
|
||||||
"harmonicParityAbnormalPointCount": 0,
|
|
||||||
"statSummaries": [
|
|
||||||
{
|
|
||||||
"statType": "AVG",
|
|
||||||
"supported": true,
|
|
||||||
"hasData": true,
|
|
||||||
"expectedPointCount": 1440,
|
|
||||||
"actualPointCount": 1439,
|
|
||||||
"missingPointCount": 1,
|
|
||||||
"dataIntegrity": 0.999306,
|
|
||||||
"dataIntegrityText": "99.93%",
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"statDetails": []
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
cURL 示例:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
curl -X GET "http://localhost:18192/steady/data-view/checksquare/detail?taskId=8f7a4d6d1f3145a88b6f9d7a8e6c1001" \
|
|
||||||
-H "Authorization: Bearer <token>"
|
|
||||||
```
|
|
||||||
|
|
||||||
## 5. 查询检测项明细
|
|
||||||
|
|
||||||
- 方法:`GET`
|
|
||||||
- 路径:`/steady/data-view/checksquare/item-detail`
|
|
||||||
- 返回:`HttpResult<SteadyChecksquareItemDetailVO>`
|
|
||||||
- 说明:按检测项 ID、明细类型查询缺数连续区间、指标值大小关系异常点或谐波奇偶关系异常点。`pageNum` 和 `pageSize` 未同时传入时保持全量返回;两者同时传入且均大于 0 时返回当前页明细,并在结果中带回分页元数据。
|
|
||||||
|
|
||||||
请求参数:
|
|
||||||
|
|
||||||
| 参数 | 必填 | 说明 |
|
|
||||||
| --- | --- | --- |
|
|
||||||
| `itemId` | 是 | 检测项 ID,即任务详情中每个 `items[].itemId` |
|
|
||||||
| `detailType` | 是 | 明细类型:`SEGMENT`、`VALUE_ORDER`、`HARMONIC_PARITY` |
|
|
||||||
| `statType` | 否 | 统计类型:`AVG`、`MAX`、`MIN`、`CP95`;查询缺数区间时建议传入 |
|
|
||||||
| `pageNum` | 否 | 明细分页页码;与 `pageSize` 同时传入且大于 0 时启用分页 |
|
|
||||||
| `pageSize` | 否 | 明细分页条数;与 `pageNum` 同时传入且大于 0 时启用分页 |
|
|
||||||
|
|
||||||
### 5.1 查询缺数连续区间
|
|
||||||
|
|
||||||
请求示例:
|
|
||||||
|
|
||||||
```http
|
|
||||||
GET /steady/data-view/checksquare/item-detail?itemId=0f4b6d6c3e9d400a902a2df101d10001&detailType=SEGMENT&statType=AVG
|
|
||||||
```
|
|
||||||
|
|
||||||
返回示例:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"itemId": "0f4b6d6c3e9d400a902a2df101d10001",
|
|
||||||
"detailType": "SEGMENT",
|
|
||||||
"statType": "AVG",
|
|
||||||
"pageNum": null,
|
|
||||||
"pageSize": null,
|
|
||||||
"total": null,
|
|
||||||
"segments": [
|
|
||||||
{
|
|
||||||
"startTime": "2026-05-01 00:00:00",
|
|
||||||
"endTime": "2026-05-01 00:09:00",
|
|
||||||
"status": "NORMAL",
|
|
||||||
"harmonicOrder": null,
|
|
||||||
"missingPointCount": 0,
|
|
||||||
"durationMinutes": 10
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"startTime": "2026-05-01 00:10:00",
|
|
||||||
"endTime": "2026-05-01 00:10:00",
|
|
||||||
"status": "MISSING",
|
|
||||||
"harmonicOrder": null,
|
|
||||||
"missingPointCount": 1,
|
|
||||||
"durationMinutes": 1
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"valueOrderDetails": [],
|
|
||||||
"harmonicParityDetails": []
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 5.2 查询指标值大小关系异常明细
|
|
||||||
|
|
||||||
请求示例:
|
|
||||||
|
|
||||||
```http
|
|
||||||
GET /steady/data-view/checksquare/item-detail?itemId=0f4b6d6c3e9d400a902a2df101d10001&detailType=VALUE_ORDER&pageNum=1&pageSize=20
|
|
||||||
```
|
|
||||||
|
|
||||||
返回示例:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"itemId": "0f4b6d6c3e9d400a902a2df101d10001",
|
|
||||||
"detailType": "VALUE_ORDER",
|
|
||||||
"statType": null,
|
|
||||||
"pageNum": 1,
|
|
||||||
"pageSize": 20,
|
|
||||||
"total": 1,
|
|
||||||
"segments": [],
|
|
||||||
"valueOrderDetails": [
|
|
||||||
{
|
|
||||||
"time": "2026-05-01 00:10:00",
|
|
||||||
"phase": "A",
|
|
||||||
"harmonicOrder": null,
|
|
||||||
"maxValue": 219.8,
|
|
||||||
"minValue": 218.1,
|
|
||||||
"avgValue": 219.0,
|
|
||||||
"cp95Value": 219.8
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"harmonicParityDetails": []
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 5.3 查询谐波奇偶关系异常明细
|
|
||||||
|
|
||||||
请求示例:
|
|
||||||
|
|
||||||
```http
|
|
||||||
GET /steady/data-view/checksquare/item-detail?itemId=0f4b6d6c3e9d400a902a2df101d10002&detailType=HARMONIC_PARITY&pageNum=1&pageSize=20
|
|
||||||
```
|
|
||||||
|
|
||||||
返回示例:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"itemId": "0f4b6d6c3e9d400a902a2df101d10002",
|
|
||||||
"detailType": "HARMONIC_PARITY",
|
|
||||||
"statType": null,
|
|
||||||
"pageNum": 1,
|
|
||||||
"pageSize": 20,
|
|
||||||
"total": 1,
|
|
||||||
"segments": [],
|
|
||||||
"valueOrderDetails": [],
|
|
||||||
"harmonicParityDetails": [
|
|
||||||
{
|
|
||||||
"time": "2026-05-01 00:10:00",
|
|
||||||
"phase": "A",
|
|
||||||
"statType": "AVG",
|
|
||||||
"evenHarmonicOrder": 4,
|
|
||||||
"evenValue": 0.32,
|
|
||||||
"oddHarmonicOrders": [3, 5],
|
|
||||||
"oddValues": [0.08, 0.09],
|
|
||||||
"oddMedianValue": 0.085,
|
|
||||||
"thresholdMultiplier": 3.0
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
cURL 示例:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
curl -X GET "http://localhost:18192/steady/data-view/checksquare/item-detail?itemId=0f4b6d6c3e9d400a902a2df101d10001&detailType=VALUE_ORDER" \
|
|
||||||
-H "Authorization: Bearer <token>"
|
|
||||||
```
|
|
||||||
|
|
||||||
分页 cURL 示例:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
curl -X GET "http://localhost:18192/steady/data-view/checksquare/item-detail?itemId=0f4b6d6c3e9d400a902a2df101d10001&detailType=VALUE_ORDER&pageNum=1&pageSize=20" \
|
|
||||||
-H "Authorization: Bearer <token>"
|
|
||||||
```
|
|
||||||
|
|
||||||
## 6. 删除数据校验任务
|
|
||||||
|
|
||||||
- 方法:`POST`
|
|
||||||
- 路径:`/steady/data-view/checksquare/delete`
|
|
||||||
- 返回:`HttpResult<Boolean>`
|
|
||||||
- 说明:按任务 ID 批量逻辑删除数据校验任务,并同步将任务下检测项置为删除态。删除后历史查询不再返回该任务,详情和检测项明细会按不存在处理。
|
|
||||||
|
|
||||||
请求示例:
|
|
||||||
|
|
||||||
```json
|
|
||||||
[
|
|
||||||
"8f7a4d6d1f3145a88b6f9d7a8e6c1001"
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
请求字段:
|
|
||||||
|
|
||||||
| 字段 | 必填 | 说明 |
|
|
||||||
| --- | --- | --- |
|
|
||||||
| 请求体 | 是 | 数据校验任务 ID 数组 |
|
|
||||||
|
|
||||||
返回示例:
|
|
||||||
|
|
||||||
```json
|
|
||||||
true
|
|
||||||
```
|
|
||||||
|
|
||||||
cURL 示例:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
curl -X POST "http://localhost:18192/steady/data-view/checksquare/delete" \
|
|
||||||
-H "Authorization: Bearer <token>" \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-d '["8f7a4d6d1f3145a88b6f9d7a8e6c1001"]'
|
|
||||||
```
|
|
||||||
|
|
||||||
## 7. 校验规则说明
|
|
||||||
|
|
||||||
### 7.1 缺数校验
|
|
||||||
|
|
||||||
- 按检测项支持的统计类型分别查询实际存在的时间点。
|
|
||||||
- 返回期望点数、实际点数、缺失点数、数据完整性和最大连续缺失时长。
|
|
||||||
- `SEGMENT` 明细按连续区间返回,`status` 可取 `NORMAL`、`MISSING`。
|
|
||||||
- `FLUC`、`PST` 固定按 10 分钟间隔校验,`PLT` 固定按 120 分钟间隔校验,其余指标按监测点统计间隔校验。
|
|
||||||
|
|
||||||
### 7.2 指标值大小关系校验
|
|
||||||
|
|
||||||
- 同一时间点、同一指标、同一相别需要满足 `MAX >= CP95 >= AVG >= MIN`。
|
|
||||||
- 缺少 `MAX`、`CP95`、`AVG`、`MIN` 任一值的时间点不计入大小关系异常,由缺数校验体现。
|
|
||||||
- 异常点写入 `VALUE_ORDER` 明细,前端可通过 `item-detail` 拉取后弹窗展示。
|
|
||||||
|
|
||||||
### 7.3 谐波奇偶关系校验
|
|
||||||
|
|
||||||
- 谐波指标固定聚合 2-50 次检测。
|
|
||||||
- 偶次谐波值需要大于 `0.1` 才继续参与奇偶关系检测。
|
|
||||||
- 异常点写入 `HARMONIC_PARITY` 明细,包含偶次谐波值、参与比较的奇次谐波值、中位数和阈值倍数。
|
|
||||||
|
|
||||||
## 8. 字段说明
|
|
||||||
|
|
||||||
### 8.1 任务字段
|
|
||||||
|
|
||||||
| 字段 | 说明 |
|
|
||||||
| --- | --- |
|
|
||||||
| `taskId` | 数据校验任务 ID |
|
|
||||||
| `taskNo` | 数据校验任务编号 |
|
|
||||||
| `lineId` | 监测点 ID |
|
|
||||||
| `lineName` | 监测点名称 |
|
|
||||||
| `timeStart` | 检测开始时间 |
|
|
||||||
| `timeEnd` | 检测结束时间 |
|
|
||||||
| `intervalMinutes` | 监测点统计间隔,单位分钟 |
|
|
||||||
| `taskStatus` | 任务状态,当前成功任务为 `SUCCESS` |
|
|
||||||
| `itemCount` | 检测项数量 |
|
|
||||||
| `abnormalItemCount` | 存在异常的检测项数量 |
|
|
||||||
| `minDataIntegrity` | 检测项中的最低数据完整性 |
|
|
||||||
| `createTime` | 任务创建时间 |
|
|
||||||
|
|
||||||
### 8.2 检测项字段
|
|
||||||
|
|
||||||
| 字段 | 说明 |
|
|
||||||
| --- | --- |
|
|
||||||
| `itemId` | 检测项 ID |
|
|
||||||
| `itemKey` | 检测项唯一键 |
|
|
||||||
| `indicatorCode` | 指标编码 |
|
|
||||||
| `indicatorName` | 指标名称 |
|
|
||||||
| `harmonicOrder` | 谐波次数;聚合检测项为空 |
|
|
||||||
| `hasData` | 当前检测项是否存在任意数据 |
|
|
||||||
| `expectedPointCount` | 期望点数 |
|
|
||||||
| `actualPointCount` | 实际点数 |
|
|
||||||
| `missingPointCount` | 缺失点数 |
|
|
||||||
| `dataIntegrity` | 数据完整性数值 |
|
|
||||||
| `dataIntegrityText` | 数据完整性文本 |
|
|
||||||
| `abnormal` | 指标值大小关系是否异常 |
|
|
||||||
| `abnormalPointCount` | 指标值大小关系异常点数 |
|
|
||||||
| `harmonicParityAbnormal` | 谐波奇偶关系是否异常 |
|
|
||||||
| `harmonicParityAbnormalPointCount` | 谐波奇偶关系异常点数 |
|
|
||||||
| `statSummaries` | 各统计类型缺数摘要 |
|
|
||||||
| `statDetails` | 兼容字段;明细建议通过 `/item-detail` 按需查询 |
|
|
||||||
|
|
||||||
### 8.3 检测项明细字段
|
|
||||||
|
|
||||||
| 字段 | 说明 |
|
|
||||||
| --- | --- |
|
|
||||||
| `itemId` | 检测项 ID |
|
|
||||||
| `detailType` | 明细类型:`SEGMENT`、`VALUE_ORDER`、`HARMONIC_PARITY` |
|
|
||||||
| `statType` | 统计类型;未传入或当前明细类型不按统计类型过滤时为空 |
|
|
||||||
| `pageNum` | 当前页码;未启用分页时为空 |
|
|
||||||
| `pageSize` | 每页条数;未启用分页时为空 |
|
|
||||||
| `total` | 当前查询条件下的总明细数;未启用分页时为空 |
|
|
||||||
| `segments` | 缺数连续区间,仅 `SEGMENT` 查询返回数据 |
|
|
||||||
| `valueOrderDetails` | 指标值大小关系异常点,仅 `VALUE_ORDER` 查询返回数据 |
|
|
||||||
| `harmonicParityDetails` | 谐波奇偶关系异常点,仅 `HARMONIC_PARITY` 查询返回数据 |
|
|
||||||
|
|
||||||
## 9. 常见错误场景
|
|
||||||
|
|
||||||
| 场景 | 后端提示 |
|
|
||||||
| --- | --- |
|
|
||||||
| 新增时 `lineId` 为空 | `监测点 ID 不能为空` |
|
|
||||||
| 新增时 `indicatorCodes` 为空 | `指标不能为空` |
|
|
||||||
| 新增时开始时间为空 | `开始时间不能为空` |
|
|
||||||
| 新增时结束时间为空 | `结束时间不能为空` |
|
|
||||||
| 开始时间大于结束时间 | `开始时间不能大于结束时间` |
|
|
||||||
| 时间格式不正确 | `时间格式不正确,仅支持 yyyy-MM-dd HH:mm:ss` |
|
|
||||||
| 监测点不存在或不可用 | `监测点不存在或不可用` |
|
|
||||||
| 指标编码不支持 | `稳态指标不支持:xxx` |
|
|
||||||
| 任务不存在或已删除 | `数据校验任务不存在` |
|
|
||||||
| 检测项不存在或已删除 | `数据校验检测项不存在` |
|
|
||||||
| 删除时任务 ID 为空 | `数据校验任务 ID 不能为空` |
|
|
||||||
| 删除时任务不存在或已删除 | `数据校验任务不存在或已删除` |
|
|
||||||
| 明细类型为空 | `明细类型不能为空` |
|
|
||||||
| 明细类型不支持 | `明细类型不支持:xxx` |
|
|
||||||
|
|
||||||
## 10. 调试流程建议
|
|
||||||
|
|
||||||
1. 调用 `/steady/data-view/ledger-tree` 获取可用监测点,取叶子节点 `lineId`。
|
|
||||||
2. 调用 `/steady/data-view/indicator-tree` 获取可用指标编码。
|
|
||||||
3. 调用 `/steady/data-view/checksquare/create` 新增校验任务。
|
|
||||||
4. 使用返回的 `taskId` 调用 `/steady/data-view/checksquare/detail` 查看任务详情和检测项列表。
|
|
||||||
5. 使用 `items[].itemId` 调用 `/steady/data-view/checksquare/item-detail` 查看缺数区间或异常点明细。
|
|
||||||
6. 调用 `/steady/data-view/checksquare/query` 验证任务已落库,并按监测点、指标、时间和异常状态筛选历史记录。
|
|
||||||
7. 调用 `/steady/data-view/checksquare/delete` 删除任务后,再调用 `/query`、`/detail` 验证任务已不可见。
|
|
||||||
|
|
||||||
## 11. 环境依赖
|
|
||||||
|
|
||||||
- MySQL:需要已初始化 4 张 `steady_checksquare_*` 结果表。
|
|
||||||
- InfluxDB:新增校验任务时需要可访问 `steady.influxdb` 配置的时序库。
|
|
||||||
- 台账:新增校验任务时需要 `lineId` 能在台账中解析到监测点路径和统计间隔。
|
|
||||||
- 指标目录:`indicatorCodes` 必须来自后端趋势指标目录。
|
|
||||||
|
|
||||||
|
|
||||||
@@ -9,6 +9,7 @@
|
|||||||
- `activate-tool`
|
- `activate-tool`
|
||||||
- `add-data`
|
- `add-data`
|
||||||
- `add-ledger`
|
- `add-ledger`
|
||||||
|
- `device-types`
|
||||||
- `mms-mapping`
|
- `mms-mapping`
|
||||||
- `parse-pqdif`
|
- `parse-pqdif`
|
||||||
- `wave-tool`
|
- `wave-tool`
|
||||||
@@ -22,12 +23,13 @@ tools/
|
|||||||
├── activate-tool/
|
├── activate-tool/
|
||||||
├── add-data/
|
├── add-data/
|
||||||
├── add-ledger/
|
├── add-ledger/
|
||||||
|
├── device-types/
|
||||||
├── mms-mapping/
|
├── mms-mapping/
|
||||||
├── parse-pqdif/
|
├── parse-pqdif/
|
||||||
└── wave-tool/
|
└── wave-tool/
|
||||||
```
|
```
|
||||||
|
|
||||||
其中 `tools/mms-mapping` 当前 Maven `artifactId` 为 `mms-mapping`。
|
其中 `tools/device-types` 当前 Maven `artifactId` 为 `device-types`,`tools/mms-mapping` 当前 Maven `artifactId` 为 `mms-mapping`。
|
||||||
|
|
||||||
## add-data 的职责
|
## add-data 的职责
|
||||||
|
|
||||||
@@ -80,6 +82,16 @@ tools/
|
|||||||
|
|
||||||
从接口层看,当前主要围绕 `/api/mms-mapping` 路径提供能力。
|
从接口层看,当前主要围绕 `/api/mms-mapping` 路径提供能力。
|
||||||
|
|
||||||
|
## device-types 的职责
|
||||||
|
|
||||||
|
`device-types` 当前提供设备类型维护与校验入口:
|
||||||
|
- 查询设备类型列表
|
||||||
|
- 新增、编辑、删除设备类型
|
||||||
|
- 保存设备类型 ICD 唯一性校验结果
|
||||||
|
- 预留设备类型 PQDIF 校验入口
|
||||||
|
|
||||||
|
从接口层看,当前主要围绕 `/api/device-types` 路径提供能力。
|
||||||
|
|
||||||
## parse-pqdif 的职责
|
## parse-pqdif 的职责
|
||||||
|
|
||||||
`parse-pqdif` 当前仅作为 PQDIF 文件解析工具的预留骨架,参照 `mms-mapping` 的 Controller、Service、ServiceImpl 和 VO 分层组织。
|
`parse-pqdif` 当前仅作为 PQDIF 文件解析工具的预留骨架,参照 `mms-mapping` 的 Controller、Service、ServiceImpl 和 VO 分层组织。
|
||||||
@@ -110,7 +122,7 @@ tools/
|
|||||||
|
|
||||||
## 依赖关系
|
## 依赖关系
|
||||||
|
|
||||||
`tools/activate-tool`、`tools/add-data`、`tools/add-ledger`、`tools/mms-mapping`、`tools/parse-pqdif` 与 `tools/wave-tool` 当前主要依赖:
|
`tools/activate-tool`、`tools/add-data`、`tools/add-ledger`、`tools/device-types`、`tools/mms-mapping`、`tools/parse-pqdif` 与 `tools/wave-tool` 当前主要依赖:
|
||||||
|
|
||||||
- `com.njcn:njcn-common`
|
- `com.njcn:njcn-common`
|
||||||
- `com.njcn:spingboot2.3.12`
|
- `com.njcn:spingboot2.3.12`
|
||||||
|
|||||||
67
tools/device-types/pom.xml
Normal file
67
tools/device-types/pom.xml
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<parent>
|
||||||
|
<groupId>com.njcn.gather</groupId>
|
||||||
|
<artifactId>tools</artifactId>
|
||||||
|
<version>1.0.0</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<artifactId>device-types</artifactId>
|
||||||
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.njcn</groupId>
|
||||||
|
<artifactId>njcn-common</artifactId>
|
||||||
|
<version>0.0.1</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.njcn</groupId>
|
||||||
|
<artifactId>mybatis-plus</artifactId>
|
||||||
|
<version>0.0.1</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.njcn</groupId>
|
||||||
|
<artifactId>spingboot2.3.12</artifactId>
|
||||||
|
<version>2.3.12</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-web</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-validation</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-test</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<finalName>device-types</finalName>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
|
<version>3.8.1</version>
|
||||||
|
<configuration>
|
||||||
|
<source>1.8</source>
|
||||||
|
<target>1.8</target>
|
||||||
|
<encoding>UTF-8</encoding>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
</project>
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
package com.njcn.gather.device.types.controller;
|
||||||
|
|
||||||
|
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.device.types.pojo.param.DeviceTypeParam;
|
||||||
|
import com.njcn.gather.device.types.pojo.vo.DeviceTypeVO;
|
||||||
|
import com.njcn.gather.device.types.pojo.vo.PqdifCheckPlaceholderVO;
|
||||||
|
import com.njcn.gather.device.types.service.DeviceTypeService;
|
||||||
|
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.GetMapping;
|
||||||
|
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.RequestParam;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设备类型维护入口。
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Api(tags = "设备类型管理")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/device-types")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class DeviceTypeController extends BaseController {
|
||||||
|
|
||||||
|
private final DeviceTypeService deviceTypeService;
|
||||||
|
|
||||||
|
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
|
||||||
|
@ApiOperation("查询设备类型列表")
|
||||||
|
@ApiImplicitParam(name = "keyword", value = "keyword", required = false)
|
||||||
|
@GetMapping
|
||||||
|
public HttpResult<List<DeviceTypeVO>> listDeviceTypes(@RequestParam(value = "keyword", required = false) String keyword) {
|
||||||
|
String methodDescribe = getMethodDescribe("listDeviceTypes");
|
||||||
|
LogUtil.njcnDebug(log, "{},开始查询设备类型列表", methodDescribe);
|
||||||
|
List<DeviceTypeVO> result = deviceTypeService.listDeviceTypes(keyword);
|
||||||
|
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe);
|
||||||
|
}
|
||||||
|
|
||||||
|
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
|
||||||
|
@ApiOperation("新增设备类型")
|
||||||
|
@PostMapping("/add")
|
||||||
|
public HttpResult<Boolean> add(@RequestBody @Validated DeviceTypeParam param) {
|
||||||
|
String methodDescribe = getMethodDescribe("add");
|
||||||
|
LogUtil.njcnDebug(log, "{},开始新增设备类型", methodDescribe);
|
||||||
|
boolean result = deviceTypeService.addDeviceType(param);
|
||||||
|
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe);
|
||||||
|
}
|
||||||
|
|
||||||
|
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
|
||||||
|
@ApiOperation("编辑设备类型")
|
||||||
|
@PostMapping("/update")
|
||||||
|
public HttpResult<Boolean> update(@RequestBody @Validated DeviceTypeParam.UpdateParam param) {
|
||||||
|
String methodDescribe = getMethodDescribe("update");
|
||||||
|
LogUtil.njcnDebug(log, "{},开始编辑设备类型,devTypeId={}", methodDescribe, param.getId());
|
||||||
|
boolean result = deviceTypeService.updateDeviceType(param);
|
||||||
|
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe);
|
||||||
|
}
|
||||||
|
|
||||||
|
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
|
||||||
|
@ApiOperation("删除设备类型")
|
||||||
|
@PostMapping("/delete")
|
||||||
|
public HttpResult<Boolean> delete(@RequestBody List<String> ids) {
|
||||||
|
String methodDescribe = getMethodDescribe("delete");
|
||||||
|
LogUtil.njcnDebug(log, "{},开始删除设备类型,ids={}", methodDescribe, ids);
|
||||||
|
boolean result = deviceTypeService.deleteDeviceType(ids);
|
||||||
|
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe);
|
||||||
|
}
|
||||||
|
|
||||||
|
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
|
||||||
|
@ApiOperation("设备类型PQDIF校验")
|
||||||
|
@ApiImplicitParam(name = "id", value = "设备类型ID", required = true)
|
||||||
|
@PostMapping("/{id}/pqdif-check")
|
||||||
|
public HttpResult<PqdifCheckPlaceholderVO> pqdifCheck(@PathVariable("id") String id) {
|
||||||
|
String methodDescribe = getMethodDescribe("pqdifCheck");
|
||||||
|
LogUtil.njcnDebug(log, "{},设备类型PQDIF校验预留入口,devTypeId={}", methodDescribe, id);
|
||||||
|
PqdifCheckPlaceholderVO result = deviceTypeService.pqdifCheck(id);
|
||||||
|
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package com.njcn.gather.device.types.mapper;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import com.njcn.gather.device.types.pojo.po.CsDevTypePO;
|
||||||
|
import com.njcn.gather.device.types.pojo.vo.DeviceTypeVO;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设备类型 Mapper。
|
||||||
|
*/
|
||||||
|
public interface CsDevTypeMapper extends BaseMapper<CsDevTypePO> {
|
||||||
|
|
||||||
|
List<DeviceTypeVO> selectDeviceTypeCheckList(@Param("keyword") String keyword);
|
||||||
|
}
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||||
<mapper namespace="com.njcn.gather.icd.mapping.mapper.CsDevTypeMapper">
|
<mapper namespace="com.njcn.gather.device.types.mapper.CsDevTypeMapper">
|
||||||
|
|
||||||
<select id="selectDeviceTypeCheckList"
|
<select id="selectDeviceTypeCheckList"
|
||||||
resultType="com.njcn.gather.icd.mapping.pojo.vo.MmsDeviceTypeVO">
|
resultType="com.njcn.gather.device.types.pojo.vo.DeviceTypeVO">
|
||||||
SELECT
|
SELECT
|
||||||
d.id AS id,
|
d.id AS id,
|
||||||
d.name AS name,
|
d.name AS name,
|
||||||
@@ -13,12 +13,21 @@
|
|||||||
p.Path AS icdPath,
|
p.Path AS icdPath,
|
||||||
p.Result AS icdResult,
|
p.Result AS icdResult,
|
||||||
p.Msg AS icdMsg,
|
p.Msg AS icdMsg,
|
||||||
|
d.power AS power,
|
||||||
|
d.Dev_Volt AS devVolt,
|
||||||
|
d.Dev_Curr AS devCurr,
|
||||||
|
d.Dev_Chns AS devChns,
|
||||||
|
d.Wave_Cmd AS waveCmd,
|
||||||
d.report_name AS reportName,
|
d.report_name AS reportName,
|
||||||
CASE WHEN d.icd IS NOT NULL AND d.icd != '' AND p.ID IS NOT NULL THEN 1 ELSE 0 END AS canCheckIcd,
|
1 AS canCheckIcd,
|
||||||
0 AS canCheckPqdif
|
0 AS canCheckPqdif
|
||||||
FROM cs_dev_type d
|
FROM cs_dev_type d
|
||||||
LEFT JOIN cs_icd_path p ON d.icd = p.ID
|
LEFT JOIN cs_icd_path p ON d.icd = p.ID
|
||||||
WHERE d.State = 1
|
WHERE d.State = 1
|
||||||
|
<if test="keyword != null and keyword != ''">
|
||||||
|
AND (d.name LIKE CONCAT('%', #{keyword}, '%')
|
||||||
|
OR p.Name LIKE CONCAT('%', #{keyword}, '%'))
|
||||||
|
</if>
|
||||||
ORDER BY d.Update_Time DESC, d.Create_Time DESC
|
ORDER BY d.Update_Time DESC, d.Create_Time DESC
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
package com.njcn.gather.device.types.pojo.param;
|
||||||
|
|
||||||
|
import io.swagger.annotations.ApiModel;
|
||||||
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
import javax.validation.constraints.NotBlank;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设备类型保存参数。
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@ApiModel("设备类型保存参数")
|
||||||
|
public class DeviceTypeParam {
|
||||||
|
|
||||||
|
@ApiModelProperty("设备类型名称")
|
||||||
|
@NotBlank(message = "设备类型名称不能为空")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@ApiModelProperty("关联ICD ID")
|
||||||
|
private String icd;
|
||||||
|
|
||||||
|
@ApiModelProperty("功率字典code,不带单位,前端从字典下拉选择后提交code")
|
||||||
|
private String power;
|
||||||
|
|
||||||
|
@ApiModelProperty("设备电压字典code,不带单位,前端从字典下拉选择后提交code")
|
||||||
|
private Float devVolt;
|
||||||
|
|
||||||
|
@ApiModelProperty("设备电流字典code,不带单位,前端从字典下拉选择后提交code")
|
||||||
|
private Float devCurr;
|
||||||
|
|
||||||
|
@ApiModelProperty("通道数字典code,不带单位,前端从字典下拉选择后提交code")
|
||||||
|
private Integer devChns;
|
||||||
|
|
||||||
|
@ApiModelProperty("录波命令")
|
||||||
|
private String waveCmd;
|
||||||
|
|
||||||
|
@ApiModelProperty("报告模板名称")
|
||||||
|
private String reportName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设备类型编辑参数。
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@ApiModel("设备类型编辑参数")
|
||||||
|
public static class UpdateParam extends DeviceTypeParam {
|
||||||
|
|
||||||
|
@ApiModelProperty("设备类型ID")
|
||||||
|
@NotBlank(message = "设备类型ID不能为空")
|
||||||
|
private String id;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.njcn.gather.icd.mapping.pojo.po;
|
package com.njcn.gather.device.types.pojo.po;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.annotation.TableField;
|
import com.baomidou.mybatisplus.annotation.TableField;
|
||||||
import com.baomidou.mybatisplus.annotation.TableId;
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.njcn.gather.icd.mapping.pojo.vo;
|
package com.njcn.gather.device.types.pojo.vo;
|
||||||
|
|
||||||
import io.swagger.annotations.ApiModel;
|
import io.swagger.annotations.ApiModel;
|
||||||
import io.swagger.annotations.ApiModelProperty;
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
@@ -9,7 +9,7 @@ import lombok.Data;
|
|||||||
*/
|
*/
|
||||||
@Data
|
@Data
|
||||||
@ApiModel("设备类型校验列表项")
|
@ApiModel("设备类型校验列表项")
|
||||||
public class MmsDeviceTypeVO {
|
public class DeviceTypeVO {
|
||||||
|
|
||||||
@ApiModelProperty("设备类型ID")
|
@ApiModelProperty("设备类型ID")
|
||||||
private String id;
|
private String id;
|
||||||
@@ -26,12 +26,27 @@ public class MmsDeviceTypeVO {
|
|||||||
@ApiModelProperty("ICD路径")
|
@ApiModelProperty("ICD路径")
|
||||||
private String icdPath;
|
private String icdPath;
|
||||||
|
|
||||||
@ApiModelProperty("ICD校验结论:0-否,1-是")
|
@ApiModelProperty("ICD校验结论,0-否,1-是")
|
||||||
private Integer icdResult;
|
private Integer icdResult;
|
||||||
|
|
||||||
@ApiModelProperty("ICD校验结论描述")
|
@ApiModelProperty("ICD校验结论描述")
|
||||||
private String icdMsg;
|
private String icdMsg;
|
||||||
|
|
||||||
|
@ApiModelProperty("功率字典code,不带单位,单位由前端展示")
|
||||||
|
private String power;
|
||||||
|
|
||||||
|
@ApiModelProperty("设备电压字典code,不带单位,单位由前端展示")
|
||||||
|
private Float devVolt;
|
||||||
|
|
||||||
|
@ApiModelProperty("设备电流字典code,不带单位,单位由前端展示")
|
||||||
|
private Float devCurr;
|
||||||
|
|
||||||
|
@ApiModelProperty("通道数字典code,不带单位,单位由前端展示")
|
||||||
|
private Integer devChns;
|
||||||
|
|
||||||
|
@ApiModelProperty("录波命令")
|
||||||
|
private String waveCmd;
|
||||||
|
|
||||||
@ApiModelProperty("报告模板名称")
|
@ApiModelProperty("报告模板名称")
|
||||||
private String reportName;
|
private String reportName;
|
||||||
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.njcn.gather.icd.mapping.pojo.vo;
|
package com.njcn.gather.device.types.pojo.vo;
|
||||||
|
|
||||||
import io.swagger.annotations.ApiModel;
|
import io.swagger.annotations.ApiModel;
|
||||||
import io.swagger.annotations.ApiModelProperty;
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package com.njcn.gather.device.types.service;
|
||||||
|
|
||||||
|
import com.njcn.gather.device.types.pojo.param.DeviceTypeParam;
|
||||||
|
import com.njcn.gather.device.types.pojo.vo.DeviceTypeVO;
|
||||||
|
import com.njcn.gather.device.types.pojo.vo.PqdifCheckPlaceholderVO;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设备类型服务。
|
||||||
|
*/
|
||||||
|
public interface DeviceTypeService {
|
||||||
|
|
||||||
|
List<DeviceTypeVO> listDeviceTypes(String keyword);
|
||||||
|
|
||||||
|
boolean addDeviceType(DeviceTypeParam param);
|
||||||
|
|
||||||
|
boolean updateDeviceType(DeviceTypeParam.UpdateParam param);
|
||||||
|
|
||||||
|
boolean deleteDeviceType(List<String> ids);
|
||||||
|
|
||||||
|
PqdifCheckPlaceholderVO pqdifCheck(String devTypeId);
|
||||||
|
}
|
||||||
@@ -0,0 +1,155 @@
|
|||||||
|
package com.njcn.gather.device.types.service.impl;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||||
|
import com.njcn.gather.device.types.mapper.CsDevTypeMapper;
|
||||||
|
import com.njcn.gather.device.types.pojo.param.DeviceTypeParam;
|
||||||
|
import com.njcn.gather.device.types.pojo.po.CsDevTypePO;
|
||||||
|
import com.njcn.gather.device.types.pojo.vo.DeviceTypeVO;
|
||||||
|
import com.njcn.gather.device.types.pojo.vo.PqdifCheckPlaceholderVO;
|
||||||
|
import com.njcn.gather.device.types.service.DeviceTypeService;
|
||||||
|
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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设备类型服务实现。
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class DeviceTypeServiceImpl implements DeviceTypeService {
|
||||||
|
|
||||||
|
private static final int STATE_NORMAL = 1;
|
||||||
|
|
||||||
|
private static final int STATE_DELETED = 0;
|
||||||
|
|
||||||
|
private static final String PQDIF_NOT_SUPPORTED = "NOT_SUPPORTED";
|
||||||
|
|
||||||
|
private final CsDevTypeMapper csDevTypeMapper;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<DeviceTypeVO> listDeviceTypes(String keyword) {
|
||||||
|
return csDevTypeMapper.selectDeviceTypeCheckList(trimToNull(keyword));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional
|
||||||
|
public boolean addDeviceType(DeviceTypeParam param) {
|
||||||
|
DeviceTypeParam checkedParam = requireParam(param);
|
||||||
|
LocalDateTime now = LocalDateTime.now();
|
||||||
|
CsDevTypePO devType = buildDevType(checkedParam);
|
||||||
|
devType.setId(UUID.randomUUID().toString().replace("-", ""));
|
||||||
|
devType.setState(STATE_NORMAL);
|
||||||
|
devType.setCreateBy(currentUserId());
|
||||||
|
devType.setCreateTime(now);
|
||||||
|
devType.setUpdateBy(currentUserId());
|
||||||
|
devType.setUpdateTime(now);
|
||||||
|
return csDevTypeMapper.insert(devType) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional
|
||||||
|
public boolean updateDeviceType(DeviceTypeParam.UpdateParam param) {
|
||||||
|
DeviceTypeParam.UpdateParam checkedParam = requireUpdateParam(param);
|
||||||
|
requireDevType(checkedParam.getId());
|
||||||
|
CsDevTypePO devType = buildDevType(checkedParam);
|
||||||
|
devType.setId(checkedParam.getId());
|
||||||
|
devType.setUpdateBy(currentUserId());
|
||||||
|
devType.setUpdateTime(LocalDateTime.now());
|
||||||
|
return csDevTypeMapper.updateById(devType) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional
|
||||||
|
public boolean deleteDeviceType(List<String> ids) {
|
||||||
|
if (ids == null || ids.isEmpty()) {
|
||||||
|
throw new IllegalArgumentException("设备类型ID不能为空");
|
||||||
|
}
|
||||||
|
CsDevTypePO devType = new CsDevTypePO();
|
||||||
|
devType.setState(STATE_DELETED);
|
||||||
|
devType.setUpdateBy(currentUserId());
|
||||||
|
devType.setUpdateTime(LocalDateTime.now());
|
||||||
|
return csDevTypeMapper.update(devType, new LambdaUpdateWrapper<CsDevTypePO>()
|
||||||
|
.in(CsDevTypePO::getId, ids)
|
||||||
|
.eq(CsDevTypePO::getState, STATE_NORMAL)) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PqdifCheckPlaceholderVO pqdifCheck(String devTypeId) {
|
||||||
|
requireDevType(devTypeId);
|
||||||
|
PqdifCheckPlaceholderVO result = new PqdifCheckPlaceholderVO();
|
||||||
|
result.setStatus(PQDIF_NOT_SUPPORTED);
|
||||||
|
result.setMessage("PQDIF校验功能待实现");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private CsDevTypePO buildDevType(DeviceTypeParam param) {
|
||||||
|
CsDevTypePO devType = new CsDevTypePO();
|
||||||
|
devType.setName(requireText(param.getName(), "设备类型名称不能为空"));
|
||||||
|
devType.setIcd(trimToNull(param.getIcd()));
|
||||||
|
devType.setPower(trimToNull(param.getPower()));
|
||||||
|
devType.setDevVolt(param.getDevVolt());
|
||||||
|
devType.setDevCurr(param.getDevCurr());
|
||||||
|
devType.setDevChns(param.getDevChns());
|
||||||
|
devType.setWaveCmd(trimToNull(param.getWaveCmd()));
|
||||||
|
devType.setReportName(trimToNull(param.getReportName()));
|
||||||
|
return devType;
|
||||||
|
}
|
||||||
|
|
||||||
|
private DeviceTypeParam requireParam(DeviceTypeParam param) {
|
||||||
|
if (param == null) {
|
||||||
|
throw new IllegalArgumentException("设备类型参数不能为空");
|
||||||
|
}
|
||||||
|
return param;
|
||||||
|
}
|
||||||
|
|
||||||
|
private DeviceTypeParam.UpdateParam requireUpdateParam(DeviceTypeParam.UpdateParam param) {
|
||||||
|
if (param == null) {
|
||||||
|
throw new IllegalArgumentException("设备类型参数不能为空");
|
||||||
|
}
|
||||||
|
requireText(param.getId(), "设备类型ID不能为空");
|
||||||
|
return param;
|
||||||
|
}
|
||||||
|
|
||||||
|
private CsDevTypePO requireDevType(String devTypeId) {
|
||||||
|
String id = requireText(devTypeId, "设备类型ID不能为空");
|
||||||
|
CsDevTypePO devType = csDevTypeMapper.selectById(id);
|
||||||
|
if (devType == null || !Integer.valueOf(STATE_NORMAL).equals(devType.getState())) {
|
||||||
|
throw new IllegalArgumentException("设备类型不存在或已删除");
|
||||||
|
}
|
||||||
|
return devType;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 "未知用户";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
package com.njcn.gather.device.types.service.impl;
|
||||||
|
|
||||||
|
import com.njcn.gather.device.types.mapper.CsDevTypeMapper;
|
||||||
|
import com.njcn.gather.device.types.pojo.param.DeviceTypeParam;
|
||||||
|
import com.njcn.gather.device.types.pojo.po.CsDevTypePO;
|
||||||
|
import org.junit.jupiter.api.Assertions;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.mockito.ArgumentCaptor;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
class DeviceTypeServiceImplTest {
|
||||||
|
|
||||||
|
private final CsDevTypeMapper csDevTypeMapper = mock(CsDevTypeMapper.class);
|
||||||
|
private final DeviceTypeServiceImpl service = new DeviceTypeServiceImpl(csDevTypeMapper);
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void listDeviceTypesShouldTrimKeywordBeforeQuery() {
|
||||||
|
service.listDeviceTypes(" PQ装置 ");
|
||||||
|
|
||||||
|
verify(csDevTypeMapper).selectDeviceTypeCheckList(eq("PQ装置"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void addDeviceTypeShouldInsertEnabledRecord() {
|
||||||
|
DeviceTypeParam param = buildParam("设备类型A");
|
||||||
|
when(csDevTypeMapper.insert(any(CsDevTypePO.class))).thenReturn(1);
|
||||||
|
|
||||||
|
boolean result = service.addDeviceType(param);
|
||||||
|
|
||||||
|
ArgumentCaptor<CsDevTypePO> captor = ArgumentCaptor.forClass(CsDevTypePO.class);
|
||||||
|
verify(csDevTypeMapper).insert(captor.capture());
|
||||||
|
Assertions.assertTrue(result);
|
||||||
|
Assertions.assertEquals("10", captor.getValue().getPower());
|
||||||
|
Assertions.assertEquals("设备类型A", captor.getValue().getName());
|
||||||
|
Assertions.assertEquals(1, captor.getValue().getState());
|
||||||
|
Assertions.assertNotNull(captor.getValue().getId());
|
||||||
|
Assertions.assertNotNull(captor.getValue().getCreateTime());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void updateDeviceTypeShouldRejectDeletedRecord() {
|
||||||
|
DeviceTypeParam.UpdateParam param = new DeviceTypeParam.UpdateParam();
|
||||||
|
param.setId("dev-type-001");
|
||||||
|
param.setName("设备类型A");
|
||||||
|
|
||||||
|
CsDevTypePO deleted = new CsDevTypePO();
|
||||||
|
deleted.setId("dev-type-001");
|
||||||
|
deleted.setState(0);
|
||||||
|
when(csDevTypeMapper.selectById(eq("dev-type-001"))).thenReturn(deleted);
|
||||||
|
|
||||||
|
IllegalArgumentException exception = Assertions.assertThrows(IllegalArgumentException.class,
|
||||||
|
() -> service.updateDeviceType(param));
|
||||||
|
|
||||||
|
Assertions.assertEquals("设备类型不存在或已删除", exception.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void deleteDeviceTypeShouldMarkRecordDeleted() {
|
||||||
|
when(csDevTypeMapper.update(any(CsDevTypePO.class), any())).thenReturn(1);
|
||||||
|
|
||||||
|
boolean result = service.deleteDeviceType(Collections.singletonList("dev-type-001"));
|
||||||
|
|
||||||
|
ArgumentCaptor<CsDevTypePO> captor = ArgumentCaptor.forClass(CsDevTypePO.class);
|
||||||
|
verify(csDevTypeMapper).update(captor.capture(), any());
|
||||||
|
Assertions.assertTrue(result);
|
||||||
|
Assertions.assertEquals(0, captor.getValue().getState());
|
||||||
|
Assertions.assertNotNull(captor.getValue().getUpdateTime());
|
||||||
|
}
|
||||||
|
|
||||||
|
private DeviceTypeParam buildParam(String name) {
|
||||||
|
DeviceTypeParam param = new DeviceTypeParam();
|
||||||
|
param.setName(name);
|
||||||
|
param.setIcd("icd-001");
|
||||||
|
param.setPower("10");
|
||||||
|
param.setDevVolt(10.0F);
|
||||||
|
param.setDevCurr(5.0F);
|
||||||
|
param.setDevChns(4);
|
||||||
|
param.setWaveCmd("cmd");
|
||||||
|
param.setReportName("report");
|
||||||
|
return param;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,546 @@
|
|||||||
|
package com.njcn.gather.icd.mapping.component;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||||
|
import com.njcn.gather.icd.mapping.pojo.bo.mapping.DataSetGroupItem;
|
||||||
|
import com.njcn.gather.icd.mapping.pojo.bo.mapping.DoiItem;
|
||||||
|
import com.njcn.gather.icd.mapping.pojo.bo.mapping.InstItem;
|
||||||
|
import com.njcn.gather.icd.mapping.pojo.bo.mapping.MappingDocument;
|
||||||
|
import com.njcn.gather.icd.mapping.pojo.bo.mapping.ReportMapItem;
|
||||||
|
import com.njcn.gather.icd.mapping.pojo.bo.mapping.SdiItem;
|
||||||
|
import com.njcn.gather.icd.mapping.pojo.param.IcdConsistencyCheckRequest;
|
||||||
|
import com.njcn.gather.icd.mapping.pojo.vo.IcdConsistencyCheckResponse;
|
||||||
|
import com.njcn.gather.icd.mapping.pojo.vo.IcdConsistencyIssue;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ICD 映射 JSON 一致性校验服务。
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class IcdConsistencyCheckService {
|
||||||
|
|
||||||
|
private static final String PASS_MESSAGE = "符合";
|
||||||
|
private static final String NOT_PASS_MESSAGE = "不符合";
|
||||||
|
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_LN_CLASSES = Arrays.asList("MMXU", "MSQI", "MHAI", "MFLK");
|
||||||
|
|
||||||
|
private final FileStorageService fileStorageService;
|
||||||
|
private final ObjectMapper objectMapper = buildMapper();
|
||||||
|
|
||||||
|
public IcdConsistencyCheckResponse check(IcdConsistencyCheckRequest request) {
|
||||||
|
if (request == null) {
|
||||||
|
throw new IllegalArgumentException("ICD 一致性校验请求不能为空");
|
||||||
|
}
|
||||||
|
MappingDocument checked = parseMapping(request.getCheckedJson(), "待校验 JSON");
|
||||||
|
MappingDocument standard = parseMapping(request.getStandardJson(), "标准 JSON");
|
||||||
|
|
||||||
|
List<IcdConsistencyIssue> issues = new ArrayList<IcdConsistencyIssue>();
|
||||||
|
validateSelfFormat(checked, "待校验映射", issues);
|
||||||
|
boolean corrected = applySelfMappingRules(checked, issues);
|
||||||
|
validateConsistency(checked, standard, issues);
|
||||||
|
corrected = applyMappingCorrections(checked, standard, issues) || corrected;
|
||||||
|
|
||||||
|
IcdConsistencyCheckResponse response = new IcdConsistencyCheckResponse();
|
||||||
|
response.setResult(issues.isEmpty() ? 1 : 0);
|
||||||
|
response.setMessage(issues.isEmpty() ? PASS_MESSAGE : NOT_PASS_MESSAGE);
|
||||||
|
response.getIssues().addAll(issues);
|
||||||
|
if (!issues.isEmpty()) {
|
||||||
|
response.setIssuesJson(toJson(issues));
|
||||||
|
if (request.isSaveToDisk()) {
|
||||||
|
fileStorageService.save(ISSUE_FILE_NAME, response.getIssuesJson(), request.getOutputDir());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (corrected) {
|
||||||
|
response.setCorrectedJson(toJson(checked));
|
||||||
|
}
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
private MappingDocument parseMapping(String json, String label) {
|
||||||
|
if (isBlank(json)) {
|
||||||
|
throw new IllegalArgumentException(label + "不能为空");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return objectMapper.readValue(json, MappingDocument.class);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
throw new IllegalArgumentException(label + "解析失败:" + ex.getMessage(), ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validateSelfFormat(MappingDocument document, String label, List<IcdConsistencyIssue> issues) {
|
||||||
|
if (document == null) {
|
||||||
|
addIssue(issues, "自身格式校验", label, label + "不能为空", null, null, false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
requireText(document.getIed(), "IED", issues);
|
||||||
|
requireText(document.getLd(), "LD", issues);
|
||||||
|
requireText(document.getDataType(), "DataType", issues);
|
||||||
|
requireText(document.getUnit(), "unit", issues);
|
||||||
|
|
||||||
|
validateRequiredReportDescs(document, issues);
|
||||||
|
validateRequiredLnClasses(document, issues);
|
||||||
|
validateDoiDuplicateNames(document, issues);
|
||||||
|
validateNestedLists(document, issues);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void requireText(String value, String path, List<IcdConsistencyIssue> issues) {
|
||||||
|
if (isBlank(value)) {
|
||||||
|
addIssue(issues, "自身格式校验", path, path + "不能为空", null, value, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validateRequiredReportDescs(MappingDocument document, List<IcdConsistencyIssue> issues) {
|
||||||
|
Set<String> actualDescs = new HashSet<String>();
|
||||||
|
if (document.getReportMap() != null) {
|
||||||
|
for (ReportMapItem item : document.getReportMap()) {
|
||||||
|
actualDescs.add(trimToEmpty(item.getDesc()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (String desc : REQUIRED_REPORT_DESCS) {
|
||||||
|
if (!actualDescs.contains(desc)) {
|
||||||
|
addIssue(issues, "自身格式校验", "ReportMap.desc", "ReportMap 最少需要包含一条" + desc, desc, null, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validateRequiredLnClasses(MappingDocument document, List<IcdConsistencyIssue> issues) {
|
||||||
|
Set<String> actualLnClasses = new HashSet<String>();
|
||||||
|
if (document.getDataSetList() != null) {
|
||||||
|
for (DataSetGroupItem item : document.getDataSetList()) {
|
||||||
|
actualLnClasses.add(trimToEmpty(item.getLnClass()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (String lnClass : REQUIRED_LN_CLASSES) {
|
||||||
|
if (!actualLnClasses.contains(lnClass)) {
|
||||||
|
addIssue(issues, "自身格式校验", "DataSetList.lnClass", "DataSetList 最少需要包含一组 lnClass=" + lnClass, lnClass, null, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validateDoiDuplicateNames(MappingDocument document, List<IcdConsistencyIssue> issues) {
|
||||||
|
if (document.getDataSetList() == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (DataSetGroupItem group : document.getDataSetList()) {
|
||||||
|
if (group.getInstList() == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (InstItem inst : group.getInstList()) {
|
||||||
|
Set<String> doiKeys = new HashSet<String>();
|
||||||
|
if (inst.getDoiList() == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (DoiItem doi : inst.getDoiList()) {
|
||||||
|
String key = buildKey(doi.getName(), doi.getDesc());
|
||||||
|
if (!doiKeys.add(key)) {
|
||||||
|
String path = buildInstPath(group, inst) + ".doiList";
|
||||||
|
addIssue(issues, "自身格式校验", path, "同一个 doiList 下不能存在 name+desc 都一样的指标", key, key, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validateNestedLists(MappingDocument document, List<IcdConsistencyIssue> issues) {
|
||||||
|
if (document.getDataSetList() == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (DataSetGroupItem group : document.getDataSetList()) {
|
||||||
|
if (isEmpty(group.getInstList())) {
|
||||||
|
addIssue(issues, "自身格式校验", buildGroupPath(group), "instList 不能为空:" + trimToEmpty(group.getDesc()), null, null, false);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (InstItem inst : group.getInstList()) {
|
||||||
|
String instPath = buildInstPath(group, inst);
|
||||||
|
if (isEmpty(inst.getDoiList())) {
|
||||||
|
addIssue(issues, "自身格式校验", instPath, "doiList 不能为空:" + joinDesc(group.getDesc(), inst.getDesc()), null, null, false);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (DoiItem doi : inst.getDoiList()) {
|
||||||
|
if (isEmpty(doi.getSdiList())) {
|
||||||
|
addIssue(issues, "自身格式校验", instPath + ".doiList[" + buildKey(doi.getName(), doi.getDesc()) + "]",
|
||||||
|
"sdiList 不能为空:" + joinDesc(group.getDesc(), inst.getDesc(), doi.getDesc()), null, null, false);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (SdiItem sdi : doi.getSdiList()) {
|
||||||
|
if (isEmpty(sdi.getTypeList())) {
|
||||||
|
addIssue(issues, "自身格式校验", instPath + ".doiList[" + buildKey(doi.getName(), doi.getDesc()) + "].sdiList[" + buildKey(sdi.getName(), sdi.getDesc()) + "]",
|
||||||
|
"typeList 不能为空:" + joinDesc(group.getDesc(), inst.getDesc(), doi.getDesc(), sdi.getDesc()), null, null, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validateConsistency(MappingDocument checked, MappingDocument standard, List<IcdConsistencyIssue> issues) {
|
||||||
|
compareValue(standard.getIed(), checked.getIed(), "基础字段", "IED", issues, false);
|
||||||
|
compareValue(standard.getLd(), checked.getLd(), "基础字段", "LD", issues, false);
|
||||||
|
compareValue(standard.getDataType(), checked.getDataType(), "基础字段", "DataType", issues, false);
|
||||||
|
compareValue(standard.getUnit(), checked.getUnit(), "基础字段", "unit", issues, true);
|
||||||
|
validateReportMapConsistency(checked, standard, issues);
|
||||||
|
validateDataSetConsistency(checked, standard, issues);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean applySelfMappingRules(MappingDocument checked, List<IcdConsistencyIssue> issues) {
|
||||||
|
if (checked.getReportMap() == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
boolean hasRtFre = false;
|
||||||
|
boolean corrected = false;
|
||||||
|
for (ReportMapItem item : checked.getReportMap()) {
|
||||||
|
if ("实时数据".equals(trimToEmpty(item.getDesc())) && trimToEmpty(item.getRptId()).contains("RtFre")) {
|
||||||
|
hasRtFre = true;
|
||||||
|
if (!"1".equals(trimToEmpty(item.getFlickerFlag()))) {
|
||||||
|
addIssue(issues, "映射规则", "ReportMap[" + buildReportKey(item) + "].FlickerFlag",
|
||||||
|
"实时数据报告 rptID 包含 RtFre,FlickerFlag 已按规则调整为 1", "1", item.getFlickerFlag(), true);
|
||||||
|
item.setFlickerFlag("1");
|
||||||
|
corrected = true;
|
||||||
|
}
|
||||||
|
if (item.getReportCount() != 0) {
|
||||||
|
addIssue(issues, "映射规则", "ReportMap[" + buildReportKey(item) + "].reportCount",
|
||||||
|
"实时数据报告 rptID 包含 RtFre,reportCount 已按规则调整为 0", "0", String.valueOf(item.getReportCount()), true);
|
||||||
|
item.setReportCount(0);
|
||||||
|
corrected = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!hasRtFre) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (ReportMapItem item : checked.getReportMap()) {
|
||||||
|
if ("统计数据".equals(trimToEmpty(item.getDesc()))) {
|
||||||
|
int adjustedCount = Math.max(0, item.getReportCount() - 1);
|
||||||
|
if (item.getReportCount() != adjustedCount) {
|
||||||
|
addIssue(issues, "映射规则", "ReportMap[" + buildReportKey(item) + "].reportCount",
|
||||||
|
"存在 RtFre 实时数据报告,统计数据 reportCount 已按规则减 1",
|
||||||
|
String.valueOf(adjustedCount), String.valueOf(item.getReportCount()), true);
|
||||||
|
item.setReportCount(adjustedCount);
|
||||||
|
corrected = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return corrected;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validateReportMapConsistency(MappingDocument checked, MappingDocument standard, List<IcdConsistencyIssue> issues) {
|
||||||
|
Map<String, ReportMapItem> checkedMap = new HashMap<String, ReportMapItem>();
|
||||||
|
if (checked.getReportMap() != null) {
|
||||||
|
for (ReportMapItem item : checked.getReportMap()) {
|
||||||
|
checkedMap.put(buildReportKey(item), item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (standard.getReportMap() == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (ReportMapItem standardItem : standard.getReportMap()) {
|
||||||
|
String key = buildReportKey(standardItem);
|
||||||
|
ReportMapItem checkedItem = checkedMap.get(key);
|
||||||
|
if (checkedItem == null) {
|
||||||
|
addIssue(issues, "一致性校验", "ReportMap[" + key + "]", "标准映射中的 ReportMap 对象在待校验映射中不存在", key, null, false);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
compareValue(String.valueOf(standardItem.getReportCount()), String.valueOf(checkedItem.getReportCount()), "ReportMap", "ReportMap[" + key + "].reportCount", issues, true);
|
||||||
|
compareValue(standardItem.getBuffered(), checkedItem.getBuffered(), "ReportMap", "ReportMap[" + key + "].buffered", issues, true);
|
||||||
|
compareValue(standardItem.getInst(), checkedItem.getInst(), "ReportMap", "ReportMap[" + key + "].inst", issues, true);
|
||||||
|
compareValue(standardItem.getFlickerFlag(), checkedItem.getFlickerFlag(), "ReportMap", "ReportMap[" + key + "].FlickerFlag", issues, true);
|
||||||
|
compareValue(standardItem.getSelect(), checkedItem.getSelect(), "ReportMap", "ReportMap[" + key + "].Select", issues, true);
|
||||||
|
compareValue(standardItem.getTrgOps(), checkedItem.getTrgOps(), "ReportMap", "ReportMap[" + key + "].TrgOps", issues, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validateDataSetConsistency(MappingDocument checked, MappingDocument standard, List<IcdConsistencyIssue> issues) {
|
||||||
|
Map<String, DataSetGroupItem> checkedGroups = indexGroups(checked);
|
||||||
|
if (standard.getDataSetList() == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (DataSetGroupItem standardGroup : standard.getDataSetList()) {
|
||||||
|
String groupKey = buildKey(standardGroup.getLnClass(), standardGroup.getDesc());
|
||||||
|
DataSetGroupItem checkedGroup = checkedGroups.get(groupKey);
|
||||||
|
if (checkedGroup == null) {
|
||||||
|
addIssue(issues, "一致性校验", "DataSetList[" + groupKey + "]", "标准映射中的 lnClass+desc 在待校验映射中不存在", groupKey, null, false);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
validateInstConsistency(checkedGroup, standardGroup, groupKey, issues);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validateInstConsistency(DataSetGroupItem checkedGroup, DataSetGroupItem standardGroup, String groupKey, List<IcdConsistencyIssue> issues) {
|
||||||
|
Map<String, InstItem> checkedInstMap = indexInst(checkedGroup);
|
||||||
|
if (standardGroup.getInstList() == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (InstItem standardInst : standardGroup.getInstList()) {
|
||||||
|
String instKey = buildKey(standardInst.getInst(), standardInst.getDesc());
|
||||||
|
InstItem checkedInst = checkedInstMap.get(instKey);
|
||||||
|
if (checkedInst == null) {
|
||||||
|
addIssue(issues, "一致性校验", "DataSetList[" + groupKey + "].instList[" + instKey + "]",
|
||||||
|
"标准映射中的 inst+desc 在待校验映射中不存在", instKey, null, false);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
validateDoiConsistency(checkedInst, standardInst, groupKey, instKey, issues);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validateDoiConsistency(InstItem checkedInst, InstItem standardInst, String groupKey, String instKey, List<IcdConsistencyIssue> issues) {
|
||||||
|
Map<String, DoiItem> checkedDoiMap = indexDoi(checkedInst);
|
||||||
|
if (standardInst.getDoiList() == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (DoiItem standardDoi : standardInst.getDoiList()) {
|
||||||
|
String doiKey = buildKey(standardDoi.getName(), standardDoi.getDesc());
|
||||||
|
DoiItem checkedDoi = checkedDoiMap.get(doiKey);
|
||||||
|
String path = "DataSetList[" + groupKey + "].instList[" + instKey + "].doiList[" + doiKey + "]";
|
||||||
|
if (checkedDoi == null) {
|
||||||
|
addIssue(issues, "一致性校验", path, "标准映射中的 name+desc 在待校验映射中不存在", doiKey, null, false);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
compareDoiFields(checkedDoi, standardDoi, path, issues);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void compareDoiFields(DoiItem checked, DoiItem standard, String path, List<IcdConsistencyIssue> issues) {
|
||||||
|
boolean canCorrectRange = canCorrectDoiRange(checked, standard) && standard.getEnd() <= checked.getIcdcout();
|
||||||
|
compareValue(String.valueOf(standard.getStart()), String.valueOf(checked.getStart()), "doiList", path + ".start", issues, canCorrectRange);
|
||||||
|
compareValue(String.valueOf(standard.getEnd()), String.valueOf(checked.getEnd()), "doiList", path + ".end", issues, canCorrectRange);
|
||||||
|
compareValue(standard.getUnit(), checked.getUnit(), "doiList", path + ".unit", issues, false);
|
||||||
|
compareValue(String.valueOf(standard.getCoefficient()), String.valueOf(checked.getCoefficient()), "doiList", path + ".coefficient", issues, false);
|
||||||
|
compareValue(String.valueOf(standard.getBaseflag()), String.valueOf(checked.getBaseflag()), "doiList", path + ".baseflag", issues, false);
|
||||||
|
compareValue(String.valueOf(standard.getBasecount()), String.valueOf(checked.getBasecount()), "doiList", path + ".basecount", issues, false);
|
||||||
|
compareValue(String.valueOf(standard.getIcdcout()), String.valueOf(checked.getIcdcout()), "doiList", path + ".icdcout", issues, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean applyMappingCorrections(MappingDocument checked, MappingDocument standard, List<IcdConsistencyIssue> issues) {
|
||||||
|
boolean corrected = false;
|
||||||
|
if (!equalsValue(checked.getUnit(), standard.getUnit())) {
|
||||||
|
checked.setUnit(standard.getUnit());
|
||||||
|
corrected = true;
|
||||||
|
}
|
||||||
|
corrected = correctReportMap(checked, standard) || corrected;
|
||||||
|
corrected = correctDoiRanges(checked, standard, issues) || corrected;
|
||||||
|
return corrected;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean correctReportMap(MappingDocument checked, MappingDocument standard) {
|
||||||
|
boolean corrected = false;
|
||||||
|
Map<String, ReportMapItem> checkedMap = new HashMap<String, ReportMapItem>();
|
||||||
|
if (checked.getReportMap() != null) {
|
||||||
|
for (ReportMapItem item : checked.getReportMap()) {
|
||||||
|
checkedMap.put(buildReportKey(item), item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (standard.getReportMap() == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (ReportMapItem standardItem : standard.getReportMap()) {
|
||||||
|
ReportMapItem checkedItem = checkedMap.get(buildReportKey(standardItem));
|
||||||
|
if (checkedItem != null && !reportOtherFieldsEqual(checkedItem, standardItem)) {
|
||||||
|
checkedItem.setReportCount(standardItem.getReportCount());
|
||||||
|
checkedItem.setBuffered(standardItem.getBuffered());
|
||||||
|
checkedItem.setInst(standardItem.getInst());
|
||||||
|
checkedItem.setFlickerFlag(standardItem.getFlickerFlag());
|
||||||
|
checkedItem.setSelect(standardItem.getSelect());
|
||||||
|
checkedItem.setTrgOps(standardItem.getTrgOps());
|
||||||
|
corrected = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return corrected;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean correctDoiRanges(MappingDocument checked, MappingDocument standard, List<IcdConsistencyIssue> issues) {
|
||||||
|
boolean corrected = false;
|
||||||
|
Map<String, DataSetGroupItem> checkedGroups = indexGroups(checked);
|
||||||
|
if (standard.getDataSetList() == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (DataSetGroupItem standardGroup : standard.getDataSetList()) {
|
||||||
|
DataSetGroupItem checkedGroup = checkedGroups.get(buildKey(standardGroup.getLnClass(), standardGroup.getDesc()));
|
||||||
|
if (checkedGroup == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
corrected = correctGroupDoiRanges(checkedGroup, standardGroup, issues) || corrected;
|
||||||
|
}
|
||||||
|
return corrected;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean correctGroupDoiRanges(DataSetGroupItem checkedGroup, DataSetGroupItem standardGroup, List<IcdConsistencyIssue> issues) {
|
||||||
|
boolean corrected = false;
|
||||||
|
Map<String, InstItem> checkedInstMap = indexInst(checkedGroup);
|
||||||
|
if (standardGroup.getInstList() == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (InstItem standardInst : standardGroup.getInstList()) {
|
||||||
|
InstItem checkedInst = checkedInstMap.get(buildKey(standardInst.getInst(), standardInst.getDesc()));
|
||||||
|
if (checkedInst == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
corrected = correctInstDoiRanges(checkedGroup, checkedInst, standardInst, issues) || corrected;
|
||||||
|
}
|
||||||
|
return corrected;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean correctInstDoiRanges(DataSetGroupItem checkedGroup, InstItem checkedInst, InstItem standardInst, List<IcdConsistencyIssue> issues) {
|
||||||
|
boolean corrected = false;
|
||||||
|
Map<String, DoiItem> checkedDoiMap = indexDoi(checkedInst);
|
||||||
|
if (standardInst.getDoiList() == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (DoiItem standardDoi : standardInst.getDoiList()) {
|
||||||
|
DoiItem checkedDoi = checkedDoiMap.get(buildKey(standardDoi.getName(), standardDoi.getDesc()));
|
||||||
|
if (checkedDoi == null || !canCorrectDoiRange(checkedDoi, standardDoi)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
String path = buildInstPath(checkedGroup, checkedInst) + ".doiList[" + buildKey(checkedDoi.getName(), checkedDoi.getDesc()) + "]";
|
||||||
|
if (standardDoi.getEnd() > checkedDoi.getIcdcout()) {
|
||||||
|
addIssue(issues, "映射修改", path, "标准 end 大于待校验 icdcout,不能自动修正 start/end",
|
||||||
|
String.valueOf(standardDoi.getEnd()), String.valueOf(checkedDoi.getIcdcout()), false);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (checkedDoi.getStart() != standardDoi.getStart() || checkedDoi.getEnd() != standardDoi.getEnd()) {
|
||||||
|
checkedDoi.setStart(standardDoi.getStart());
|
||||||
|
checkedDoi.setEnd(standardDoi.getEnd());
|
||||||
|
corrected = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return corrected;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void compareValue(String standardValue, String checkedValue, String scope, String path,
|
||||||
|
List<IcdConsistencyIssue> issues, boolean corrected) {
|
||||||
|
if (!equalsValue(standardValue, checkedValue)) {
|
||||||
|
addIssue(issues, scope, path, path + " 与标准映射不一致", standardValue, checkedValue, corrected);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addIssue(List<IcdConsistencyIssue> issues, String scope, String path, String message,
|
||||||
|
String standardValue, String checkedValue, boolean corrected) {
|
||||||
|
IcdConsistencyIssue issue = new IcdConsistencyIssue();
|
||||||
|
issue.setScope(scope);
|
||||||
|
issue.setPath(path);
|
||||||
|
issue.setMessage(message);
|
||||||
|
issue.setStandardValue(standardValue);
|
||||||
|
issue.setCheckedValue(checkedValue);
|
||||||
|
issue.setCorrected(corrected);
|
||||||
|
issues.add(issue);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<String, DataSetGroupItem> indexGroups(MappingDocument document) {
|
||||||
|
Map<String, DataSetGroupItem> result = new HashMap<String, DataSetGroupItem>();
|
||||||
|
if (document.getDataSetList() == null) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
for (DataSetGroupItem item : document.getDataSetList()) {
|
||||||
|
result.put(buildKey(item.getLnClass(), item.getDesc()), item);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<String, InstItem> indexInst(DataSetGroupItem group) {
|
||||||
|
Map<String, InstItem> result = new HashMap<String, InstItem>();
|
||||||
|
if (group.getInstList() == null) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
for (InstItem item : group.getInstList()) {
|
||||||
|
result.put(buildKey(item.getInst(), item.getDesc()), item);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<String, DoiItem> indexDoi(InstItem inst) {
|
||||||
|
Map<String, DoiItem> result = new HashMap<String, DoiItem>();
|
||||||
|
if (inst.getDoiList() == null) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
for (DoiItem item : inst.getDoiList()) {
|
||||||
|
result.put(buildKey(item.getName(), item.getDesc()), item);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String buildReportKey(ReportMapItem item) {
|
||||||
|
return buildKey(item.getDesc(), item.getRptId(), item.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
private String buildGroupPath(DataSetGroupItem group) {
|
||||||
|
return "DataSetList[" + buildKey(group.getLnClass(), group.getDesc()) + "]";
|
||||||
|
}
|
||||||
|
|
||||||
|
private String buildInstPath(DataSetGroupItem group, InstItem inst) {
|
||||||
|
return buildGroupPath(group) + ".instList[" + buildKey(inst.getInst(), inst.getDesc()) + "]";
|
||||||
|
}
|
||||||
|
|
||||||
|
private String buildKey(String... values) {
|
||||||
|
List<String> parts = new ArrayList<String>();
|
||||||
|
for (String value : values) {
|
||||||
|
parts.add(trimToEmpty(value));
|
||||||
|
}
|
||||||
|
return String.join("+", parts);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean reportOtherFieldsEqual(ReportMapItem checked, ReportMapItem standard) {
|
||||||
|
return checked.getReportCount() == standard.getReportCount()
|
||||||
|
&& equalsValue(checked.getBuffered(), standard.getBuffered())
|
||||||
|
&& equalsValue(checked.getInst(), standard.getInst())
|
||||||
|
&& equalsValue(checked.getFlickerFlag(), standard.getFlickerFlag())
|
||||||
|
&& equalsValue(checked.getSelect(), standard.getSelect())
|
||||||
|
&& equalsValue(checked.getTrgOps(), standard.getTrgOps());
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean canCorrectDoiRange(DoiItem checked, DoiItem standard) {
|
||||||
|
return checked.getBaseflag() == standard.getBaseflag()
|
||||||
|
&& (checked.getBaseflag() == 1 || checked.getBaseflag() == 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean equalsValue(String left, String right) {
|
||||||
|
return trimToEmpty(left).equals(trimToEmpty(right));
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isBlank(String value) {
|
||||||
|
return value == null || value.trim().isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String trimToEmpty(String value) {
|
||||||
|
return value == null ? "" : value.trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isEmpty(List<?> list) {
|
||||||
|
return list == null || list.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String joinDesc(String... descs) {
|
||||||
|
List<String> values = new ArrayList<String>();
|
||||||
|
for (String desc : descs) {
|
||||||
|
if (!isBlank(desc)) {
|
||||||
|
values.add(desc.trim());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return String.join(" / ", values);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String toJson(Object value) {
|
||||||
|
try {
|
||||||
|
return objectMapper.writeValueAsString(value);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
throw new IllegalArgumentException("ICD 一致性校验结果序列化失败:" + ex.getMessage(), ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ObjectMapper buildMapper() {
|
||||||
|
ObjectMapper mapper = new ObjectMapper();
|
||||||
|
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
|
||||||
|
mapper.enable(SerializationFeature.INDENT_OUTPUT);
|
||||||
|
return mapper;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,151 @@
|
|||||||
|
package com.njcn.gather.icd.mapping.controller;
|
||||||
|
|
||||||
|
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.icd.mapping.pojo.param.CsIcdPathParam;
|
||||||
|
import com.njcn.gather.icd.mapping.pojo.param.IcdCheckResultSaveParam;
|
||||||
|
import com.njcn.gather.icd.mapping.pojo.vo.CsIcdPathVO;
|
||||||
|
import com.njcn.gather.icd.mapping.service.CsIcdPathService;
|
||||||
|
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.RequestPart;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ICD 存储记录维护入口。
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Api(tags = "ICD存储记录管理")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/mms-mapping/icd-paths")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class CsIcdPathController extends BaseController {
|
||||||
|
|
||||||
|
private final CsIcdPathService csIcdPathService;
|
||||||
|
|
||||||
|
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
|
||||||
|
@ApiOperation("查询ICD存储记录列表")
|
||||||
|
@PostMapping("/list")
|
||||||
|
public HttpResult<List<CsIcdPathVO>> list(@RequestBody(required = false) CsIcdPathParam.ListParam param) {
|
||||||
|
String methodDescribe = getMethodDescribe("list");
|
||||||
|
LogUtil.njcnDebug(log, "{},开始查询ICD存储记录列表", methodDescribe);
|
||||||
|
List<CsIcdPathVO> result = csIcdPathService.listIcdPaths(param);
|
||||||
|
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe);
|
||||||
|
}
|
||||||
|
|
||||||
|
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
|
||||||
|
@ApiOperation("新增ICD存储记录")
|
||||||
|
@PostMapping(value = "/add", consumes = {"application/json"})
|
||||||
|
public HttpResult<Boolean> add(@RequestBody @Validated CsIcdPathParam param) {
|
||||||
|
String methodDescribe = getMethodDescribe("add");
|
||||||
|
LogUtil.njcnDebug(log, "{},开始新增ICD存储记录", methodDescribe);
|
||||||
|
boolean result = csIcdPathService.addIcdPath(param);
|
||||||
|
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe);
|
||||||
|
}
|
||||||
|
|
||||||
|
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
|
||||||
|
@ApiOperation("上传并新增ICD存储记录")
|
||||||
|
@PostMapping(value = "/add", consumes = {"multipart/form-data"})
|
||||||
|
public HttpResult<Boolean> addWithFile(@RequestPart("icdFile") MultipartFile icdFile,
|
||||||
|
@RequestPart("request") @Validated CsIcdPathParam param) {
|
||||||
|
String methodDescribe = getMethodDescribe("addWithFile");
|
||||||
|
LogUtil.njcnDebug(log, "{},开始上传并新增ICD存储记录,fileName={}", methodDescribe, resolveFileName(icdFile));
|
||||||
|
fillIcdFile(param, icdFile);
|
||||||
|
boolean result = csIcdPathService.addIcdPath(param);
|
||||||
|
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe);
|
||||||
|
}
|
||||||
|
|
||||||
|
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
|
||||||
|
@ApiOperation("编辑ICD存储记录")
|
||||||
|
@PostMapping(value = "/update", consumes = {"application/json"})
|
||||||
|
public HttpResult<Boolean> update(@RequestBody @Validated CsIcdPathParam.UpdateParam param) {
|
||||||
|
String methodDescribe = getMethodDescribe("update");
|
||||||
|
LogUtil.njcnDebug(log, "{},开始编辑ICD存储记录,icdId={}", methodDescribe, param.getId());
|
||||||
|
boolean result = csIcdPathService.updateIcdPath(param);
|
||||||
|
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe);
|
||||||
|
}
|
||||||
|
|
||||||
|
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
|
||||||
|
@ApiOperation("上传并编辑ICD存储记录")
|
||||||
|
@PostMapping(value = "/update", consumes = {"multipart/form-data"})
|
||||||
|
public HttpResult<Boolean> updateWithFile(@RequestPart("icdFile") MultipartFile icdFile,
|
||||||
|
@RequestPart("request") @Validated CsIcdPathParam.UpdateParam param) {
|
||||||
|
String methodDescribe = getMethodDescribe("updateWithFile");
|
||||||
|
LogUtil.njcnDebug(log, "{},开始上传并编辑ICD存储记录,icdId={},fileName={}", methodDescribe, param.getId(), resolveFileName(icdFile));
|
||||||
|
fillIcdFile(param, icdFile);
|
||||||
|
boolean result = csIcdPathService.updateIcdPath(param);
|
||||||
|
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe);
|
||||||
|
}
|
||||||
|
|
||||||
|
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
|
||||||
|
@ApiOperation("激活ICD存储记录")
|
||||||
|
@ApiImplicitParam(name = "id", value = "ICD记录ID", required = true)
|
||||||
|
@PostMapping("/{id}/activate")
|
||||||
|
public HttpResult<Boolean> activate(@PathVariable("id") String id) {
|
||||||
|
String methodDescribe = getMethodDescribe("activate");
|
||||||
|
LogUtil.njcnDebug(log, "{},开始激活ICD存储记录,icdId={}", methodDescribe, id);
|
||||||
|
boolean result = csIcdPathService.activateIcdPath(id);
|
||||||
|
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe);
|
||||||
|
}
|
||||||
|
|
||||||
|
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
|
||||||
|
@ApiOperation("删除ICD存储记录")
|
||||||
|
@PostMapping("/delete")
|
||||||
|
public HttpResult<Boolean> delete(@RequestBody List<String> ids) {
|
||||||
|
String methodDescribe = getMethodDescribe("delete");
|
||||||
|
LogUtil.njcnDebug(log, "{},开始删除ICD存储记录,ids={}", methodDescribe, ids);
|
||||||
|
boolean result = csIcdPathService.deleteIcdPath(ids);
|
||||||
|
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-result")
|
||||||
|
public HttpResult<Boolean> saveIcdCheckResult(@PathVariable("id") String id,
|
||||||
|
@RequestBody IcdCheckResultSaveParam param) {
|
||||||
|
String methodDescribe = getMethodDescribe("saveIcdCheckResult");
|
||||||
|
LogUtil.njcnDebug(log, "{},开始保存ICD校验结果,icdId={}", methodDescribe, id);
|
||||||
|
boolean result = csIcdPathService.saveIcdCheckResult(id, param);
|
||||||
|
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fillIcdFile(CsIcdPathParam param, MultipartFile icdFile) {
|
||||||
|
if (icdFile == null || icdFile.isEmpty()) {
|
||||||
|
throw new IllegalArgumentException("ICD文件不能为空");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
param.setIcdContent(icdFile.getBytes());
|
||||||
|
if (param.getPath() == null || param.getPath().trim().isEmpty()) {
|
||||||
|
param.setPath(resolveFileName(icdFile));
|
||||||
|
}
|
||||||
|
} catch (IOException ex) {
|
||||||
|
throw new IllegalArgumentException("读取ICD文件失败:" + ex.getMessage(), ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String resolveFileName(MultipartFile icdFile) {
|
||||||
|
if (icdFile == null || icdFile.getOriginalFilename() == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String fileName = icdFile.getOriginalFilename().trim();
|
||||||
|
return fileName.isEmpty() ? null : fileName;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,6 +6,7 @@ import com.njcn.common.pojo.enums.response.CommonResponseEnum;
|
|||||||
import com.njcn.common.pojo.response.HttpResult;
|
import com.njcn.common.pojo.response.HttpResult;
|
||||||
import com.njcn.common.utils.LogUtil;
|
import com.njcn.common.utils.LogUtil;
|
||||||
import com.njcn.gather.icd.mapping.component.IcdToXmlResponseConverter;
|
import com.njcn.gather.icd.mapping.component.IcdToXmlResponseConverter;
|
||||||
|
import com.njcn.gather.icd.mapping.component.IcdConsistencyCheckService;
|
||||||
import com.njcn.gather.icd.mapping.component.IndexSelectionBuildService;
|
import com.njcn.gather.icd.mapping.component.IndexSelectionBuildService;
|
||||||
import com.njcn.gather.icd.mapping.component.MappingRequestConverter;
|
import com.njcn.gather.icd.mapping.component.MappingRequestConverter;
|
||||||
import com.njcn.gather.icd.mapping.component.MappingResponseConverter;
|
import com.njcn.gather.icd.mapping.component.MappingResponseConverter;
|
||||||
@@ -14,9 +15,11 @@ import com.njcn.gather.icd.mapping.pojo.bo.IcdToXmlGenerateResult;
|
|||||||
import com.njcn.gather.icd.mapping.pojo.dto.GenerateFromIcdCommand;
|
import com.njcn.gather.icd.mapping.pojo.dto.GenerateFromIcdCommand;
|
||||||
import com.njcn.gather.icd.mapping.pojo.param.BuildIndexSelectionRequest;
|
import com.njcn.gather.icd.mapping.pojo.param.BuildIndexSelectionRequest;
|
||||||
import com.njcn.gather.icd.mapping.pojo.param.GenerateMappingFromIcdRequest;
|
import com.njcn.gather.icd.mapping.pojo.param.GenerateMappingFromIcdRequest;
|
||||||
|
import com.njcn.gather.icd.mapping.pojo.param.IcdConsistencyCheckRequest;
|
||||||
import com.njcn.gather.icd.mapping.pojo.param.IndexCandidateRequest;
|
import com.njcn.gather.icd.mapping.pojo.param.IndexCandidateRequest;
|
||||||
import com.njcn.gather.icd.mapping.pojo.param.JsonToXmlRequest;
|
import com.njcn.gather.icd.mapping.pojo.param.JsonToXmlRequest;
|
||||||
import com.njcn.gather.icd.mapping.pojo.param.SubmitIndexSelectionRequest;
|
import com.njcn.gather.icd.mapping.pojo.param.SubmitIndexSelectionRequest;
|
||||||
|
import com.njcn.gather.icd.mapping.pojo.vo.IcdConsistencyCheckResponse;
|
||||||
import com.njcn.gather.icd.mapping.pojo.vo.IcdToXmlResponse;
|
import com.njcn.gather.icd.mapping.pojo.vo.IcdToXmlResponse;
|
||||||
import com.njcn.gather.icd.mapping.pojo.vo.IndexConfirmGroupResponse;
|
import com.njcn.gather.icd.mapping.pojo.vo.IndexConfirmGroupResponse;
|
||||||
import com.njcn.gather.icd.mapping.pojo.vo.IndexSelectionGroupResponse;
|
import com.njcn.gather.icd.mapping.pojo.vo.IndexSelectionGroupResponse;
|
||||||
@@ -70,6 +73,9 @@ public class MappingController extends BaseController {
|
|||||||
/** ICD 结构确认弹窗结果组装服务。 */
|
/** ICD 结构确认弹窗结果组装服务。 */
|
||||||
private final IndexSelectionBuildService indexSelectionBuildService;
|
private final IndexSelectionBuildService indexSelectionBuildService;
|
||||||
|
|
||||||
|
/** ICD 映射 JSON 一致性校验服务。 */
|
||||||
|
private final IcdConsistencyCheckService icdConsistencyCheckService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 上传 ICD 文件,返回候选结果和可编辑的 ICD 解析结果。
|
* 上传 ICD 文件,返回候选结果和可编辑的 ICD 解析结果。
|
||||||
*/
|
*/
|
||||||
@@ -181,4 +187,17 @@ public class MappingController extends BaseController {
|
|||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将待校验 MMS 映射 JSON 与标准 MMS 映射 JSON 做 ICD 一致性校验。
|
||||||
|
*/
|
||||||
|
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
|
||||||
|
@ApiOperation("ICD 一致性校验")
|
||||||
|
@ApiImplicitParam(name = "request", value = "ICD 一致性校验参数", required = true, dataType = "IcdConsistencyCheckRequest")
|
||||||
|
@PostMapping("/check-icd-json-consistency")
|
||||||
|
public IcdConsistencyCheckResponse checkIcdJsonConsistency(@Validated @RequestBody IcdConsistencyCheckRequest request) {
|
||||||
|
String methodDescribe = getMethodDescribe("checkIcdJsonConsistency");
|
||||||
|
LogUtil.njcnDebug(log, "{},开始执行 ICD 一致性校验", methodDescribe);
|
||||||
|
return icdConsistencyCheckService.check(request);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,72 +0,0 @@
|
|||||||
package com.njcn.gather.icd.mapping.controller;
|
|
||||||
|
|
||||||
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.icd.mapping.pojo.param.IcdCheckResultSaveParam;
|
|
||||||
import com.njcn.gather.icd.mapping.pojo.vo.MmsDeviceTypeVO;
|
|
||||||
import com.njcn.gather.icd.mapping.pojo.vo.PqdifCheckPlaceholderVO;
|
|
||||||
import com.njcn.gather.icd.mapping.service.MmsDeviceTypeService;
|
|
||||||
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.web.bind.annotation.GetMapping;
|
|
||||||
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.RestController;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 设备类型校验入口。
|
|
||||||
*/
|
|
||||||
@Slf4j
|
|
||||||
@Api(tags = "设备类型ICD校验")
|
|
||||||
@RestController
|
|
||||||
@RequestMapping("/api/mms-mapping/dev-types")
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
public class MmsDeviceTypeController extends BaseController {
|
|
||||||
|
|
||||||
private final MmsDeviceTypeService mmsDeviceTypeService;
|
|
||||||
|
|
||||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
|
|
||||||
@ApiOperation("查询设备类型校验列表")
|
|
||||||
@GetMapping
|
|
||||||
public HttpResult<List<MmsDeviceTypeVO>> listDeviceTypes() {
|
|
||||||
String methodDescribe = getMethodDescribe("listDeviceTypes");
|
|
||||||
LogUtil.njcnDebug(log, "{},开始查询设备类型校验列表", methodDescribe);
|
|
||||||
List<MmsDeviceTypeVO> result = mmsDeviceTypeService.listDeviceTypes();
|
|
||||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe);
|
|
||||||
}
|
|
||||||
|
|
||||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
|
|
||||||
@ApiOperation("保存设备类型ICD唯一性校验结果")
|
|
||||||
@ApiImplicitParam(name = "id", value = "设备类型ID", required = true)
|
|
||||||
@PostMapping("/{id}/icd-check-result")
|
|
||||||
public HttpResult<Boolean> saveIcdCheckResult(@PathVariable("id") String id,
|
|
||||||
@RequestBody IcdCheckResultSaveParam param) {
|
|
||||||
String methodDescribe = getMethodDescribe("saveIcdCheckResult");
|
|
||||||
LogUtil.njcnDebug(log, "{},开始保存设备类型ICD校验结果,devTypeId={}", methodDescribe, id);
|
|
||||||
boolean result = mmsDeviceTypeService.saveIcdCheckResult(id, param);
|
|
||||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe);
|
|
||||||
}
|
|
||||||
|
|
||||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
|
|
||||||
@ApiOperation("设备类型PQDIF校验")
|
|
||||||
@ApiImplicitParam(name = "id", value = "设备类型ID", required = true)
|
|
||||||
@PostMapping("/{id}/pqdif-check")
|
|
||||||
public HttpResult<PqdifCheckPlaceholderVO> pqdifCheck(@PathVariable("id") String id) {
|
|
||||||
String methodDescribe = getMethodDescribe("pqdifCheck");
|
|
||||||
LogUtil.njcnDebug(log, "{},设备类型PQDIF校验预留入口,devTypeId={}", methodDescribe, id);
|
|
||||||
PqdifCheckPlaceholderVO result = mmsDeviceTypeService.pqdifCheck(id);
|
|
||||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
package com.njcn.gather.icd.mapping.mapper;
|
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
|
||||||
import com.njcn.gather.icd.mapping.pojo.po.CsDevTypePO;
|
|
||||||
import com.njcn.gather.icd.mapping.pojo.vo.MmsDeviceTypeVO;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 设备类型 Mapper。
|
|
||||||
*/
|
|
||||||
public interface CsDevTypeMapper extends BaseMapper<CsDevTypePO> {
|
|
||||||
|
|
||||||
List<MmsDeviceTypeVO> selectDeviceTypeCheckList();
|
|
||||||
}
|
|
||||||
@@ -2,9 +2,17 @@ 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.CsIcdPathVO;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ICD 路径 Mapper。
|
* ICD 路径 Mapper。
|
||||||
*/
|
*/
|
||||||
public interface CsIcdPathMapper extends BaseMapper<CsIcdPathPO> {
|
public interface CsIcdPathMapper extends BaseMapper<CsIcdPathPO> {
|
||||||
|
|
||||||
|
List<CsIcdPathVO> selectIcdPathList(@Param("keyword") String keyword,
|
||||||
|
@Param("type") Integer type,
|
||||||
|
@Param("result") Integer result);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
<?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.icd.mapping.mapper.CsIcdPathMapper">
|
||||||
|
|
||||||
|
<select id="selectIcdPathList"
|
||||||
|
resultType="com.njcn.gather.icd.mapping.pojo.vo.CsIcdPathVO">
|
||||||
|
SELECT
|
||||||
|
ID AS id,
|
||||||
|
Name AS name,
|
||||||
|
Path AS path,
|
||||||
|
Angle AS angle,
|
||||||
|
Use_Phase_Index AS usePhaseIndex,
|
||||||
|
State AS state,
|
||||||
|
Json_Str AS jsonStr,
|
||||||
|
Xml_Str AS xmlStr,
|
||||||
|
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
|
||||||
|
<if test="keyword != null and keyword != ''">
|
||||||
|
AND (Name LIKE CONCAT('%', #{keyword}, '%')
|
||||||
|
OR Path LIKE CONCAT('%', #{keyword}, '%'))
|
||||||
|
</if>
|
||||||
|
<if test="type != null">
|
||||||
|
AND Type = #{type}
|
||||||
|
</if>
|
||||||
|
<if test="result != null">
|
||||||
|
AND Result = #{result}
|
||||||
|
</if>
|
||||||
|
ORDER BY Update_Time DESC, Create_Time DESC
|
||||||
|
</select>
|
||||||
|
|
||||||
|
</mapper>
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
package com.njcn.gather.icd.mapping.pojo.param;
|
||||||
|
|
||||||
|
import io.swagger.annotations.ApiModel;
|
||||||
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
import javax.validation.constraints.NotBlank;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ICD 存储记录保存参数。
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@ApiModel("ICD存储记录保存参数")
|
||||||
|
public class CsIcdPathParam {
|
||||||
|
|
||||||
|
@ApiModelProperty("ICD名称")
|
||||||
|
@NotBlank(message = "ICD名称不能为空")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@ApiModelProperty("ICD存储路径")
|
||||||
|
private String path;
|
||||||
|
|
||||||
|
@ApiModelProperty("ICD文件二进制内容")
|
||||||
|
private byte[] icdContent;
|
||||||
|
|
||||||
|
@ApiModelProperty("角度")
|
||||||
|
private Integer angle;
|
||||||
|
|
||||||
|
@ApiModelProperty("是否使用相位索引")
|
||||||
|
private Integer usePhaseIndex;
|
||||||
|
|
||||||
|
@ApiModelProperty("ICD类型,1-标准ICD")
|
||||||
|
private Integer type;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ICD 存储记录编辑参数。
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@ApiModel("ICD存储记录编辑参数")
|
||||||
|
public static class UpdateParam extends CsIcdPathParam {
|
||||||
|
|
||||||
|
@ApiModelProperty("ICD记录ID")
|
||||||
|
@NotBlank(message = "ICD记录ID不能为空")
|
||||||
|
private String id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ICD 存储记录列表查询参数。
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@ApiModel("ICD存储记录列表查询参数")
|
||||||
|
public static class ListParam {
|
||||||
|
|
||||||
|
@ApiModelProperty("关键字,匹配ICD名称或路径")
|
||||||
|
private String keyword;
|
||||||
|
|
||||||
|
@ApiModelProperty("ICD类型")
|
||||||
|
private Integer type;
|
||||||
|
|
||||||
|
@ApiModelProperty("ICD校验结果,0-否,1-是")
|
||||||
|
private Integer result;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package com.njcn.gather.icd.mapping.pojo.param;
|
||||||
|
|
||||||
|
import io.swagger.annotations.ApiModel;
|
||||||
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ICD 一致性校验请求参数。
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@ApiModel("ICD 一致性校验请求参数")
|
||||||
|
public class IcdConsistencyCheckRequest {
|
||||||
|
|
||||||
|
/** 待校验 MMS 映射 JSON。 */
|
||||||
|
@ApiModelProperty(value = "待校验 MMS 映射 JSON", required = true)
|
||||||
|
private String checkedJson;
|
||||||
|
|
||||||
|
/** 标准 MMS 映射 JSON。 */
|
||||||
|
@ApiModelProperty(value = "标准 MMS 映射 JSON", required = true)
|
||||||
|
private String standardJson;
|
||||||
|
|
||||||
|
/** 是否将不符合原因 JSON 保存到磁盘。 */
|
||||||
|
@ApiModelProperty("是否将不符合原因 JSON 保存到磁盘")
|
||||||
|
private boolean saveToDisk;
|
||||||
|
|
||||||
|
/** 输出目录,仅 saveToDisk=true 且存在不符合原因时生效。 */
|
||||||
|
@ApiModelProperty("输出目录,仅 saveToDisk=true 且存在不符合原因时生效")
|
||||||
|
private String outputDir;
|
||||||
|
}
|
||||||
@@ -26,6 +26,9 @@ public class CsIcdPathPO implements Serializable {
|
|||||||
@TableField("Path")
|
@TableField("Path")
|
||||||
private String path;
|
private String path;
|
||||||
|
|
||||||
|
@TableField("Icd_Content")
|
||||||
|
private byte[] icdContent;
|
||||||
|
|
||||||
@TableField("Angle")
|
@TableField("Angle")
|
||||||
private Integer angle;
|
private Integer angle;
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,63 @@
|
|||||||
|
package com.njcn.gather.icd.mapping.pojo.vo;
|
||||||
|
|
||||||
|
import io.swagger.annotations.ApiModel;
|
||||||
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ICD 存储记录列表项。
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@ApiModel("ICD存储记录列表项")
|
||||||
|
public class CsIcdPathVO {
|
||||||
|
|
||||||
|
@ApiModelProperty("ICD记录ID")
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
@ApiModelProperty("ICD名称")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@ApiModelProperty("ICD存储路径")
|
||||||
|
private String path;
|
||||||
|
|
||||||
|
@ApiModelProperty("角度")
|
||||||
|
private Integer angle;
|
||||||
|
|
||||||
|
@ApiModelProperty("是否使用相位索引")
|
||||||
|
private Integer usePhaseIndex;
|
||||||
|
|
||||||
|
@ApiModelProperty("状态,1-正常,0-删除")
|
||||||
|
private Integer state;
|
||||||
|
|
||||||
|
@ApiModelProperty("MMS映射JSON")
|
||||||
|
private String jsonStr;
|
||||||
|
|
||||||
|
@ApiModelProperty("MMS映射XML")
|
||||||
|
private String xmlStr;
|
||||||
|
|
||||||
|
@ApiModelProperty("校验结论,0-否,1-是")
|
||||||
|
private Integer result;
|
||||||
|
|
||||||
|
@ApiModelProperty("校验结论描述")
|
||||||
|
private String msg;
|
||||||
|
|
||||||
|
@ApiModelProperty("ICD类型,1-标准ICD")
|
||||||
|
private Integer type;
|
||||||
|
|
||||||
|
@ApiModelProperty("标准ICD引用ID")
|
||||||
|
private String referenceIcdId;
|
||||||
|
|
||||||
|
@ApiModelProperty("创建人")
|
||||||
|
private String createBy;
|
||||||
|
|
||||||
|
@ApiModelProperty("创建时间")
|
||||||
|
private LocalDateTime createTime;
|
||||||
|
|
||||||
|
@ApiModelProperty("更新人")
|
||||||
|
private String updateBy;
|
||||||
|
|
||||||
|
@ApiModelProperty("更新时间")
|
||||||
|
private LocalDateTime updateTime;
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
package com.njcn.gather.icd.mapping.pojo.vo;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||||
|
import io.swagger.annotations.ApiModel;
|
||||||
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ICD 一致性校验响应。
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@JsonInclude(JsonInclude.Include.NON_EMPTY)
|
||||||
|
@ApiModel("ICD 一致性校验响应")
|
||||||
|
public class IcdConsistencyCheckResponse {
|
||||||
|
|
||||||
|
/** 校验结论:1-符合,0-不符合。 */
|
||||||
|
@ApiModelProperty("校验结论:1-符合,0-不符合")
|
||||||
|
private Integer result;
|
||||||
|
|
||||||
|
/** 校验结论描述。 */
|
||||||
|
@ApiModelProperty("校验结论描述")
|
||||||
|
private String message;
|
||||||
|
|
||||||
|
/** 不符合原因结构化列表。 */
|
||||||
|
@ApiModelProperty("不符合原因结构化列表")
|
||||||
|
private List<IcdConsistencyIssue> issues = new ArrayList<IcdConsistencyIssue>();
|
||||||
|
|
||||||
|
/** 不符合原因 JSON 字符串。 */
|
||||||
|
@ApiModelProperty("不符合原因 JSON 字符串")
|
||||||
|
private String issuesJson;
|
||||||
|
|
||||||
|
/** 按映射修改规则修正后的 JSON,存在可修正内容时返回。 */
|
||||||
|
@ApiModelProperty("按映射修改规则修正后的 JSON,存在可修正内容时返回")
|
||||||
|
private String correctedJson;
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
package com.njcn.gather.icd.mapping.pojo.vo;
|
||||||
|
|
||||||
|
import io.swagger.annotations.ApiModel;
|
||||||
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ICD 一致性校验问题项。
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@ApiModel("ICD 一致性校验问题项")
|
||||||
|
public class IcdConsistencyIssue {
|
||||||
|
|
||||||
|
/** 问题所属规则或区域。 */
|
||||||
|
@ApiModelProperty("问题所属规则或区域")
|
||||||
|
private String scope;
|
||||||
|
|
||||||
|
/** JSON 路径或业务定位信息。 */
|
||||||
|
@ApiModelProperty("JSON 路径或业务定位信息")
|
||||||
|
private String path;
|
||||||
|
|
||||||
|
/** 问题描述。 */
|
||||||
|
@ApiModelProperty("问题描述")
|
||||||
|
private String message;
|
||||||
|
|
||||||
|
/** 标准映射中的值。 */
|
||||||
|
@ApiModelProperty("标准映射中的值")
|
||||||
|
private String standardValue;
|
||||||
|
|
||||||
|
/** 待校验映射中的值。 */
|
||||||
|
@ApiModelProperty("待校验映射中的值")
|
||||||
|
private String checkedValue;
|
||||||
|
|
||||||
|
/** 是否已按映射修改规则自动修正。 */
|
||||||
|
@ApiModelProperty("是否已按映射修改规则自动修正")
|
||||||
|
private boolean corrected;
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package com.njcn.gather.icd.mapping.service;
|
||||||
|
|
||||||
|
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.vo.CsIcdPathVO;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ICD 存储记录服务。
|
||||||
|
*/
|
||||||
|
public interface CsIcdPathService {
|
||||||
|
|
||||||
|
List<CsIcdPathVO> listIcdPaths(CsIcdPathParam.ListParam param);
|
||||||
|
|
||||||
|
boolean addIcdPath(CsIcdPathParam param);
|
||||||
|
|
||||||
|
boolean updateIcdPath(CsIcdPathParam.UpdateParam param);
|
||||||
|
|
||||||
|
boolean activateIcdPath(String icdId);
|
||||||
|
|
||||||
|
boolean deleteIcdPath(List<String> ids);
|
||||||
|
|
||||||
|
boolean saveIcdCheckResult(String icdId, IcdCheckResultSaveParam param);
|
||||||
|
}
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
package com.njcn.gather.icd.mapping.service;
|
|
||||||
|
|
||||||
import com.njcn.gather.icd.mapping.pojo.param.IcdCheckResultSaveParam;
|
|
||||||
import com.njcn.gather.icd.mapping.pojo.vo.MmsDeviceTypeVO;
|
|
||||||
import com.njcn.gather.icd.mapping.pojo.vo.PqdifCheckPlaceholderVO;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 设备类型校验服务。
|
|
||||||
*/
|
|
||||||
public interface MmsDeviceTypeService {
|
|
||||||
|
|
||||||
List<MmsDeviceTypeVO> listDeviceTypes();
|
|
||||||
|
|
||||||
boolean saveIcdCheckResult(String devTypeId, IcdCheckResultSaveParam param);
|
|
||||||
|
|
||||||
PqdifCheckPlaceholderVO pqdifCheck(String devTypeId);
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,215 @@
|
|||||||
|
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.njcn.gather.icd.mapping.mapper.CsIcdPathMapper;
|
||||||
|
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.po.CsIcdPathPO;
|
||||||
|
import com.njcn.gather.icd.mapping.pojo.vo.CsIcdPathVO;
|
||||||
|
import com.njcn.gather.icd.mapping.service.CsIcdPathService;
|
||||||
|
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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ICD 存储记录服务实现。
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class CsIcdPathServiceImpl implements CsIcdPathService {
|
||||||
|
|
||||||
|
private static final int STATE_NORMAL = 1;
|
||||||
|
|
||||||
|
private static final int STATE_DELETED = 0;
|
||||||
|
|
||||||
|
private static final int ICD_TYPE_STANDARD = 1;
|
||||||
|
|
||||||
|
private final CsIcdPathMapper csIcdPathMapper;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<CsIcdPathVO> listIcdPaths(CsIcdPathParam.ListParam param) {
|
||||||
|
CsIcdPathParam.ListParam checkedParam = param == null ? new CsIcdPathParam.ListParam() : param;
|
||||||
|
return csIcdPathMapper.selectIcdPathList(
|
||||||
|
trimToNull(checkedParam.getKeyword()),
|
||||||
|
checkedParam.getType(),
|
||||||
|
checkedParam.getResult());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional
|
||||||
|
public boolean addIcdPath(CsIcdPathParam param) {
|
||||||
|
CsIcdPathParam checkedParam = requireParam(param);
|
||||||
|
LocalDateTime now = LocalDateTime.now();
|
||||||
|
CsIcdPathPO icdPath = buildIcdPath(checkedParam);
|
||||||
|
icdPath.setId(UUID.randomUUID().toString().replace("-", ""));
|
||||||
|
icdPath.setState(STATE_NORMAL);
|
||||||
|
icdPath.setCreateBy(currentUserId());
|
||||||
|
icdPath.setCreateTime(now);
|
||||||
|
icdPath.setUpdateBy(currentUserId());
|
||||||
|
icdPath.setUpdateTime(now);
|
||||||
|
return csIcdPathMapper.insert(icdPath) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional
|
||||||
|
public boolean updateIcdPath(CsIcdPathParam.UpdateParam param) {
|
||||||
|
CsIcdPathParam.UpdateParam checkedParam = requireUpdateParam(param);
|
||||||
|
requireIcdPath(checkedParam.getId());
|
||||||
|
CsIcdPathPO icdPath = buildIcdPath(checkedParam);
|
||||||
|
icdPath.setId(checkedParam.getId());
|
||||||
|
icdPath.setUpdateBy(currentUserId());
|
||||||
|
icdPath.setUpdateTime(LocalDateTime.now());
|
||||||
|
return csIcdPathMapper.updateById(icdPath) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional
|
||||||
|
public boolean activateIcdPath(String icdId) {
|
||||||
|
CsIcdPathPO targetIcdPath = requireIcdPath(icdId);
|
||||||
|
LocalDateTime now = LocalDateTime.now();
|
||||||
|
String currentUserId = currentUserId();
|
||||||
|
|
||||||
|
csIcdPathMapper.update(null, new LambdaUpdateWrapper<CsIcdPathPO>()
|
||||||
|
.set(CsIcdPathPO::getType, null)
|
||||||
|
.set(CsIcdPathPO::getUpdateBy, currentUserId)
|
||||||
|
.set(CsIcdPathPO::getUpdateTime, now)
|
||||||
|
.eq(CsIcdPathPO::getState, STATE_NORMAL));
|
||||||
|
|
||||||
|
CsIcdPathPO activeIcdPath = new CsIcdPathPO();
|
||||||
|
activeIcdPath.setType(ICD_TYPE_STANDARD);
|
||||||
|
activeIcdPath.setUpdateBy(currentUserId);
|
||||||
|
activeIcdPath.setUpdateTime(now);
|
||||||
|
return csIcdPathMapper.update(activeIcdPath, new LambdaUpdateWrapper<CsIcdPathPO>()
|
||||||
|
.eq(CsIcdPathPO::getId, targetIcdPath.getId())
|
||||||
|
.eq(CsIcdPathPO::getState, STATE_NORMAL)) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional
|
||||||
|
public boolean deleteIcdPath(List<String> ids) {
|
||||||
|
if (ids == null || ids.isEmpty()) {
|
||||||
|
throw new IllegalArgumentException("ICD记录ID不能为空");
|
||||||
|
}
|
||||||
|
CsIcdPathPO icdPath = new CsIcdPathPO();
|
||||||
|
icdPath.setState(STATE_DELETED);
|
||||||
|
icdPath.setUpdateBy(currentUserId());
|
||||||
|
icdPath.setUpdateTime(LocalDateTime.now());
|
||||||
|
return csIcdPathMapper.update(icdPath, new LambdaUpdateWrapper<CsIcdPathPO>()
|
||||||
|
.in(CsIcdPathPO::getId, ids)
|
||||||
|
.eq(CsIcdPathPO::getState, STATE_NORMAL)) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional
|
||||||
|
public boolean saveIcdCheckResult(String icdId, IcdCheckResultSaveParam param) {
|
||||||
|
if (param == null) {
|
||||||
|
throw new IllegalArgumentException("ICD校验结果不能为空");
|
||||||
|
}
|
||||||
|
CsIcdPathPO icdPath = requireIcdPath(icdId);
|
||||||
|
CsIcdPathPO referenceIcd = requireUniqueReferenceIcd();
|
||||||
|
icdPath.setJsonStr(trimToNull(param.getMappingJson()));
|
||||||
|
icdPath.setXmlStr(trimToNull(param.getXml()));
|
||||||
|
icdPath.setResult(normalizeResult(param.getResult()));
|
||||||
|
icdPath.setMsg(trimToNull(param.getMsg()));
|
||||||
|
icdPath.setReferenceIcdId(referenceIcd.getId());
|
||||||
|
icdPath.setUpdateBy(currentUserId());
|
||||||
|
icdPath.setUpdateTime(LocalDateTime.now());
|
||||||
|
return csIcdPathMapper.updateById(icdPath) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private CsIcdPathPO buildIcdPath(CsIcdPathParam param) {
|
||||||
|
CsIcdPathPO icdPath = new CsIcdPathPO();
|
||||||
|
icdPath.setName(requireText(param.getName(), "ICD名称不能为空"));
|
||||||
|
icdPath.setPath(requireText(param.getPath(), "ICD存储路径不能为空"));
|
||||||
|
icdPath.setIcdContent(param.getIcdContent());
|
||||||
|
icdPath.setAngle(param.getAngle());
|
||||||
|
icdPath.setUsePhaseIndex(param.getUsePhaseIndex());
|
||||||
|
icdPath.setType(param.getType());
|
||||||
|
return icdPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
private CsIcdPathParam requireParam(CsIcdPathParam param) {
|
||||||
|
if (param == null) {
|
||||||
|
throw new IllegalArgumentException("ICD记录参数不能为空");
|
||||||
|
}
|
||||||
|
return param;
|
||||||
|
}
|
||||||
|
|
||||||
|
private CsIcdPathParam.UpdateParam requireUpdateParam(CsIcdPathParam.UpdateParam param) {
|
||||||
|
if (param == null) {
|
||||||
|
throw new IllegalArgumentException("ICD记录参数不能为空");
|
||||||
|
}
|
||||||
|
requireText(param.getId(), "ICD记录ID不能为空");
|
||||||
|
return param;
|
||||||
|
}
|
||||||
|
|
||||||
|
private CsIcdPathPO requireIcdPath(String icdId) {
|
||||||
|
String id = requireText(icdId, "ICD记录ID不能为空");
|
||||||
|
CsIcdPathPO icdPath = csIcdPathMapper.selectById(id);
|
||||||
|
if (icdPath == null || !Integer.valueOf(STATE_NORMAL).equals(icdPath.getState())) {
|
||||||
|
throw new IllegalArgumentException("ICD记录不存在或已删除");
|
||||||
|
}
|
||||||
|
return icdPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 全系统只允许一个正常状态的标准 ICD 作为唯一参照。
|
||||||
|
*/
|
||||||
|
private CsIcdPathPO requireUniqueReferenceIcd() {
|
||||||
|
List<CsIcdPathPO> referenceIcdList = csIcdPathMapper.selectList(new LambdaQueryWrapper<CsIcdPathPO>()
|
||||||
|
.eq(CsIcdPathPO::getState, STATE_NORMAL)
|
||||||
|
.eq(CsIcdPathPO::getType, ICD_TYPE_STANDARD));
|
||||||
|
if (referenceIcdList == null || referenceIcdList.isEmpty()) {
|
||||||
|
throw new IllegalArgumentException("未配置标准ICD,无法执行唯一性校验");
|
||||||
|
}
|
||||||
|
if (referenceIcdList.size() > 1) {
|
||||||
|
throw new IllegalArgumentException("存在多个标准ICD,无法确定唯一参照");
|
||||||
|
}
|
||||||
|
return referenceIcdList.get(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 "未知用户";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,141 +0,0 @@
|
|||||||
package com.njcn.gather.icd.mapping.service.impl;
|
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
||||||
import com.njcn.gather.icd.mapping.mapper.CsDevTypeMapper;
|
|
||||||
import com.njcn.gather.icd.mapping.mapper.CsIcdPathMapper;
|
|
||||||
import com.njcn.gather.icd.mapping.pojo.param.IcdCheckResultSaveParam;
|
|
||||||
import com.njcn.gather.icd.mapping.pojo.po.CsDevTypePO;
|
|
||||||
import com.njcn.gather.icd.mapping.pojo.po.CsIcdPathPO;
|
|
||||||
import com.njcn.gather.icd.mapping.pojo.vo.MmsDeviceTypeVO;
|
|
||||||
import com.njcn.gather.icd.mapping.pojo.vo.PqdifCheckPlaceholderVO;
|
|
||||||
import com.njcn.gather.icd.mapping.service.MmsDeviceTypeService;
|
|
||||||
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;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 设备类型校验服务实现。
|
|
||||||
*/
|
|
||||||
@Service
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
public class MmsDeviceTypeServiceImpl implements MmsDeviceTypeService {
|
|
||||||
|
|
||||||
private static final int STATE_NORMAL = 1;
|
|
||||||
|
|
||||||
private static final int ICD_TYPE_STANDARD = 1;
|
|
||||||
|
|
||||||
private static final String PQDIF_NOT_SUPPORTED = "NOT_SUPPORTED";
|
|
||||||
|
|
||||||
private final CsDevTypeMapper csDevTypeMapper;
|
|
||||||
|
|
||||||
private final CsIcdPathMapper csIcdPathMapper;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<MmsDeviceTypeVO> listDeviceTypes() {
|
|
||||||
return csDevTypeMapper.selectDeviceTypeCheckList();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@Transactional
|
|
||||||
public boolean saveIcdCheckResult(String devTypeId, IcdCheckResultSaveParam param) {
|
|
||||||
if (param == null) {
|
|
||||||
throw new IllegalArgumentException("ICD校验结果不能为空");
|
|
||||||
}
|
|
||||||
CsDevTypePO devType = requireDevType(devTypeId);
|
|
||||||
if (isBlank(devType.getIcd())) {
|
|
||||||
throw new IllegalArgumentException("设备类型未关联ICD,不能保存校验结果");
|
|
||||||
}
|
|
||||||
|
|
||||||
CsIcdPathPO referenceIcd = requireUniqueReferenceIcd();
|
|
||||||
CsIcdPathPO icdPath = csIcdPathMapper.selectById(devType.getIcd());
|
|
||||||
if (icdPath == null || !Integer.valueOf(STATE_NORMAL).equals(icdPath.getState())) {
|
|
||||||
throw new IllegalArgumentException("设备类型关联的ICD不存在或已删除");
|
|
||||||
}
|
|
||||||
|
|
||||||
icdPath.setJsonStr(trimToNull(param.getMappingJson()));
|
|
||||||
icdPath.setXmlStr(trimToNull(param.getXml()));
|
|
||||||
icdPath.setResult(normalizeResult(param.getResult()));
|
|
||||||
icdPath.setMsg(trimToNull(param.getMsg()));
|
|
||||||
icdPath.setReferenceIcdId(referenceIcd.getId());
|
|
||||||
icdPath.setUpdateBy(currentUserId());
|
|
||||||
icdPath.setUpdateTime(LocalDateTime.now());
|
|
||||||
return csIcdPathMapper.updateById(icdPath) > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public PqdifCheckPlaceholderVO pqdifCheck(String devTypeId) {
|
|
||||||
requireDevType(devTypeId);
|
|
||||||
PqdifCheckPlaceholderVO result = new PqdifCheckPlaceholderVO();
|
|
||||||
result.setStatus(PQDIF_NOT_SUPPORTED);
|
|
||||||
result.setMessage("PQDIF校验功能待实现");
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private CsDevTypePO requireDevType(String devTypeId) {
|
|
||||||
String id = requireText(devTypeId, "设备类型ID不能为空");
|
|
||||||
CsDevTypePO devType = csDevTypeMapper.selectById(id);
|
|
||||||
if (devType == null || !Integer.valueOf(STATE_NORMAL).equals(devType.getState())) {
|
|
||||||
throw new IllegalArgumentException("设备类型不存在或已删除");
|
|
||||||
}
|
|
||||||
return devType;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 全系统只允许一个正常状态的标准 ICD 作为唯一参照。
|
|
||||||
*/
|
|
||||||
private CsIcdPathPO requireUniqueReferenceIcd() {
|
|
||||||
List<CsIcdPathPO> referenceIcdList = csIcdPathMapper.selectList(new LambdaQueryWrapper<CsIcdPathPO>()
|
|
||||||
.eq(CsIcdPathPO::getState, STATE_NORMAL)
|
|
||||||
.eq(CsIcdPathPO::getType, ICD_TYPE_STANDARD));
|
|
||||||
if (referenceIcdList == null || referenceIcdList.isEmpty()) {
|
|
||||||
throw new IllegalArgumentException("未配置标准ICD,无法执行唯一性校验");
|
|
||||||
}
|
|
||||||
if (referenceIcdList.size() > 1) {
|
|
||||||
throw new IllegalArgumentException("存在多个标准ICD,无法确定唯一参照");
|
|
||||||
}
|
|
||||||
return referenceIcdList.get(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
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 "未知用户";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,104 @@
|
|||||||
|
package com.njcn.gather.icd.mapping.component;
|
||||||
|
|
||||||
|
import com.njcn.gather.icd.mapping.pojo.param.IcdConsistencyCheckRequest;
|
||||||
|
import com.njcn.gather.icd.mapping.pojo.vo.IcdConsistencyCheckResponse;
|
||||||
|
import org.junit.jupiter.api.Assertions;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.io.TempDir;
|
||||||
|
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
|
||||||
|
class IcdConsistencyCheckServiceTest {
|
||||||
|
|
||||||
|
private final FileStorageService fileStorageService = new FileStorageService();
|
||||||
|
private final IcdConsistencyCheckService service = new IcdConsistencyCheckService(fileStorageService);
|
||||||
|
|
||||||
|
@TempDir
|
||||||
|
Path tempDir;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void checkShouldReturnMismatchIssuesAndCorrectedJsonWithoutSavedPath() throws Exception {
|
||||||
|
IcdConsistencyCheckRequest request = new IcdConsistencyCheckRequest();
|
||||||
|
request.setStandardJson(buildStandardJson());
|
||||||
|
request.setCheckedJson(buildCheckedJson());
|
||||||
|
request.setSaveToDisk(true);
|
||||||
|
request.setOutputDir(tempDir.toString());
|
||||||
|
|
||||||
|
IcdConsistencyCheckResponse response = service.check(request);
|
||||||
|
|
||||||
|
Assertions.assertEquals(0, response.getResult());
|
||||||
|
Assertions.assertEquals("不符合", response.getMessage());
|
||||||
|
Assertions.assertFalse(response.getIssues().isEmpty());
|
||||||
|
Assertions.assertTrue(response.getIssuesJson().contains("ReportMap"));
|
||||||
|
Assertions.assertTrue(response.getCorrectedJson().contains("\"unit\" : \"s\""));
|
||||||
|
Assertions.assertTrue(response.getCorrectedJson().contains("\"reportCount\" : 2"));
|
||||||
|
Assertions.assertTrue(response.getCorrectedJson().contains("\"start\" : 1"));
|
||||||
|
Assertions.assertTrue(response.getCorrectedJson().contains("\"end\" : 4"));
|
||||||
|
Assertions.assertTrue(Files.list(tempDir).findAny().isPresent());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void checkShouldReturnPassWhenCheckedJsonMatchesStandardJson() {
|
||||||
|
IcdConsistencyCheckRequest request = new IcdConsistencyCheckRequest();
|
||||||
|
request.setStandardJson(buildStandardJson());
|
||||||
|
request.setCheckedJson(buildStandardJson());
|
||||||
|
|
||||||
|
IcdConsistencyCheckResponse response = service.check(request);
|
||||||
|
|
||||||
|
Assertions.assertEquals(1, response.getResult());
|
||||||
|
Assertions.assertEquals("符合", response.getMessage());
|
||||||
|
Assertions.assertTrue(response.getIssues().isEmpty());
|
||||||
|
Assertions.assertNull(response.getCorrectedJson());
|
||||||
|
}
|
||||||
|
|
||||||
|
private String buildStandardJson() {
|
||||||
|
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" +
|
||||||
|
buildDataSet("MMXU", "统计数据", "1", "A相", "Hz", "频率", 1, 4, "Hz") + ",\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 buildCheckedJson() {
|
||||||
|
return "{\n" +
|
||||||
|
" \"IED\":\"IED1\",\n" +
|
||||||
|
" \"LD\":\"LD0\",\n" +
|
||||||
|
" \"DataType\":\"1\",\n" +
|
||||||
|
" \"unit\":\"ms\",\n" +
|
||||||
|
" \"ReportMap\":[\n" +
|
||||||
|
" {\"desc\":\"统计数据\",\"reportCount\":1,\"rptID\":\"rpt-stat\",\"name\":\"brcbStat\",\"buffered\":\"BR\",\"inst\":\"01\",\"FlickerFlag\":\"0\",\"Select\":\"part\",\"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" +
|
||||||
|
buildDataSet("MMXU", "统计数据", "1", "A相", "Hz", "频率", 3, 6, "Hz") + ",\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 buildDataSet(String lnClass, String groupDesc, String inst, String instDesc,
|
||||||
|
String doiName, String doiDesc, int start, int end, String unit) {
|
||||||
|
return " {\"desc\":\"" + groupDesc + "\",\"lnClass\":\"" + lnClass + "\",\"instList\":[{\"inst\":\"" + inst +
|
||||||
|
"\",\"desc\":\"" + instDesc + "\",\"doiList\":[{\"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\":\"浮点\"}]}]}]}]}";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,175 @@
|
|||||||
|
package com.njcn.gather.icd.mapping.service.impl;
|
||||||
|
|
||||||
|
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.IcdCheckResultSaveParam;
|
||||||
|
import com.njcn.gather.icd.mapping.pojo.po.CsIcdPathPO;
|
||||||
|
import org.junit.jupiter.api.Assertions;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.mockito.ArgumentCaptor;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.times;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
class CsIcdPathServiceImplTest {
|
||||||
|
|
||||||
|
private final CsIcdPathMapper csIcdPathMapper = mock(CsIcdPathMapper.class);
|
||||||
|
private final CsIcdPathServiceImpl service = new CsIcdPathServiceImpl(csIcdPathMapper);
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void listIcdPathsShouldTrimKeywordBeforeQuery() {
|
||||||
|
CsIcdPathParam.ListParam param = new CsIcdPathParam.ListParam();
|
||||||
|
param.setKeyword(" standard ");
|
||||||
|
|
||||||
|
service.listIcdPaths(param);
|
||||||
|
|
||||||
|
verify(csIcdPathMapper).selectIcdPathList(eq("standard"), eq(null), eq(null));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void addIcdPathShouldInsertEnabledRecord() {
|
||||||
|
CsIcdPathParam param = buildParam("标准ICD");
|
||||||
|
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("标准ICD", captor.getValue().getName());
|
||||||
|
Assertions.assertEquals("D:/icd/standard.icd", captor.getValue().getPath());
|
||||||
|
Assertions.assertEquals(1, captor.getValue().getState());
|
||||||
|
Assertions.assertNotNull(captor.getValue().getId());
|
||||||
|
Assertions.assertNotNull(captor.getValue().getCreateTime());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void addIcdPathShouldSaveIcdBinaryContent() {
|
||||||
|
CsIcdPathParam param = buildParam("标准ICD");
|
||||||
|
byte[] fileContent = "<SCL></SCL>".getBytes();
|
||||||
|
param.setIcdContent(fileContent);
|
||||||
|
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.assertArrayEquals(fileContent, captor.getValue().getIcdContent());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void updateIcdPathShouldRejectDeletedRecord() {
|
||||||
|
CsIcdPathParam.UpdateParam param = new CsIcdPathParam.UpdateParam();
|
||||||
|
param.setId("icd-001");
|
||||||
|
param.setName("标准ICD");
|
||||||
|
param.setPath("D:/icd/standard.icd");
|
||||||
|
|
||||||
|
CsIcdPathPO deleted = new CsIcdPathPO();
|
||||||
|
deleted.setId("icd-001");
|
||||||
|
deleted.setState(0);
|
||||||
|
when(csIcdPathMapper.selectById(eq("icd-001"))).thenReturn(deleted);
|
||||||
|
|
||||||
|
IllegalArgumentException exception = Assertions.assertThrows(IllegalArgumentException.class,
|
||||||
|
() -> service.updateIcdPath(param));
|
||||||
|
|
||||||
|
Assertions.assertEquals("ICD记录不存在或已删除", exception.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void updateIcdPathShouldSaveIcdBinaryContentWhenProvided() {
|
||||||
|
CsIcdPathParam.UpdateParam param = new CsIcdPathParam.UpdateParam();
|
||||||
|
byte[] fileContent = "<SCL version=\"1\"></SCL>".getBytes();
|
||||||
|
param.setId("icd-001");
|
||||||
|
param.setName("标准ICD");
|
||||||
|
param.setPath("standard.icd");
|
||||||
|
param.setIcdContent(fileContent);
|
||||||
|
|
||||||
|
CsIcdPathPO existed = new CsIcdPathPO();
|
||||||
|
existed.setId("icd-001");
|
||||||
|
existed.setState(1);
|
||||||
|
when(csIcdPathMapper.selectById(eq("icd-001"))).thenReturn(existed);
|
||||||
|
when(csIcdPathMapper.updateById(any(CsIcdPathPO.class))).thenReturn(1);
|
||||||
|
|
||||||
|
boolean result = service.updateIcdPath(param);
|
||||||
|
|
||||||
|
ArgumentCaptor<CsIcdPathPO> captor = ArgumentCaptor.forClass(CsIcdPathPO.class);
|
||||||
|
verify(csIcdPathMapper).updateById(captor.capture());
|
||||||
|
Assertions.assertTrue(result);
|
||||||
|
Assertions.assertArrayEquals(fileContent, captor.getValue().getIcdContent());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void deleteIcdPathShouldMarkRecordDeleted() {
|
||||||
|
when(csIcdPathMapper.update(any(CsIcdPathPO.class), any())).thenReturn(1);
|
||||||
|
|
||||||
|
boolean result = service.deleteIcdPath(Collections.singletonList("icd-001"));
|
||||||
|
|
||||||
|
ArgumentCaptor<CsIcdPathPO> captor = ArgumentCaptor.forClass(CsIcdPathPO.class);
|
||||||
|
verify(csIcdPathMapper).update(captor.capture(), any());
|
||||||
|
Assertions.assertTrue(result);
|
||||||
|
Assertions.assertEquals(0, captor.getValue().getState());
|
||||||
|
Assertions.assertNotNull(captor.getValue().getUpdateTime());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void activateIcdPathShouldOnlyKeepTargetAsStandardIcd() {
|
||||||
|
CsIcdPathPO icdPath = new CsIcdPathPO();
|
||||||
|
icdPath.setId("icd-001");
|
||||||
|
icdPath.setState(1);
|
||||||
|
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(2)).update(captor.capture(), any());
|
||||||
|
Assertions.assertTrue(result);
|
||||||
|
Assertions.assertNull(captor.getAllValues().get(0));
|
||||||
|
Assertions.assertEquals(1, captor.getAllValues().get(1).getType());
|
||||||
|
Assertions.assertNotNull(captor.getAllValues().get(1).getUpdateTime());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void saveIcdCheckResultShouldUpdateIcdRecord() {
|
||||||
|
CsIcdPathPO referenceIcd = new CsIcdPathPO();
|
||||||
|
referenceIcd.setId("reference-icd");
|
||||||
|
when(csIcdPathMapper.selectList(any())).thenReturn(Collections.singletonList(referenceIcd));
|
||||||
|
|
||||||
|
CsIcdPathPO icdPath = new CsIcdPathPO();
|
||||||
|
icdPath.setId("icd-001");
|
||||||
|
icdPath.setState(1);
|
||||||
|
when(csIcdPathMapper.selectById(eq("icd-001"))).thenReturn(icdPath);
|
||||||
|
when(csIcdPathMapper.updateById(any(CsIcdPathPO.class))).thenReturn(1);
|
||||||
|
|
||||||
|
IcdCheckResultSaveParam param = new IcdCheckResultSaveParam();
|
||||||
|
param.setMappingJson("{}");
|
||||||
|
param.setXml("<xml/>");
|
||||||
|
param.setResult(1);
|
||||||
|
param.setMsg("通过");
|
||||||
|
|
||||||
|
boolean result = service.saveIcdCheckResult("icd-001", param);
|
||||||
|
|
||||||
|
ArgumentCaptor<CsIcdPathPO> captor = ArgumentCaptor.forClass(CsIcdPathPO.class);
|
||||||
|
verify(csIcdPathMapper).updateById(captor.capture());
|
||||||
|
Assertions.assertTrue(result);
|
||||||
|
Assertions.assertEquals("reference-icd", captor.getValue().getReferenceIcdId());
|
||||||
|
Assertions.assertEquals(1, captor.getValue().getResult());
|
||||||
|
}
|
||||||
|
|
||||||
|
private CsIcdPathParam buildParam(String name) {
|
||||||
|
CsIcdPathParam param = new CsIcdPathParam();
|
||||||
|
param.setName(name);
|
||||||
|
param.setPath("D:/icd/standard.icd");
|
||||||
|
param.setAngle(0);
|
||||||
|
param.setUsePhaseIndex(1);
|
||||||
|
param.setType(1);
|
||||||
|
return param;
|
||||||
|
}
|
||||||
|
}
|
||||||
1
tools/parse-pqdif/lib/.gitkeep
Normal file
1
tools/parse-pqdif/lib/.gitkeep
Normal file
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
Binary file not shown.
@@ -36,6 +36,12 @@
|
|||||||
<artifactId>spring-boot-starter-validation</artifactId>
|
<artifactId>spring-boot-starter-validation</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>net.java.dev.jna</groupId>
|
||||||
|
<artifactId>jna</artifactId>
|
||||||
|
<version>5.13.0</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.njcn</groupId>
|
<groupId>com.njcn</groupId>
|
||||||
<artifactId>pqdif-native-basic-bridge</artifactId>
|
<artifactId>pqdif-native-basic-bridge</artifactId>
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
<!-- Retain platform tools in the aggregator and add dedicated utility modules
|
<!-- Retain platform tools in the aggregator and add dedicated utility modules
|
||||||
here when they are actually introduced into this repository. -->
|
here when they are actually introduced into this repository. -->
|
||||||
<module>activate-tool</module>
|
<module>activate-tool</module>
|
||||||
|
<module>device-types</module>
|
||||||
<module>mms-mapping</module>
|
<module>mms-mapping</module>
|
||||||
<module>wave-tool</module>
|
<module>wave-tool</module>
|
||||||
<module>add-data</module>
|
<module>add-data</module>
|
||||||
|
|||||||
Reference in New Issue
Block a user