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:
2026-06-15 08:38:19 +08:00
parent 1edee2bf12
commit fd6e5097d7
91 changed files with 2620 additions and 882 deletions

View 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>

View File

@@ -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);
}
}

View File

@@ -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);
}

View File

@@ -0,0 +1,34 @@
<?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.device.types.mapper.CsDevTypeMapper">
<select id="selectDeviceTypeCheckList"
resultType="com.njcn.gather.device.types.pojo.vo.DeviceTypeVO">
SELECT
d.id AS id,
d.name AS name,
d.icd AS icdId,
p.Name AS icdName,
p.Path AS icdPath,
p.Result AS icdResult,
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,
1 AS canCheckIcd,
0 AS canCheckPqdif
FROM cs_dev_type d
LEFT JOIN cs_icd_path p ON d.icd = p.ID
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
</select>
</mapper>

View File

@@ -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;
}
}

View File

@@ -0,0 +1,61 @@
package com.njcn.gather.device.types.pojo.po;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.io.Serializable;
import java.time.LocalDateTime;
/**
* 设备类型表。
*/
@Data
@TableName("cs_dev_type")
public class CsDevTypePO implements Serializable {
private static final long serialVersionUID = 1L;
@TableId("id")
private String id;
@TableField("name")
private String name;
@TableField("icd")
private String icd;
@TableField("power")
private String power;
@TableField("Dev_Volt")
private Float devVolt;
@TableField("Dev_Curr")
private Float devCurr;
@TableField("Dev_Chns")
private Integer devChns;
@TableField("Wave_Cmd")
private String waveCmd;
@TableField("State")
private Integer state;
@TableField("Create_By")
private String createBy;
@TableField("Create_Time")
private LocalDateTime createTime;
@TableField("Update_By")
private String updateBy;
@TableField("Update_Time")
private LocalDateTime updateTime;
@TableField("report_name")
private String reportName;
}

View File

@@ -0,0 +1,58 @@
package com.njcn.gather.device.types.pojo.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* 设备类型校验列表项。
*/
@Data
@ApiModel("设备类型校验列表项")
public class DeviceTypeVO {
@ApiModelProperty("设备类型ID")
private String id;
@ApiModelProperty("设备类型名称")
private String name;
@ApiModelProperty("关联ICD ID")
private String icdId;
@ApiModelProperty("ICD名称")
private String icdName;
@ApiModelProperty("ICD路径")
private String icdPath;
@ApiModelProperty("ICD校验结论0-否1-是")
private Integer icdResult;
@ApiModelProperty("ICD校验结论描述")
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("报告模板名称")
private String reportName;
@ApiModelProperty("是否可执行ICD唯一性校验")
private Boolean canCheckIcd;
@ApiModelProperty("是否可执行PQDIF校验")
private Boolean canCheckPqdif;
}

View File

@@ -0,0 +1,19 @@
package com.njcn.gather.device.types.pojo.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* PQDIF 校验预留响应。
*/
@Data
@ApiModel("PQDIF校验预留响应")
public class PqdifCheckPlaceholderVO {
@ApiModelProperty("状态")
private String status;
@ApiModelProperty("提示信息")
private String message;
}

View File

@@ -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);
}

View File

@@ -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 "未知用户";
}
}
}

View File

@@ -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;
}
}