Compare commits
28 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
be318b2b54 | ||
| e3478e71d5 | |||
| 88d3e00559 | |||
|
|
1de66ce4dc | ||
|
|
597f70edd1 | ||
|
|
6f8acd3fb5 | ||
|
|
3c0d7a6268 | ||
|
|
7fd1563f3a | ||
|
|
4fd813f476 | ||
|
|
9dfb42f917 | ||
|
|
9990183c5d | ||
|
|
d746e68d63 | ||
|
|
1ff2d373f5 | ||
|
|
f298a7bb56 | ||
|
|
aafa38000d | ||
|
|
287ca2cddc | ||
|
|
82fdd7664b | ||
|
|
547d40ce99 | ||
|
|
49599582ba | ||
|
|
31a956a1fc | ||
|
|
55fe1fe05f | ||
| 366d1fadfa | |||
|
|
b35bbf11f7 | ||
| e2c7e745c8 | |||
|
|
29ddc8c2de | ||
|
|
212f1295eb | ||
|
|
7366e7815f | ||
|
|
a4858a818e |
6
.gitignore
vendored
6
.gitignore
vendored
@@ -52,4 +52,8 @@ rebel.xml
|
||||
# 个人工作文档,不与团队共享
|
||||
CLAUDE.md
|
||||
docs/
|
||||
data/
|
||||
data/
|
||||
.m2
|
||||
|
||||
# Windows 保留设备名误生成的真实文件(在 Git Bash 里把 2>nul 当成丢弃报错的写法,会真的建出名为 nul 的文件)
|
||||
nul
|
||||
@@ -145,7 +145,12 @@
|
||||
<artifactId>activate-tool</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
||||
</project>
|
||||
</project>
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
package com.njcn.gather.detection.controller;
|
||||
|
||||
import com.njcn.common.pojo.annotation.OperateInfo;
|
||||
import com.njcn.common.pojo.enums.response.CommonResponseEnum;
|
||||
import com.njcn.common.pojo.response.HttpResult;
|
||||
import com.njcn.gather.detection.lock.DetectionLock;
|
||||
import com.njcn.gather.detection.lock.DetectionLockManager;
|
||||
import com.njcn.gather.detection.pojo.vo.DetectionLockHolderVO;
|
||||
import com.njcn.web.controller.BaseController;
|
||||
import com.njcn.web.utils.HttpResultUtil;
|
||||
import com.njcn.web.utils.RequestUtil;
|
||||
import io.swagger.annotations.Api;
|
||||
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.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* 检测互斥锁管理接口。
|
||||
* - GET /detection/lock/current 查询当前持锁状态;空闲返回 data=null
|
||||
* - POST /detection/lock/forceRelease 管理员强制释放
|
||||
*
|
||||
* 鉴权:默认由 AuthGlobalFilter 做 JWT 校验,登录用户均可访问;
|
||||
* 强释操作通过 @OperateInfo 落审计日志,谁操作谁担责。
|
||||
*/
|
||||
@Slf4j
|
||||
@Api(tags = "检测互斥锁")
|
||||
@RestController
|
||||
@RequestMapping("/detection/lock")
|
||||
@RequiredArgsConstructor
|
||||
public class LockController extends BaseController {
|
||||
|
||||
@GetMapping("/current")
|
||||
@ApiOperation("查询当前持锁状态;空闲返回 data=null")
|
||||
public HttpResult<DetectionLockHolderVO> current() {
|
||||
String methodDescribe = getMethodDescribe("current");
|
||||
DetectionLock cur = DetectionLockManager.getInstance().getCurrent();
|
||||
DetectionLockHolderVO data = cur == null ? null : DetectionLockManager.toHolderVO(cur);
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, data, methodDescribe);
|
||||
}
|
||||
|
||||
@PostMapping("/forceRelease")
|
||||
@OperateInfo
|
||||
@ApiOperation("管理员强制释放检测锁")
|
||||
public HttpResult<?> forceRelease() {
|
||||
String methodDescribe = getMethodDescribe("forceRelease");
|
||||
String operator = RequestUtil.getUserId();
|
||||
DetectionLockManager.getInstance().forceRelease(operator, "ADMIN_FORCE");
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, null, methodDescribe);
|
||||
}
|
||||
}
|
||||
@@ -105,13 +105,25 @@ public class PreDetectionController extends BaseController {
|
||||
@ApiOperation("启动")
|
||||
@ApiImplicitParam(name = "param", value = "查询参数", required = true)
|
||||
public HttpResult<?> startTestSimulate(@RequestBody @Validated SimulateDetectionParam param) {
|
||||
String methodDescribe = getMethodDescribe("startTestSimulate");
|
||||
HttpResult<DetectionLockHolderVO> busy = requireHolderSelf();
|
||||
String methodDescribe = getMethodDescribe("startSimulateTest");
|
||||
// ContrastDetectionParam 无 userPageId 字段,用 loginName 作为会话标识(与 WS 会话 key 一致)
|
||||
HttpResult<DetectionLockHolderVO> busy = tryAcquireLock(param.getUserPageId());
|
||||
if (busy != null) {
|
||||
return busy;
|
||||
}
|
||||
preDetectionService.sendScript(param);
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, null, methodDescribe);
|
||||
// 同步阶段抛异常时回滚锁,理由同 startPreTest
|
||||
boolean keepLock = false;
|
||||
try {
|
||||
FormalTestManager.stopTime = 0;
|
||||
FormalTestManager.hasStopFlag = false;
|
||||
preDetectionService.sendScript(param);
|
||||
keepLock = true;
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, null, methodDescribe);
|
||||
} finally {
|
||||
if (!keepLock) {
|
||||
releaseLockSelf("START_SIMULATE_TEST_SYNC_FAILED");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.njcn.gather.detection.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.gather.detection.sntp.SntpServerManager;
|
||||
import com.njcn.web.controller.BaseController;
|
||||
import com.njcn.web.utils.HttpResultUtil;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@Slf4j
|
||||
@Api(tags = "SNTP对时")
|
||||
@RestController
|
||||
@RequestMapping("/sntp")
|
||||
@RequiredArgsConstructor
|
||||
public class SntpController extends BaseController {
|
||||
|
||||
private final SntpServerManager sntpServerManager;
|
||||
|
||||
@OperateInfo(info = LogEnum.SYSTEM_COMMON)
|
||||
@PostMapping("/start")
|
||||
@ApiOperation("启动SNTP对时服务")
|
||||
public HttpResult<?> start() {
|
||||
String methodDescribe = getMethodDescribe("start");
|
||||
sntpServerManager.start();
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, null, methodDescribe);
|
||||
}
|
||||
|
||||
@OperateInfo(info = LogEnum.SYSTEM_COMMON)
|
||||
@PostMapping("/stop")
|
||||
@ApiOperation("停止SNTP对时服务")
|
||||
public HttpResult<?> stop() {
|
||||
String methodDescribe = getMethodDescribe("stop");
|
||||
sntpServerManager.stop();
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, null, methodDescribe);
|
||||
}
|
||||
}
|
||||
@@ -356,6 +356,7 @@ public class SocketContrastResponseService {
|
||||
|
||||
|
||||
FormalTestManager.currentStep = SourceOperateCodeEnum.YJC_SBTXJY;
|
||||
FormalTestManager.checkStartTime = LocalDateTime.now();
|
||||
}
|
||||
|
||||
public void deal(PreDetectionParam param, String msg) throws Exception {
|
||||
@@ -554,7 +555,6 @@ public class SocketContrastResponseService {
|
||||
this.sendAlignData(s, requestOperateCode);
|
||||
|
||||
FormalTestManager.currentStep = SourceOperateCodeEnum.YJC_ALIGN;
|
||||
|
||||
// if (FormalTestManager.isWaveCheck) {
|
||||
// System.out.println("(仅有闪变、录波)模型一致性校验成功!》》》》》》》》》》》》》》》》》》》》》》》》》》》》》开始相序校验》》》》》》》》》》》》》》》》");
|
||||
// this.sendXu(s);
|
||||
|
||||
@@ -29,6 +29,7 @@ import com.njcn.gather.device.service.IPqDevSubService;
|
||||
import com.njcn.gather.plan.pojo.po.AdPlan;
|
||||
import com.njcn.gather.plan.service.IAdPlanService;
|
||||
import com.njcn.gather.result.pojo.enums.ResultUnitEnum;
|
||||
import com.njcn.gather.result.service.IResultService;
|
||||
import com.njcn.gather.script.pojo.param.PqScriptCheckDataParam;
|
||||
import com.njcn.gather.script.pojo.param.PqScriptIssueParam;
|
||||
import com.njcn.gather.script.pojo.po.SourceIssue;
|
||||
@@ -46,6 +47,7 @@ import com.njcn.gather.system.dictionary.pojo.po.DictData;
|
||||
import com.njcn.gather.system.dictionary.service.IDictDataService;
|
||||
import com.njcn.gather.system.pojo.enums.DicDataEnum;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
@@ -80,6 +82,10 @@ public class SocketDevResponseService {
|
||||
private final IAdPlanService adPlanService;
|
||||
private final IDictDataService dictDataService;
|
||||
private final IPqSourceService pqSourceService;
|
||||
private final IResultService resultService;
|
||||
|
||||
@Value("${dataCheck.enable}")
|
||||
private Boolean dataCheck;
|
||||
|
||||
/**
|
||||
* 存储的装置相序数据
|
||||
@@ -1380,6 +1386,9 @@ public class SocketDevResponseService {
|
||||
List<String> valueType = iPqScriptCheckDataService.getValueType(checkDataParam);
|
||||
|
||||
iPqDevService.updateResult(param.getDevIds(), valueType, param.getCode(), param.getUserId(), param.getTemperature(), param.getHumidity(), true);
|
||||
if (Boolean.TRUE.equals(dataCheck)) {
|
||||
resultService.tryNotifyThirdPartyAfterFormalTest(param);
|
||||
}
|
||||
CnSocketUtil.quitSend(param);
|
||||
// 数模式检测全部小项完成 → 释放锁,避免用户必须点"停止"才能让出
|
||||
DetectionLockManager.getInstance()
|
||||
@@ -1562,6 +1571,7 @@ public class SocketDevResponseService {
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param issue
|
||||
* @return key为V或I,value为对应的源下发信息
|
||||
@@ -1805,6 +1815,8 @@ public class SocketDevResponseService {
|
||||
}
|
||||
|
||||
FormalTestManager.overload = getOverloadResult(param);
|
||||
FormalTestManager.checkStartTime = LocalDateTime.now();
|
||||
FormalTestManager.reCheckType = param.getReCheckType();
|
||||
}
|
||||
|
||||
|
||||
@@ -1862,29 +1874,17 @@ public class SocketDevResponseService {
|
||||
// 谐波判断
|
||||
if (channelListDTO.getHarmFlag()) {
|
||||
List<SourceIssue.ChannelListDTO.HarmModel> harmList = channelListDTO.getHarmList();
|
||||
double sum = harmList.stream().map(harmModel -> harmModel.getFAmp() * harmModel.getFAmp() / 10000).mapToDouble(x -> x).sum();
|
||||
if (channelType.contains("U")) {
|
||||
if (maxVoltage.compareTo(BigDecimal.valueOf(Math.sqrt(1 + sum) * fAmp)) < 0) {
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
if (maxCurrent.compareTo(BigDecimal.valueOf(Math.sqrt(1 + sum) * fAmp)) < 0) {
|
||||
return 2;
|
||||
}
|
||||
double thd = harmList.stream().map(harmModel -> harmModel.getFAmp() * harmModel.getFAmp() / 10000).mapToDouble(x -> x).sum();
|
||||
if (maxVoltage.compareTo(BigDecimal.valueOf(Math.sqrt(1 + thd) * fAmp)) < 0) {
|
||||
return channelType.contains("U") ? 1 : 2;
|
||||
}
|
||||
}
|
||||
// 间谐波判断
|
||||
if (channelListDTO.getInHarmFlag()) {
|
||||
List<SourceIssue.ChannelListDTO.InharmModel> inharmList = channelListDTO.getInharmList();
|
||||
double sum = inharmList.stream().map(harmModel -> harmModel.getFAmp() * harmModel.getFAmp() / 10000).mapToDouble(x -> x).sum();
|
||||
if (channelType.contains("U")) {
|
||||
if (maxVoltage.compareTo(BigDecimal.valueOf(Math.sqrt(1 + sum) * fAmp)) < 0) {
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
if (maxCurrent.compareTo(BigDecimal.valueOf(Math.sqrt(1 + sum) * fAmp)) < 0) {
|
||||
return 2;
|
||||
}
|
||||
double thd = inharmList.stream().map(harmModel -> harmModel.getFAmp() * harmModel.getFAmp() / 10000).mapToDouble(x -> x).sum();
|
||||
if (maxVoltage.compareTo(BigDecimal.valueOf(Math.sqrt(1 + thd) * fAmp)) < 0) {
|
||||
return channelType.contains("U") ? 1 : 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,6 +121,15 @@ public class SocketSourceResponseService {
|
||||
sendErrorAndQuit(param, socketDataMsg, errorCode.getMessage());
|
||||
}
|
||||
|
||||
private void sendSimulateInitFailure(PreDetectionParam param, String errorMessage) {
|
||||
SocketDataMsg socketDataMsg = new SocketDataMsg();
|
||||
socketDataMsg.setRequestId(SourceOperateCodeEnum.YJC_YTXJY.getValue());
|
||||
socketDataMsg.setOperateCode(SourceOperateCodeEnum.INIT_GATHER.getValue());
|
||||
socketDataMsg.setCode(SourceResponseCodeEnum.UNKNOWN_ERROR.getCode());
|
||||
socketDataMsg.setData(errorMessage);
|
||||
sendWebSocketMessage(param.getUserPageId(), socketDataMsg);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 当前检测会话中的设备列表
|
||||
@@ -214,12 +223,12 @@ public class SocketSourceResponseService {
|
||||
break;
|
||||
|
||||
default:
|
||||
// TODO: 记录未知操作码到日志,并向前端发送友好提示
|
||||
sendSimulateInitFailure(param, "未知操作码");
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// TODO: 向前端发送错误提示
|
||||
log.error("程控源响应消息操作码解析失败,原始消息: {}, 解析结果: {}", msg, enumByCode);
|
||||
sendSimulateInitFailure(param, "未知操作码");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -272,10 +281,11 @@ public class SocketSourceResponseService {
|
||||
sendWebSocketMessage(param.getUserPageId(), socketDataMsg);
|
||||
break;
|
||||
default:
|
||||
// 未识别的响应码:发送通用错误消息
|
||||
WebServiceManager.sendUnknownErrorMessage(param.getUserPageId());
|
||||
sendSimulateInitFailure(param, "未知状态码");
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
sendSimulateInitFailure(param, "未知状态码");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -33,7 +33,9 @@ public enum DetectionCodeEnum {
|
||||
IA("IA", "电流相角"),
|
||||
I1A("I1A", "电流基波角度值"),
|
||||
V_UNBAN("V_UNBAN", "三相电压负序不平衡度"),
|
||||
SeqV("SeqV", "电压序分量"),
|
||||
I_UNBAN("I_UNBAN", "三相电流负序不平衡度"),
|
||||
SeqA("SeqA", "电流序分量"),
|
||||
PST("PST", "短时间闪变"),
|
||||
W("W", "有功功率"),
|
||||
VARW("VARW", "无功功率"),
|
||||
|
||||
@@ -5,7 +5,6 @@ import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.NumberUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.github.yulichang.wrapper.MPJLambdaWrapper;
|
||||
import com.njcn.gather.detection.pojo.enums.DetectionCodeEnum;
|
||||
@@ -205,22 +204,12 @@ public class DetectionServiceImpl {
|
||||
* 三相电压不平衡度
|
||||
*/
|
||||
case IMBV:
|
||||
SimAndDigNonHarmonicResult vUnban = isUnBalanceOrFlickerQualified(dev, devIdMapComm, errDtlsCheckData, U, sourceIssue, dataRule, "V_UNBAN", oneConfig.getScale());
|
||||
if (ObjectUtil.isNotNull(vUnban)) {
|
||||
detectionDataDealService.acceptNonHarmonicResult(Arrays.asList(vUnban), code);
|
||||
return vUnban.getResultFlag();
|
||||
}
|
||||
return 4;
|
||||
return isUnBalanceOrFlickerQualified(dev, devIdMapComm, errDtlsCheckData, U, sourceIssue, dataRule, code, oneConfig.getScale());
|
||||
/**
|
||||
* 三相电流不平衡度
|
||||
*/
|
||||
case IMBA:
|
||||
SimAndDigNonHarmonicResult iUnban = isUnBalanceOrFlickerQualified(dev, devIdMapComm, errDtlsCheckData, I, sourceIssue, dataRule, "I_UNBAN", oneConfig.getScale());
|
||||
if (ObjectUtil.isNotNull(iUnban)) {
|
||||
detectionDataDealService.acceptNonHarmonicResult(Arrays.asList(iUnban), code);
|
||||
return iUnban.getResultFlag();
|
||||
}
|
||||
return 4;
|
||||
return isUnBalanceOrFlickerQualified(dev, devIdMapComm, errDtlsCheckData, I, sourceIssue, dataRule, code, oneConfig.getScale());
|
||||
/**
|
||||
* 谐波有功功率
|
||||
*/
|
||||
@@ -235,12 +224,7 @@ public class DetectionServiceImpl {
|
||||
* 闪变
|
||||
*/
|
||||
case F:
|
||||
SimAndDigNonHarmonicResult pst = isUnBalanceOrFlickerQualified(dev, devIdMapComm, errDtlsCheckData, null, sourceIssue, dataRule, "PST", oneConfig.getScale());
|
||||
if (ObjectUtil.isNotNull(pst)) {
|
||||
detectionDataDealService.acceptNonHarmonicResult(Arrays.asList(pst), code);
|
||||
return pst.getResultFlag();
|
||||
}
|
||||
return 4;
|
||||
return isUnBalanceOrFlickerQualified(dev, devIdMapComm, errDtlsCheckData, DetectionCodeEnum.PST.getCode(), sourceIssue, dataRule, code, oneConfig.getScale());
|
||||
/**
|
||||
* 暂态
|
||||
*/
|
||||
@@ -435,6 +419,10 @@ public class DetectionServiceImpl {
|
||||
}
|
||||
// 根据数据处理规则取值。key为相别,value为值列表
|
||||
Map<String, List<Double>> map = devListMap(dev, dataRule, s.split("\\$")[1]);
|
||||
List<ErrDtlsCheckDataVO> dtlsCheckData = null;
|
||||
if (CollUtil.isNotEmpty(errDtlsCheckData)) {
|
||||
dtlsCheckData = errDtlsCheckData.stream().filter(x -> x.getValueTypeCode().equals(s.split("\\$")[1])).collect(Collectors.toList());
|
||||
}
|
||||
Double fData = 1.0;
|
||||
if (U.equals(type)) {
|
||||
fData = sourceIssue.getFUn();
|
||||
@@ -448,7 +436,7 @@ public class DetectionServiceImpl {
|
||||
if (P.equals(type)) {
|
||||
fData = sourceIssue.getFUn() * sourceIssue.getFIn() * 0.01;
|
||||
Double finalFData = fData;
|
||||
errDtlsCheckData.stream().forEach(x -> x.setValue(x.getValue() * finalFData));
|
||||
dtlsCheckData.stream().forEach(x -> x.setValue(x.getValue() * finalFData));
|
||||
}
|
||||
SimAndDigNonHarmonicResult result = new SimAndDigNonHarmonicResult();
|
||||
String[] split = dev.get(0).getId().split("_");
|
||||
@@ -459,52 +447,50 @@ public class DetectionServiceImpl {
|
||||
result.setDataType(sourceIssue.getDataType());
|
||||
List<PqErrSysDtls> pqErrSysDtls = new ArrayList<>();
|
||||
Integer isQualified = 4;
|
||||
if (CollUtil.isNotEmpty(errDtlsCheckData)) {
|
||||
List<ErrDtlsCheckDataVO> dtlsCheckData = errDtlsCheckData.stream().filter(x -> x.getValueTypeCode().equals(s.split("\\$")[1])).collect(Collectors.toList());
|
||||
if (CollUtil.isNotEmpty(dtlsCheckData)) {
|
||||
result.setAdType(dtlsCheckData.get(0).getValueType());
|
||||
isQualified = dtlsCheckData.get(0).getIsQualified();
|
||||
if (CollUtil.isNotEmpty(dtlsCheckData.get(0).getErrSysDtls())) {
|
||||
pqErrSysDtls = dtlsCheckData.get(0).getErrSysDtls();
|
||||
}
|
||||
if (CollUtil.isNotEmpty(dtlsCheckData)) {
|
||||
result.setAdType(dtlsCheckData.get(0).getValueType());
|
||||
isQualified = dtlsCheckData.get(0).getIsQualified();
|
||||
if (CollUtil.isNotEmpty(dtlsCheckData.get(0).getErrSysDtls())) {
|
||||
pqErrSysDtls = dtlsCheckData.get(0).getErrSysDtls();
|
||||
}
|
||||
if (map.containsKey(TYPE_T)) {
|
||||
List<ErrDtlsCheckDataVO> checkDataT = dtlsCheckData.stream().filter(x -> TYPE_T.equals(x.getPhase())).collect(Collectors.toList());
|
||||
if (CollUtil.isNotEmpty(checkDataT)) {
|
||||
DetectionData t = rangeComparisonList(map.get(TYPE_T), isQualified, pqErrSysDtls, fData, checkDataT.get(0).getValue(), dataRule, scale);
|
||||
result.setTValue(JSON.toJSONString(t));
|
||||
result.setResultFlag(t.getIsData());
|
||||
}
|
||||
} else {
|
||||
List<DetectionData> resultFlag = new ArrayList<>();
|
||||
Map<String, BiConsumer<SimAndDigNonHarmonicResult, DetectionData>> setters = new HashMap<>();
|
||||
setters.put(TYPE_A, (r, d) -> r.setAValue(JSON.toJSONString(d)));
|
||||
setters.put(TYPE_B, (r, d) -> r.setBValue(JSON.toJSONString(d)));
|
||||
setters.put(TYPE_C, (r, d) -> r.setCValue(JSON.toJSONString(d)));
|
||||
}
|
||||
result.setResultFlag(isQualified);
|
||||
if (map.containsKey(TYPE_T)) {
|
||||
List<ErrDtlsCheckDataVO> checkDataT = dtlsCheckData.stream().filter(x -> TYPE_T.equals(x.getPhase())).collect(Collectors.toList());
|
||||
if (CollUtil.isNotEmpty(checkDataT)) {
|
||||
DetectionData t = rangeComparisonList(map.get(TYPE_T), isQualified, pqErrSysDtls, fData, checkDataT.get(0).getValue(), dataRule, scale);
|
||||
result.setTValue(JSON.toJSONString(t));
|
||||
result.setResultFlag(t.getIsData());
|
||||
}
|
||||
} else {
|
||||
List<DetectionData> resultFlag = new ArrayList<>();
|
||||
Map<String, BiConsumer<SimAndDigNonHarmonicResult, DetectionData>> setters = new HashMap<>();
|
||||
setters.put(TYPE_A, (r, d) -> r.setAValue(JSON.toJSONString(d)));
|
||||
setters.put(TYPE_B, (r, d) -> r.setBValue(JSON.toJSONString(d)));
|
||||
setters.put(TYPE_C, (r, d) -> r.setCValue(JSON.toJSONString(d)));
|
||||
|
||||
List<String> phases = Arrays.asList(TYPE_A, TYPE_B, TYPE_C);
|
||||
for (String phase : phases) {
|
||||
List<ErrDtlsCheckDataVO> checkData = dtlsCheckData.stream()
|
||||
.filter(x -> phase.equals(x.getPhase()))
|
||||
.collect(Collectors.toList());
|
||||
if (CollUtil.isNotEmpty(checkData)) {
|
||||
List<Double> phaseValue = map.get(phase);
|
||||
// 注意:如果map中不存在该phase的键,phaseValue可能为null,需确保map包含该键
|
||||
DetectionData detectionData = rangeComparisonList(phaseValue, isQualified, pqErrSysDtls, fData, checkData.get(0).getValue(), dataRule, scale);
|
||||
resultFlag.add(detectionData);
|
||||
BiConsumer<SimAndDigNonHarmonicResult, DetectionData> setter = setters.get(phase);
|
||||
if (setter != null) {
|
||||
setter.accept(result, detectionData);
|
||||
} else {
|
||||
// 处理未定义的setter的情况
|
||||
throw new IllegalArgumentException("Setter not defined for phase: " + phase);
|
||||
}
|
||||
List<String> phases = Arrays.asList(TYPE_A, TYPE_B, TYPE_C);
|
||||
for (String phase : phases) {
|
||||
List<ErrDtlsCheckDataVO> checkData = dtlsCheckData.stream()
|
||||
.filter(x -> phase.equals(x.getPhase()))
|
||||
.collect(Collectors.toList());
|
||||
if (CollUtil.isNotEmpty(checkData)) {
|
||||
List<Double> phaseValue = map.get(phase);
|
||||
// 注意:如果map中不存在该phase的键,phaseValue可能为null,需确保map包含该键
|
||||
DetectionData detectionData = rangeComparisonList(phaseValue, isQualified, pqErrSysDtls, fData, checkData.get(0).getValue(), dataRule, scale);
|
||||
resultFlag.add(detectionData);
|
||||
BiConsumer<SimAndDigNonHarmonicResult, DetectionData> setter = setters.get(phase);
|
||||
if (setter != null) {
|
||||
setter.accept(result, detectionData);
|
||||
} else {
|
||||
// 处理未定义的setter的情况
|
||||
throw new IllegalArgumentException("Setter not defined for phase: " + phase);
|
||||
}
|
||||
}
|
||||
result.setResultFlag(setResultFlag(resultFlag));
|
||||
}
|
||||
info.add(result);
|
||||
result.setResultFlag(setResultFlag(resultFlag));
|
||||
}
|
||||
info.add(result);
|
||||
}
|
||||
if (CollUtil.isNotEmpty(info)) {
|
||||
detectionDataDealService.acceptNonHarmonicResult(info, code);
|
||||
@@ -607,74 +593,110 @@ public class DetectionServiceImpl {
|
||||
* @param dataRule 数据处理原则
|
||||
* @return
|
||||
*/
|
||||
public SimAndDigNonHarmonicResult isUnBalanceOrFlickerQualified(List<DevData> dev,
|
||||
Map<String, String> devIdMapComm,
|
||||
List<ErrDtlsCheckDataVO> errDtlsCheckData,
|
||||
String type,
|
||||
SourceIssue sourceIssue,
|
||||
DictDataEnum dataRule,
|
||||
String code, Integer scale) {
|
||||
public Integer isUnBalanceOrFlickerQualified(List<DevData> dev,
|
||||
Map<String, String> devIdMapComm,
|
||||
List<ErrDtlsCheckDataVO> errDtlsCheckData,
|
||||
String type,
|
||||
SourceIssue sourceIssue,
|
||||
DictDataEnum dataRule,
|
||||
String code, Integer scale) {
|
||||
List<SimAndDigNonHarmonicResult> info = new ArrayList<>();
|
||||
List<PqScriptCheckData> checkData = pqScriptCheckDataService.list(new MPJLambdaWrapper<PqScriptCheckData>()
|
||||
.eq(PqScriptCheckData::getScriptIndex, sourceIssue.getIndex())
|
||||
.eq(PqScriptCheckData::getScriptId, sourceIssue.getScriptId())
|
||||
);
|
||||
Map<String, List<Double>> map = devListMap(dev, dataRule, code);
|
||||
if (CollUtil.isNotEmpty(map)) {
|
||||
Double fData = 1.0;
|
||||
if (U.equals(type)) {
|
||||
fData = sourceIssue.getFUn();
|
||||
}
|
||||
if (I.equals(type)) {
|
||||
fData = sourceIssue.getFIn();
|
||||
}
|
||||
SimAndDigNonHarmonicResult result = new SimAndDigNonHarmonicResult();
|
||||
String[] split = dev.get(0).getId().split("_");
|
||||
String devID = devIdMapComm.get(split[0]);
|
||||
result.setDevMonitorId(devID + "_" + split[1]);
|
||||
result.setScriptId(sourceIssue.getScriptId());
|
||||
result.setSort(sourceIssue.getIndex());
|
||||
List<PqErrSysDtls> pqErrSysDtls = new ArrayList<>();
|
||||
Integer isQualified = 4;
|
||||
if (CollUtil.isNotEmpty(errDtlsCheckData)) {
|
||||
result.setAdType(errDtlsCheckData.get(0).getValueType());
|
||||
isQualified = errDtlsCheckData.get(0).getIsQualified();
|
||||
if (CollUtil.isNotEmpty(errDtlsCheckData.get(0).getErrSysDtls())) {
|
||||
pqErrSysDtls = errDtlsCheckData.get(0).getErrSysDtls();
|
||||
List<String> devValueTypeList = sourceIssue.getDevValueTypeList();
|
||||
for (String s : devValueTypeList) {
|
||||
String[] splitArr = s.split("\\$");
|
||||
Map<String, List<Double>> map = devListMap(dev, dataRule, splitArr[1]);
|
||||
if (CollUtil.isNotEmpty(map)) {
|
||||
List<ErrDtlsCheckDataVO> dtlsCheckData = null;
|
||||
if (CollUtil.isNotEmpty(errDtlsCheckData)) {
|
||||
dtlsCheckData = errDtlsCheckData.stream().filter(x -> x.getValueTypeCode().equals(s.split("\\$")[1])).collect(Collectors.toList());
|
||||
}
|
||||
Double fData = 1.0;
|
||||
if (U.equals(type)) {
|
||||
fData = sourceIssue.getFUn();
|
||||
}
|
||||
if (I.equals(type)) {
|
||||
fData = sourceIssue.getFIn();
|
||||
}
|
||||
SimAndDigNonHarmonicResult result = new SimAndDigNonHarmonicResult();
|
||||
String[] split = dev.get(0).getId().split("_");
|
||||
String devID = devIdMapComm.get(split[0]);
|
||||
result.setDevMonitorId(devID + "_" + split[1]);
|
||||
result.setScriptId(sourceIssue.getScriptId());
|
||||
result.setSort(sourceIssue.getIndex());
|
||||
result.setDataType(sourceIssue.getDataType());
|
||||
List<PqErrSysDtls> pqErrSysDtls = new ArrayList<>();
|
||||
Integer isQualified = 4;
|
||||
if (CollUtil.isNotEmpty(dtlsCheckData)) {
|
||||
result.setAdType(dtlsCheckData.get(0).getValueType());
|
||||
isQualified = dtlsCheckData.get(0).getIsQualified();
|
||||
if (CollUtil.isNotEmpty(dtlsCheckData.get(0).getErrSysDtls())) {
|
||||
pqErrSysDtls = dtlsCheckData.get(0).getErrSysDtls();
|
||||
}
|
||||
}
|
||||
result.setResultFlag(isQualified);
|
||||
// 闪变
|
||||
if (DetectionCodeEnum.PST.getCode().equals(splitArr[1]) || DetectionCodeEnum.SeqV.getCode().equals(splitArr[1]) || DetectionCodeEnum.SeqA.getCode().equals(splitArr[1])) {
|
||||
|
||||
//取出源所对应的相别信息
|
||||
List<PqScriptCheckData> channelTypeAList = checkData.stream()
|
||||
.filter(x -> TYPE_A.equals(x.getPhase()))
|
||||
.collect(Collectors.toList());
|
||||
Double channelDataA = channelTypeAList.get(0).getValue();
|
||||
if (DetectionCodeEnum.SeqV.getCode().equals(splitArr[1]) || DetectionCodeEnum.SeqA.getCode().equals(splitArr[1])) {
|
||||
channelDataA = BigDecimal.valueOf(channelDataA / 100 * fData)
|
||||
.setScale(scale, RoundingMode.HALF_UP)
|
||||
.doubleValue();
|
||||
}
|
||||
DetectionData a = rangeComparisonList(map.get(TYPE_A), isQualified, pqErrSysDtls, fData, channelDataA, dataRule, scale);
|
||||
result.setAValue(JSON.toJSONString(a));
|
||||
|
||||
List<PqScriptCheckData> channelTypeBList = checkData.stream()
|
||||
.filter(x -> TYPE_B.equals(x.getPhase()))
|
||||
.collect(Collectors.toList());
|
||||
Double channelDataB = channelTypeBList.get(0).getValue();
|
||||
if (DetectionCodeEnum.SeqV.getCode().equals(splitArr[1]) || DetectionCodeEnum.SeqA.getCode().equals(splitArr[1])) {
|
||||
channelDataB = BigDecimal.valueOf(channelDataB / 100 * fData)
|
||||
.setScale(scale, RoundingMode.HALF_UP)
|
||||
.doubleValue();
|
||||
}
|
||||
DetectionData b = rangeComparisonList(map.get(TYPE_B), isQualified, pqErrSysDtls, fData, channelDataB, dataRule, scale);
|
||||
result.setBValue(JSON.toJSONString(b));
|
||||
|
||||
List<PqScriptCheckData> channelTypeCList = checkData.stream()
|
||||
.filter(x -> TYPE_C.equals(x.getPhase()))
|
||||
.collect(Collectors.toList());
|
||||
Double channelDataC = channelTypeCList.get(0).getValue();
|
||||
if (DetectionCodeEnum.SeqV.getCode().equals(splitArr[1]) || DetectionCodeEnum.SeqA.getCode().equals(splitArr[1])) {
|
||||
channelDataC = BigDecimal.valueOf(channelDataC / 100 * fData)
|
||||
.setScale(scale, RoundingMode.HALF_UP)
|
||||
.doubleValue();
|
||||
}
|
||||
DetectionData c = rangeComparisonList(map.get(TYPE_C), isQualified, pqErrSysDtls, fData, channelDataC, dataRule, scale);
|
||||
result.setCValue(JSON.toJSONString(c));
|
||||
|
||||
result.setResultFlag(setResultFlag(Arrays.asList(a, b, c)));
|
||||
} else {
|
||||
// 三项不平衡
|
||||
List<PqScriptCheckData> channelTypeBList = checkData.stream()
|
||||
.filter(x -> TYPE_T.equals(x.getPhase()))
|
||||
.collect(Collectors.toList());
|
||||
DetectionData t = rangeComparisonList(map.get(TYPE_B), isQualified, pqErrSysDtls, fData, channelTypeBList.get(0).getValue(), dataRule, scale);
|
||||
result.setTValue(JSON.toJSONString(t));
|
||||
result.setResultFlag(setResultFlag(Arrays.asList(t)));
|
||||
}
|
||||
info.add(result);
|
||||
}
|
||||
result.setDataType(sourceIssue.getDataType());
|
||||
if (StrUtil.isBlank(type)) {
|
||||
//取出源所对应的相别信息
|
||||
List<PqScriptCheckData> channelTypeAList = checkData.stream()
|
||||
.filter(x -> TYPE_A.equals(x.getPhase()))
|
||||
.collect(Collectors.toList());
|
||||
DetectionData a = rangeComparisonList(map.get(TYPE_A), isQualified, pqErrSysDtls, fData, channelTypeAList.get(0).getValue(), dataRule, scale);
|
||||
result.setAValue(JSON.toJSONString(a));
|
||||
|
||||
List<PqScriptCheckData> channelTypeBList = checkData.stream()
|
||||
.filter(x -> TYPE_B.equals(x.getPhase()))
|
||||
.collect(Collectors.toList());
|
||||
DetectionData b = rangeComparisonList(map.get(TYPE_B), isQualified, pqErrSysDtls, fData, channelTypeBList.get(0).getValue(), dataRule, scale);
|
||||
result.setBValue(JSON.toJSONString(b));
|
||||
|
||||
List<PqScriptCheckData> channelTypeCList = checkData.stream()
|
||||
.filter(x -> TYPE_C.equals(x.getPhase()))
|
||||
.collect(Collectors.toList());
|
||||
DetectionData c = rangeComparisonList(map.get(TYPE_C), isQualified, pqErrSysDtls, fData, channelTypeCList.get(0).getValue(), dataRule, scale);
|
||||
result.setCValue(JSON.toJSONString(c));
|
||||
|
||||
result.setResultFlag(setResultFlag(Arrays.asList(a, b, c)));
|
||||
} else {
|
||||
List<PqScriptCheckData> channelTypeBList = checkData.stream()
|
||||
.filter(x -> TYPE_T.equals(x.getPhase()))
|
||||
.collect(Collectors.toList());
|
||||
DetectionData t = rangeComparisonList(map.get(TYPE_B), isQualified, pqErrSysDtls, fData, channelTypeBList.get(0).getValue(), dataRule, scale);
|
||||
result.setTValue(JSON.toJSONString(t));
|
||||
result.setResultFlag(setResultFlag(Arrays.asList(t)));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return null;
|
||||
if (CollUtil.isNotEmpty(info)) {
|
||||
detectionDataDealService.acceptNonHarmonicResult(info, code);
|
||||
List<Integer> resultFlag = info.stream().map(SimAndDigNonHarmonicResult::getResultFlag).distinct().collect(Collectors.toList());
|
||||
return StorageUtil.getInteger(resultFlag);
|
||||
}
|
||||
return 4;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -223,6 +223,7 @@ public class PreDetectionServiceImpl implements PreDetectionService {
|
||||
private void sendYtxSocketSimulate(PreDetectionParam param) {
|
||||
SourceInitialize sourceParam = pqSourceService.getSourceInitializeParam(param.getSourceId());
|
||||
param.setSourceId(sourceParam.getSourceId());
|
||||
param.setSourceName(sourceParam.getSourceId());
|
||||
String loginName = RequestUtil.getLoginNameByToken();
|
||||
WebServiceManager.addPreDetectionParam(loginName, param);
|
||||
if (ObjectUtil.isNotNull(sourceParam)) {
|
||||
@@ -333,6 +334,7 @@ public class PreDetectionServiceImpl implements PreDetectionService {
|
||||
SourceInitialize sourceParam = pqSourceService.getSourceInitializeParam(param.getSourceId());
|
||||
PreDetectionParam preDetectionParam = new PreDetectionParam();
|
||||
preDetectionParam.setSourceId(sourceParam.getSourceId());
|
||||
preDetectionParam.setSourceName(sourceParam.getSourceId());
|
||||
preDetectionParam.setUserPageId(param.getUserPageId());
|
||||
CnSocketUtil.quitSendSource(preDetectionParam);
|
||||
WebServiceManager.removePreDetectionParam();
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.njcn.gather.detection.sntp;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.time.Instant;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
public class SntpExchange {
|
||||
|
||||
private InetSocketAddress clientAddress;
|
||||
|
||||
private int version;
|
||||
|
||||
private Instant deviceInstant;
|
||||
|
||||
private byte[] clientTransmitTimestamp;
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
package com.njcn.gather.detection.sntp;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.util.Arrays;
|
||||
|
||||
@Service
|
||||
public class SntpPacketService {
|
||||
|
||||
private static final int MIN_PACKET_LENGTH = 48;
|
||||
private static final int CLIENT_MODE = 3;
|
||||
private static final int SERVER_MODE = 4;
|
||||
// NTP纪元偏移量:2208988800秒(1900年到1970年的秒数差)
|
||||
private static final long NTP_EPOCH_OFFSET = 2208988800L;
|
||||
private static final ZoneId SHANGHAI_ZONE = ZoneId.of("Asia/Shanghai");
|
||||
private static final byte[] REFERENCE_ID = "LOCL".getBytes(StandardCharsets.US_ASCII);
|
||||
|
||||
public SntpExchange parseRequest(byte[] request, InetSocketAddress clientAddress) {
|
||||
if (request == null || request.length < MIN_PACKET_LENGTH) {
|
||||
return null;
|
||||
}
|
||||
|
||||
int mode = request[0] & 0x07;
|
||||
if (mode != CLIENT_MODE) {
|
||||
return null;
|
||||
}
|
||||
|
||||
int version = (request[0] >> 3) & 0x07;
|
||||
byte[] clientTransmitTimestamp = Arrays.copyOfRange(request, 40, 48);
|
||||
Instant deviceInstant = fromNtpTimestamp(clientTransmitTimestamp);
|
||||
return new SntpExchange(clientAddress, version == 0 ? 3 : version, deviceInstant, clientTransmitTimestamp);
|
||||
}
|
||||
|
||||
public byte[] buildResponse(SntpExchange exchange, Instant receiveInstant, Instant transmitInstant) {
|
||||
byte[] response = new byte[MIN_PACKET_LENGTH];
|
||||
int version = exchange.getVersion() == 0 ? 3 : exchange.getVersion();
|
||||
response[0] = (byte) ((version << 3) | SERVER_MODE);
|
||||
response[1] = 0x01;
|
||||
response[2] = 0x04;
|
||||
response[3] = (byte) 0xEC;
|
||||
System.arraycopy(REFERENCE_ID, 0, response, 12, REFERENCE_ID.length);
|
||||
|
||||
byte[] receiveTimestamp = toNtpTimestamp(receiveInstant);
|
||||
byte[] transmitTimestamp = toNtpTimestamp(transmitInstant);
|
||||
|
||||
System.arraycopy(receiveTimestamp, 0, response, 16, receiveTimestamp.length);
|
||||
System.arraycopy(exchange.getClientTransmitTimestamp(), 0, response, 24, exchange.getClientTransmitTimestamp().length);
|
||||
System.arraycopy(receiveTimestamp, 0, response, 32, receiveTimestamp.length);
|
||||
System.arraycopy(transmitTimestamp, 0, response, 40, transmitTimestamp.length);
|
||||
return response;
|
||||
}
|
||||
|
||||
public SntpPushMessage toPushMessage(String deviceIp, Instant computerInstant, Instant deviceInstant) {
|
||||
long computerTimestampMs = computerInstant.toEpochMilli();
|
||||
long deviceTimestampMs = deviceInstant.toEpochMilli();
|
||||
return new SntpPushMessage(
|
||||
"sntp_time_update",
|
||||
deviceIp,
|
||||
formatShanghaiTime(computerInstant),
|
||||
formatShanghaiTime(deviceInstant),
|
||||
computerTimestampMs,
|
||||
deviceTimestampMs,
|
||||
computerTimestampMs - deviceTimestampMs
|
||||
);
|
||||
}
|
||||
|
||||
public static byte[] toNtpTimestamp(Instant instant) {
|
||||
long ntpSeconds = instant.getEpochSecond() + NTP_EPOCH_OFFSET;
|
||||
long fraction = ((long) instant.getNano() << 32) / 1_000_000_000L;
|
||||
byte[] bytes = new byte[8];
|
||||
writeUnsignedInt(bytes, 0, ntpSeconds);
|
||||
writeUnsignedInt(bytes, 4, fraction);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
private static Instant fromNtpTimestamp(byte[] bytes) {
|
||||
long seconds = readUnsignedInt(bytes, 0);
|
||||
long fraction = readUnsignedInt(bytes, 4);
|
||||
long epochSeconds = seconds - NTP_EPOCH_OFFSET;
|
||||
long nanos = ((fraction * 1_000_000_000L) + 0x80000000L) >>> 32;
|
||||
if (nanos >= 1_000_000_000L) {
|
||||
epochSeconds += 1;
|
||||
nanos -= 1_000_000_000L;
|
||||
}
|
||||
return Instant.ofEpochSecond(epochSeconds, nanos);
|
||||
}
|
||||
|
||||
private String formatShanghaiTime(Instant instant) {
|
||||
return LocalDateTime.ofInstant(instant, SHANGHAI_ZONE).toString().replace('T', ' ');
|
||||
}
|
||||
|
||||
private static long readUnsignedInt(byte[] bytes, int offset) {
|
||||
return ((long) bytes[offset] & 0xFF) << 24
|
||||
| ((long) bytes[offset + 1] & 0xFF) << 16
|
||||
| ((long) bytes[offset + 2] & 0xFF) << 8
|
||||
| ((long) bytes[offset + 3] & 0xFF);
|
||||
}
|
||||
|
||||
private static void writeUnsignedInt(byte[] target, int offset, long value) {
|
||||
target[offset] = (byte) ((value >>> 24) & 0xFF);
|
||||
target[offset + 1] = (byte) ((value >>> 16) & 0xFF);
|
||||
target[offset + 2] = (byte) ((value >>> 8) & 0xFF);
|
||||
target[offset + 3] = (byte) (value & 0xFF);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.njcn.gather.detection.sntp;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class SntpPushMessage {
|
||||
|
||||
private String type = "sntp_time_update";
|
||||
|
||||
private String deviceIp;
|
||||
|
||||
private String computerTime;
|
||||
|
||||
private String deviceTime;
|
||||
|
||||
private Long computerTimestampMs;
|
||||
|
||||
private Long deviceTimestampMs;
|
||||
|
||||
private Long errorMs;
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
package com.njcn.gather.detection.sntp;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.njcn.common.pojo.enums.response.CommonResponseEnum;
|
||||
import com.njcn.common.pojo.exception.BusinessException;
|
||||
import com.njcn.gather.detection.util.socket.websocket.WebServiceManager;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.PreDestroy;
|
||||
import java.io.IOException;
|
||||
import java.net.DatagramPacket;
|
||||
import java.net.DatagramSocket;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.SocketException;
|
||||
import java.time.Instant;
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class SntpServerManager {
|
||||
|
||||
private final SntpServerProperties sntpServerProperties;
|
||||
private final SntpPacketService sntpPacketService;
|
||||
|
||||
private final AtomicBoolean running = new AtomicBoolean(false);
|
||||
private final Object lifecycleMonitor = new Object();
|
||||
|
||||
private volatile DatagramSocket datagramSocket;
|
||||
private volatile ExecutorService executorService;
|
||||
|
||||
public void start() {
|
||||
// 使用同步锁和原子变量防止重复启动
|
||||
synchronized (lifecycleMonitor) {
|
||||
if (running.get()) {
|
||||
return;
|
||||
}
|
||||
int port = resolvePort();
|
||||
DatagramSocket socket = createSocket(port);
|
||||
ExecutorService executor = Executors.newSingleThreadExecutor(runnable -> {
|
||||
Thread thread = new Thread(runnable, "sntp-server");
|
||||
thread.setDaemon(true);
|
||||
return thread;
|
||||
});
|
||||
|
||||
datagramSocket = socket;
|
||||
executorService = executor;
|
||||
running.set(true);
|
||||
executor.submit(this::receiveLoop);
|
||||
log.info("SNTP服务已启动,监听端口: {}", port);
|
||||
}
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
synchronized (lifecycleMonitor) {
|
||||
if (!running.get()) {
|
||||
return;
|
||||
}
|
||||
running.set(false);
|
||||
closeSocketQuietly(datagramSocket);
|
||||
datagramSocket = null;
|
||||
if (executorService != null) {
|
||||
executorService.shutdownNow();
|
||||
executorService = null;
|
||||
}
|
||||
log.info("SNTP服务已停止");
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isRunning() {
|
||||
return running.get();
|
||||
}
|
||||
|
||||
@PreDestroy
|
||||
public void destroy() {
|
||||
stop();
|
||||
}
|
||||
|
||||
private void receiveLoop() {
|
||||
byte[] buffer = new byte[512];
|
||||
while (running.get()) {
|
||||
DatagramSocket socket = datagramSocket;
|
||||
if (socket == null || socket.isClosed()) {
|
||||
break;
|
||||
}
|
||||
|
||||
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
|
||||
try {
|
||||
socket.receive(packet);
|
||||
handlePacket(socket, packet);
|
||||
log.info("SNTP服务接收报文: {}", Arrays.toString(packet.getData()));
|
||||
} catch (SocketException e) {
|
||||
if (running.get()) {
|
||||
log.error("SNTP服务接收报文失败", e);
|
||||
}
|
||||
break;
|
||||
} catch (Exception e) {
|
||||
log.error("SNTP服务处理报文失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
synchronized (lifecycleMonitor) {
|
||||
if (running.get()) {
|
||||
running.set(false);
|
||||
closeSocketQuietly(datagramSocket);
|
||||
datagramSocket = null;
|
||||
if (executorService != null) {
|
||||
executorService.shutdownNow();
|
||||
executorService = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void handlePacket(DatagramSocket socket, DatagramPacket packet) throws IOException {
|
||||
Instant receiveInstant = Instant.now(); //T2:服务器接收请求时间
|
||||
byte[] request = Arrays.copyOf(packet.getData(), packet.getLength());
|
||||
InetSocketAddress clientAddress = new InetSocketAddress(packet.getAddress(), packet.getPort());
|
||||
SntpExchange exchange = sntpPacketService.parseRequest(request, clientAddress);
|
||||
if (exchange == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Instant transmitInstant = Instant.now(); //T3:服务器发送响应时间
|
||||
byte[] response = sntpPacketService.buildResponse(exchange, receiveInstant, transmitInstant);
|
||||
DatagramPacket responsePacket = new DatagramPacket(response, response.length, packet.getAddress(), packet.getPort());
|
||||
socket.send(responsePacket);
|
||||
|
||||
String deviceIp = clientAddress.getAddress().getHostAddress();
|
||||
// SntpPushMessage pushMessage = sntpPacketService.toPushMessage(deviceIp, transmitInstant, exchange.getDeviceInstant());
|
||||
SntpPushMessage pushMessage = sntpPacketService.toPushMessage(deviceIp, receiveInstant, exchange.getDeviceInstant());
|
||||
WebServiceManager.broadcast(JSON.toJSONString(pushMessage));
|
||||
}
|
||||
|
||||
private DatagramSocket createSocket(int port) {
|
||||
try {
|
||||
DatagramSocket socket = new DatagramSocket(port);
|
||||
socket.setReuseAddress(true);
|
||||
return socket;
|
||||
} catch (SocketException e) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, "SNTP服务启动失败,端口绑定异常");
|
||||
}
|
||||
}
|
||||
|
||||
private int resolvePort() {
|
||||
Integer port = sntpServerProperties.getPort();
|
||||
if (port == null || port < 1 || port > 65535) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, "SNTP服务启动失败,端口配置无效");
|
||||
}
|
||||
return port;
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭UDP Socket
|
||||
*
|
||||
* @param socket
|
||||
*/
|
||||
private void closeSocketQuietly(DatagramSocket socket) {
|
||||
if (socket != null && !socket.isClosed()) {
|
||||
socket.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.njcn.gather.detection.sntp;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Data
|
||||
@Component
|
||||
@ConfigurationProperties(prefix = "sntp")
|
||||
public class SntpServerProperties {
|
||||
|
||||
private Integer port = 123;
|
||||
}
|
||||
@@ -49,7 +49,11 @@ public class CnSocketUtil {
|
||||
JSONObject jsonObject = new JSONObject();
|
||||
jsonObject.put("sourceId", param.getSourceName());
|
||||
socketMsg.setData(jsonObject.toJSONString());
|
||||
SocketManager.sendMsg(param.getUserPageId() + SOURCE_TAG, JSON.toJSONString(socketMsg));
|
||||
String sourceKey = param.getUserPageId() + SOURCE_TAG;
|
||||
if (SocketManager.isChannelActive(sourceKey)) {
|
||||
SocketManager.markActiveClose(sourceKey);
|
||||
}
|
||||
SocketManager.sendMsg(sourceKey, JSON.toJSONString(socketMsg));
|
||||
WebServiceManager.removePreDetectionParam(param.getUserPageId());
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ import com.njcn.gather.plan.pojo.po.AdPlanTestConfig;
|
||||
import com.njcn.gather.script.pojo.po.SourceIssue;
|
||||
import com.njcn.gather.system.dictionary.pojo.enums.DictDataEnum;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
@@ -213,4 +214,15 @@ public class FormalTestManager {
|
||||
* 过载信息,如果检测到电压过载,overload=1;如果检测到电流过载,overload=2;如果检测到电压&&电流过载,overload=3;反之,overload=4
|
||||
*/
|
||||
public static int overload;
|
||||
|
||||
/**
|
||||
* 检测开始时间
|
||||
*/
|
||||
public static LocalDateTime checkStartTime;
|
||||
|
||||
|
||||
/**
|
||||
* 数模式 检测类型"1"-"全部检测" , "2"-"不合格项复检"
|
||||
*/
|
||||
public static String reCheckType;
|
||||
}
|
||||
|
||||
@@ -55,6 +55,11 @@ public class SocketManager {
|
||||
*/
|
||||
private static final Map<String, NioEventLoopGroup> socketGroup = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* 主动关闭中的连接。用于区分业务主动 close 和对端异常断线。
|
||||
*/
|
||||
private static final Map<String, Boolean> activeClosingUsers = new ConcurrentHashMap<>();
|
||||
|
||||
public static void addUser(String userId, Channel channel) {
|
||||
socketSessions.put(userId, channel);
|
||||
}
|
||||
@@ -65,20 +70,31 @@ public class SocketManager {
|
||||
|
||||
public static void removeUser(String userId) {
|
||||
Channel channel = socketSessions.get(userId);
|
||||
removeMapping(userId, false);
|
||||
if (ObjectUtil.isNotNull(channel)) {
|
||||
try {
|
||||
markActiveClose(userId);
|
||||
channel.close().sync();
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
NioEventLoopGroup eventExecutors = socketGroup.get(userId);
|
||||
if (ObjectUtil.isNotNull(eventExecutors)) {
|
||||
eventExecutors.shutdownGracefully();
|
||||
System.out.println(userId + "__" + channel.id() + "关闭了客户端");
|
||||
}
|
||||
System.out.println(userId + "__" + channel.id() + "关闭了客户端");
|
||||
}
|
||||
}
|
||||
|
||||
public static void removeMapping(String userId) {
|
||||
removeMapping(userId, true);
|
||||
}
|
||||
|
||||
private static void removeMapping(String userId, boolean clearActiveClose) {
|
||||
socketSessions.remove(userId);
|
||||
socketGroup.remove(userId);
|
||||
NioEventLoopGroup eventExecutors = socketGroup.remove(userId);
|
||||
if (clearActiveClose) {
|
||||
consumeActiveClose(userId);
|
||||
}
|
||||
if (ObjectUtil.isNotNull(eventExecutors)) {
|
||||
eventExecutors.shutdownGracefully();
|
||||
}
|
||||
}
|
||||
|
||||
public static Channel getChannelByUserId(String userId) {
|
||||
@@ -89,6 +105,20 @@ public class SocketManager {
|
||||
return socketGroup.get(userId);
|
||||
}
|
||||
|
||||
public static void markActiveClose(String userId) {
|
||||
if (StrUtil.isNotBlank(userId)) {
|
||||
activeClosingUsers.put(userId, Boolean.TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean consumeActiveClose(String userId) {
|
||||
return StrUtil.isNotBlank(userId) && activeClosingUsers.remove(userId) != null;
|
||||
}
|
||||
|
||||
public static boolean isActiveClosing(String userId) {
|
||||
return StrUtil.isNotBlank(userId) && activeClosingUsers.containsKey(userId);
|
||||
}
|
||||
|
||||
public static void sendMsg(String userId, String msg) {
|
||||
Channel channel = socketSessions.get(userId);
|
||||
if (ObjectUtil.isNotNull(channel)) {
|
||||
|
||||
@@ -115,7 +115,11 @@ public class NettyDevClientHandler extends SimpleChannelInboundHandler<String> {
|
||||
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
|
||||
log.warn("设备通讯客户端断线");
|
||||
ctx.close();
|
||||
SocketManager.removeUser(param.getUserPageId() + CnSocketUtil.DEV_TAG);
|
||||
String key = param.getUserPageId() + CnSocketUtil.DEV_TAG;
|
||||
if (SocketManager.consumeActiveClose(key)) {
|
||||
return;
|
||||
}
|
||||
SocketManager.removeUser(key);
|
||||
CnSocketUtil.quitSendSource(param);
|
||||
// 设备主动断开 → 本次检测视为结束,释放检测锁
|
||||
DetectionLockManager.getInstance()
|
||||
|
||||
@@ -70,7 +70,12 @@ public class NettySourceClientHandler extends SimpleChannelInboundHandler<String
|
||||
ctx.close();
|
||||
// 从Socket管理器中移除用户通道映射
|
||||
if (webUser != null && StrUtil.isNotBlank(userId)) {
|
||||
SocketManager.removeUser(userId + CnSocketUtil.SOURCE_TAG);
|
||||
String key = userId + CnSocketUtil.SOURCE_TAG;
|
||||
if (SocketManager.isActiveClosing(key)) {
|
||||
SocketManager.removeMapping(key);
|
||||
return;
|
||||
}
|
||||
SocketManager.removeUser(key);
|
||||
// 源端主动断开 → 本次检测视为结束,释放检测锁
|
||||
DetectionLockManager.getInstance()
|
||||
.releaseIfMatchPage(userId, "SOURCE_CHANNEL_INACTIVE");
|
||||
@@ -90,7 +95,7 @@ public class NettySourceClientHandler extends SimpleChannelInboundHandler<String
|
||||
}
|
||||
|
||||
String userId = webUser.getUserPageId();
|
||||
log.debug("源设备接收服务端数据, userId: {}, message: {}", userId, msg);
|
||||
log.info("source接收server端数据, userId: {}, message: {}", userId, msg);
|
||||
|
||||
try {
|
||||
// 委托给专门的响应处理服务处理业务逻辑
|
||||
@@ -120,6 +125,11 @@ public class NettySourceClientHandler extends SimpleChannelInboundHandler<String
|
||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
|
||||
String userId = webUser != null ? webUser.getUserPageId() : "unknown";
|
||||
String channelId = ctx.channel().id().toString();
|
||||
if (StrUtil.isNotBlank(userId) && SocketManager.isActiveClosing(userId + CnSocketUtil.SOURCE_TAG)) {
|
||||
log.debug("ignore source exception during active close, channelId: {}, userId: {}", channelId, userId);
|
||||
ctx.close();
|
||||
return;
|
||||
}
|
||||
|
||||
// 根据异常类型进行分类处理和日志记录
|
||||
if (cause instanceof ConnectException) {
|
||||
|
||||
@@ -87,6 +87,16 @@ public class WebServiceManager {
|
||||
return userSessions.remove(userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 仅当当前会话仍绑定到指定通道时才移除,避免旧连接误删新连接映射。
|
||||
*/
|
||||
public static boolean removeByUserId(String userId, Channel channel) {
|
||||
if (userId == null || channel == null) {
|
||||
return false;
|
||||
}
|
||||
return userSessions.remove(userId, channel);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据channelId移除会话(兼容老版本)
|
||||
* 时间复杂度:O(n),建议使用removeByUserId替代
|
||||
@@ -143,6 +153,19 @@ public class WebServiceManager {
|
||||
}
|
||||
}
|
||||
|
||||
public static void broadcast(String msg) {
|
||||
for (Map.Entry<String, Channel> entry : userSessions.entrySet()) {
|
||||
String userId = entry.getKey();
|
||||
Channel channel = entry.getValue();
|
||||
if (Objects.nonNull(channel) && channel.isActive()) {
|
||||
channel.writeAndFlush(new TextWebSocketFrame(msg));
|
||||
} else {
|
||||
log.error("WebSocket broadcast failed, disconnected user, time: {}, userId: {}", LocalDateTime.now(), userId);
|
||||
WebSocketHandler.cleanupSocketResources(userId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 存储检测参数(基于用户ID)
|
||||
* 支持多用户并发检测,每个用户的检测参数独立存储
|
||||
@@ -337,4 +360,4 @@ public class WebServiceManager {
|
||||
.releaseIfMatchPage(userId, "WS_ERROR_PUSH:" + errorType.name());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -111,7 +111,7 @@ public class WebSocketHandler extends SimpleChannelInboundHandler<TextWebSocketF
|
||||
public void handlerRemoved(ChannelHandlerContext ctx) {
|
||||
log.info("webSocket客户端退出,channelId: {}, userId: {}", ctx.channel().id(), this.userId);
|
||||
if (this.userId != null) {
|
||||
WebServiceManager.removeByUserId(this.userId);
|
||||
WebServiceManager.removeByUserId(this.userId, ctx.channel());
|
||||
} else {
|
||||
// 备用方案:如果userId为空,使用传统方法
|
||||
WebServiceManager.removeChannel(ctx.channel().id().toString());
|
||||
@@ -168,7 +168,7 @@ public class WebSocketHandler extends SimpleChannelInboundHandler<TextWebSocketF
|
||||
log.error("客户端心跳检测空闲次数超过{}次,关闭连接,channelId: {}, userId: {}", MAX_HEARTBEAT_MISS_COUNT, ctx.channel().id(), this.userId);
|
||||
ctx.channel().close();
|
||||
if (this.userId != null) {
|
||||
WebServiceManager.removeByUserId(this.userId);
|
||||
WebServiceManager.removeByUserId(this.userId, ctx.channel());
|
||||
} else {
|
||||
WebServiceManager.removeChannel(ctx.channel().id().toString());
|
||||
}
|
||||
@@ -326,7 +326,7 @@ public class WebSocketHandler extends SimpleChannelInboundHandler<TextWebSocketF
|
||||
|
||||
// 清理会话
|
||||
if (this.userId != null) {
|
||||
WebServiceManager.removeByUserId(this.userId);
|
||||
WebServiceManager.removeByUserId(this.userId, ctx.channel());
|
||||
log.debug("已清理WebSocket会话,userId: {}, channelId: {}", this.userId, channelId);
|
||||
} else {
|
||||
WebServiceManager.removeChannel(channelId);
|
||||
@@ -427,4 +427,4 @@ public class WebSocketHandler extends SimpleChannelInboundHandler<TextWebSocketF
|
||||
}
|
||||
|
||||
// ================================ URL解析工具已移至WebSocketPreprocessor ================================
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import com.njcn.common.pojo.enums.response.CommonResponseEnum;
|
||||
import com.njcn.common.pojo.exception.BusinessException;
|
||||
import com.njcn.common.pojo.response.HttpResult;
|
||||
import com.njcn.common.utils.LogUtil;
|
||||
import com.njcn.gather.device.pojo.dto.DataCheckResDTO;
|
||||
import com.njcn.gather.device.pojo.param.PqDevParam;
|
||||
import com.njcn.gather.device.pojo.vo.PqDevVO;
|
||||
import com.njcn.gather.device.service.IPqDevService;
|
||||
@@ -170,23 +171,14 @@ public class PqDevController extends BaseController {
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe);
|
||||
}
|
||||
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON, operateType = OperateType.UPLOAD)
|
||||
@PostMapping(value = "/ttt")
|
||||
@ApiOperation("批量导入被检设备")
|
||||
@ApiImplicitParams({
|
||||
@ApiImplicitParam(name = "file", value = "被检设备数据文件", required = true),
|
||||
@ApiImplicitParam(name = "patternId", value = "模式id", required = true)
|
||||
})
|
||||
public HttpResult ttt(@RequestParam("file") MultipartFile file, @RequestParam("patternId") String patternId, @RequestParam("planId") String planId, @RequestParam(value = "cover", defaultValue = "0") Integer cover, HttpServletResponse response) {
|
||||
String methodDescribe = getMethodDescribe("ttt");
|
||||
LogUtil.njcnDebug(log, "{},上传文件为:{}", methodDescribe, file.getOriginalFilename());
|
||||
boolean fileType = FileUtil.judgeFileIsExcel(file.getOriginalFilename());
|
||||
if (!fileType) {
|
||||
throw new BusinessException(CommonResponseEnum.FILE_XLSX_ERROR);
|
||||
}
|
||||
if ("null".equals(planId)) {
|
||||
planId = null;
|
||||
}
|
||||
return pqDevService.importDev(file, patternId, planId, response, cover);
|
||||
@OperateInfo
|
||||
@GetMapping("/getDataCheckRes")
|
||||
@ApiOperation("获取接入测试-数据校验结果")
|
||||
@ApiImplicitParam(name = "monitorId", value = "监测点id", required = true)
|
||||
public HttpResult<DataCheckResDTO> getDataCheckRes(@RequestParam("monitorId") String monitorId) {
|
||||
String methodDescribe = getMethodDescribe("getDataCheckRes");
|
||||
DataCheckResDTO dataCheckRes = pqDevService.getDataCheckRes(monitorId);
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, dataCheckRes, methodDescribe);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.njcn.gather.device.pojo.dto;
|
||||
|
||||
/**
|
||||
* @author caozehui
|
||||
* @data 2026-06-15
|
||||
*/
|
||||
public class DataCheckResDTO {
|
||||
}
|
||||
@@ -56,10 +56,16 @@ public class PqDevSub {
|
||||
private String checkBy;
|
||||
|
||||
/**
|
||||
* 检测时间
|
||||
* 检测开始时间
|
||||
*/
|
||||
@TableField("Check_Time")
|
||||
private LocalDateTime checkTime;
|
||||
@TableField("Check_Start_Time")
|
||||
private LocalDateTime checkStartTime;
|
||||
|
||||
/**
|
||||
* 检测结束时间
|
||||
*/
|
||||
@TableField("Check_End_Time")
|
||||
private LocalDateTime checkEndTime;
|
||||
|
||||
/**
|
||||
* 预检测耗时
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.njcn.gather.device.pojo.vo;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.njcn.gather.device.pojo.po.PqDev;
|
||||
import com.njcn.gather.monitor.pojo.po.PqMonitor;
|
||||
import lombok.Data;
|
||||
@@ -73,9 +74,14 @@ public class PqDevVO extends PqDev {
|
||||
private String checkBy;
|
||||
|
||||
/**
|
||||
* 检测时间
|
||||
* 检测开始时间
|
||||
*/
|
||||
private LocalDateTime checkTime;
|
||||
private LocalDateTime checkStartTime;
|
||||
|
||||
/**
|
||||
* 检测结束时间
|
||||
*/
|
||||
private LocalDateTime checkEndTime;
|
||||
|
||||
/**
|
||||
* 预检测耗时
|
||||
|
||||
@@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.njcn.common.pojo.poi.PullDown;
|
||||
import com.njcn.common.pojo.response.HttpResult;
|
||||
import com.njcn.gather.device.pojo.dto.DataCheckResDTO;
|
||||
import com.njcn.gather.device.pojo.enums.TimeCheckResultEnum;
|
||||
import com.njcn.gather.device.pojo.param.PqDevParam;
|
||||
import com.njcn.gather.device.pojo.po.PqDev;
|
||||
@@ -289,4 +290,11 @@ public interface IPqDevService extends IService<PqDev> {
|
||||
* @return
|
||||
*/
|
||||
List<ContrastDevExcel> getExportContrastDevData(List<PqDevVO> pqDevVOList);
|
||||
|
||||
/**
|
||||
* 获取接入测试-数据校验结果
|
||||
* @param monitorId
|
||||
* @return
|
||||
*/
|
||||
DataCheckResDTO getDataCheckRes(String monitorId);
|
||||
}
|
||||
|
||||
@@ -3,10 +3,14 @@ package com.njcn.gather.device.service;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.njcn.gather.device.pojo.po.PqDevSub;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author caozehui
|
||||
* @date 2025-07-04
|
||||
*/
|
||||
public interface IPqDevSubService extends IService<PqDevSub> {
|
||||
|
||||
LocalDateTime resolveBatchMaxCheckEndTime(List<String> devIds);
|
||||
}
|
||||
|
||||
@@ -26,7 +26,10 @@ import com.njcn.common.pojo.poi.PullDown;
|
||||
import com.njcn.common.pojo.response.HttpResult;
|
||||
import com.njcn.common.utils.EncryptionUtil;
|
||||
import com.njcn.db.mybatisplus.constant.DbConstant;
|
||||
import com.njcn.gather.detection.pojo.enums.SourceOperateCodeEnum;
|
||||
import com.njcn.gather.detection.util.socket.FormalTestManager;
|
||||
import com.njcn.gather.device.mapper.PqDevMapper;
|
||||
import com.njcn.gather.device.pojo.dto.DataCheckResDTO;
|
||||
import com.njcn.gather.device.pojo.enums.*;
|
||||
import com.njcn.gather.device.pojo.param.PqDevParam;
|
||||
import com.njcn.gather.device.pojo.po.PqDev;
|
||||
@@ -37,8 +40,8 @@ import com.njcn.gather.device.service.IPqDevSubService;
|
||||
import com.njcn.gather.monitor.pojo.po.PqMonitor;
|
||||
import com.njcn.gather.monitor.pojo.vo.PqMonitorExcel;
|
||||
import com.njcn.gather.monitor.service.IPqMonitorService;
|
||||
import com.njcn.gather.plan.pojo.enums.DataSourceEnum;
|
||||
import com.njcn.gather.plan.mapper.AdPlanMapper;
|
||||
import com.njcn.gather.plan.pojo.enums.DataSourceEnum;
|
||||
import com.njcn.gather.plan.pojo.po.AdPlan;
|
||||
import com.njcn.gather.plan.service.IPqDevCheckHistoryService;
|
||||
import com.njcn.gather.pojo.enums.DetectionResponseEnum;
|
||||
@@ -54,6 +57,7 @@ import com.njcn.gather.type.pojo.po.DevType;
|
||||
import com.njcn.gather.type.service.IDevTypeService;
|
||||
import com.njcn.gather.user.user.pojo.po.SysUser;
|
||||
import com.njcn.gather.user.user.service.ISysUserService;
|
||||
import com.njcn.http.util.RestTemplateUtil;
|
||||
import com.njcn.web.factory.PageFactory;
|
||||
import com.njcn.web.utils.ExcelUtil;
|
||||
import com.njcn.web.utils.HttpResultUtil;
|
||||
@@ -92,6 +96,7 @@ public class PqDevServiceImpl extends ServiceImpl<PqDevMapper, PqDev> implements
|
||||
private final IPqDevSubService pqDevSubService;
|
||||
private final AdPlanMapper adPlanMapper;
|
||||
private final IPqDevCheckHistoryService pqDevCheckHistoryService;
|
||||
private final RestTemplateUtil restTemplateUtil;
|
||||
|
||||
@Override
|
||||
public Page<PqDevVO> listPqDevs(PqDevParam.QueryParam queryParam) {
|
||||
@@ -505,7 +510,8 @@ public class PqDevServiceImpl extends ServiceImpl<PqDevMapper, PqDev> implements
|
||||
LambdaUpdateWrapper<PqDevSub> wrapper = new LambdaUpdateWrapper<PqDevSub>()
|
||||
.set(PqDevSub::getCheckResult, result.get(pqDevVo.getId()))
|
||||
.set(StrUtil.isNotBlank(userId), PqDevSub::getCheckBy, userId)
|
||||
.set(PqDevSub::getCheckTime, LocalDateTime.now())
|
||||
.set(updateCheckNum && SourceOperateCodeEnum.ALL_TEST.getValue().equals(FormalTestManager.reCheckType), PqDevSub::getCheckEndTime, LocalDateTime.now())
|
||||
.set(updateCheckNum && SourceOperateCodeEnum.ALL_TEST.getValue().equals(FormalTestManager.reCheckType) && ObjectUtil.isNotNull(FormalTestManager.checkStartTime), PqDevSub::getCheckStartTime, FormalTestManager.checkStartTime)
|
||||
.eq(PqDevSub::getDevId, pqDevVo.getId());
|
||||
String currrentScene = sysTestConfigService.getCurrrentScene();
|
||||
if (SceneEnum.PROVINCE_PLATFORM.getValue().equals(currrentScene)) {
|
||||
@@ -583,13 +589,23 @@ public class PqDevServiceImpl extends ServiceImpl<PqDevMapper, PqDev> implements
|
||||
|
||||
SysUser user = userService.getById(userId);
|
||||
|
||||
// 查询当前设备的检测时间
|
||||
PqDevSub currentDevSub = pqDevSubService.lambdaQuery()
|
||||
.eq(PqDevSub::getDevId, devId)
|
||||
.one();
|
||||
|
||||
LambdaUpdateChainWrapper<PqDevSub> w = pqDevSubService.lambdaUpdate()
|
||||
.set(PqDevSub::getCheckState, checkState)
|
||||
.set(PqDevSub::getCheckResult, checkResult)
|
||||
.set(PqDevSub::getReportState, DevReportStateEnum.NOT_GENERATED.getValue())
|
||||
.set(PqDevSub::getCheckTime, LocalDateTime.now())
|
||||
.eq(PqDevSub::getDevId, devId);
|
||||
|
||||
// 只有当checkTime为空时,才设置为当前时间
|
||||
if (currentDevSub != null && currentDevSub.getCheckEndTime() == null) {
|
||||
w.set(PqDevSub::getCheckEndTime, LocalDateTime.now());
|
||||
w.set(ObjectUtil.isNotNull(FormalTestManager.checkStartTime), PqDevSub::getCheckStartTime, currentDevSub.getCheckStartTime());
|
||||
}
|
||||
|
||||
if (ObjectUtil.isNotNull(user)) {
|
||||
w.set(PqDevSub::getCheckBy, user.getName());
|
||||
}
|
||||
@@ -644,7 +660,7 @@ public class PqDevServiceImpl extends ServiceImpl<PqDevMapper, PqDev> implements
|
||||
}
|
||||
pqDevVo.setRecheckNum(recheckNum);
|
||||
pqDevVo.setCheckBy(userId);
|
||||
pqDevVo.setCheckTime(LocalDateTime.now());
|
||||
pqDevVo.setCheckEndTime(LocalDateTime.now());
|
||||
pqDevCheckHistoryService.saveOrUpdateDeviceHistory(plan, pqDevVo);
|
||||
}
|
||||
|
||||
@@ -1667,4 +1683,11 @@ public class PqDevServiceImpl extends ServiceImpl<PqDevMapper, PqDev> implements
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataCheckResDTO getDataCheckRes(String monitorId) {
|
||||
// restTemplateUtil.postJson();
|
||||
DataCheckResDTO dataCheckResDTO = restTemplateUtil.postJson("http://localhost:8080/api/v1/dataCheck/getDataCheckRes", monitorId, DataCheckResDTO.class);
|
||||
return dataCheckResDTO;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,20 @@
|
||||
package com.njcn.gather.device.service.impl;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.njcn.gather.device.pojo.po.PqDevSub;
|
||||
import com.njcn.gather.device.mapper.PqDevSubMapper;
|
||||
import com.njcn.gather.device.pojo.po.PqDevSub;
|
||||
import com.njcn.gather.device.service.IPqDevSubService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* @author caozehui
|
||||
* @date 2025-07-04
|
||||
@@ -16,5 +23,19 @@ import org.springframework.stereotype.Service;
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class PqDevSubServiceImpl extends ServiceImpl<PqDevSubMapper, PqDevSub> implements IPqDevSubService {
|
||||
|
||||
|
||||
@Override
|
||||
public LocalDateTime resolveBatchMaxCheckEndTime(List<String> devIds) {
|
||||
if (CollUtil.isEmpty(devIds)) {
|
||||
return null;
|
||||
}
|
||||
return this.list(new LambdaQueryWrapper<PqDevSub>()
|
||||
.in(PqDevSub::getDevId, devIds)
|
||||
.isNotNull(PqDevSub::getCheckEndTime))
|
||||
.stream()
|
||||
.map(PqDevSub::getCheckEndTime)
|
||||
.filter(Objects::nonNull)
|
||||
.max(Comparator.naturalOrder())
|
||||
.orElse(null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
package com.njcn.gather.icd.controller;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.njcn.common.pojo.exception.BusinessException;
|
||||
import com.njcn.common.pojo.annotation.OperateInfo;
|
||||
import com.njcn.common.pojo.constant.OperateType;
|
||||
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.pojo.enums.IcdResponseEnum;
|
||||
import com.njcn.gather.icd.pojo.param.PqIcdPathParam;
|
||||
import com.njcn.gather.icd.pojo.po.PqIcdPath;
|
||||
import com.njcn.gather.icd.service.IPqIcdPathService;
|
||||
@@ -18,12 +21,20 @@ import io.swagger.annotations.ApiImplicitParam;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.core.io.ByteArrayResource;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import org.springframework.web.util.UriUtils;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import java.time.LocalDate;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
/**
|
||||
* @author caozehui
|
||||
* @date 2025-02-10
|
||||
@@ -38,7 +49,7 @@ public class PqIcdPathController extends BaseController {
|
||||
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
|
||||
@GetMapping("/listAll")
|
||||
@ApiOperation("获取所有icd")
|
||||
@ApiOperation("获取全部icd")
|
||||
public HttpResult<List<PqIcdPath>> listAll() {
|
||||
String methodDescribe = getMethodDescribe("listAll");
|
||||
List<PqIcdPath> result = pqIcdPathService.listAll();
|
||||
@@ -60,7 +71,7 @@ public class PqIcdPathController extends BaseController {
|
||||
@PostMapping("/add")
|
||||
@ApiOperation("新增icd")
|
||||
@ApiImplicitParam(name = "param", value = "icd新增参数", required = true)
|
||||
public HttpResult<Boolean> add(PqIcdPathParam param) {
|
||||
public HttpResult<Boolean> add(@Validated PqIcdPathParam.CreateParam param) {
|
||||
String methodDescribe = getMethodDescribe("add");
|
||||
LogUtil.njcnDebug(log, "{},新增数据为:{}", methodDescribe, param);
|
||||
|
||||
@@ -72,11 +83,27 @@ public class PqIcdPathController extends BaseController {
|
||||
}
|
||||
}
|
||||
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON, operateType = OperateType.ADD)
|
||||
@PostMapping("/external/add")
|
||||
@ApiOperation("外部系统新增icd")
|
||||
@ApiImplicitParam(name = "param", value = "外部系统icd新增参数", required = true)
|
||||
public HttpResult<Boolean> externalAdd(@RequestBody List<PqIcdPathParam.ExternalCreateParam> param) {
|
||||
String methodDescribe = getMethodDescribe("externalAdd");
|
||||
LogUtil.njcnDebug(log, "{},外部新增数据为:{}", methodDescribe, param);
|
||||
|
||||
boolean result = pqIcdPathService.addUpstreamIcd(param);
|
||||
if (result) {
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, true, methodDescribe);
|
||||
} else {
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.FAIL, false, methodDescribe);
|
||||
}
|
||||
}
|
||||
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON, operateType = OperateType.UPDATE)
|
||||
@PostMapping("/update")
|
||||
@ApiOperation("修改icd")
|
||||
@ApiImplicitParam(name = "param", value = "icd修改参数", required = true)
|
||||
public HttpResult<Boolean> update(PqIcdPathParam.UpdateParam param) {
|
||||
public HttpResult<Boolean> update(@Validated PqIcdPathParam.UpdateParam param) {
|
||||
String methodDescribe = getMethodDescribe("update");
|
||||
LogUtil.njcnDebug(log, "{},修改数据为:{}", methodDescribe, param);
|
||||
|
||||
@@ -88,6 +115,24 @@ public class PqIcdPathController extends BaseController {
|
||||
}
|
||||
}
|
||||
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
|
||||
@GetMapping("/standard-list")
|
||||
@ApiOperation("获取标准ICD列表")
|
||||
public HttpResult<List<PqIcdPath>> standardList() {
|
||||
String methodDescribe = getMethodDescribe("standardList");
|
||||
List<PqIcdPath> result = pqIcdPathService.listStandardIcd();
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe);
|
||||
}
|
||||
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
|
||||
@GetMapping("/getById")
|
||||
@ApiOperation("根据id获取ICD详情")
|
||||
public HttpResult<PqIcdPath> getById(@RequestParam String id) {
|
||||
String methodDescribe = getMethodDescribe("getById");
|
||||
PqIcdPath result = pqIcdPathService.getIcdById(id);
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe);
|
||||
}
|
||||
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON, operateType = OperateType.DELETE)
|
||||
@PostMapping("/delete")
|
||||
@ApiOperation("删除icd")
|
||||
@@ -103,5 +148,70 @@ public class PqIcdPathController extends BaseController {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
|
||||
@GetMapping("/export/{id}")
|
||||
@ApiOperation("导出icd")
|
||||
public ResponseEntity<ByteArrayResource> export(@PathVariable String id) {
|
||||
PqIcdPath detail = pqIcdPathService.getIcdById(id);
|
||||
byte[] body = pqIcdPathService.exportIcd(id);
|
||||
String fileName = UriUtils.encode(detail.getName() + ".zip", StandardCharsets.UTF_8);
|
||||
return ResponseEntity.ok()
|
||||
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename*=UTF-8''" + fileName)
|
||||
.contentType(MediaType.APPLICATION_OCTET_STREAM)
|
||||
.contentLength(body.length)
|
||||
.body(new ByteArrayResource(body));
|
||||
}
|
||||
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
|
||||
@PostMapping("/export/json")
|
||||
@ApiOperation("导出icd json")
|
||||
@ApiImplicitParam(name = "ids", value = "id集合", required = true)
|
||||
public ResponseEntity<ByteArrayResource> exportJson(@RequestBody List<String> ids) {
|
||||
byte[] body = pqIcdPathService.exportIcdJson(ids);
|
||||
String fileName = UriUtils.encode(LocalDate.now() + ".json", StandardCharsets.UTF_8);
|
||||
return ResponseEntity.ok()
|
||||
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename*=UTF-8''" + fileName)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.contentLength(body.length)
|
||||
.body(new ByteArrayResource(body));
|
||||
}
|
||||
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON, operateType = OperateType.ADD)
|
||||
@PostMapping("/import/json")
|
||||
@ApiOperation("导入icd json")
|
||||
@ApiImplicitParam(name = "file", value = "json文件", required = true)
|
||||
public HttpResult<Boolean> importJson(@RequestParam("file") MultipartFile file) {
|
||||
String methodDescribe = getMethodDescribe("importJson");
|
||||
LogUtil.njcnDebug(log, "{},导入文件为:{}", methodDescribe, file == null ? null : file.getOriginalFilename());
|
||||
|
||||
List<PqIcdPathParam.ExternalCreateParam> param = parseExternalCreateParams(file);
|
||||
boolean result = pqIcdPathService.addUpstreamIcd(param);
|
||||
if (result) {
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, true, methodDescribe);
|
||||
} else {
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.FAIL, false, methodDescribe);
|
||||
}
|
||||
}
|
||||
|
||||
private List<PqIcdPathParam.ExternalCreateParam> parseExternalCreateParams(MultipartFile file) {
|
||||
if (file == null || file.isEmpty()) {
|
||||
throw new BusinessException(IcdResponseEnum.ICD_FILE_NOT_NULL);
|
||||
}
|
||||
String fileName = file.getOriginalFilename();
|
||||
if (fileName == null || !fileName.toLowerCase().endsWith(".json")) {
|
||||
throw new BusinessException(IcdResponseEnum.JSON_FORMAT_ERROR);
|
||||
}
|
||||
try {
|
||||
String content = new String(file.getBytes(), StandardCharsets.UTF_8);
|
||||
List<PqIcdPathParam.ExternalCreateParam> params = JSON.parseArray(content, PqIcdPathParam.ExternalCreateParam.class);
|
||||
if (params == null) {
|
||||
throw new BusinessException(IcdResponseEnum.JSON_FORMAT_ERROR);
|
||||
}
|
||||
return params;
|
||||
} catch (BusinessException ex) {
|
||||
throw ex;
|
||||
} catch (Exception ex) {
|
||||
throw new BusinessException(IcdResponseEnum.JSON_FORMAT_ERROR);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
package com.njcn.gather.icd.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.github.yulichang.base.MPJBaseMapper;
|
||||
import com.njcn.gather.icd.pojo.po.PqIcdPath;
|
||||
import com.njcn.gather.icd.pojo.vo.PqIcdExportJsonVO;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author caozehui
|
||||
* @date 2025-02-10
|
||||
@@ -17,5 +21,21 @@ public interface PqIcdPathMapper extends MPJBaseMapper<PqIcdPath> {
|
||||
* @return
|
||||
*/
|
||||
PqIcdPath selectIcdByDevType(@Param("devTypeId") String devTypeId);
|
||||
|
||||
List<PqIcdPath> selectPageList(Page<PqIcdPath> page, @Param("name") String name);
|
||||
|
||||
List<PqIcdPath> selectStandardList();
|
||||
|
||||
PqIcdPath selectStandardByName(@Param("name") String name);
|
||||
|
||||
List<PqIcdPath> selectByIdsWithoutBlob(@Param("ids") List<String> ids);
|
||||
|
||||
Long countActiveReferencesToStandards(@Param("standardIds") List<String> standardIds);
|
||||
|
||||
PqIcdPath selectDetailById(@Param("id") String id);
|
||||
|
||||
PqIcdPath selectExportById(@Param("id") String id);
|
||||
|
||||
List<PqIcdExportJsonVO> selectExportJsonByIds(@Param("ids") List<String> ids);
|
||||
}
|
||||
|
||||
|
||||
@@ -4,10 +4,135 @@
|
||||
|
||||
|
||||
<select id="selectIcdByDevType" resultType="com.njcn.gather.icd.pojo.po.PqIcdPath">
|
||||
select *
|
||||
select path.*
|
||||
from pq_icd_path path
|
||||
inner join pq_dev_type type on path.id = type.icd
|
||||
where type.id = #{devTypeId}
|
||||
</select>
|
||||
|
||||
<select id="selectPageList" resultType="com.njcn.gather.icd.pojo.po.PqIcdPath">
|
||||
select p.id,
|
||||
p.name,
|
||||
p.state,
|
||||
p.angle,
|
||||
p.use_phase_index as usePhaseIndex,
|
||||
p.Json_Str as jsonStr,
|
||||
p.Xml_Str as xmlStr,
|
||||
p.Result as result,
|
||||
p.Msg as msg,
|
||||
p.Type as type,
|
||||
p.Reference_Icd_Id as referenceIcdId,
|
||||
ref.name as referenceIcdName,
|
||||
case when p.Icd is null then false else true end as hasIcdFile,
|
||||
p.create_by as createBy,
|
||||
p.create_time as createTime,
|
||||
p.update_by as updateBy,
|
||||
p.update_time as updateTime
|
||||
from pq_icd_path p
|
||||
left join pq_icd_path ref on p.Reference_Icd_Id = ref.id
|
||||
and ref.state = 1
|
||||
and ref.Type in (1, 3)
|
||||
where p.state = 1
|
||||
<if test="name != null and name != ''">
|
||||
and p.name like concat('%', #{name}, '%')
|
||||
</if>
|
||||
order by p.create_time desc
|
||||
</select>
|
||||
|
||||
<select id="selectStandardList" resultType="com.njcn.gather.icd.pojo.po.PqIcdPath">
|
||||
select id, name, Type as type
|
||||
from pq_icd_path
|
||||
where state = 1
|
||||
and Type in (1, 3)
|
||||
order by create_time desc
|
||||
</select>
|
||||
|
||||
<select id="selectStandardByName" resultType="com.njcn.gather.icd.pojo.po.PqIcdPath">
|
||||
select id, name, Type as type, state
|
||||
from pq_icd_path
|
||||
where state = 1
|
||||
and Type in (1, 3)
|
||||
and name = #{name}
|
||||
limit 1
|
||||
</select>
|
||||
|
||||
<select id="selectByIdsWithoutBlob" resultType="com.njcn.gather.icd.pojo.po.PqIcdPath">
|
||||
select id, name, Type as type, state
|
||||
from pq_icd_path
|
||||
where id in
|
||||
<foreach collection="ids" item="id" open="(" separator="," close=")">
|
||||
#{id}
|
||||
</foreach>
|
||||
and state = 1
|
||||
</select>
|
||||
|
||||
<select id="countActiveReferencesToStandards" resultType="java.lang.Long">
|
||||
select count(1)
|
||||
from pq_icd_path
|
||||
where state = 1
|
||||
and Type in (2, 4)
|
||||
and Reference_Icd_Id in
|
||||
<foreach collection="standardIds" item="id" open="(" separator="," close=")">
|
||||
#{id}
|
||||
</foreach>
|
||||
</select>
|
||||
|
||||
<select id="selectDetailById" resultType="com.njcn.gather.icd.pojo.po.PqIcdPath">
|
||||
select p.id,
|
||||
p.name,
|
||||
p.state,
|
||||
p.angle,
|
||||
p.use_phase_index as usePhaseIndex,
|
||||
p.Json_Str as jsonStr,
|
||||
p.Xml_Str as xmlStr,
|
||||
p.Result as result,
|
||||
p.Msg as msg,
|
||||
p.Type as type,
|
||||
p.Reference_Icd_Id as referenceIcdId,
|
||||
ref.name as referenceIcdName,
|
||||
case when p.Icd is null then false else true end as hasIcdFile,
|
||||
p.create_by as createBy,
|
||||
p.create_time as createTime,
|
||||
p.update_by as updateBy,
|
||||
p.update_time as updateTime
|
||||
from pq_icd_path p
|
||||
left join pq_icd_path ref on p.Reference_Icd_Id = ref.id
|
||||
and ref.state = 1
|
||||
and ref.Type in (1, 3)
|
||||
where p.id = #{id}
|
||||
and p.state = 1
|
||||
</select>
|
||||
|
||||
<select id="selectExportById" resultType="com.njcn.gather.icd.pojo.po.PqIcdPath">
|
||||
select id,
|
||||
name,
|
||||
state,
|
||||
Json_Str as jsonStr,
|
||||
Xml_Str as xmlStr,
|
||||
Icd as icd
|
||||
from pq_icd_path
|
||||
where id = #{id}
|
||||
and state = 1
|
||||
</select>
|
||||
|
||||
<select id="selectExportJsonByIds" resultType="com.njcn.gather.icd.pojo.vo.PqIcdExportJsonVO">
|
||||
select id,
|
||||
name,
|
||||
Icd as rawIcd,
|
||||
angle,
|
||||
use_phase_index as usePhaseIndex,
|
||||
Json_Str as jsonStr,
|
||||
Xml_Str as xmlStr,
|
||||
Result as result,
|
||||
Msg as msg,
|
||||
Type as type,
|
||||
Reference_Icd_Id as referenceIcdId
|
||||
from pq_icd_path
|
||||
where state = 1
|
||||
and id in
|
||||
<foreach collection="ids" item="id" open="(" separator="," close=")">
|
||||
#{id}
|
||||
</foreach>
|
||||
</select>
|
||||
</mapper>
|
||||
|
||||
|
||||
@@ -1,16 +1,45 @@
|
||||
package com.njcn.gather.icd.pojo.enums;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* @author caozehui
|
||||
* @data 2025-11-11
|
||||
*/
|
||||
@Getter
|
||||
public enum IcdResponseEnum {
|
||||
FILE_NOT_NULL("A018001", "映射文件不能为空"),
|
||||
FILE_TYPE_ERROR("A018002", "映射文件类型错误"),
|
||||
FILE_SIZE_ERROR("A018003", "映射文件大小超出限制");
|
||||
FILE_SIZE_ERROR("A018003", "映射文件大小超出限制"),
|
||||
ICD_FILE_NOT_NULL("A018004", "ICD原始文件不能为空"),
|
||||
ICD_FILE_TYPE_ERROR("A018005", "ICD原始文件类型错误"),
|
||||
ICD_FILE_READ_ERROR("A018006", "ICD原始文件读取失败"),
|
||||
JSON_FORMAT_ERROR("A018007", "JSON格式错误"),
|
||||
XML_FORMAT_ERROR("A018008", "XML格式错误"),
|
||||
STANDARD_ICD_IN_USE("A018009", "该标准ICD已被非标准ICD引用,禁止删除"),
|
||||
ICD_FILE_NOT_FOUND("A018010", "ICD原始文件不存在"),
|
||||
ICD_EXPORT_FAILED("A018011", "ICD导出失败"),
|
||||
ICD_NOT_FOUND("A018012", "ICD不存在"),
|
||||
RESULT_NOT_NULL("A018013", "结论不能为空"),
|
||||
MSG_NOT_BLANK("A018014", "当结论为否时,描述不能为空"),
|
||||
TYPE_NOT_NULL("A018015", "ICD类型不能为空"),
|
||||
TYPE_VALUE_ERROR("A018016", "ICD类型取值错误"),
|
||||
REFERENCE_ICD_NOT_BLANK("A018017", "非标准ICD必须选择参照标准ICD"),
|
||||
REFERENCE_ICD_INVALID("A018018", "参照标准ICD无效"),
|
||||
GENERATE_FILE("A018019", "文件生成失败"),
|
||||
ICD_TXT_SYNC_FAILED("A018020", "ICD txt文件同步失败"),
|
||||
REFERENCE_STANDARD_ICD_NAME_NOT_FOUND("A018021", "参照标准ICD名称不存在"),
|
||||
EXTERNAL_ICD_ITEM_NOT_NULL("A018022", "导入数据项不能为空"),
|
||||
EXTERNAL_ICD_ID_REPEAT("A018023", "导入数据中存在重复id"),
|
||||
EXTERNAL_ICD_DELETED_NOT_ALLOW_OVERWRITE("A018024", "该ICD已删除,不允许通过导入覆盖"),
|
||||
EXTERNAL_ICD_IMPORT_FAILED("A018025", "ICD导入失败"),
|
||||
ANGLE_NOT_NULL("A018026", "是否支持相角不能为空"),
|
||||
ANGLE_VALUE_ERROR("A018027", "是否支持相角取值错误"),
|
||||
USE_PHASE_INDEX_NOT_NULL("A018028", "是否使用相别指标不能为空"),
|
||||
USE_PHASE_INDEX_VALUE_ERROR("A018029", "是否使用相别指标取值错误");
|
||||
|
||||
private String code;
|
||||
private String message;
|
||||
private final String code;
|
||||
private final String message;
|
||||
|
||||
IcdResponseEnum(String code, String message) {
|
||||
this.code = code;
|
||||
|
||||
@@ -17,39 +17,63 @@ import javax.validation.constraints.Pattern;
|
||||
*/
|
||||
@Data
|
||||
public class PqIcdPathParam {
|
||||
@ApiModelProperty(value = "名称", required = true)
|
||||
@NotBlank(message = DetectionValidMessage.NAME_NOT_BLANK)
|
||||
@Pattern(regexp = PatternRegex.ICD_NAME_REGEX, message = DetectionValidMessage.ICD_NAME_FORMAT_ERROR)
|
||||
private String name;
|
||||
|
||||
@ApiModelProperty(value = "存储路径", required = true)
|
||||
@NotBlank(message = DetectionValidMessage.ICD_PATH_NOT_BLANK)
|
||||
@Pattern(regexp = PatternRegex.ICD_PATH_REGEX, message = DetectionValidMessage.ICD_PATH_FORMAT_ERROR)
|
||||
private String path;
|
||||
|
||||
@ApiModelProperty(value = "是否支持电压相角、电流相角指标,0表示否,1表示是", required = true)
|
||||
@ApiModelProperty(value = "angle", required = true)
|
||||
private Integer angle;
|
||||
|
||||
@ApiModelProperty(value = "角型接线时是否使用相别的指标来进行检测,0表示否,1表示是", required = true)
|
||||
@ApiModelProperty(value = "usePhaseIndex", required = true)
|
||||
private Integer usePhaseIndex;
|
||||
|
||||
@ApiModelProperty(value = "映射文件", required = true)
|
||||
private MultipartFile mappingFile;
|
||||
@ApiModelProperty(value = "jsonStr")
|
||||
private String jsonStr;
|
||||
|
||||
@ApiModelProperty(value = "xmlStr")
|
||||
private String xmlStr;
|
||||
|
||||
@ApiModelProperty(value = "result")
|
||||
private Integer result;
|
||||
|
||||
@ApiModelProperty(value = "msg")
|
||||
private String msg;
|
||||
|
||||
@ApiModelProperty(value = "type")
|
||||
private Integer type;
|
||||
|
||||
@ApiModelProperty(value = "referenceIcdId")
|
||||
private String referenceIcdId;
|
||||
|
||||
/**
|
||||
* 分页查询实体
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public static class QueryParam extends BaseParam {
|
||||
@ApiModelProperty(value = "名称", required = true)
|
||||
@ApiModelProperty(value = "name", required = true)
|
||||
private String name;
|
||||
}
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public static class CreateParam extends PqIcdPathParam {
|
||||
@ApiModelProperty(value = "icdFile", required = true)
|
||||
private MultipartFile icdFile;
|
||||
}
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public static class ExternalCreateParam extends PqIcdPathParam {
|
||||
@ApiModelProperty(value = "id", required = true)
|
||||
@NotBlank(message = DetectionValidMessage.ID_NOT_BLANK)
|
||||
@Pattern(regexp = PatternRegex.SYSTEM_ID, message = DetectionValidMessage.ID_FORMAT_ERROR)
|
||||
private String id;
|
||||
|
||||
@ApiModelProperty(value = "name", required = true)
|
||||
@NotBlank(message = DetectionValidMessage.NAME_NOT_BLANK)
|
||||
@Pattern(regexp = PatternRegex.SCRIPT_NAME_REGEX, message = DetectionValidMessage.ICD_NAME_FORMAT_ERROR)
|
||||
private String name;
|
||||
|
||||
@ApiModelProperty(value = "icd", required = true)
|
||||
@NotBlank(message = IcdExternalValidMessage.ICD_FILE_NOT_BLANK)
|
||||
private String icd;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新参数
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public static class UpdateParam extends PqIcdPathParam {
|
||||
@@ -57,5 +81,12 @@ public class PqIcdPathParam {
|
||||
@NotBlank(message = DetectionValidMessage.ID_NOT_BLANK)
|
||||
@Pattern(regexp = PatternRegex.SYSTEM_ID, message = DetectionValidMessage.ID_FORMAT_ERROR)
|
||||
private String id;
|
||||
|
||||
@ApiModelProperty(value = "icdFile")
|
||||
private MultipartFile icdFile;
|
||||
}
|
||||
|
||||
private interface IcdExternalValidMessage {
|
||||
String ICD_FILE_NOT_BLANK = "ICD原始文件不能为空";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,11 +28,6 @@ public class PqIcdPath extends BaseEntity implements Serializable {
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* icd存储地址
|
||||
*/
|
||||
private String path;
|
||||
|
||||
/**
|
||||
* 状态:0-删除 1-正常
|
||||
*/
|
||||
@@ -46,19 +41,55 @@ public class PqIcdPath extends BaseEntity implements Serializable {
|
||||
/**
|
||||
* 角型接线时是否使用相别的指标来进行检测,0表示否,1表示是
|
||||
*/
|
||||
@TableField("use_phase_index")
|
||||
private Integer usePhaseIndex;
|
||||
|
||||
/**
|
||||
* 映射文件路径
|
||||
* 解析后的json字符串
|
||||
*/
|
||||
@TableField("Json_Str")
|
||||
private String jsonStr;
|
||||
|
||||
/**
|
||||
* 解析后的xml字符串
|
||||
*/
|
||||
@TableField("Xml_Str")
|
||||
private String xmlStr;
|
||||
|
||||
/**
|
||||
* ICD原始文件二进制
|
||||
*/
|
||||
@TableField("Icd")
|
||||
private byte[] icd;
|
||||
|
||||
/**
|
||||
* 结论
|
||||
*/
|
||||
@TableField("Result")
|
||||
private Integer result;
|
||||
|
||||
/**
|
||||
* 结论描述
|
||||
*/
|
||||
@TableField("Msg")
|
||||
private String msg;
|
||||
|
||||
/**
|
||||
* ICD类型
|
||||
*/
|
||||
@TableField("Type")
|
||||
private Integer type;
|
||||
|
||||
/**
|
||||
* 参照标准ICD
|
||||
*/
|
||||
@TableField("Reference_Icd_Id")
|
||||
private String referenceIcdId;
|
||||
|
||||
@TableField(exist = false)
|
||||
private FileVO mappingFile;
|
||||
private String referenceIcdName;
|
||||
|
||||
@Data
|
||||
public static class FileVO{
|
||||
private String name;
|
||||
|
||||
private String url;
|
||||
}
|
||||
@TableField(exist = false)
|
||||
private Boolean hasIcdFile;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.njcn.gather.icd.pojo.vo;
|
||||
|
||||
import com.alibaba.fastjson.annotation.JSONField;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* ICD JSON export item.
|
||||
*
|
||||
* @author caozehui
|
||||
* @date 2026-06-17
|
||||
*/
|
||||
@Data
|
||||
public class PqIcdExportJsonVO {
|
||||
private String id;
|
||||
|
||||
private String name;
|
||||
|
||||
@JSONField(serialize = false)
|
||||
private byte[] rawIcd;
|
||||
|
||||
private String icd;
|
||||
|
||||
private Integer angle;
|
||||
|
||||
private Integer usePhaseIndex;
|
||||
|
||||
private String jsonStr;
|
||||
|
||||
private String xmlStr;
|
||||
|
||||
private Integer result;
|
||||
|
||||
private String msg;
|
||||
|
||||
private Integer type;
|
||||
|
||||
private String referenceIcdId;
|
||||
}
|
||||
@@ -28,13 +28,22 @@ public interface IPqIcdPathService extends IService<PqIcdPath> {
|
||||
*/
|
||||
Page<PqIcdPath> listIcd(PqIcdPathParam.QueryParam param);
|
||||
|
||||
/**
|
||||
* 获取所有标准ICD
|
||||
*
|
||||
* @return 标准ICD列表
|
||||
*/
|
||||
List<PqIcdPath> listStandardIcd();
|
||||
|
||||
/**
|
||||
* 新增Icd
|
||||
*
|
||||
* @param param 新增Icd参数
|
||||
* @return 成功返回true,失败返回false
|
||||
*/
|
||||
boolean addIcd(PqIcdPathParam param);
|
||||
boolean addIcd(PqIcdPathParam.CreateParam param);
|
||||
|
||||
boolean addUpstreamIcd(List<PqIcdPathParam.ExternalCreateParam> param);
|
||||
|
||||
/**
|
||||
* 修改Icd
|
||||
@@ -52,6 +61,24 @@ public interface IPqIcdPathService extends IService<PqIcdPath> {
|
||||
*/
|
||||
boolean deleteIcd(List<String> ids);
|
||||
|
||||
/**
|
||||
* 根据id获取ICD详情
|
||||
*
|
||||
* @param id ICD id
|
||||
* @return ICD详情
|
||||
*/
|
||||
PqIcdPath getIcdById(String id);
|
||||
|
||||
/**
|
||||
* 导出ICD压缩包
|
||||
*
|
||||
* @param id ICD id
|
||||
* @return zip字节数组
|
||||
*/
|
||||
byte[] exportIcd(String id);
|
||||
|
||||
byte[] exportIcdJson(List<String> ids);
|
||||
|
||||
/**
|
||||
* 根据设备类型id获取Icd
|
||||
*
|
||||
|
||||
@@ -1,34 +1,42 @@
|
||||
package com.njcn.gather.icd.service.impl;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.ReUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.njcn.common.pojo.enums.common.DataStateEnum;
|
||||
import com.njcn.common.pojo.exception.BusinessException;
|
||||
import com.njcn.common.pojo.constant.PatternRegex;
|
||||
import com.njcn.gather.icd.mapper.PqIcdPathMapper;
|
||||
import com.njcn.gather.icd.pojo.enums.IcdResponseEnum;
|
||||
import com.njcn.gather.icd.pojo.param.PqIcdPathParam;
|
||||
import com.njcn.gather.icd.pojo.po.PqIcdPath;
|
||||
import com.njcn.gather.icd.pojo.vo.PqIcdExportJsonVO;
|
||||
import com.njcn.gather.pojo.constant.DetectionValidMessage;
|
||||
import com.njcn.gather.icd.service.IPqIcdPathService;
|
||||
import com.njcn.gather.pojo.enums.DetectionResponseEnum;
|
||||
import com.njcn.gather.report.pojo.enums.ReportResponseEnum;
|
||||
import com.njcn.gather.icd.service.support.IcdArchiveBuilder;
|
||||
import com.njcn.gather.icd.service.support.IcdPayloadAssembler;
|
||||
import com.njcn.web.factory.PageFactory;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.util.Base64;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author caozehui
|
||||
@@ -38,174 +46,165 @@ import java.util.List;
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class PqIcdPathServiceImpl extends ServiceImpl<PqIcdPathMapper, PqIcdPath> implements IPqIcdPathService {
|
||||
// 手动录入标准ICD
|
||||
private static final int TYPE_STANDARD = 1;
|
||||
// 手动录入非标准ICD
|
||||
private static final int TYPE_NON_STANDARD = 2;
|
||||
// 上游录入的标准ICD
|
||||
private static final int TYPE_UPSTREAM_STANDARD = 3;
|
||||
|
||||
// 上游录入的非标准ICD
|
||||
private static final int TYPE_UPSTREAM_NON_STANDARD = 4;
|
||||
private static final int RESULT_NO = 0;
|
||||
private static final int RESULT_YES = 1;
|
||||
|
||||
@Value("${icd.txt-dir}")
|
||||
private String icdTxtDir;
|
||||
|
||||
private final IcdPayloadAssembler icdPayloadAssembler;
|
||||
private final IcdArchiveBuilder icdArchiveBuilder;
|
||||
|
||||
@Override
|
||||
public List<PqIcdPath> listAll() {
|
||||
return this.lambdaQuery().eq(PqIcdPath::getState, DataStateEnum.ENABLE.getCode()).list();
|
||||
return this.lambdaQuery()
|
||||
.select(PqIcdPath::getId, PqIcdPath::getName, PqIcdPath::getType, PqIcdPath::getReferenceIcdId)
|
||||
.eq(PqIcdPath::getState, DataStateEnum.ENABLE.getCode())
|
||||
.orderByDesc(PqIcdPath::getCreateTime)
|
||||
.list();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PqIcdPath> listStandardIcd() {
|
||||
return this.baseMapper.selectStandardList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<PqIcdPath> listIcd(PqIcdPathParam.QueryParam param) {
|
||||
LambdaQueryWrapper<PqIcdPath> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(PqIcdPath::getState, DataStateEnum.ENABLE.getCode())
|
||||
.like(StrUtil.isNotBlank(param.getName()), PqIcdPath::getName, param.getName())
|
||||
.orderByDesc(PqIcdPath::getCreateTime);
|
||||
Page<PqIcdPath> page = this.page(new Page<>(PageFactory.getPageNum(param), PageFactory.getPageSize(param)), wrapper);
|
||||
String commInstallPath = this.getCommInstallPath();
|
||||
page.getRecords().forEach(pqIcdPath -> {
|
||||
PqIcdPath.FileVO fileVO = new PqIcdPath.FileVO();
|
||||
fileVO.setUrl(commInstallPath + "\\DeviceControl\\Config\\" + pqIcdPath.getName() + ".txt");
|
||||
fileVO.setName(pqIcdPath.getName() + ".txt");
|
||||
pqIcdPath.setMappingFile(fileVO);
|
||||
});
|
||||
Page<PqIcdPath> page = new Page<>(PageFactory.getPageNum(param), PageFactory.getPageSize(param));
|
||||
page.setRecords(this.baseMapper.selectPageList(page, StrUtil.trim(param.getName())));
|
||||
return page;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public boolean addIcd(PqIcdPathParam param) {
|
||||
param.setName(param.getName().trim());
|
||||
this.checkRepeat(param, false);
|
||||
PqIcdPath pqIcdPath = new PqIcdPath();
|
||||
BeanUtils.copyProperties(param, pqIcdPath);
|
||||
pqIcdPath.setState(DataStateEnum.ENABLE.getCode());
|
||||
|
||||
String commInstallPath = this.getCommInstallPath();
|
||||
System.out.println("commInstallPath = " + commInstallPath);
|
||||
long FILE_SIZE_LIMIT = 1 * 1024 * 1024;
|
||||
MultipartFile mappingFile = param.getMappingFile();
|
||||
|
||||
System.out.println("mappingFile = " + ObjectUtil.isNotNull(mappingFile) + " " + !mappingFile.isEmpty());
|
||||
if (ObjectUtil.isNotNull(mappingFile) && !mappingFile.isEmpty()) {
|
||||
String mappingFilename = mappingFile.getOriginalFilename();
|
||||
|
||||
if (!mappingFilename.endsWith(".txt")) {
|
||||
throw new BusinessException(IcdResponseEnum.FILE_TYPE_ERROR);
|
||||
}
|
||||
if (mappingFile.getSize() > FILE_SIZE_LIMIT) {
|
||||
throw new BusinessException(IcdResponseEnum.FILE_SIZE_ERROR);
|
||||
}
|
||||
|
||||
try {
|
||||
// 如果文件存在,则先删除
|
||||
String mappingFilePath = commInstallPath + "\\DeviceControl\\Config\\" + mappingFilename;
|
||||
System.out.println("mappingFilePath = " + mappingFilePath);
|
||||
Path path = Paths.get(mappingFilePath);
|
||||
File file = path.toFile();
|
||||
if (file.exists()) {
|
||||
file.delete();
|
||||
}
|
||||
|
||||
// 保存文件
|
||||
byte[] bytes = mappingFile.getBytes();
|
||||
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(new FileOutputStream(mappingFilePath));
|
||||
bufferedOutputStream.write(bytes);
|
||||
bufferedOutputStream.flush();
|
||||
|
||||
bufferedOutputStream.close();
|
||||
System.out.println("File saved successfully");
|
||||
} catch (IOException e) {
|
||||
throw new BusinessException(ReportResponseEnum.FILE_UPLOAD_FAILED);
|
||||
}
|
||||
} else {
|
||||
System.out.println("mappingFile is null or empty");
|
||||
throw new BusinessException(IcdResponseEnum.FILE_NOT_NULL);
|
||||
public boolean addIcd(PqIcdPathParam.CreateParam param) {
|
||||
validateBusinessFields(param, null);
|
||||
PqIcdPath pqIcdPath = icdPayloadAssembler.fromCreate(param);
|
||||
validateReferenceIcd(pqIcdPath.getType(), pqIcdPath.getReferenceIcdId(), null);
|
||||
boolean saved = this.save(pqIcdPath);
|
||||
if (saved) {
|
||||
writeIcdTextFile(resolveIcdTxtDir(), pqIcdPath.getName(), pqIcdPath.getJsonStr());
|
||||
}
|
||||
|
||||
this.executeRestartCmd(commInstallPath);
|
||||
|
||||
return this.save(pqIcdPath);
|
||||
return saved;
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行重启通讯服务脚本
|
||||
*
|
||||
* @param commInstallPath
|
||||
*/
|
||||
private void executeRestartCmd(String commInstallPath) {
|
||||
// 以管理员身份运行bat脚本
|
||||
String batFilePath = commInstallPath + "\\重启所有服务.bat";
|
||||
try {
|
||||
Runtime.getRuntime().exec(batFilePath);
|
||||
} catch (IOException e) {
|
||||
log.error("重启通讯服务失败", e);
|
||||
@Override
|
||||
@Transactional
|
||||
public boolean addUpstreamIcd(List<PqIcdPathParam.ExternalCreateParam> param) {
|
||||
if (CollUtil.isEmpty(param)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private String getCommInstallPath() {
|
||||
String workDir = System.getProperty("user.dir");
|
||||
// String workDir = "D:\\program\\CN_Gather";
|
||||
// 获取映射文件存放文件夹
|
||||
String dirPath = workDir + "\\9100";
|
||||
int index = workDir.indexOf("\\resources\\extraResources\\java");
|
||||
if (index != -1) {
|
||||
dirPath = workDir.substring(0, workDir.indexOf("\\resources\\extraResources\\java")) + "\\9100";
|
||||
validateExternalItems(param);
|
||||
Set<String> requestStandardIds = collectRequestStandardIds(param);
|
||||
Path directory = resolveIcdTxtDir();
|
||||
for (PqIcdPathParam.ExternalCreateParam item : param) {
|
||||
validateBusinessFields(item, null);
|
||||
PqIcdPath current = this.baseMapper.selectById(item.getId());
|
||||
if (current != null && !ObjectUtil.equals(current.getState(), DataStateEnum.ENABLE.getCode())) {
|
||||
throw new BusinessException(IcdResponseEnum.EXTERNAL_ICD_DELETED_NOT_ALLOW_OVERWRITE);
|
||||
}
|
||||
PqIcdPath pqIcdPath = icdPayloadAssembler.fromExternalCreate(item, item.getType(), item.getAngle(), item.getUsePhaseIndex());
|
||||
String currentId = current == null ? null : current.getId();
|
||||
String oldName = current == null ? null : current.getName();
|
||||
validateExternalReferenceIcd(pqIcdPath.getType(), pqIcdPath.getReferenceIcdId(), currentId, requestStandardIds);
|
||||
boolean saved = current == null ? this.save(pqIcdPath) : this.updateById(pqIcdPath);
|
||||
if (!saved) {
|
||||
throw new BusinessException(IcdResponseEnum.EXTERNAL_ICD_IMPORT_FAILED);
|
||||
}
|
||||
if (current == null) {
|
||||
writeIcdTextFile(directory, pqIcdPath.getName(), pqIcdPath.getJsonStr());
|
||||
} else {
|
||||
syncUpdatedIcdTextFile(directory, oldName, pqIcdPath.getName(), pqIcdPath.getJsonStr());
|
||||
}
|
||||
}
|
||||
return dirPath;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public boolean updateIcd(PqIcdPathParam.UpdateParam param) {
|
||||
param.setName(param.getName().trim());
|
||||
this.checkRepeat(param, true);
|
||||
PqIcdPath pqIcdPath = new PqIcdPath();
|
||||
BeanUtils.copyProperties(param, pqIcdPath);
|
||||
|
||||
String commInstallPath = this.getCommInstallPath();
|
||||
long FILE_SIZE_LIMIT = 1 * 1024 * 1024;
|
||||
MultipartFile mappingFile = param.getMappingFile();
|
||||
if (ObjectUtil.isNotNull(mappingFile) && !mappingFile.isEmpty()) {
|
||||
String mappingFilename = mappingFile.getOriginalFilename();
|
||||
|
||||
if (!mappingFilename.endsWith(".txt")) {
|
||||
throw new BusinessException(IcdResponseEnum.FILE_TYPE_ERROR);
|
||||
}
|
||||
if (mappingFile.getSize() > FILE_SIZE_LIMIT) {
|
||||
throw new BusinessException(IcdResponseEnum.FILE_SIZE_ERROR);
|
||||
}
|
||||
|
||||
try {
|
||||
// 如果文件存在,则先删除
|
||||
String mappingFilePath = commInstallPath + "\\DeviceControl\\Config\\" + mappingFilename;
|
||||
Path path = Paths.get(mappingFilePath);
|
||||
File file = path.toFile();
|
||||
if (file.exists()) {
|
||||
file.delete();
|
||||
}
|
||||
|
||||
// 保存文件
|
||||
byte[] bytes = mappingFile.getBytes();
|
||||
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(new FileOutputStream(mappingFilePath));
|
||||
bufferedOutputStream.write(bytes);
|
||||
bufferedOutputStream.flush();
|
||||
|
||||
bufferedOutputStream.close();
|
||||
} catch (IOException e) {
|
||||
throw new BusinessException(ReportResponseEnum.FILE_UPLOAD_FAILED);
|
||||
}
|
||||
PqIcdPath current = getActiveEntity(param.getId());
|
||||
String oldName = current.getName();
|
||||
validateBusinessFields(param, current.getId());
|
||||
PqIcdPath pqIcdPath = icdPayloadAssembler.merge(current, param);
|
||||
validateReferenceIcd(pqIcdPath.getType(), pqIcdPath.getReferenceIcdId(), current.getId());
|
||||
boolean updated = this.updateById(pqIcdPath);
|
||||
if (updated) {
|
||||
syncUpdatedIcdTextFile(resolveIcdTxtDir(), oldName, pqIcdPath.getName(), pqIcdPath.getJsonStr());
|
||||
}
|
||||
|
||||
this.executeRestartCmd(commInstallPath);
|
||||
|
||||
return this.updateById(pqIcdPath);
|
||||
return updated;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public boolean deleteIcd(List<String> ids) {
|
||||
List<PqIcdPath> pqIcdPaths = this.listByIds(ids);
|
||||
String commInstallPath = this.getCommInstallPath();
|
||||
pqIcdPaths.forEach(pqIcdPath -> {
|
||||
String mappingFilePath = commInstallPath + "\\DeviceControl\\Config\\" + pqIcdPath.getName() + ".txt";
|
||||
Path path = Paths.get(mappingFilePath);
|
||||
File file = path.toFile();
|
||||
if (file.exists()) {
|
||||
file.delete();
|
||||
if (CollUtil.isEmpty(ids)) {
|
||||
return true;
|
||||
}
|
||||
List<PqIcdPath> pqIcdPaths = this.baseMapper.selectByIdsWithoutBlob(ids);
|
||||
List<String> standardIds = pqIcdPaths.stream()
|
||||
.filter(pqIcdPath -> ObjectUtil.equals(pqIcdPath.getType(), TYPE_STANDARD))
|
||||
.map(PqIcdPath::getId)
|
||||
.collect(Collectors.toList());
|
||||
if (!standardIds.isEmpty()) {
|
||||
Long count = this.baseMapper.countActiveReferencesToStandards(standardIds);
|
||||
if (ObjectUtil.isNotNull(count) && count > 0) {
|
||||
throw new BusinessException(IcdResponseEnum.STANDARD_ICD_IN_USE);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return this.lambdaUpdate().in(PqIcdPath::getId, ids).set(PqIcdPath::getState, DataStateEnum.DELETED.getCode()).update();
|
||||
boolean deleted = this.lambdaUpdate().in(PqIcdPath::getId, ids).set(PqIcdPath::getState, DataStateEnum.DELETED.getCode()).update();
|
||||
if (deleted) {
|
||||
Path directory = resolveIcdTxtDir();
|
||||
for (PqIcdPath pqIcdPath : pqIcdPaths) {
|
||||
deleteIcdTextFile(directory, pqIcdPath.getName());
|
||||
}
|
||||
}
|
||||
return deleted;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PqIcdPath getIcdById(String id) {
|
||||
PqIcdPath detail = this.baseMapper.selectDetailById(id);
|
||||
if (ObjectUtil.isNull(detail)) {
|
||||
throw new BusinessException(IcdResponseEnum.ICD_NOT_FOUND);
|
||||
}
|
||||
return detail;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] exportIcd(String id) {
|
||||
PqIcdPath detail = this.baseMapper.selectExportById(id);
|
||||
if (ObjectUtil.isNull(detail)) {
|
||||
throw new BusinessException(IcdResponseEnum.ICD_NOT_FOUND);
|
||||
}
|
||||
return icdArchiveBuilder.build(detail);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] exportIcdJson(List<String> ids) {
|
||||
if (CollUtil.isEmpty(ids)) {
|
||||
throw new BusinessException(IcdResponseEnum.ICD_NOT_FOUND);
|
||||
}
|
||||
List<PqIcdExportJsonVO> details = this.baseMapper.selectExportJsonByIds(ids);
|
||||
if (CollUtil.isEmpty(details)) {
|
||||
throw new BusinessException(IcdResponseEnum.ICD_NOT_FOUND);
|
||||
}
|
||||
for (PqIcdExportJsonVO detail : details) {
|
||||
detail.setIcd(encodeIcdFile(detail.getRawIcd()));
|
||||
normalizeExportDetail(detail);
|
||||
}
|
||||
return JSON.toJSONString(details).getBytes(StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -213,18 +212,188 @@ public class PqIcdPathServiceImpl extends ServiceImpl<PqIcdPathMapper, PqIcdPath
|
||||
return this.baseMapper.selectIcdByDevType(devTypeId);
|
||||
}
|
||||
|
||||
private void checkRepeat(PqIcdPathParam param, boolean isExcludeSelf) {
|
||||
LambdaQueryWrapper<PqIcdPath> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(PqIcdPath::getName, param.getName())
|
||||
.eq(PqIcdPath::getState, DataStateEnum.ENABLE.getCode());
|
||||
if (isExcludeSelf) {
|
||||
if (param instanceof PqIcdPathParam.UpdateParam) {
|
||||
wrapper.ne(PqIcdPath::getId, ((PqIcdPathParam.UpdateParam) param).getId());
|
||||
}
|
||||
private void validateBusinessFields(PqIcdPathParam param, String currentId) {
|
||||
if (ObjectUtil.isNull(param.getResult())) {
|
||||
throw new BusinessException(IcdResponseEnum.RESULT_NOT_NULL);
|
||||
}
|
||||
int count = this.count(wrapper);
|
||||
if (count > 0) {
|
||||
throw new BusinessException(DetectionResponseEnum.ICD_PATH_NAME_REPEAT);
|
||||
if (ObjectUtil.equals(param.getResult(), RESULT_NO) && StrUtil.isBlank(param.getMsg())) {
|
||||
throw new BusinessException(IcdResponseEnum.MSG_NOT_BLANK);
|
||||
}
|
||||
if (ObjectUtil.isNull(param.getType())) {
|
||||
throw new BusinessException(IcdResponseEnum.TYPE_NOT_NULL);
|
||||
}
|
||||
if (!ObjectUtil.equals(param.getType(), TYPE_STANDARD)
|
||||
&& !ObjectUtil.equals(param.getType(), TYPE_NON_STANDARD)
|
||||
&& !ObjectUtil.equals(param.getType(), TYPE_UPSTREAM_STANDARD)
|
||||
&& !ObjectUtil.equals(param.getType(), TYPE_UPSTREAM_NON_STANDARD)) {
|
||||
throw new BusinessException(IcdResponseEnum.TYPE_VALUE_ERROR);
|
||||
}
|
||||
if (requiresReferenceIcd(param.getType()) && StrUtil.isBlank(param.getReferenceIcdId())) {
|
||||
throw new BusinessException(IcdResponseEnum.REFERENCE_ICD_NOT_BLANK);
|
||||
}
|
||||
if (StrUtil.isNotBlank(currentId) && StrUtil.equals(currentId, StrUtil.trim(param.getReferenceIcdId()))) {
|
||||
throw new BusinessException(IcdResponseEnum.REFERENCE_ICD_INVALID);
|
||||
}
|
||||
}
|
||||
|
||||
private void validateReferenceIcd(Integer type, String referenceIcdId, String currentId) {
|
||||
if (!requiresReferenceIcd(type)) {
|
||||
return;
|
||||
}
|
||||
if (StrUtil.isNotBlank(currentId) && StrUtil.equals(currentId, referenceIcdId)) {
|
||||
throw new BusinessException(IcdResponseEnum.REFERENCE_ICD_INVALID);
|
||||
}
|
||||
long count = this.lambdaQuery()
|
||||
.eq(PqIcdPath::getId, referenceIcdId)
|
||||
.in(PqIcdPath::getType, TYPE_STANDARD, TYPE_UPSTREAM_STANDARD)
|
||||
.eq(PqIcdPath::getState, DataStateEnum.ENABLE.getCode())
|
||||
.count();
|
||||
if (count <= 0L) {
|
||||
throw new BusinessException(IcdResponseEnum.REFERENCE_ICD_INVALID);
|
||||
}
|
||||
}
|
||||
|
||||
private void validateExternalReferenceIcd(Integer type, String referenceIcdId, String currentId, Set<String> requestStandardIds) {
|
||||
if (!requiresReferenceIcd(type)) {
|
||||
return;
|
||||
}
|
||||
String trimmedReferenceId = StrUtil.trim(referenceIcdId);
|
||||
if (StrUtil.isNotBlank(currentId) && StrUtil.equals(currentId, trimmedReferenceId)) {
|
||||
throw new BusinessException(IcdResponseEnum.REFERENCE_ICD_INVALID);
|
||||
}
|
||||
if (StrUtil.isNotBlank(trimmedReferenceId) && requestStandardIds.contains(trimmedReferenceId)) {
|
||||
return;
|
||||
}
|
||||
validateReferenceIcd(type, trimmedReferenceId, currentId);
|
||||
}
|
||||
|
||||
private Set<String> collectRequestStandardIds(List<PqIcdPathParam.ExternalCreateParam> params) {
|
||||
Set<String> ids = new HashSet<>();
|
||||
for (PqIcdPathParam.ExternalCreateParam param : params) {
|
||||
if (isStandardType(param.getType()) && StrUtil.isNotBlank(param.getId())) {
|
||||
ids.add(StrUtil.trim(param.getId()));
|
||||
}
|
||||
}
|
||||
return ids;
|
||||
}
|
||||
|
||||
private void validateExternalItems(List<PqIcdPathParam.ExternalCreateParam> params) {
|
||||
Set<String> ids = new HashSet<>();
|
||||
for (PqIcdPathParam.ExternalCreateParam param : params) {
|
||||
if (param == null) {
|
||||
throw new BusinessException(IcdResponseEnum.EXTERNAL_ICD_ITEM_NOT_NULL);
|
||||
}
|
||||
validateExternalItem(param);
|
||||
String trimmedId = StrUtil.trim(param.getId());
|
||||
if (!ids.add(trimmedId)) {
|
||||
throw new BusinessException(IcdResponseEnum.EXTERNAL_ICD_ID_REPEAT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void validateExternalItem(PqIcdPathParam.ExternalCreateParam param) {
|
||||
if (StrUtil.isBlank(param.getId())) {
|
||||
throw new BusinessException(DetectionValidMessage.ID_NOT_BLANK);
|
||||
}
|
||||
if (!ReUtil.isMatch(PatternRegex.SYSTEM_ID, StrUtil.trim(param.getId()))) {
|
||||
throw new BusinessException(DetectionValidMessage.ID_FORMAT_ERROR);
|
||||
}
|
||||
if (StrUtil.isBlank(param.getName())) {
|
||||
throw new BusinessException(DetectionValidMessage.NAME_NOT_BLANK);
|
||||
}
|
||||
if (!ReUtil.isMatch(PatternRegex.SCRIPT_NAME_REGEX, StrUtil.trim(param.getName()))) {
|
||||
throw new BusinessException(DetectionValidMessage.ICD_NAME_FORMAT_ERROR);
|
||||
}
|
||||
if (StrUtil.isBlank(param.getIcd())) {
|
||||
throw new BusinessException(IcdResponseEnum.ICD_FILE_NOT_NULL);
|
||||
}
|
||||
if (ObjectUtil.isNull(param.getAngle())) {
|
||||
throw new BusinessException(IcdResponseEnum.ANGLE_NOT_NULL);
|
||||
}
|
||||
if (!ObjectUtil.equals(param.getAngle(), 0) && !ObjectUtil.equals(param.getAngle(), 1)) {
|
||||
throw new BusinessException(IcdResponseEnum.ANGLE_VALUE_ERROR);
|
||||
}
|
||||
if (ObjectUtil.isNull(param.getUsePhaseIndex())) {
|
||||
throw new BusinessException(IcdResponseEnum.USE_PHASE_INDEX_NOT_NULL);
|
||||
}
|
||||
if (!ObjectUtil.equals(param.getUsePhaseIndex(), 0) && !ObjectUtil.equals(param.getUsePhaseIndex(), 1)) {
|
||||
throw new BusinessException(IcdResponseEnum.USE_PHASE_INDEX_VALUE_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean requiresReferenceIcd(Integer type) {
|
||||
return ObjectUtil.equals(type, TYPE_NON_STANDARD) || ObjectUtil.equals(type, TYPE_UPSTREAM_NON_STANDARD);
|
||||
}
|
||||
|
||||
private boolean isStandardType(Integer type) {
|
||||
return ObjectUtil.equals(type, TYPE_STANDARD) || ObjectUtil.equals(type, TYPE_UPSTREAM_STANDARD);
|
||||
}
|
||||
|
||||
private PqIcdPath getActiveEntity(String id) {
|
||||
PqIcdPath current = this.lambdaQuery()
|
||||
.eq(PqIcdPath::getId, id)
|
||||
.eq(PqIcdPath::getState, DataStateEnum.ENABLE.getCode())
|
||||
.one();
|
||||
if (ObjectUtil.isNull(current)) {
|
||||
throw new BusinessException(IcdResponseEnum.ICD_NOT_FOUND);
|
||||
}
|
||||
return current;
|
||||
}
|
||||
|
||||
Path resolveIcdTxtDir() {
|
||||
if (StrUtil.isBlank(icdTxtDir)) {
|
||||
throw new BusinessException(IcdResponseEnum.ICD_TXT_SYNC_FAILED);
|
||||
}
|
||||
return Paths.get(icdTxtDir.trim());
|
||||
}
|
||||
|
||||
void writeIcdTextFile(Path directory, String name, String jsonStr) {
|
||||
try {
|
||||
Files.createDirectories(directory);
|
||||
Files.write(resolveTxtFile(directory, name), StrUtil.nullToDefault(jsonStr, "").getBytes(StandardCharsets.UTF_8),
|
||||
StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.WRITE);
|
||||
} catch (IOException ex) {
|
||||
log.error("sync icd txt file failed, name={}", name, ex);
|
||||
throw new BusinessException(IcdResponseEnum.ICD_TXT_SYNC_FAILED);
|
||||
}
|
||||
}
|
||||
|
||||
void syncUpdatedIcdTextFile(Path directory, String oldName, String newName, String jsonStr) {
|
||||
if (StrUtil.isNotBlank(oldName) && !StrUtil.equals(oldName, newName)) {
|
||||
deleteIcdTextFile(directory, oldName);
|
||||
}
|
||||
writeIcdTextFile(directory, newName, jsonStr);
|
||||
}
|
||||
|
||||
void deleteIcdTextFile(Path directory, String name) {
|
||||
try {
|
||||
Files.deleteIfExists(resolveTxtFile(directory, name));
|
||||
} catch (IOException ex) {
|
||||
log.error("delete icd txt file failed, name={}", name, ex);
|
||||
throw new BusinessException(IcdResponseEnum.ICD_TXT_SYNC_FAILED);
|
||||
}
|
||||
}
|
||||
|
||||
private Path resolveTxtFile(Path directory, String name) {
|
||||
return directory.resolve(name + ".txt");
|
||||
}
|
||||
|
||||
private String encodeIcdFile(byte[] rawValue) {
|
||||
if (rawValue == null || rawValue.length == 0) {
|
||||
return null;
|
||||
}
|
||||
return Base64.getEncoder().encodeToString(rawValue);
|
||||
}
|
||||
|
||||
private void normalizeExportDetail(PqIcdExportJsonVO detail) {
|
||||
detail.setRawIcd(null);
|
||||
detail.setAngle(detail.getAngle() == null ? 0 : detail.getAngle());
|
||||
detail.setUsePhaseIndex(detail.getUsePhaseIndex() == null ? 1 : detail.getUsePhaseIndex());
|
||||
if (isStandardType(detail.getType())) {
|
||||
detail.setType(TYPE_UPSTREAM_STANDARD);
|
||||
detail.setReferenceIcdId(null);
|
||||
return;
|
||||
}
|
||||
detail.setType(TYPE_UPSTREAM_NON_STANDARD);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
package com.njcn.gather.icd.service.support;
|
||||
|
||||
import com.njcn.common.pojo.exception.BusinessException;
|
||||
import com.njcn.gather.icd.pojo.enums.IcdResponseEnum;
|
||||
import com.njcn.gather.icd.pojo.po.PqIcdPath;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
|
||||
@Component
|
||||
public class IcdArchiveBuilder {
|
||||
|
||||
public byte[] build(PqIcdPath item) {
|
||||
if (item == null || item.getIcd() == null || item.getIcd().length == 0) {
|
||||
throw new BusinessException(IcdResponseEnum.ICD_FILE_NOT_FOUND);
|
||||
}
|
||||
try (ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
ZipOutputStream zos = new ZipOutputStream(bos, StandardCharsets.UTF_8)) {
|
||||
writeTextEntry(zos, item.getName() + ".json", item.getJsonStr());
|
||||
writeTextEntry(zos, item.getName() + ".xml", item.getXmlStr());
|
||||
writeBinaryEntry(zos, item.getName() + ".icd", item.getIcd());
|
||||
zos.finish();
|
||||
return bos.toByteArray();
|
||||
} catch (IOException ex) {
|
||||
throw new BusinessException(IcdResponseEnum.ICD_EXPORT_FAILED);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 向ZIP流中写入文本条目
|
||||
*
|
||||
* @param zos ZIP输出流
|
||||
* @param name 条目名称(文件路径)
|
||||
* @param value 文本内容,如果为null则写入空字节数组
|
||||
* @throws IOException 写入异常
|
||||
*/
|
||||
private void writeTextEntry(ZipOutputStream zos, String name, String value) throws IOException {
|
||||
// 将文本转换为UTF-8编码的字节数组,null值转换为空数组
|
||||
writeBinaryEntry(zos, name, value == null ? new byte[0] : value.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
/**
|
||||
* 向ZIP流中写入二进制条目
|
||||
*
|
||||
* @param zos ZIP输出流
|
||||
* @param name 条目名称(文件路径)
|
||||
* @param data 二进制数据
|
||||
* @throws IOException 写入异常
|
||||
*/
|
||||
private void writeBinaryEntry(ZipOutputStream zos, String name, byte[] data) throws IOException {
|
||||
// 创建新的ZIP条目并设置文件名
|
||||
zos.putNextEntry(new ZipEntry(name));
|
||||
// 写入二进制数据
|
||||
zos.write(data);
|
||||
// 关闭当前条目,准备写入下一个条目
|
||||
zos.closeEntry();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
package com.njcn.gather.icd.service.support;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.njcn.common.pojo.enums.common.DataStateEnum;
|
||||
import com.njcn.common.pojo.exception.BusinessException;
|
||||
import com.njcn.gather.icd.pojo.enums.IcdResponseEnum;
|
||||
import com.njcn.gather.icd.pojo.param.PqIcdPathParam;
|
||||
import com.njcn.gather.icd.pojo.po.PqIcdPath;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class IcdPayloadAssembler {
|
||||
|
||||
private final IcdPayloadValidator validator;
|
||||
|
||||
public PqIcdPath fromCreate(PqIcdPathParam.CreateParam param) {
|
||||
MultipartFile file = validator.validateIcdFile(param.getIcdFile(), true);
|
||||
validator.validateJson(param.getJsonStr());
|
||||
validator.validateXml(param.getXmlStr());
|
||||
|
||||
PqIcdPath entity = new PqIcdPath();
|
||||
applyCommonFields(entity, param);
|
||||
entity.setName(validator.extractName(file.getOriginalFilename()));
|
||||
entity.setIcd(getBytes(file));
|
||||
entity.setState(DataStateEnum.ENABLE.getCode());
|
||||
return entity;
|
||||
}
|
||||
|
||||
public PqIcdPath fromExternalCreate(PqIcdPathParam.ExternalCreateParam param, int type, int angle, int usePhaseIndex) {
|
||||
byte[] fileBytes = validator.decodeExternalIcdFile(param.getIcd());
|
||||
validator.validateJson(param.getJsonStr());
|
||||
validator.validateXml(param.getXmlStr());
|
||||
|
||||
PqIcdPath entity = new PqIcdPath();
|
||||
entity.setId(normalizeNullableText(param.getId()));
|
||||
param.setType(type);
|
||||
param.setAngle(angle);
|
||||
param.setUsePhaseIndex(usePhaseIndex);
|
||||
applyCommonFields(entity, param);
|
||||
entity.setName(normalizeNullableText(param.getName()));
|
||||
entity.setIcd(fileBytes);
|
||||
entity.setState(DataStateEnum.ENABLE.getCode());
|
||||
return entity;
|
||||
}
|
||||
|
||||
public PqIcdPath merge(PqIcdPath current, PqIcdPathParam.UpdateParam param) {
|
||||
MultipartFile file = validator.validateIcdFile(param.getIcdFile(), false);
|
||||
validator.validateJson(param.getJsonStr());
|
||||
validator.validateXml(param.getXmlStr());
|
||||
|
||||
applyCommonFields(current, param);
|
||||
if (file != null && !file.isEmpty()) {
|
||||
current.setName(validator.extractName(file.getOriginalFilename()));
|
||||
current.setIcd(getBytes(file));
|
||||
}
|
||||
return current;
|
||||
}
|
||||
|
||||
private void applyCommonFields(PqIcdPath entity, PqIcdPathParam param) {
|
||||
entity.setAngle(param.getAngle());
|
||||
entity.setUsePhaseIndex(param.getUsePhaseIndex());
|
||||
entity.setJsonStr(param.getJsonStr());
|
||||
entity.setXmlStr(param.getXmlStr());
|
||||
entity.setResult(param.getResult());
|
||||
entity.setMsg(param.getResult() != null && param.getResult() == 1 ? null : normalizeNullableText(param.getMsg()));
|
||||
entity.setType(param.getType());
|
||||
entity.setReferenceIcdId(requiresReferenceIcd(param.getType()) ? normalizeNullableText(param.getReferenceIcdId()) : null);
|
||||
}
|
||||
|
||||
private String normalizeNullableText(String value) {
|
||||
return StrUtil.emptyToNull(StrUtil.trim(value));
|
||||
}
|
||||
|
||||
private boolean requiresReferenceIcd(Integer type) {
|
||||
return type != null && (type == 2 || type == 4);
|
||||
}
|
||||
|
||||
private byte[] getBytes(MultipartFile file) {
|
||||
try {
|
||||
return file.getBytes();
|
||||
} catch (IOException ex) {
|
||||
throw new BusinessException(IcdResponseEnum.ICD_FILE_READ_ERROR);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package com.njcn.gather.icd.service.support;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.njcn.common.pojo.exception.BusinessException;
|
||||
import com.njcn.gather.icd.pojo.enums.IcdResponseEnum;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import org.xml.sax.InputSource;
|
||||
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import java.io.StringReader;
|
||||
import java.util.Base64;
|
||||
import java.util.Locale;
|
||||
|
||||
@Component
|
||||
public class IcdPayloadValidator {
|
||||
|
||||
public MultipartFile validateIcdFile(MultipartFile file, boolean required) {
|
||||
if ((file == null || file.isEmpty()) && required) {
|
||||
throw new BusinessException(IcdResponseEnum.ICD_FILE_NOT_NULL);
|
||||
}
|
||||
if (file == null || file.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
String originalName = file.getOriginalFilename();
|
||||
if (originalName == null || !originalName.toLowerCase(Locale.ROOT).endsWith(".icd")) {
|
||||
throw new BusinessException(IcdResponseEnum.ICD_FILE_TYPE_ERROR);
|
||||
}
|
||||
return file;
|
||||
}
|
||||
|
||||
public String extractName(String originalName) {
|
||||
int suffixIndex = originalName.toLowerCase(Locale.ROOT).lastIndexOf(".icd");
|
||||
return suffixIndex >= 0 ? originalName.substring(0, suffixIndex) : originalName;
|
||||
}
|
||||
|
||||
public byte[] decodeExternalIcdFile(String base64Content) {
|
||||
if (base64Content == null || base64Content.trim().isEmpty()) {
|
||||
throw new BusinessException(IcdResponseEnum.ICD_FILE_NOT_NULL);
|
||||
}
|
||||
try {
|
||||
return Base64.getDecoder().decode(base64Content.trim());
|
||||
} catch (IllegalArgumentException ex) {
|
||||
throw new BusinessException(IcdResponseEnum.ICD_FILE_READ_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
public void validateJson(String jsonStr) {
|
||||
try {
|
||||
JSON.parse(jsonStr);
|
||||
} catch (Exception ex) {
|
||||
throw new BusinessException(IcdResponseEnum.JSON_FORMAT_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
public void validateXml(String xmlStr) {
|
||||
try {
|
||||
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
|
||||
// 禁用 DOCTYPE 声明
|
||||
factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
|
||||
factory.newDocumentBuilder().parse(new InputSource(new StringReader(xmlStr)));
|
||||
} catch (Exception ex) {
|
||||
throw new BusinessException(IcdResponseEnum.XML_FORMAT_ERROR);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -29,13 +29,4 @@ public interface AdPlanMapper extends MPJBaseMapper<AdPlan> {
|
||||
* @return
|
||||
*/
|
||||
PqReport getPqReportById(String id);
|
||||
|
||||
/**
|
||||
* 获取所有报告模板名称
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
List<String> listAllReportTemplateName();
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.njcn.gather.plan.mapper.AdPlanMapper">
|
||||
|
||||
|
||||
<select id="getReportIdByNameAndVersion" resultType="java.lang.String">
|
||||
SELECT id
|
||||
FROM pq_report
|
||||
@@ -17,10 +16,4 @@
|
||||
WHERE id = #{id}
|
||||
and state = 1
|
||||
</select>
|
||||
<select id="listAllReportTemplateName" resultType="java.lang.String">
|
||||
SELECT concat(name, '_', version) as name
|
||||
FROM pq_report
|
||||
WHERE state = 1
|
||||
</select>
|
||||
</mapper>
|
||||
|
||||
|
||||
@@ -40,6 +40,9 @@ public class PqDevCheckHistory {
|
||||
@TableField("Result")
|
||||
private Integer result;
|
||||
|
||||
/**
|
||||
* 检测结束时间
|
||||
*/
|
||||
@TableField("Check_Time")
|
||||
private LocalDateTime checkTime;
|
||||
|
||||
|
||||
@@ -1906,7 +1906,9 @@ public class AdPlanServiceImpl extends ServiceImpl<AdPlanMapper, AdPlan> impleme
|
||||
pullDowns.add(pullDown);
|
||||
|
||||
// 报告模板
|
||||
List<String> strings = this.baseMapper.listAllReportTemplateName();
|
||||
List<String> strings = SpringUtil.getBean(IPqReportService.class).listOptions(patternId).stream()
|
||||
.map(option -> option.getDisplayName())
|
||||
.collect(Collectors.toList());
|
||||
pullDown = new PullDown();
|
||||
pullDown.setFirstCol(8);
|
||||
pullDown.setLastCol(8);
|
||||
|
||||
@@ -108,7 +108,7 @@ public class PqDevCheckHistoryServiceImpl extends ServiceImpl<PqDevCheckHistoryM
|
||||
history.setManufacturer(device.getManufacturer());
|
||||
history.setItemName(itemInfo.getItemName());
|
||||
history.setResult(hasUnqualified ? 0 : 1);
|
||||
history.setCheckTime(ObjectUtil.isNotNull(device.getCheckTime()) ? device.getCheckTime() : now);
|
||||
history.setCheckTime(ObjectUtil.isNotNull(device.getCheckEndTime()) ? device.getCheckEndTime() : now);
|
||||
history.setState(DataStateEnum.ENABLE.getCode());
|
||||
history.setUpdateBy(device.getCheckBy());
|
||||
history.setUpdateTime(now);
|
||||
|
||||
@@ -75,7 +75,8 @@ public enum DetectionResponseEnum {
|
||||
PLAN_REPEATED_IN_SAME_LEVEL("A02096", "该父计划下存在同名的子计划"),
|
||||
PLEASE_UNASSIGN_STANDARD_DEV("A02097", "存在已分配给子计划的标准设备,请先解除分配"),
|
||||
PLEASE_UNASSIGN_DEVICE("A02098", "存在已分配给计划的被检设备,请先解除分配"),
|
||||
DEV_IP_PORT_EXIST("A02099", "存在重复被检设备");
|
||||
DEV_IP_PORT_EXIST("A02099", "存在重复被检设备"),
|
||||
PQDIF_NOT_EXIST("A02100", "PQDIF不存在");
|
||||
|
||||
private final String code;
|
||||
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
package com.njcn.gather.pqdif.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.njcn.common.pojo.annotation.OperateInfo;
|
||||
import com.njcn.common.pojo.constant.OperateType;
|
||||
import com.njcn.common.pojo.enums.common.LogEnum;
|
||||
import com.njcn.common.pojo.enums.response.CommonResponseEnum;
|
||||
import com.njcn.common.pojo.exception.BusinessException;
|
||||
import com.njcn.common.pojo.response.HttpResult;
|
||||
import com.njcn.common.utils.LogUtil;
|
||||
import com.njcn.gather.pqdif.pojo.enums.PqdifResponseEnum;
|
||||
import com.njcn.gather.pqdif.pojo.param.PqPqdifPathParam;
|
||||
import com.njcn.gather.pqdif.pojo.po.PqPqdifPath;
|
||||
import com.njcn.gather.pqdif.service.IPqPqdifPathService;
|
||||
import com.njcn.gather.pqdif.service.support.PqdifImportParser;
|
||||
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.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 org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.List;
|
||||
|
||||
@Slf4j
|
||||
@Api(tags = "PQDIF管理")
|
||||
@RestController
|
||||
@RequestMapping("/pqdif")
|
||||
@RequiredArgsConstructor
|
||||
public class PqPqdifPathController extends BaseController {
|
||||
private final IPqPqdifPathService pqPqdifPathService;
|
||||
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
|
||||
@GetMapping("/listAll")
|
||||
@ApiOperation("获取全部PQDIF")
|
||||
public HttpResult<List<PqPqdifPath>> listAll() {
|
||||
String methodDescribe = getMethodDescribe("listAll");
|
||||
List<PqPqdifPath> result = pqPqdifPathService.listAll();
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe);
|
||||
}
|
||||
|
||||
@OperateInfo
|
||||
@PostMapping("/list")
|
||||
@ApiOperation("分页查询PQDIF")
|
||||
@ApiImplicitParam(name = "param", value = "查询参数", required = true)
|
||||
public HttpResult<Page<PqPqdifPath>> list(@RequestBody @Validated PqPqdifPathParam.QueryParam param) {
|
||||
String methodDescribe = getMethodDescribe("list");
|
||||
LogUtil.njcnDebug(log, "{},查询数据为:{}", methodDescribe, param);
|
||||
Page<PqPqdifPath> result = pqPqdifPathService.listPqdif(param);
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe);
|
||||
}
|
||||
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON, operateType = OperateType.ADD)
|
||||
@PostMapping("/import/json")
|
||||
@ApiOperation("导入PQDIF json")
|
||||
@ApiImplicitParam(name = "file", value = "json文件", required = true)
|
||||
public HttpResult<Boolean> importJson(@RequestParam("file") MultipartFile file) {
|
||||
String methodDescribe = getMethodDescribe("importJson");
|
||||
LogUtil.njcnDebug(log, "{},导入文件为:{}", methodDescribe, file == null ? null : file.getOriginalFilename());
|
||||
List<PqPqdifPathParam.ImportItem> items = parseImportItems(file);
|
||||
boolean result = pqPqdifPathService.importPqdif(items);
|
||||
if (result) {
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, true, methodDescribe);
|
||||
}
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.FAIL, false, methodDescribe);
|
||||
}
|
||||
|
||||
private List<PqPqdifPathParam.ImportItem> parseImportItems(MultipartFile file) {
|
||||
if (file == null || file.isEmpty()) {
|
||||
throw new BusinessException(PqdifResponseEnum.JSON_FILE_NOT_NULL);
|
||||
}
|
||||
String fileName = file.getOriginalFilename();
|
||||
if (fileName == null || !fileName.toLowerCase().endsWith(".json")) {
|
||||
throw new BusinessException(PqdifResponseEnum.JSON_FORMAT_ERROR);
|
||||
}
|
||||
try {
|
||||
return PqdifImportParser.parse(new String(file.getBytes(), StandardCharsets.UTF_8));
|
||||
} catch (BusinessException ex) {
|
||||
throw ex;
|
||||
} catch (Exception ex) {
|
||||
throw new BusinessException(PqdifResponseEnum.JSON_FORMAT_ERROR);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.njcn.gather.pqdif.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.github.yulichang.base.MPJBaseMapper;
|
||||
import com.njcn.gather.pqdif.pojo.po.PqPqdifPath;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface PqPqdifPathMapper extends MPJBaseMapper<PqPqdifPath> {
|
||||
List<PqPqdifPath> selectPageList(Page<PqPqdifPath> page, @Param("name") String name, @Param("result") Integer result);
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<?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.pqdif.mapper.PqPqdifPathMapper">
|
||||
<select id="selectPageList" resultType="com.njcn.gather.pqdif.pojo.po.PqPqdifPath">
|
||||
select id,
|
||||
Name as name,
|
||||
File_Path as filePath,
|
||||
Record_Count as recordCount,
|
||||
Observation_Count as observationCount,
|
||||
Sample_Value_Count as sampleValueCount,
|
||||
Result as result,
|
||||
Msg as msg,
|
||||
State as state,
|
||||
Create_By as createBy,
|
||||
Create_Time as createTime,
|
||||
Update_By as updateBy,
|
||||
Update_Time as updateTime
|
||||
from pq_pqdif_path
|
||||
where State = 1
|
||||
<if test="name != null and name != ''">
|
||||
and Name like concat('%', #{name}, '%')
|
||||
</if>
|
||||
<if test="result != null">
|
||||
and Result = #{result}
|
||||
</if>
|
||||
order by Update_Time desc, Create_Time desc
|
||||
</select>
|
||||
</mapper>
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.njcn.gather.pqdif.pojo.enums;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
public enum PqdifResponseEnum {
|
||||
JSON_FILE_NOT_NULL("A019001", "JSON文件不能为空"),
|
||||
JSON_FORMAT_ERROR("A019002", "JSON格式错误"),
|
||||
IMPORT_ITEM_NOT_NULL("A019003", "导入数据项不能为空"),
|
||||
IMPORT_ID_REPEAT("A019004", "导入数据中存在重复id"),
|
||||
IMPORT_FAILED("A019005", "PQDIF导入失败");
|
||||
|
||||
private final String code;
|
||||
private final String message;
|
||||
|
||||
PqdifResponseEnum(String code, String message) {
|
||||
this.code = code;
|
||||
this.message = message;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.njcn.gather.pqdif.pojo.param;
|
||||
|
||||
import com.njcn.web.pojo.param.BaseParam;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
@Data
|
||||
public class PqPqdifPathParam {
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public static class QueryParam extends BaseParam {
|
||||
@ApiModelProperty(value = "name")
|
||||
private String name;
|
||||
|
||||
@ApiModelProperty(value = "result")
|
||||
private Integer result;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class ImportItem {
|
||||
private String id;
|
||||
private String name;
|
||||
private String filePath;
|
||||
private Long recordCount;
|
||||
private Long observationCount;
|
||||
private Integer sampleValueCount;
|
||||
private Integer result;
|
||||
private String msg;
|
||||
private Integer state;
|
||||
private Object parseResult;
|
||||
private String createBy;
|
||||
private String createTime;
|
||||
private String updateBy;
|
||||
private String updateTime;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.njcn.gather.pqdif.pojo.po;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.njcn.db.mybatisplus.bo.BaseEntity;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("pq_pqdif_path")
|
||||
public class PqPqdifPath extends BaseEntity implements Serializable {
|
||||
private static final long serialVersionUID = 2536904714178389110L;
|
||||
|
||||
private String id;
|
||||
|
||||
private String name;
|
||||
|
||||
@TableField("File_Path")
|
||||
private String filePath;
|
||||
|
||||
@TableField("Record_Count")
|
||||
private Long recordCount;
|
||||
|
||||
@TableField("Observation_Count")
|
||||
private Long observationCount;
|
||||
|
||||
@TableField("Sample_Value_Count")
|
||||
private Integer sampleValueCount;
|
||||
|
||||
@TableField("Result")
|
||||
private Integer result;
|
||||
|
||||
@TableField("Msg")
|
||||
private String msg;
|
||||
|
||||
@TableField("State")
|
||||
private Integer state;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.njcn.gather.pqdif.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.njcn.gather.pqdif.pojo.param.PqPqdifPathParam;
|
||||
import com.njcn.gather.pqdif.pojo.po.PqPqdifPath;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface IPqPqdifPathService extends IService<PqPqdifPath> {
|
||||
List<PqPqdifPath> listAll();
|
||||
|
||||
Page<PqPqdifPath> listPqdif(PqPqdifPathParam.QueryParam param);
|
||||
|
||||
boolean importPqdif(List<PqPqdifPathParam.ImportItem> items);
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
package com.njcn.gather.pqdif.service.impl;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.ReUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.njcn.common.pojo.constant.PatternRegex;
|
||||
import com.njcn.common.pojo.enums.common.DataStateEnum;
|
||||
import com.njcn.common.pojo.exception.BusinessException;
|
||||
import com.njcn.gather.pojo.constant.DetectionValidMessage;
|
||||
import com.njcn.gather.pqdif.mapper.PqPqdifPathMapper;
|
||||
import com.njcn.gather.pqdif.pojo.enums.PqdifResponseEnum;
|
||||
import com.njcn.gather.pqdif.pojo.param.PqPqdifPathParam;
|
||||
import com.njcn.gather.pqdif.pojo.po.PqPqdifPath;
|
||||
import com.njcn.gather.pqdif.service.IPqPqdifPathService;
|
||||
import com.njcn.web.factory.PageFactory;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
public class PqPqdifPathServiceImpl extends ServiceImpl<PqPqdifPathMapper, PqPqdifPath> implements IPqPqdifPathService {
|
||||
|
||||
@Override
|
||||
public List<PqPqdifPath> listAll() {
|
||||
return this.lambdaQuery()
|
||||
.select(PqPqdifPath::getId, PqPqdifPath::getName)
|
||||
.eq(PqPqdifPath::getState, DataStateEnum.ENABLE.getCode())
|
||||
.orderByDesc(PqPqdifPath::getCreateTime)
|
||||
.list();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<PqPqdifPath> listPqdif(PqPqdifPathParam.QueryParam param) {
|
||||
Page<PqPqdifPath> page = new Page<>(PageFactory.getPageNum(param), PageFactory.getPageSize(param));
|
||||
page.setRecords(this.baseMapper.selectPageList(page, StrUtil.trim(param.getName()), param.getResult()));
|
||||
return page;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public boolean importPqdif(List<PqPqdifPathParam.ImportItem> items) {
|
||||
if (CollUtil.isEmpty(items)) {
|
||||
return true;
|
||||
}
|
||||
validateItems(items);
|
||||
for (PqPqdifPathParam.ImportItem item : items) {
|
||||
PqPqdifPath entity = toEntity(item);
|
||||
boolean saved = this.saveOrUpdate(entity);
|
||||
if (!saved) {
|
||||
throw new BusinessException(PqdifResponseEnum.IMPORT_FAILED);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void validateItems(List<PqPqdifPathParam.ImportItem> items) {
|
||||
Set<String> ids = new HashSet<>();
|
||||
for (PqPqdifPathParam.ImportItem item : items) {
|
||||
if (item == null) {
|
||||
throw new BusinessException(PqdifResponseEnum.IMPORT_ITEM_NOT_NULL);
|
||||
}
|
||||
String id = StrUtil.trim(item.getId());
|
||||
if (StrUtil.isBlank(id)) {
|
||||
throw new BusinessException(DetectionValidMessage.ID_NOT_BLANK);
|
||||
}
|
||||
if (!ReUtil.isMatch(PatternRegex.SYSTEM_ID, id)) {
|
||||
throw new BusinessException(DetectionValidMessage.ID_FORMAT_ERROR);
|
||||
}
|
||||
if (!ids.add(id)) {
|
||||
throw new BusinessException(PqdifResponseEnum.IMPORT_ID_REPEAT);
|
||||
}
|
||||
if (StrUtil.isBlank(item.getName())) {
|
||||
throw new BusinessException(DetectionValidMessage.NAME_NOT_BLANK);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private PqPqdifPath toEntity(PqPqdifPathParam.ImportItem item) {
|
||||
PqPqdifPath entity = new PqPqdifPath();
|
||||
entity.setId(StrUtil.trim(item.getId()));
|
||||
entity.setName(StrUtil.trim(item.getName()));
|
||||
entity.setFilePath(StrUtil.trim(item.getFilePath()));
|
||||
entity.setRecordCount(item.getRecordCount());
|
||||
entity.setObservationCount(item.getObservationCount());
|
||||
entity.setSampleValueCount(item.getSampleValueCount());
|
||||
entity.setResult(item.getResult());
|
||||
entity.setMsg(item.getMsg());
|
||||
entity.setState(item.getState() == null ? DataStateEnum.ENABLE.getCode() : item.getState());
|
||||
entity.setCreateBy(StrUtil.trim(item.getCreateBy()));
|
||||
entity.setUpdateBy(StrUtil.trim(item.getUpdateBy()));
|
||||
entity.setCreateTime(parseDateTime(item.getCreateTime()));
|
||||
entity.setUpdateTime(parseDateTime(item.getUpdateTime()));
|
||||
return entity;
|
||||
}
|
||||
|
||||
private LocalDateTime parseDateTime(String value) {
|
||||
if (StrUtil.isBlank(value)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return LocalDateTime.parse(StrUtil.trim(value));
|
||||
} catch (Exception ex) {
|
||||
throw new BusinessException(PqdifResponseEnum.JSON_FORMAT_ERROR);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package com.njcn.gather.pqdif.service.support;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.njcn.common.pojo.exception.BusinessException;
|
||||
import com.njcn.gather.pqdif.pojo.enums.PqdifResponseEnum;
|
||||
import com.njcn.gather.pqdif.pojo.param.PqPqdifPathParam;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public final class PqdifImportParser {
|
||||
|
||||
private PqdifImportParser() {
|
||||
}
|
||||
|
||||
public static List<PqPqdifPathParam.ImportItem> parse(String content) {
|
||||
try {
|
||||
JSONArray jsonArray = JSON.parseArray(content);
|
||||
if (jsonArray == null) {
|
||||
throw new BusinessException(PqdifResponseEnum.JSON_FORMAT_ERROR);
|
||||
}
|
||||
List<PqPqdifPathParam.ImportItem> items = new ArrayList<>();
|
||||
for (int i = 0; i < jsonArray.size(); i++) {
|
||||
JSONObject jsonObject = jsonArray.getJSONObject(i);
|
||||
if (jsonObject == null) {
|
||||
throw new BusinessException(PqdifResponseEnum.IMPORT_ITEM_NOT_NULL);
|
||||
}
|
||||
PqPqdifPathParam.ImportItem item = jsonObject.toJavaObject(PqPqdifPathParam.ImportItem.class);
|
||||
Object msg = jsonObject.get("msg");
|
||||
item.setMsg(normalizeMsg(msg));
|
||||
items.add(item);
|
||||
}
|
||||
return items;
|
||||
} catch (BusinessException ex) {
|
||||
throw ex;
|
||||
} catch (Exception ex) {
|
||||
throw new BusinessException(PqdifResponseEnum.JSON_FORMAT_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
private static String normalizeMsg(Object msg) {
|
||||
if (msg == null) {
|
||||
return null;
|
||||
}
|
||||
if (msg instanceof String) {
|
||||
return (String) msg;
|
||||
}
|
||||
return JSON.toJSONString(msg);
|
||||
}
|
||||
}
|
||||
@@ -134,11 +134,12 @@ public class ReportController extends BaseController {
|
||||
}
|
||||
|
||||
@OperateInfo
|
||||
@GetMapping("/listAllName")
|
||||
@GetMapping("/listOptions")
|
||||
@ApiOperation("查询所有报告模板名称")
|
||||
public HttpResult<List<String>> listAllName() {
|
||||
String methodDescribe = getMethodDescribe("listAllName");
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, pqReportService.listAllName(), methodDescribe);
|
||||
public HttpResult<List<PqReportVO.OptionVO>> listOptions(@RequestParam("pattern") String pattern) {
|
||||
String methodDescribe = getMethodDescribe("listOptions");
|
||||
LogUtil.njcnDebug(log, "{}锛屾煡璇㈠弬鏁颁负锛歿}", methodDescribe, pattern);
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, pqReportService.listOptions(pattern), methodDescribe);
|
||||
}
|
||||
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON, operateType = OperateType.UPDATE)
|
||||
|
||||
@@ -2,18 +2,17 @@ package com.njcn.gather.report.mapper;
|
||||
|
||||
import com.github.yulichang.base.MPJBaseMapper;
|
||||
import com.njcn.gather.report.pojo.po.PqReport;
|
||||
import com.njcn.gather.report.pojo.vo.PqReportVO;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author makejava
|
||||
* @date 2025-03-19
|
||||
*/
|
||||
public interface PqReportMapper extends MPJBaseMapper<PqReport> {
|
||||
/**
|
||||
* 获取所有已被计划绑定的报告模板id
|
||||
* @return
|
||||
*/
|
||||
List<String> getBoundReportIds();
|
||||
}
|
||||
|
||||
List<PqReportVO.OptionVO> listOptionsByPattern(@Param("pattern") String pattern);
|
||||
}
|
||||
|
||||
@@ -2,9 +2,19 @@
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.njcn.gather.report.mapper.PqReportMapper">
|
||||
|
||||
|
||||
<select id="getBoundReportIds" resultType="java.lang.String">
|
||||
select distinct Report_Template_Id from ad_plan where state = 1
|
||||
</select>
|
||||
</mapper>
|
||||
|
||||
<select id="listOptionsByPattern" resultType="com.njcn.gather.report.pojo.vo.PqReportVO$OptionVO">
|
||||
select
|
||||
id,
|
||||
name,
|
||||
version,
|
||||
concat(name, '_', version) as displayName
|
||||
from pq_report
|
||||
where state = 1
|
||||
and pattern = #{pattern}
|
||||
order by update_time desc
|
||||
</select>
|
||||
</mapper>
|
||||
|
||||
@@ -12,18 +12,32 @@ import lombok.Getter;
|
||||
public enum AffectEnum {
|
||||
|
||||
|
||||
BASE("base", "额定工作条件下的检测"),
|
||||
VOL("vol", "电压对XX测量的影响"),
|
||||
FREQ("freq", "频率对XX测量的影响"),
|
||||
HARM("harm", "谐波对XX测量的影响");
|
||||
// 第三个参数 sort 为报告小节排序档位:额定=1、单影响量=2、多影响量=3(同档内再按 Script_Index 升序)
|
||||
BASE("Base", "额定条件XX准确度测试", 1),
|
||||
VOL("VOL", "电压幅值对XX测量的影响", 2),
|
||||
FREQ("Freq", "频率变化对XX测量的影响", 2),
|
||||
HARM("Harm", "谐波对XX测量的影响", 2),
|
||||
// Single / Many 是检测树的分组标题,不会作为叶子 Script_SubType 落到设备数据上
|
||||
SINGLE("Single", "单影响量下XX准确度测试", 2),
|
||||
MANY("Many", "多影响量下XX准确度测试", 3),
|
||||
// 以下为真实的多影响量组合 Script_SubType(报告与检测树共用同一份文案)
|
||||
MANY_VOL_HARM_INHARM("Many-Vol-Harm-InHarm", "电压和谐波和间谐波对XX的影响", 3),
|
||||
MANY_IMBV_HARM_INHARM("Many-IMBV-Harm-InHarm", "三项电压不平衡和谐波和间谐波对XX的影响", 3),
|
||||
MANY_FREQ_HARM_INHARM("Many-Freq-Harm-InHarm", "频率和谐波和间谐波对XX的影响", 3);
|
||||
|
||||
private String key;
|
||||
|
||||
private String desc;
|
||||
|
||||
AffectEnum(String key, String desc) {
|
||||
/**
|
||||
* 报告小节排序档位:额定=1、单影响量=2、多影响量=3;同档内再按 Script_Index 升序
|
||||
*/
|
||||
private int sort;
|
||||
|
||||
AffectEnum(String key, String desc, int sort) {
|
||||
this.key = key;
|
||||
this.desc = desc;
|
||||
this.sort = sort;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -17,9 +17,11 @@ public enum BaseReportKeyEnum {
|
||||
|
||||
DEV_TYPE("devType","设备型号、规格"),
|
||||
DEV_CODE("createId","装置编号"),
|
||||
DEV_NAME("devName","装置名称"),
|
||||
POWER("power","工作电源"),
|
||||
DEV_CURR("devCurr","额定电流"),
|
||||
DEV_VOLT("devVolt","额定电压"),
|
||||
SOURCE_NAME("sourceName","检测源名称"),
|
||||
COUNT("count","通道数"),
|
||||
MANUFACTURER("manufacturer","设备厂家、制造厂商"),
|
||||
SAMPLE_ID("sampleId","样品编号"),
|
||||
|
||||
@@ -6,10 +6,6 @@ import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
/**
|
||||
* @author caozehui
|
||||
* @data 2025-03-19
|
||||
*/
|
||||
@Data
|
||||
public class ReportParam {
|
||||
|
||||
@@ -19,23 +15,29 @@ public class ReportParam {
|
||||
@ApiModelProperty(value = "版本号", required = true)
|
||||
private String version;
|
||||
|
||||
@ApiModelProperty(value = "模式id", required = true)
|
||||
private String pattern;
|
||||
|
||||
@ApiModelProperty(value = "描述信息", required = true)
|
||||
private String description;
|
||||
|
||||
@ApiModelProperty(value = "基础模板文件", required = true)
|
||||
private MultipartFile baseFile;
|
||||
|
||||
@ApiModelProperty(value = "检测项模版文件", required = true)
|
||||
@ApiModelProperty(value = "检测项模板文件", required = true)
|
||||
private MultipartFile detailFile;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public static class QueryParam extends BaseParam {
|
||||
@ApiModelProperty(value = "报告模板名称", required = true)
|
||||
@ApiModelProperty(value = "报告模板名称")
|
||||
private String name;
|
||||
|
||||
@ApiModelProperty(value = "版本号", required = true)
|
||||
@ApiModelProperty(value = "版本号")
|
||||
private String version;
|
||||
|
||||
@ApiModelProperty(value = "模式id", required = true)
|
||||
private String pattern;
|
||||
}
|
||||
|
||||
@Data
|
||||
|
||||
@@ -6,17 +6,13 @@ import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* @author makejava
|
||||
* @date 2025-03-19
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("pq_report")
|
||||
public class PqReport extends BaseEntity implements Serializable {
|
||||
private static final long serialVersionUID = 582972970946593407L;
|
||||
|
||||
/**
|
||||
* 报告模板id
|
||||
*/
|
||||
@@ -32,13 +28,18 @@ public class PqReport extends BaseEntity implements Serializable {
|
||||
*/
|
||||
private String version;
|
||||
|
||||
/**
|
||||
* 模式id
|
||||
*/
|
||||
private String pattern;
|
||||
|
||||
/**
|
||||
* 基础模板文件路径
|
||||
*/
|
||||
private String basePath;
|
||||
|
||||
/**
|
||||
* 检测项模版文件路径
|
||||
* 检测项模板文件路径
|
||||
*/
|
||||
private String detailPath;
|
||||
|
||||
@@ -52,4 +53,3 @@ public class PqReport extends BaseEntity implements Serializable {
|
||||
*/
|
||||
private Integer state;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,60 +9,42 @@ import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* @author caozehui
|
||||
* @data 2025-03-19
|
||||
*/
|
||||
@Data
|
||||
public class PqReportVO {
|
||||
/**
|
||||
* 报告模板id
|
||||
*/
|
||||
private String id;
|
||||
|
||||
/**
|
||||
* 报告模板名称
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 版本号
|
||||
*/
|
||||
private String version;
|
||||
|
||||
private String pattern;
|
||||
|
||||
/**
|
||||
* 基础模板文件路径
|
||||
*/
|
||||
private FileVO baseFileVO;
|
||||
|
||||
/**
|
||||
* 检测项模版文件路径
|
||||
*/
|
||||
private FileVO detailFileVO;
|
||||
|
||||
/**
|
||||
* 描述信息
|
||||
*/
|
||||
private String description;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@JsonDeserialize(using = LocalDateTimeDeserializer.class)
|
||||
@JsonSerialize(using = LocalDateTimeSerializer.class)
|
||||
private LocalDateTime createTime;
|
||||
|
||||
/**
|
||||
* 状态:0-删除 1-正常
|
||||
*/
|
||||
//private Integer state;
|
||||
|
||||
@Data
|
||||
public static class FileVO{
|
||||
public static class FileVO {
|
||||
private String name;
|
||||
|
||||
private String url;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class OptionVO {
|
||||
private String id;
|
||||
|
||||
private String name;
|
||||
|
||||
private String version;
|
||||
|
||||
private String displayName;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,19 +55,12 @@ public interface IPqReportService extends IService<PqReport> {
|
||||
*/
|
||||
boolean delete(List<String> ids);
|
||||
|
||||
/**
|
||||
* 查询所有报告名称
|
||||
*
|
||||
* @return key为报告id,value为报告名称
|
||||
*/
|
||||
List<String> listAllName();
|
||||
List<PqReportVO.OptionVO> listOptions(String pattern);
|
||||
|
||||
void generateReport(DevReportParam devReportParam);
|
||||
|
||||
|
||||
void downloadReport(DevReportParam devReportParam, HttpServletResponse response);
|
||||
|
||||
|
||||
/**
|
||||
* 设备归档操作
|
||||
*
|
||||
@@ -82,5 +75,4 @@ public interface IPqReportService extends IService<PqReport> {
|
||||
* @param deviceIds 被检设备ID列表,为空时上传所有已生成报告的设备
|
||||
*/
|
||||
void uploadReportToCloud(List<String> deviceIds);
|
||||
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.njcn.common.pojo.constant.PatternRegex;
|
||||
import com.njcn.common.pojo.enums.common.DataStateEnum;
|
||||
import com.njcn.common.pojo.enums.response.CommonResponseEnum;
|
||||
import com.njcn.common.pojo.exception.BusinessException;
|
||||
import com.njcn.common.utils.images.ImageConverter;
|
||||
import com.njcn.gather.detection.pojo.constant.DetectionCommunicateConstant;
|
||||
@@ -46,6 +47,7 @@ import com.njcn.gather.plan.pojo.enums.PlanReportStateEnum;
|
||||
import com.njcn.gather.plan.pojo.po.AdPlan;
|
||||
import com.njcn.gather.plan.pojo.po.AdPlanTestConfig;
|
||||
import com.njcn.gather.plan.service.IAdPlanService;
|
||||
import com.njcn.gather.plan.service.IAdPlanSourceService;
|
||||
import com.njcn.gather.plan.service.IAdPlanTestConfigService;
|
||||
import com.njcn.gather.pojo.enums.DetectionResponseEnum;
|
||||
import com.njcn.gather.report.mapper.PqReportMapper;
|
||||
@@ -61,6 +63,7 @@ import com.njcn.gather.report.service.IPqReportService;
|
||||
import com.njcn.gather.result.service.IResultService;
|
||||
import com.njcn.gather.script.pojo.vo.PqScriptDtlDataVO;
|
||||
import com.njcn.gather.script.service.IPqScriptDtlsService;
|
||||
import com.njcn.gather.source.pojo.po.PqSource;
|
||||
import com.njcn.gather.storage.pojo.param.SingleNonHarmParam;
|
||||
import com.njcn.gather.storage.pojo.po.SimAndDigHarmonicResult;
|
||||
import com.njcn.gather.storage.pojo.po.SimAndDigNonHarmonicResult;
|
||||
@@ -184,6 +187,7 @@ public class PqReportServiceImpl extends ServiceImpl<PqReportMapper, PqReport> i
|
||||
private final IPqDevSubService iPqDevSubService;
|
||||
private final IDictDataService dictDataService;
|
||||
private final IAdPlanService adPlanService;
|
||||
private final IAdPlanSourceService adPlanSourceService;
|
||||
private final IPqScriptDtlsService pqScriptDtlsService;
|
||||
private final SimAndDigNonHarmonicService adNonHarmonicService;
|
||||
private final SimAndDigHarmonicService adHarmonicService;
|
||||
@@ -206,6 +210,7 @@ public class PqReportServiceImpl extends ServiceImpl<PqReportMapper, PqReport> i
|
||||
QueryWrapper<PqReport> wrapper = new QueryWrapper<>();
|
||||
wrapper.like(StrUtil.isNotBlank(queryParam.getName()), "name", queryParam.getName())
|
||||
.eq(StrUtil.isNotBlank(queryParam.getVersion()), "version", queryParam.getVersion())
|
||||
.eq("pattern", queryParam.getPattern())
|
||||
.eq("state", DataStateEnum.ENABLE.getCode());
|
||||
wrapper.orderByDesc("Update_Time");
|
||||
Page<PqReport> page1 = this.page(new Page<>(PageFactory.getPageNum(queryParam), PageFactory.getPageSize(queryParam)), wrapper);
|
||||
@@ -307,11 +312,8 @@ public class PqReportServiceImpl extends ServiceImpl<PqReportMapper, PqReport> i
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> listAllName() {
|
||||
List<PqReport> reportList = this.lambdaQuery()
|
||||
.eq(PqReport::getState, DataStateEnum.ENABLE.getCode()).list();
|
||||
List<String> collect = reportList.stream().map(pqReport -> pqReport.getName() + "_" + pqReport.getVersion()).collect(Collectors.toList());
|
||||
return collect;
|
||||
public List<PqReportVO.OptionVO> listOptions(String pattern) {
|
||||
return this.baseMapper.listOptionsByPattern(pattern);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -324,7 +326,7 @@ public class PqReportServiceImpl extends ServiceImpl<PqReportMapper, PqReport> i
|
||||
private void uploadFile(ReportParam reportParam, PqReport pqReport, boolean isAdd) {
|
||||
MultipartFile baseFile = reportParam.getBaseFile();
|
||||
MultipartFile detailFile = reportParam.getDetailFile();
|
||||
String relativePath = reportParam.getName() + File.separator + reportParam.getVersion() + File.separator;
|
||||
String relativePath = this.buildReportTemplateRelativeDir(reportParam.getPattern(), reportParam.getName(), reportParam.getVersion());
|
||||
String newDir = pathConfig.getReportTemplatePath() + File.separator + relativePath;
|
||||
|
||||
long FILE_SIZE_LIMIT = 5 * 1024 * 1024;
|
||||
@@ -914,6 +916,10 @@ public class PqReportServiceImpl extends ServiceImpl<PqReportMapper, PqReport> i
|
||||
WordprocessingMLPackage detailModelDocument = WordprocessingMLPackage.load(detailInputStream);
|
||||
// 获取文档基础部分,并替换占位符
|
||||
Map<String, String> baseModelDataMap = dealBaseModelData(pqDevVO, devType);
|
||||
// 补充检测源名称:计划↔源走 ad_plan_source 中间表(业务上单源),datasourceId 是数据源类型枚举、不指向 pq_source
|
||||
List<PqSource> planSources = adPlanSourceService.listPqSourceByPlanId(plan.getId());
|
||||
baseModelDataMap.put(BaseReportKeyEnum.SOURCE_NAME.getKey(),
|
||||
CollUtil.isNotEmpty(planSources) ? planSources.get(0).getName() : StrUtil.EMPTY);
|
||||
InputStream wordFinishInputStream = wordReportService.replacePlaceholders(baseInputStream, baseModelDataMap);
|
||||
WordprocessingMLPackage baseModelDocument = WordprocessingMLPackage.load(wordFinishInputStream);
|
||||
MainDocumentPart baseDocumentPart = baseModelDocument.getMainDocumentPart();
|
||||
@@ -1270,6 +1276,11 @@ public class PqReportServiceImpl extends ServiceImpl<PqReportMapper, PqReport> i
|
||||
bookmarkInfo = BookmarkUtil.getBookmarkInfo(BookmarkEnum.DATA_LINE.getKey(), bookmarks);
|
||||
todoInsertList = dealDataLine(detailDocumentPart, devReportParam, pqDevVO, resultMap);
|
||||
if (Objects.nonNull(bookmarkInfo) && CollectionUtil.isNotEmpty(todoInsertList)) {
|
||||
// 锚点位于 base 模板中间(其后仍有有效内容)时,在数据块末尾补一个分页符,
|
||||
// 让 base 剩余部分另起新页;锚点在文档末尾时不加,存量模板行为不变
|
||||
if (hasContentAfterBookmark(bookmarkInfo)) {
|
||||
todoInsertList.add(Docx4jUtil.getPageBreak());
|
||||
}
|
||||
BookmarkUtil.insertElement(bookmarkInfo, todoInsertList);
|
||||
BookmarkUtil.removeBookmark(bookmarkInfo);
|
||||
}
|
||||
@@ -1322,6 +1333,40 @@ public class PqReportServiceImpl extends ServiceImpl<PqReportMapper, PqReport> i
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断书签段落之后是否还有有意义的内容(非空文本段落,或表格等非段落元素)。
|
||||
* <p>
|
||||
* 用于数模式 DATA_LINE 锚点位于 base 模板中间时,决定是否在插入的数据块末尾追加分页符,
|
||||
* 使 base 剩余部分另起新页;书签在文档末尾(其后仅剩空段落)时返回 false,不追加分页符,
|
||||
* 避免末尾凭空多出一页空白,存量模板(锚点在末尾)行为保持不变。
|
||||
* <p>
|
||||
* 注:仅以文本判断段落是否有意义,纯图片段落不会被识别为有效内容。
|
||||
*
|
||||
* @param bookmarkInfo 书签信息
|
||||
* @return 书签之后存在有意义内容时返回 true
|
||||
*/
|
||||
private boolean hasContentAfterBookmark(BookmarkUtil.BookmarkInfo bookmarkInfo) {
|
||||
List<Object> parentContent = bookmarkInfo.parentContainer.getContent();
|
||||
int idx = parentContent.indexOf(bookmarkInfo.parentParagraph);
|
||||
if (idx < 0) {
|
||||
return false;
|
||||
}
|
||||
for (int i = idx + 1; i < parentContent.size(); i++) {
|
||||
Object obj = parentContent.get(i);
|
||||
Object realObj = (obj instanceof JAXBElement) ? ((JAXBElement<?>) obj).getValue() : obj;
|
||||
if (realObj instanceof P) {
|
||||
// 非空文本段落才算有意义,纯空段落不触发分页
|
||||
if (StrUtil.isNotBlank(Docx4jUtil.getTextFromP((P) realObj))) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
// 表格等非段落元素一律视为有意义内容
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 如何处理结果性数据进文档,各省级平台的结果表格不一致,如何做到一致
|
||||
@@ -2078,13 +2123,14 @@ public class PqReportServiceImpl extends ServiceImpl<PqReportMapper, PqReport> i
|
||||
// 软件版本
|
||||
baseModelMap.put(BaseReportKeyEnum.SW_VERSION.getKey(), StrUtil.isNotBlank(pqDevVO.getSoftwareVersion()) ? pqDevVO.getSoftwareVersion() : StrUtil.EMPTY);
|
||||
// 调试日期
|
||||
if (pqDevVO.getCheckTime() != null) {
|
||||
baseModelMap.put(BaseReportKeyEnum.TEST_DATE.getKey(), DateUtil.format(pqDevVO.getCheckTime(), DatePattern.CHINESE_DATE_PATTERN));
|
||||
if (pqDevVO.getCheckEndTime() != null) {
|
||||
baseModelMap.put(BaseReportKeyEnum.TEST_DATE.getKey(), DateUtil.format(pqDevVO.getCheckEndTime(), DatePattern.CHINESE_DATE_PATTERN));
|
||||
} else {
|
||||
baseModelMap.put(BaseReportKeyEnum.TEST_DATE.getKey(), DateUtil.format(new Date(), DatePattern.CHINESE_DATE_PATTERN));
|
||||
}
|
||||
// 装置编码
|
||||
baseModelMap.put(BaseReportKeyEnum.DEV_CODE.getKey(), pqDevVO.getCreateId());
|
||||
baseModelMap.put(BaseReportKeyEnum.DEV_NAME.getKey(), StrUtil.isBlank(pqDevVO.getName()) ? StrPool.TAB : pqDevVO.getName());
|
||||
// 工作电源
|
||||
baseModelMap.put(BaseReportKeyEnum.POWER.getKey(), devType.getPower());
|
||||
// 额定电流
|
||||
@@ -2123,7 +2169,7 @@ public class PqReportServiceImpl extends ServiceImpl<PqReportMapper, PqReport> i
|
||||
// 收样日期
|
||||
baseModelMap.put(BaseReportKeyEnum.ARRIVED_DATE.getKey(), Objects.isNull(pqDevVO.getArrivedDate()) ? StrPool.TAB : String.valueOf(pqDevVO.getArrivedDate()));
|
||||
// 检测日期
|
||||
baseModelMap.put(BaseReportKeyEnum.TEST_DATE.getKey(), Objects.isNull(pqDevVO.getCheckTime()) ? StrPool.TAB : String.valueOf(pqDevVO.getCheckTime()).substring(0, 10));
|
||||
baseModelMap.put(BaseReportKeyEnum.TEST_DATE.getKey(), Objects.isNull(pqDevVO.getCheckEndTime()) ? StrPool.TAB : String.valueOf(pqDevVO.getCheckEndTime()).substring(0, 10));
|
||||
baseModelMap.put(BaseReportKeyEnum.TEMPERATURE.getKey(), Objects.isNull(pqDevVO.getTemperature()) ? StrPool.TAB : pqDevVO.getTemperature().toString());
|
||||
baseModelMap.put(BaseReportKeyEnum.HUMIDITY.getKey(), Objects.isNull(pqDevVO.getHumidity()) ? StrPool.TAB : pqDevVO.getHumidity().toString());
|
||||
baseModelMap.put(BaseReportKeyEnum.YEAR.getKey(), DateUtil.format(new Date(), DatePattern.NORM_YEAR_PATTERN));
|
||||
@@ -2196,9 +2242,9 @@ public class PqReportServiceImpl extends ServiceImpl<PqReportMapper, PqReport> i
|
||||
// 检测日期
|
||||
PqDevSub devSub = iPqDevSubService.lambdaQuery().eq(PqDevSub::getDevId, pqDevVO.getId()).one();
|
||||
if (Objects.nonNull(devSub)) {
|
||||
LocalDateTime checkTime = devSub.getCheckTime();
|
||||
if (Objects.nonNull(checkTime)) {
|
||||
baseModelMap.put(BaseReportKeyEnum.TEST_DATE.getKey(), DateUtil.format(checkTime, DatePattern.NORM_DATE_PATTERN));
|
||||
LocalDateTime checkEndTime = devSub.getCheckEndTime();
|
||||
if (Objects.nonNull(checkEndTime)) {
|
||||
baseModelMap.put(BaseReportKeyEnum.TEST_DATE.getKey(), DateUtil.format(checkEndTime, DatePattern.NORM_DATE_PATTERN));
|
||||
} else {
|
||||
baseModelMap.put(BaseReportKeyEnum.TEST_DATE.getKey(), StrPool.TAB);
|
||||
}
|
||||
@@ -2455,7 +2501,8 @@ public class PqReportServiceImpl extends ServiceImpl<PqReportMapper, PqReport> i
|
||||
*/
|
||||
private void checkRepeat(ReportParam reportParam, boolean isExcludeSelf) {
|
||||
QueryWrapper<PqReport> wrapper = new QueryWrapper();
|
||||
wrapper.eq("name", reportParam.getName())
|
||||
wrapper.eq("pattern", reportParam.getPattern())
|
||||
.eq("name", reportParam.getName())
|
||||
.eq("version", reportParam.getVersion())
|
||||
.eq("state", DataStateEnum.ENABLE.getCode());
|
||||
if (isExcludeSelf) {
|
||||
@@ -2469,6 +2516,21 @@ public class PqReportServiceImpl extends ServiceImpl<PqReportMapper, PqReport> i
|
||||
}
|
||||
}
|
||||
|
||||
private String sanitizePathSegment(String value) {
|
||||
return StrUtil.blankToDefault(value, StrUtil.EMPTY).replaceAll("[\\\\/:*?\"<>|]", "_").trim();
|
||||
}
|
||||
|
||||
private String buildReportTemplateRelativeDir(String patternId, String name, String version) {
|
||||
DictData dictData = dictDataService.getDictDataById(patternId);
|
||||
if (ObjectUtil.isNull(dictData) || StrUtil.isBlank(dictData.getName())) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, "模式不存在或字典名称缺失");
|
||||
}
|
||||
String patternName = this.sanitizePathSegment(dictData.getName());
|
||||
String reportName = this.sanitizePathSegment(name);
|
||||
String reportVersion = this.sanitizePathSegment(version);
|
||||
return patternName + File.separator + reportName + File.separator + reportVersion + File.separator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void uploadReportToCloud(List<String> deviceIds) {
|
||||
log.info("开始批量上传检测报告到云端,设备ID列表:{}", deviceIds);
|
||||
@@ -2527,13 +2589,14 @@ public class PqReportServiceImpl extends ServiceImpl<PqReportMapper, PqReport> i
|
||||
List<Docx4jUtil.HeadingContent> headingContents = contentMap.get(PowerIndexEnum.LINE_TITLE.getKey());
|
||||
int lineNo = currentLine + 1;
|
||||
// 如果contentMap中有指定内容,创建大纲级别为2的标题
|
||||
// 回路标题恒带回路号(单回路也显示"测量回路1"),与结论表头"测量回路N"保持一致
|
||||
if (CollUtil.isNotEmpty(headingContents)) {
|
||||
return Docx4jUtil.createTitle(factory, 2, totalLine > 1 ? lineNo + ".测量回路" + lineNo : lineNo + ".测量回路",
|
||||
return Docx4jUtil.createTitle(factory, 2, lineNo + ".测量回路" + lineNo,
|
||||
"SimSun", 30, true);
|
||||
}
|
||||
// 没有模板配置时,创建默认样式段落
|
||||
P titleParagraph = factory.createP();
|
||||
Docx4jUtil.createTitle(factory, titleParagraph, totalLine > 1 ? "测量回路" + lineNo : "测量回路",
|
||||
Docx4jUtil.createTitle(factory, titleParagraph, "测量回路" + lineNo,
|
||||
28, true);
|
||||
return titleParagraph;
|
||||
}
|
||||
|
||||
@@ -163,6 +163,18 @@ public class ResultController extends BaseController {
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe);
|
||||
}
|
||||
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
|
||||
@GetMapping("/createChecksquareTask")
|
||||
@ApiOperation("调用第三方数模数据检测接口")
|
||||
@ApiImplicitParam(name = "devId", value = "设备id", required = true)
|
||||
public HttpResult<DataCheckResultVO> createChecksquareTask(@RequestParam("devId") String devId) {
|
||||
String methodDescribe = getMethodDescribe("createChecksquareTask");
|
||||
LogUtil.njcnDebug(log, "{},调用第三方数模数据检测接口,设备id为:{}", methodDescribe, devId);
|
||||
|
||||
DataCheckResultVO result = resultService.createChecksquareTaskByDevId(devId);
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe);
|
||||
}
|
||||
|
||||
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
|
||||
@GetMapping("/getMonitorDataSourceResult")
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.njcn.gather.result.pojo.param;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class DataCheckRequest {
|
||||
private List<String> lineIds;
|
||||
private List<String> indicatorCodes;
|
||||
private String timeStart;
|
||||
private String timeEnd;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.njcn.gather.result.pojo.vo;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Value;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 数据校验通知 上下文
|
||||
*/
|
||||
@Value
|
||||
@Builder
|
||||
public class DataCheckNotifyContext {
|
||||
String notifyKey;
|
||||
String planId;
|
||||
List<String> sortedDevIds;
|
||||
List<String> lineIds;
|
||||
LocalDateTime timeStart;
|
||||
LocalDateTime timeEnd;
|
||||
String reCheckType;
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
package com.njcn.gather.result.pojo.vo;
|
||||
|
||||
import com.alibaba.fastjson.annotation.JSONField;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author caozehui
|
||||
* @data 2026-06-26
|
||||
*/
|
||||
@Data
|
||||
public class DataCheckResultVO {
|
||||
/**
|
||||
* 任务 ID
|
||||
*/
|
||||
@JSONField(name = "taskId")
|
||||
private String taskId;
|
||||
/**
|
||||
* 任务编号
|
||||
*/
|
||||
@JSONField(name = "taskNo")
|
||||
private String taskNo;
|
||||
/**
|
||||
* 监测点Id集合
|
||||
*/
|
||||
@JSONField(name = "lineIds")
|
||||
private List<String> lineIds;
|
||||
/**
|
||||
* 监测点名称;多监测点时为多个名称拼接结果
|
||||
*/
|
||||
@JSONField(name = "lineName")
|
||||
private String lineName;
|
||||
/**
|
||||
* 开始时间
|
||||
*/
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private LocalDateTime timeStart;
|
||||
/**
|
||||
* 结束时间
|
||||
*/
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private LocalDateTime timeEnd;
|
||||
/**
|
||||
* 统计间隔,单位分钟
|
||||
*/
|
||||
private long intervalMinutes;
|
||||
/**
|
||||
* 任务状态:RUNNING、SUCCESS、FAIL
|
||||
*/
|
||||
private String taskStatus;
|
||||
/**
|
||||
* 检测项数量
|
||||
*/
|
||||
private long itemCount;
|
||||
/**
|
||||
* 异常检测项数量
|
||||
*/
|
||||
private long abnormalItemCount;
|
||||
/**
|
||||
* 最低数据完整性,0 到 1 的小数
|
||||
*/
|
||||
private long minDataIntegrity;
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private LocalDateTime createTime;
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
package com.njcn.gather.result.service;
|
||||
|
||||
import com.njcn.gather.result.pojo.param.DataCheckRequest;
|
||||
import com.njcn.gather.result.pojo.vo.DataCheckNotifyContext;
|
||||
import com.njcn.gather.result.service.impl.ResultServiceImpl;
|
||||
import com.njcn.http.util.RestTemplateUtil;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class DataCheckAsyncNotifier {
|
||||
|
||||
private final static String url="http://172.17.100.111:18091/steady/checksquare/create";
|
||||
private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||
|
||||
private final RestTemplateUtil restTemplateUtil;
|
||||
|
||||
@Async
|
||||
public void notifyAsync(DataCheckNotifyContext context, Map<String, ResultServiceImpl.NotifyState> notifyStates) {
|
||||
notifyDirect(context, notifyStates);
|
||||
}
|
||||
|
||||
void notifyDirect(DataCheckNotifyContext context, Map<String, ResultServiceImpl.NotifyState> notifyStates) {
|
||||
int maxRetries = 3;
|
||||
for (int attempt = 1; attempt <= maxRetries; attempt++) {
|
||||
try {
|
||||
DataCheckRequest request = new DataCheckRequest();
|
||||
request.setLineIds(context.getLineIds());
|
||||
request.setIndicatorCodes(Collections.emptyList());
|
||||
request.setTimeStart(FORMATTER.format(context.getTimeStart()));
|
||||
request.setTimeEnd(FORMATTER.format(context.getTimeEnd()));
|
||||
|
||||
restTemplateUtil.postJson(url, request, String.class);
|
||||
notifyStates.put(context.getNotifyKey(),
|
||||
new ResultServiceImpl.NotifyState(ResultServiceImpl.NotifyStatus.SUCCESS, attempt - 1, null, LocalDateTime.now()));
|
||||
log.info("checksquare notify success, key={}, planId={}, lineCount={}", context.getNotifyKey(), context.getPlanId(), context.getLineIds().size());
|
||||
return;
|
||||
} catch (Exception ex) {
|
||||
int failCount = attempt;
|
||||
if (failCount > maxRetries) {
|
||||
notifyStates.put(context.getNotifyKey(),
|
||||
new ResultServiceImpl.NotifyState(ResultServiceImpl.NotifyStatus.FAIL, maxRetries, ex.getMessage(), LocalDateTime.now()));
|
||||
log.error("checksquare notify failed, key={}, failCount={}", context.getNotifyKey(), maxRetries, ex);
|
||||
return;
|
||||
}
|
||||
if (!sleepBeforeRetry(context, failCount)) {
|
||||
notifyStates.put(context.getNotifyKey(),
|
||||
new ResultServiceImpl.NotifyState(ResultServiceImpl.NotifyStatus.FAIL, failCount, "retry interrupted", LocalDateTime.now()));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean sleepBeforeRetry(DataCheckNotifyContext context, int failCount) {
|
||||
long delayMillis = (long) Math.pow(2, failCount) * 1000L;
|
||||
log.warn("checksquare notify retry scheduled, key={}, failCount={}, delayMillis={}", context.getNotifyKey(), failCount, delayMillis);
|
||||
try {
|
||||
Thread.sleep(delayMillis);
|
||||
return true;
|
||||
} catch (InterruptedException ex) {
|
||||
Thread.currentThread().interrupt();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.njcn.gather.result.service;
|
||||
|
||||
import com.njcn.gather.detection.pojo.param.PreDetectionParam;
|
||||
import com.njcn.gather.device.pojo.vo.PqDevVO;
|
||||
import com.njcn.gather.report.pojo.DevReportParam;
|
||||
import com.njcn.gather.report.pojo.result.ContrastTestResult;
|
||||
@@ -17,6 +18,16 @@ import java.util.Map;
|
||||
* @data 2024-12-30
|
||||
*/
|
||||
public interface IResultService {
|
||||
void tryNotifyThirdPartyAfterFormalTest(PreDetectionParam param);
|
||||
|
||||
/**
|
||||
* 调用第三方数据校验结果
|
||||
*
|
||||
* @param devId
|
||||
* @return
|
||||
*/
|
||||
DataCheckResultVO createChecksquareTaskByDevId(String devId);
|
||||
|
||||
/**
|
||||
* 检测详情表单头
|
||||
*
|
||||
|
||||
@@ -9,6 +9,7 @@ import cn.hutool.core.text.StrPool;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.ReflectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
@@ -23,6 +24,7 @@ import com.njcn.common.utils.PubUtils;
|
||||
import com.njcn.gather.detection.handler.SocketContrastResponseService;
|
||||
import com.njcn.gather.detection.pojo.enums.DetectionCodeEnum;
|
||||
import com.njcn.gather.detection.pojo.enums.ResultEnum;
|
||||
import com.njcn.gather.detection.pojo.enums.SourceOperateCodeEnum;
|
||||
import com.njcn.gather.detection.pojo.param.PreDetectionParam;
|
||||
import com.njcn.gather.detection.pojo.po.AdPair;
|
||||
import com.njcn.gather.detection.pojo.po.DevData;
|
||||
@@ -31,12 +33,14 @@ import com.njcn.gather.detection.pojo.vo.DetectionData;
|
||||
import com.njcn.gather.detection.service.IAdPariService;
|
||||
import com.njcn.gather.detection.service.impl.DetectionServiceImpl;
|
||||
import com.njcn.gather.detection.util.socket.CnSocketUtil;
|
||||
import com.njcn.gather.detection.util.socket.FormalTestManager;
|
||||
import com.njcn.gather.device.pojo.enums.CommonEnum;
|
||||
import com.njcn.gather.device.pojo.enums.PatternEnum;
|
||||
import com.njcn.gather.device.pojo.po.PqDev;
|
||||
import com.njcn.gather.device.pojo.po.PqStandardDev;
|
||||
import com.njcn.gather.device.pojo.vo.PqDevVO;
|
||||
import com.njcn.gather.device.service.IPqDevService;
|
||||
import com.njcn.gather.device.service.IPqDevSubService;
|
||||
import com.njcn.gather.device.service.IPqStandardDevService;
|
||||
import com.njcn.gather.err.service.IPqErrSysService;
|
||||
import com.njcn.gather.monitor.pojo.po.PqMonitor;
|
||||
@@ -55,8 +59,10 @@ import com.njcn.gather.report.pojo.enums.PowerIndexEnum;
|
||||
import com.njcn.gather.report.pojo.result.ContrastTestResult;
|
||||
import com.njcn.gather.report.pojo.result.SingleTestResult;
|
||||
import com.njcn.gather.result.pojo.enums.ResultUnitEnum;
|
||||
import com.njcn.gather.result.pojo.param.DataCheckRequest;
|
||||
import com.njcn.gather.result.pojo.param.ResultParam;
|
||||
import com.njcn.gather.result.pojo.vo.*;
|
||||
import com.njcn.gather.result.service.DataCheckAsyncNotifier;
|
||||
import com.njcn.gather.result.service.IResultService;
|
||||
import com.njcn.gather.script.mapper.PqScriptMapper;
|
||||
import com.njcn.gather.script.pojo.param.PqScriptCheckDataParam;
|
||||
@@ -85,8 +91,10 @@ import com.njcn.gather.system.dictionary.service.IDictDataService;
|
||||
import com.njcn.gather.system.dictionary.service.IDictTreeService;
|
||||
import com.njcn.gather.system.pojo.enums.DicDataEnum;
|
||||
import com.njcn.gather.util.StorageUtil;
|
||||
import com.njcn.http.util.RestTemplateUtil;
|
||||
import com.njcn.web.utils.ExcelUtil;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.Value;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
@@ -96,6 +104,7 @@ import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
@@ -132,6 +141,13 @@ public class ResultServiceImpl implements IResultService {
|
||||
private final IPqMonitorService pqMonitorService;
|
||||
private final IPqErrSysService pqErrSysService;
|
||||
private final IPqStandardDevService standardDevService;
|
||||
private final IPqDevSubService pqDevSubService;
|
||||
private final DataCheckAsyncNotifier dataCheckAsyncNotifier;
|
||||
private final RestTemplateUtil restTemplateUtil;
|
||||
|
||||
public static final String CHECKSQUARE_CREATE_URL = "http://192.168.2.147:18091/api/steady/checksquare/create";
|
||||
private static final DateTimeFormatter CHECKSQUARE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||
private final Map<String, NotifyState> notifyStates = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* 谐波类code,取树形字典表中的code
|
||||
@@ -139,6 +155,96 @@ public class ResultServiceImpl implements IResultService {
|
||||
private final List<String> HARMONIC_TYPE_CODE = Arrays.asList("HV", "HI", "HP", "HSV", "HSI");
|
||||
|
||||
|
||||
@Override
|
||||
public void tryNotifyThirdPartyAfterFormalTest(PreDetectionParam param) {
|
||||
if (param == null || !SourceOperateCodeEnum.ALL_TEST.getValue().equals(FormalTestManager.reCheckType)) {
|
||||
return;
|
||||
}
|
||||
if (ObjectUtil.isNull(FormalTestManager.checkStartTime)
|
||||
|| StrUtil.isBlank(param.getPlanId())
|
||||
|| CollUtil.isEmpty(param.getDevIds())
|
||||
|| CollUtil.isEmpty(FormalTestManager.monitorIdListComm)) {
|
||||
return;
|
||||
}
|
||||
|
||||
LocalDateTime batchEndTime = pqDevSubService.resolveBatchMaxCheckEndTime(param.getDevIds());
|
||||
if (ObjectUtil.isNull(batchEndTime)) {
|
||||
log.warn("checksquare notify skipped, batch end time is empty, planId={}, devIds={}", param.getPlanId(), param.getDevIds());
|
||||
return;
|
||||
}
|
||||
|
||||
List<String> sortedDevIds = param.getDevIds().stream().sorted().collect(Collectors.toList());
|
||||
String notifyKey = param.getPlanId() + "|" + String.join(",", sortedDevIds) + "|"
|
||||
+ CHECKSQUARE_TIME_FORMATTER.format(FormalTestManager.checkStartTime) + "|" + FormalTestManager.reCheckType;
|
||||
if (notifyStates.get(notifyKey) != null) {
|
||||
return;
|
||||
}
|
||||
if (notifyStates.putIfAbsent(notifyKey, NotifyState.running()) != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
DataCheckNotifyContext context = DataCheckNotifyContext.builder()
|
||||
.notifyKey(notifyKey)
|
||||
.planId(param.getPlanId())
|
||||
.sortedDevIds(sortedDevIds)
|
||||
.lineIds(new ArrayList<>(FormalTestManager.monitorIdListComm))
|
||||
.timeStart(FormalTestManager.checkStartTime)
|
||||
.timeEnd(batchEndTime)
|
||||
.reCheckType(FormalTestManager.reCheckType)
|
||||
.build();
|
||||
dataCheckAsyncNotifier.notifyAsync(context, notifyStates);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataCheckResultVO createChecksquareTaskByDevId(String devId) {
|
||||
if (StrUtil.isBlank(devId)) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, "设备id不能为空");
|
||||
}
|
||||
|
||||
List<PqMonitor> monitorList = pqMonitorService.listPqMonitorByDevIds(Collections.singletonList(devId));
|
||||
if (CollUtil.isEmpty(monitorList)) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, "该设备监测点不存在");
|
||||
}
|
||||
|
||||
// PqDevSub devSub = pqDevSubService.getOne(new LambdaQueryWrapper<PqDevSub>()
|
||||
// .eq(PqDevSub::getDevId, devId), false);
|
||||
// if (ObjectUtil.isNull(devSub)
|
||||
// || ObjectUtil.isNull(devSub.getCheckStartTime())
|
||||
// || ObjectUtil.isNull(devSub.getCheckEndTime())) {
|
||||
// throw new BusinessException(CommonResponseEnum.FAIL, "该设备检测开始时间或结束时间为空");
|
||||
// }
|
||||
|
||||
DataCheckRequest request = new DataCheckRequest();
|
||||
// request.setLineIds(monitorList.stream().map(PqMonitor::getId).collect(Collectors.toList()));
|
||||
// request.setIndicatorCodes(Collections.emptyList());
|
||||
// request.setTimeStart(CHECKSQUARE_TIME_FORMATTER.format(devSub.getCheckStartTime()));
|
||||
// request.setTimeEnd(CHECKSQUARE_TIME_FORMATTER.format(devSub.getCheckEndTime()));
|
||||
request.setLineIds(Arrays.asList("ee9a33337bfd4d5588c00a2dbef6bc7e"));
|
||||
request.setIndicatorCodes(Arrays.asList("V_RMS"));
|
||||
request.setTimeStart("2026-06-08 00:00:00");
|
||||
request.setTimeEnd("2026-06-08 23:59:59");
|
||||
String s = restTemplateUtil.postJson(CHECKSQUARE_CREATE_URL, request, String.class);
|
||||
JSONObject obj = JSONUtil.parseObj(s);
|
||||
DataCheckResultVO data = BeanUtil.copyProperties(obj.get("data"), DataCheckResultVO.class);
|
||||
return data;
|
||||
}
|
||||
|
||||
public enum NotifyStatus {
|
||||
RUNNING, SUCCESS, FAIL
|
||||
}
|
||||
|
||||
@Value
|
||||
public static class NotifyState {
|
||||
NotifyStatus status;
|
||||
int failCount;
|
||||
String lastError;
|
||||
LocalDateTime triggerTime;
|
||||
|
||||
public static NotifyState running() {
|
||||
return new NotifyState(NotifyStatus.RUNNING, 0, null, LocalDateTime.now());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public FormContentVO getFormContent(ResultParam.QueryParam queryParam) {
|
||||
FormContentVO formContentVO = new FormContentVO();
|
||||
@@ -221,12 +327,11 @@ public class ResultServiceImpl implements IResultService {
|
||||
List<DictTree> dictTreeById = dictTreeService.getDictTreeById(new ArrayList<>(dtlsSortMap.keySet()));
|
||||
|
||||
Map<String, DictTree> dictTreeMap = dictTreeById.stream().collect(Collectors.toMap(DictTree::getId, Function.identity()));
|
||||
Map<String, String> subName = new LinkedHashMap<>();
|
||||
subName.put("Base", "额定条件XX准确度测试");
|
||||
subName.put("VOL", "电压幅值对XX测量的影响");
|
||||
subName.put("Freq", "频率变化对XX测量的影响");
|
||||
subName.put("Harm", "谐波对XX测量的影响");
|
||||
subName.put("Single", "单影响量下XX准确度测试");
|
||||
// 影响量文案统一由 AffectEnum 提供;大小写不敏感 Map,兼容 Script_SubType 的大小写差异
|
||||
Map<String, String> subName = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
|
||||
for (AffectEnum affectEnum : AffectEnum.values()) {
|
||||
subName.put(affectEnum.getKey(), affectEnum.getDesc());
|
||||
}
|
||||
Boolean isValueType = scriptMapper.selectScriptIsValueType(param.getScriptId());
|
||||
|
||||
List<TreeDataVO> infoVOS = new ArrayList<>();
|
||||
@@ -243,7 +348,7 @@ public class ResultServiceImpl implements IResultService {
|
||||
//额定条件下频率准确度测试
|
||||
LinkedHashMap<String, List<PqScriptDtls>> subTypeMap = value.stream()
|
||||
.sorted(Comparator.comparing(PqScriptDtls::getScriptIndex))
|
||||
.filter(x -> "Base".equals(x.getScriptSubType()))
|
||||
.filter(x -> "Base".equalsIgnoreCase(x.getScriptSubType()))
|
||||
.collect(Collectors.groupingBy(PqScriptDtls::getScriptSubType, LinkedHashMap::new, Collectors.toList()));
|
||||
subTypeMap.forEach((subKey, subValue) -> {
|
||||
if (!"VOLTAGE".equals(dictTree.getCode())) {
|
||||
@@ -282,7 +387,7 @@ public class ResultServiceImpl implements IResultService {
|
||||
//单影响量下频率准确度测试
|
||||
LinkedHashMap<String, List<PqScriptDtls>> subSingleTypeMap = value.stream()
|
||||
.sorted(Comparator.comparing(PqScriptDtls::getScriptIndex))
|
||||
.filter(x -> !"Base".equals(x.getScriptSubType()))
|
||||
.filter(x -> !"Base".equalsIgnoreCase(x.getScriptSubType()) && !StrUtil.containsIgnoreCase(x.getScriptSubType(), "Many"))
|
||||
.collect(Collectors.groupingBy(PqScriptDtls::getScriptSubType, LinkedHashMap::new, Collectors.toList()));
|
||||
if (CollUtil.isNotEmpty(subSingleTypeMap)) {
|
||||
TreeDataVO subType = new TreeDataVO();
|
||||
@@ -304,13 +409,13 @@ public class ResultServiceImpl implements IResultService {
|
||||
dtlType.setIndex(index);
|
||||
dtlType.setScriptType(scriptDtlIndexList.get(0).getScriptType());
|
||||
dtlType.setScriptTypeCode(subKey);
|
||||
if ("Harm".equals(subKey)) {
|
||||
if ("Harm".equalsIgnoreCase(subKey)) {
|
||||
harmScriptTypeName(scriptDtlIndexList, dictTree, isValueType, dtlType);
|
||||
}
|
||||
if ("VOL".equals(subKey)) {
|
||||
if ("VOL".equalsIgnoreCase(subKey)) {
|
||||
volScriptTypeName(scriptDtlIndexList, dictTree, isValueType, dtlType);
|
||||
}
|
||||
if ("Freq".equals(subKey)) {
|
||||
if ("Freq".equalsIgnoreCase(subKey)) {
|
||||
freqScriptTypeName(scriptDtlIndexList, dictTree, isValueType, dtlType);
|
||||
}
|
||||
dtlType.setSourceDesc(ScriptDtlsDesc.getStringBuffer(scriptDtlIndexList, false, isValueType).toString());
|
||||
@@ -329,6 +434,56 @@ public class ResultServiceImpl implements IResultService {
|
||||
scriptSubList.add(subType);
|
||||
}
|
||||
}
|
||||
//多影响量下xx
|
||||
LinkedHashMap<String, List<PqScriptDtls>> subManyTypeMap = value.stream()
|
||||
.sorted(Comparator.comparing(PqScriptDtls::getScriptIndex))
|
||||
.filter(x -> StrUtil.containsIgnoreCase(x.getScriptSubType(), "Many"))
|
||||
.collect(Collectors.groupingBy(PqScriptDtls::getScriptSubType, LinkedHashMap::new, Collectors.toList()));
|
||||
if (CollUtil.isNotEmpty(subManyTypeMap)) {
|
||||
TreeDataVO subType = new TreeDataVO();
|
||||
subType.setScriptTypeName(!subName.containsKey("Many") ? "" : subName.get("Many").replace("XX", dictTree.getName()));
|
||||
subType.setScriptTypeCode(dictTree.getCode());
|
||||
//多影响量下xx准测量集合
|
||||
List<TreeDataVO> subSingleList = new ArrayList<>();
|
||||
subManyTypeMap.forEach((subKey, subValue) -> {
|
||||
TreeDataVO treeDataVO = new TreeDataVO();
|
||||
treeDataVO.setScriptTypeName(!subName.containsKey(subKey) ? "" : subName.get(subKey).replace("XX", dictTree.getName()));
|
||||
treeDataVO.setScriptTypeCode(subKey);
|
||||
List<TreeDataVO> subTypeList = new ArrayList<>();
|
||||
|
||||
LinkedHashMap<Integer, List<PqScriptDtls>> indexMap = subValue.stream()
|
||||
.sorted(Comparator.comparing(PqScriptDtls::getScriptIndex))
|
||||
.collect(Collectors.groupingBy(PqScriptDtls::getScriptIndex, LinkedHashMap::new, Collectors.toList()));
|
||||
indexMap.forEach((index, scriptDtlIndexList) -> {
|
||||
TreeDataVO dtlType = new TreeDataVO();
|
||||
dtlType.setIndex(index);
|
||||
dtlType.setScriptType(scriptDtlIndexList.get(0).getScriptType());
|
||||
dtlType.setScriptTypeCode(subKey);
|
||||
if ("Many-Vol-Harm-InHarm".equalsIgnoreCase(subKey)) {
|
||||
volAndHarmAndInHarmScriptTypeName(scriptDtlIndexList, dictTree, isValueType, dtlType);
|
||||
}
|
||||
if ("Many-IMBV-Harm-InHarm".equalsIgnoreCase(subKey)) {
|
||||
imbvAndHarmAndInHarmScriptTypeName(scriptDtlIndexList, dictTree, isValueType, dtlType);
|
||||
}
|
||||
if ("Many-Freq-Harm-InHarm".equalsIgnoreCase(subKey)) {
|
||||
freqAndHarmAndInHarmScriptTypeName(scriptDtlIndexList, dictTree, isValueType, dtlType);
|
||||
}
|
||||
dtlType.setSourceDesc(ScriptDtlsDesc.getStringBuffer(scriptDtlIndexList, false, isValueType).toString());
|
||||
if (finalResultMap.containsKey(index)) {
|
||||
dtlType.setFly(conform(finalResultMap.get(index)));
|
||||
}
|
||||
subTypeList.add(dtlType);
|
||||
});
|
||||
if (CollUtil.isNotEmpty(subTypeList)) {
|
||||
treeDataVO.setChildren(subTypeList);
|
||||
subSingleList.add(treeDataVO);
|
||||
}
|
||||
});
|
||||
if (CollUtil.isNotEmpty(subSingleList)) {
|
||||
subType.setChildren(subSingleList);
|
||||
scriptSubList.add(subType);
|
||||
}
|
||||
}
|
||||
if (CollUtil.isNotEmpty(scriptSubList)) {
|
||||
infoVO.setChildren(scriptSubList);
|
||||
infoVOS.add(infoVO);
|
||||
@@ -583,7 +738,7 @@ public class ResultServiceImpl implements IResultService {
|
||||
case F:
|
||||
//Pst=1(变动率=1.459%*1)
|
||||
List<PqScriptDtls> flicker = scriptDtlIndexList.stream().filter(x -> "Flicker".equals(x.getValueType())).collect(Collectors.toList());
|
||||
dtlType.setScriptTypeName("Pst=" + flicker.get(0).getChagFre() + "(变动率=" + flicker.get(0).getChagValue() + "%*" + flicker.get(0).getChagFre() + ")");
|
||||
dtlType.setScriptTypeName("变动频度=" + flicker.get(0).getChagFre() + "次/min," + "变动率=" + flicker.get(0).getChagValue() + "%");
|
||||
break;
|
||||
/**
|
||||
* 暂态
|
||||
@@ -867,6 +1022,246 @@ public class ResultServiceImpl implements IResultService {
|
||||
}
|
||||
}
|
||||
|
||||
private void volAndHarmAndInHarmScriptTypeName(List<PqScriptDtls> dtls, DictTree dictTree, Boolean isValueType, TreeDataVO dtlType) {
|
||||
ResultUnitEnum resultUnitEnumByCode = ResultUnitEnum.getResultUnitEnumByCode(dictTree.getCode());
|
||||
List<PqScriptDtls> f = dtls.stream().filter(x -> "Freq".equals(x.getValueType())).collect(Collectors.toList());
|
||||
String unit;
|
||||
if (isValueType) {
|
||||
unit = ResultUnitEnum.V_RELATIVE.getUnit();
|
||||
} else {
|
||||
unit = ResultUnitEnum.V_ABSOLUTELY.getUnit();
|
||||
}
|
||||
List<PqScriptDtls> v = dtls.stream().filter(x -> "VOL".equals(x.getValueType())).collect(Collectors.toList());
|
||||
switch (resultUnitEnumByCode) {
|
||||
/**
|
||||
* 频率
|
||||
*/
|
||||
case FREQ:
|
||||
dtlType.setScriptTypeName("电压:" + v.get(0).getValue() + unit + ",叠加多次谐波和间谐波对" + dictTree.getName() + "=" + f.get(0).getValue() + ResultUnitEnum.FREQ.getUnit() + "的影响");
|
||||
break;
|
||||
/**
|
||||
* 电压
|
||||
*/
|
||||
case V_RELATIVE:
|
||||
case V_ABSOLUTELY:
|
||||
break;
|
||||
/**
|
||||
* 电流
|
||||
*/
|
||||
case I_RELATIVE:
|
||||
case I_ABSOLUTELY:
|
||||
break;
|
||||
/**
|
||||
* 谐波电压
|
||||
*/
|
||||
case HV:
|
||||
break;
|
||||
/**
|
||||
* 谐波电流
|
||||
*/
|
||||
case HI:
|
||||
break;
|
||||
/**
|
||||
* 间谐波电压
|
||||
*/
|
||||
case HSV:
|
||||
break;
|
||||
/**
|
||||
* 间谐波电流
|
||||
*/
|
||||
case HSI:
|
||||
break;
|
||||
/**
|
||||
* 三相电压不平衡度
|
||||
*/
|
||||
case IMBV:
|
||||
break;
|
||||
/**
|
||||
* 三相电流不平衡度
|
||||
*/
|
||||
case IMBA:
|
||||
break;
|
||||
/**
|
||||
* 谐波有功功率
|
||||
*/
|
||||
case HP:
|
||||
break;
|
||||
/**
|
||||
* 闪变
|
||||
*/
|
||||
case F:
|
||||
break;
|
||||
/**
|
||||
* 暂态
|
||||
*/
|
||||
case VOLTAGE_MAG:
|
||||
case VOLTAGE_DUR:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void imbvAndHarmAndInHarmScriptTypeName(List<PqScriptDtls> dtls, DictTree dictTree, Boolean isValueType, TreeDataVO dtlType) {
|
||||
ResultUnitEnum resultUnitEnumByCode = ResultUnitEnum.getResultUnitEnumByCode(dictTree.getCode());
|
||||
List<PqScriptDtls> f = dtls.stream().filter(x -> "Freq".equals(x.getValueType())).collect(Collectors.toList());
|
||||
switch (resultUnitEnumByCode) {
|
||||
/**
|
||||
* 频率
|
||||
*/
|
||||
case FREQ:
|
||||
dtlType.setScriptTypeName("三项电压不平衡和叠加多次谐波和间谐波对" + dictTree.getName() + "=" + f.get(0).getValue() + ResultUnitEnum.FREQ.getUnit() + "的影响");
|
||||
break;
|
||||
/**
|
||||
* 电压
|
||||
*/
|
||||
case V_RELATIVE:
|
||||
case V_ABSOLUTELY:
|
||||
String unit;
|
||||
if (isValueType) {
|
||||
unit = ResultUnitEnum.V_RELATIVE.getUnit();
|
||||
} else {
|
||||
unit = ResultUnitEnum.V_ABSOLUTELY.getUnit();
|
||||
}
|
||||
List<PqScriptDtls> v = dtls.stream().filter(x -> "VOL".equals(x.getValueType())).collect(Collectors.toList());
|
||||
dtlType.setScriptTypeName("三项电压不平衡和叠加多次谐波和间谐波对" + dictTree.getName() + "=" + v.get(0).getValue() + unit + "的影响");
|
||||
break;
|
||||
/**
|
||||
* 电流
|
||||
*/
|
||||
case I_RELATIVE:
|
||||
case I_ABSOLUTELY:
|
||||
break;
|
||||
/**
|
||||
* 谐波电压
|
||||
*/
|
||||
case HV:
|
||||
break;
|
||||
/**
|
||||
* 谐波电流
|
||||
*/
|
||||
case HI:
|
||||
break;
|
||||
/**
|
||||
* 间谐波电压
|
||||
*/
|
||||
case HSV:
|
||||
break;
|
||||
/**
|
||||
* 间谐波电流
|
||||
*/
|
||||
case HSI:
|
||||
break;
|
||||
/**
|
||||
* 三相电压不平衡度
|
||||
*/
|
||||
case IMBV:
|
||||
break;
|
||||
/**
|
||||
* 三相电流不平衡度
|
||||
*/
|
||||
case IMBA:
|
||||
break;
|
||||
/**
|
||||
* 谐波有功功率
|
||||
*/
|
||||
case HP:
|
||||
break;
|
||||
/**
|
||||
* 闪变
|
||||
*/
|
||||
case F:
|
||||
break;
|
||||
/**
|
||||
* 暂态
|
||||
*/
|
||||
case VOLTAGE_MAG:
|
||||
case VOLTAGE_DUR:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void freqAndHarmAndInHarmScriptTypeName(List<PqScriptDtls> dtls, DictTree dictTree, Boolean isValueType, TreeDataVO dtlType) {
|
||||
ResultUnitEnum resultUnitEnumByCode = ResultUnitEnum.getResultUnitEnumByCode(dictTree.getCode());
|
||||
switch (resultUnitEnumByCode) {
|
||||
/**
|
||||
* 频率
|
||||
*/
|
||||
case FREQ:
|
||||
break;
|
||||
/**
|
||||
* 电压
|
||||
*/
|
||||
case V_RELATIVE:
|
||||
case V_ABSOLUTELY:
|
||||
String unit;
|
||||
if (isValueType) {
|
||||
unit = ResultUnitEnum.V_RELATIVE.getUnit();
|
||||
} else {
|
||||
unit = ResultUnitEnum.V_ABSOLUTELY.getUnit();
|
||||
}
|
||||
List<PqScriptDtls> v = dtls.stream().filter(x -> "VOL".equals(x.getValueType())).collect(Collectors.toList());
|
||||
List<PqScriptDtls> f = dtls.stream().filter(x -> "Freq".equals(x.getValueType())).collect(Collectors.toList());
|
||||
dtlType.setScriptTypeName("频率:" + f.get(0).getValue() + ResultUnitEnum.FREQ.getUnit() + ",叠加多次谐波和间谐波对" + dictTree.getName() + "=" + v.get(0).getValue() + unit + "的影响");
|
||||
break;
|
||||
/**
|
||||
* 电流
|
||||
*/
|
||||
case I_RELATIVE:
|
||||
case I_ABSOLUTELY:
|
||||
break;
|
||||
/**
|
||||
* 谐波电压
|
||||
*/
|
||||
case HV:
|
||||
break;
|
||||
/**
|
||||
* 谐波电流
|
||||
*/
|
||||
case HI:
|
||||
break;
|
||||
/**
|
||||
* 间谐波电压
|
||||
*/
|
||||
case HSV:
|
||||
break;
|
||||
/**
|
||||
* 间谐波电流
|
||||
*/
|
||||
case HSI:
|
||||
break;
|
||||
/**
|
||||
* 三相电压不平衡度
|
||||
*/
|
||||
case IMBV:
|
||||
break;
|
||||
/**
|
||||
* 三相电流不平衡度
|
||||
*/
|
||||
case IMBA:
|
||||
break;
|
||||
/**
|
||||
* 谐波有功功率
|
||||
*/
|
||||
case HP:
|
||||
break;
|
||||
/**
|
||||
* 闪变
|
||||
*/
|
||||
case F:
|
||||
break;
|
||||
/**
|
||||
* 暂态
|
||||
*/
|
||||
case VOLTAGE_MAG:
|
||||
case VOLTAGE_DUR:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResultVO resultData(ResultParam param) {
|
||||
@@ -1031,10 +1426,19 @@ public class ResultServiceImpl implements IResultService {
|
||||
@Override
|
||||
public SingleTestResult getFinalContent(List<PqScriptDtlDataVO> dtlDataVOList, String planCode, String devId, Integer lineNo, List<String> tableKeys) {
|
||||
SingleTestResult singleTestResult = new SingleTestResult();
|
||||
Map<String/*subType影响量,额定或某单影响量*/, List<Map<String/*误差范围*/, List<Map<String/*填充key*/, String/*实际值*/>>>>> finalContent = new HashMap<>();
|
||||
// 用 LinkedHashMap 保住下方按 AffectEnum.sort + Script_Index 排好的影响量小节顺序,一路保到下游 fillContentInTemplate 按 entrySet 顺序消费
|
||||
Map<String/*subType影响量,额定或某单影响量*/, List<Map<String/*误差范围*/, List<Map<String/*填充key*/, String/*实际值*/>>>>> finalContent = new LinkedHashMap<>();
|
||||
if (CollUtil.isNotEmpty(dtlDataVOList)) {
|
||||
// 首先区分测试条件
|
||||
Map<String, List<PqScriptDtlDataVO>> subTypeMap = dtlDataVOList.stream().collect(Collectors.groupingBy(PqScriptDtlDataVO::getScriptSubType));
|
||||
// 首先区分测试条件;分组前先按 AffectEnum.sort(额定→单影响→多影响)排序,同档内再按 Script_Index 升序,
|
||||
// 收进 LinkedHashMap,使各影响量小节顺序固定为:额定、单影响量、多影响量
|
||||
Map<String, List<PqScriptDtlDataVO>> subTypeMap = dtlDataVOList.stream()
|
||||
.sorted(Comparator
|
||||
.comparingInt((PqScriptDtlDataVO v) -> {
|
||||
AffectEnum ae = AffectEnum.getByKey(v.getScriptSubType());
|
||||
return ae == null ? Integer.MAX_VALUE : ae.getSort(); // 识别不了的 subType 沉底
|
||||
})
|
||||
.thenComparingInt(v -> v.getScriptIndex() == null ? Integer.MAX_VALUE : v.getScriptIndex()))
|
||||
.collect(Collectors.groupingBy(PqScriptDtlDataVO::getScriptSubType, LinkedHashMap::new, Collectors.toList()));
|
||||
subTypeMap.forEach((subType, scriptDtlDataVOList) -> {
|
||||
AffectEnum affectEnum = AffectEnum.getByKey(subType);
|
||||
if (Objects.nonNull(affectEnum)) {
|
||||
@@ -1054,12 +1458,21 @@ public class ResultServiceImpl implements IResultService {
|
||||
for (Integer sort : indexList) {
|
||||
SingleNonHarmParam param = new SingleNonHarmParam(planCode, devId, lineNo, valueTypeList, Collections.singletonList(sort));
|
||||
List<SimAndDigNonHarmonicResult> nonHarmList = simAndDigNonHarmonicService.queryByCondition(param);
|
||||
if (CollUtil.isNotEmpty(nonHarmList)) {
|
||||
Map<String, String> keyFillMap = new HashMap<>(16);
|
||||
fillVoltagePhaseData(nonHarmList, keyFillMap, tableKeys);
|
||||
if (CollUtil.isEmpty(nonHarmList)) {
|
||||
log.warn("生成暂态类表格数据跳过,结果表数据为空,planCode={}, devId={}, lineNo={}, scriptCode={}, scriptIndex={}",
|
||||
planCode, devId, lineNo, scriptCode, sort);
|
||||
continue;
|
||||
}
|
||||
Map<String, String> keyFillMap = new HashMap<>(16);
|
||||
if (fillVoltagePhaseData(nonHarmList, keyFillMap, tableKeys)) {
|
||||
keyFillMapList.add(keyFillMap);
|
||||
}
|
||||
}
|
||||
if (CollUtil.isEmpty(keyFillMapList)) {
|
||||
log.warn("生成暂态类表格数据跳过,当前脚本无可填充结果,planCode={}, devId={}, lineNo={}, scriptCode={}, scriptIndexList={}",
|
||||
planCode, devId, lineNo, scriptCode, indexList);
|
||||
return;
|
||||
}
|
||||
// 需要对所有填充进行按误差范围分组
|
||||
Map<String, List<Map<String, String>>> errorScoperMap = keyFillMapList.stream()
|
||||
.collect(Collectors.groupingBy(map -> map.get(ItemReportKeyEnum.ERROR_SCOPE.getKey())));
|
||||
@@ -1079,6 +1492,11 @@ public class ResultServiceImpl implements IResultService {
|
||||
// 获取谐波数据
|
||||
SingleNonHarmParam param = new SingleNonHarmParam(planCode, devId, lineNo, valueType, indexList.get(0));
|
||||
SimAndDigHarmonicResult singleResult = simAndDigHarmonicService.getSingleResult(param);
|
||||
if (Objects.isNull(singleResult)) {
|
||||
log.warn("生成谐波类表格数据跳过,结果表数据为空,planCode={}, devId={}, lineNo={}, scriptCode={}, valueType={}, scriptIndex={}",
|
||||
planCode, devId, lineNo, scriptCode, valueType, indexList.get(0));
|
||||
continue;
|
||||
}
|
||||
// 注:如果ABC的标准值一致,则同步到standard中
|
||||
Map<Double, List<PqScriptCheckData>> checkDataHarmNumMap = scriptCheckDataList.stream().collect(Collectors.groupingBy(PqScriptCheckData::getHarmNum));
|
||||
List<Map<String, String>> keyFillMapList = new ArrayList<>();
|
||||
@@ -1098,8 +1516,9 @@ public class ResultServiceImpl implements IResultService {
|
||||
double timeDouble = Math.round(harmNum);
|
||||
int timeInt = (int) timeDouble;
|
||||
// 填充结果数据
|
||||
fillThreePhaseData(singleResult, timeInt, keyFillMap, scriptCode);
|
||||
keyFillMapList.add(keyFillMap);
|
||||
if (fillThreePhaseData(singleResult, timeInt, keyFillMap, scriptCode)) {
|
||||
keyFillMapList.add(keyFillMap);
|
||||
}
|
||||
});
|
||||
if (CollUtil.isNotEmpty(keyFillMapList)) {
|
||||
// 按次数排序
|
||||
@@ -1122,38 +1541,44 @@ public class ResultServiceImpl implements IResultService {
|
||||
// 获取该三相的数据
|
||||
SingleNonHarmParam param = new SingleNonHarmParam(planCode, devId, lineNo, Collections.singletonList(valueType), indexList);
|
||||
List<SimAndDigNonHarmonicResult> nonHarmList = simAndDigNonHarmonicService.queryByCondition(param);
|
||||
if (CollUtil.isNotEmpty(nonHarmList)) {
|
||||
List<Map<String, String>> keyFillMapList = new ArrayList<>();
|
||||
for (SimAndDigNonHarmonicResult SimAndDigNonHarmonicResult : nonHarmList) {
|
||||
Map<String, String> keyFillMap = new HashMap<>(16);
|
||||
fillThreePhaseData(SimAndDigNonHarmonicResult, null, keyFillMap, scriptCode);
|
||||
if (CollUtil.isEmpty(nonHarmList)) {
|
||||
log.warn("生成三相类表格数据跳过,结果表数据为空,planCode={}, devId={}, lineNo={}, scriptCode={}, valueType={}, scriptIndexList={}",
|
||||
planCode, devId, lineNo, scriptCode, valueType, indexList);
|
||||
continue;
|
||||
}
|
||||
List<Map<String, String>> keyFillMapList = new ArrayList<>();
|
||||
for (SimAndDigNonHarmonicResult SimAndDigNonHarmonicResult : nonHarmList) {
|
||||
Map<String, String> keyFillMap = new HashMap<>(16);
|
||||
if (fillThreePhaseData(SimAndDigNonHarmonicResult, null, keyFillMap, scriptCode)) {
|
||||
keyFillMapList.add(keyFillMap);
|
||||
}
|
||||
if (CollUtil.isNotEmpty(keyFillMapList)) {
|
||||
// 需要对所有的填充进行按误差范围分组
|
||||
Map<String, List<Map<String, String>>> errorScoperMap = keyFillMapList.stream()
|
||||
.collect(Collectors.groupingBy(map -> map.get(ItemReportKeyEnum.ERROR_SCOPE.getKey())));
|
||||
// 分组后,还需要针对标准值进行一个升序
|
||||
errorScoperMap.forEach((errorScope, maps) -> {
|
||||
PubUtils.sortByDoubleValue(maps, ItemReportKeyEnum.STANDARD.getKey());
|
||||
});
|
||||
List<Map<String, List<Map<String, String>>>> errorList = new ArrayList<>();
|
||||
errorList.add(errorScoperMap);
|
||||
// 最后赋值返回
|
||||
finalContent.put(affectName, errorList);
|
||||
}
|
||||
} else {
|
||||
log.error("生成三相类表格数据失败,结果表数据丢失,请核实。");
|
||||
}
|
||||
if (CollUtil.isNotEmpty(keyFillMapList)) {
|
||||
// 需要对所有的填充进行按误差范围分组
|
||||
Map<String, List<Map<String, String>>> errorScoperMap = keyFillMapList.stream()
|
||||
.collect(Collectors.groupingBy(map -> map.get(ItemReportKeyEnum.ERROR_SCOPE.getKey())));
|
||||
// 分组后,还需要针对标准值进行一个升序
|
||||
errorScoperMap.forEach((errorScope, maps) -> {
|
||||
PubUtils.sortByDoubleValue(maps, ItemReportKeyEnum.STANDARD.getKey());
|
||||
});
|
||||
List<Map<String, List<Map<String, String>>>> errorList = new ArrayList<>();
|
||||
errorList.add(errorScoperMap);
|
||||
// 最后赋值返回
|
||||
finalContent.put(affectName, errorList);
|
||||
}
|
||||
} else {
|
||||
// 非三相且非暂态,通常只有一个数据,所以直接赋值即可
|
||||
List<Map<String, String>> keyFillMapList = new ArrayList<>();
|
||||
SingleNonHarmParam param = new SingleNonHarmParam(planCode, devId, lineNo, Collections.singletonList(valueType), indexList);
|
||||
List<SimAndDigNonHarmonicResult> nonHarmList = simAndDigNonHarmonicService.queryByCondition(param);
|
||||
if (CollUtil.isNotEmpty(nonHarmList)) {
|
||||
for (SimAndDigNonHarmonicResult simAndDigNonHarmonicResult : nonHarmList) {
|
||||
Map<String, String> keyFillMap = new HashMap<>(8);
|
||||
fillTPhaseData(simAndDigNonHarmonicResult, null, keyFillMap);
|
||||
if (CollUtil.isEmpty(nonHarmList)) {
|
||||
log.warn("生成T相类表格数据跳过,结果表数据为空,planCode={}, devId={}, lineNo={}, scriptCode={}, valueType={}, scriptIndexList={}",
|
||||
planCode, devId, lineNo, scriptCode, valueType, indexList);
|
||||
continue;
|
||||
}
|
||||
for (SimAndDigNonHarmonicResult simAndDigNonHarmonicResult : nonHarmList) {
|
||||
Map<String, String> keyFillMap = new HashMap<>(8);
|
||||
if (fillTPhaseData(simAndDigNonHarmonicResult, null, keyFillMap)) {
|
||||
keyFillMapList.add(keyFillMap);
|
||||
}
|
||||
}
|
||||
@@ -1197,10 +1622,14 @@ public class ResultServiceImpl implements IResultService {
|
||||
* @param timeInt 谐波类需要传指定次数
|
||||
* @param keyFillMap 待填充的集合Map
|
||||
*/
|
||||
private void fillThreePhaseData(Object singleResult, Integer timeInt, Map<String, String> keyFillMap, String scriptCode) {
|
||||
private boolean fillThreePhaseData(Object singleResult, Integer timeInt, Map<String, String> keyFillMap, String scriptCode) {
|
||||
// boolean isCurrentI = "I".equalsIgnoreCase(scriptCode);
|
||||
DetectionData tempA = getResultData(singleResult, timeInt, PowerConstant.PHASE_A);
|
||||
DetectionData tempB = getResultData(singleResult, timeInt, PowerConstant.PHASE_B);
|
||||
DetectionData tempC = getResultData(singleResult, timeInt, PowerConstant.PHASE_C);
|
||||
if (Objects.isNull(tempA) && Objects.isNull(tempB) && Objects.isNull(tempC)) {
|
||||
return false;
|
||||
}
|
||||
// 待填充Key
|
||||
String standard = "/", standardA = "/", standardB = "/", standardC = "/",
|
||||
testA = "/", testB = "/", testC = "/",
|
||||
@@ -1254,11 +1683,11 @@ public class ResultServiceImpl implements IResultService {
|
||||
}
|
||||
}
|
||||
// 浙江脚本特殊处理
|
||||
if (scriptCode.equalsIgnoreCase("I")) {
|
||||
resultA = "/";
|
||||
resultB = "/";
|
||||
resultC = "/";
|
||||
}
|
||||
// if (isCurrentI) {
|
||||
// resultA = "/";
|
||||
// resultB = "/";
|
||||
// resultC = "/";
|
||||
// }
|
||||
if (standardA.equals(standardB) && standardA.equals(standardC)) {
|
||||
standard = standardA;
|
||||
}
|
||||
@@ -1290,6 +1719,7 @@ public class ResultServiceImpl implements IResultService {
|
||||
keyFillMap.put(ItemReportKeyEnum.RESULT.getKey(), result);
|
||||
errorScope = dealErrorScope(errorScope).concat(unit);
|
||||
keyFillMap.put(ItemReportKeyEnum.ERROR_SCOPE.getKey(), errorScope);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1340,25 +1770,27 @@ public class ResultServiceImpl implements IResultService {
|
||||
* @param timeInt 谐波类需要传指定次数
|
||||
* @param keyFillMap 待填充的集合Map
|
||||
*/
|
||||
private void fillTPhaseData(Object singleResult, Integer timeInt, Map<String, String> keyFillMap) {
|
||||
private boolean fillTPhaseData(Object singleResult, Integer timeInt, Map<String, String> keyFillMap) {
|
||||
String standard = "/", test = "/", error = "/", result = "/", errorScope = "/", unit = "";
|
||||
DetectionData tempT = getResultData(singleResult, timeInt, PowerConstant.PHASE_T);
|
||||
if (Objects.nonNull(tempT)) {
|
||||
standard = PubUtils.doubleRoundStr(4, tempT.getResultData()) == null ? "/" : PubUtils.doubleRoundStr(4, tempT.getResultData());
|
||||
test = PubUtils.doubleRoundStr(4, tempT.getData()) == null ? "/" : PubUtils.doubleRoundStr(4, tempT.getData());
|
||||
if (Objects.nonNull(tempT.getErrorData())) {
|
||||
error = PubUtils.doubleRoundStr(4, tempT.getErrorData().doubleValue());
|
||||
}
|
||||
result = tempT.getIsData() == 1 ? "合格" : tempT.getIsData() == 2 ? "不合格" : "/";
|
||||
unit = tempT.getUnit() == null ? "" : tempT.getUnit();
|
||||
errorScope = tempT.getRadius() == null ? "/" : tempT.getRadius();
|
||||
if (Objects.isNull(tempT)) {
|
||||
return false;
|
||||
}
|
||||
standard = PubUtils.doubleRoundStr(4, tempT.getResultData()) == null ? "/" : PubUtils.doubleRoundStr(4, tempT.getResultData());
|
||||
test = PubUtils.doubleRoundStr(4, tempT.getData()) == null ? "/" : PubUtils.doubleRoundStr(4, tempT.getData());
|
||||
if (Objects.nonNull(tempT.getErrorData())) {
|
||||
error = PubUtils.doubleRoundStr(4, tempT.getErrorData().doubleValue());
|
||||
}
|
||||
result = tempT.getIsData() == 1 ? "合格" : tempT.getIsData() == 2 ? "不合格" : "/";
|
||||
unit = tempT.getUnit() == null ? "" : tempT.getUnit();
|
||||
errorScope = tempT.getRadius() == null ? "/" : tempT.getRadius();
|
||||
keyFillMap.put(ItemReportKeyEnum.STANDARD.getKey(), standard);
|
||||
keyFillMap.put(ItemReportKeyEnum.TEST.getKey(), test);
|
||||
keyFillMap.put(ItemReportKeyEnum.ERROR.getKey(), error);
|
||||
keyFillMap.put(ItemReportKeyEnum.RESULT.getKey(), result);
|
||||
errorScope = dealErrorScope(errorScope).concat(unit);
|
||||
keyFillMap.put(ItemReportKeyEnum.ERROR_SCOPE.getKey(), errorScope);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1368,30 +1800,33 @@ public class ResultServiceImpl implements IResultService {
|
||||
* @param keyFillMap 待填充的集合Map
|
||||
* @param tableKeys 模板表格中的key
|
||||
*/
|
||||
private void fillVoltagePhaseData(List<SimAndDigNonHarmonicResult> nonHarmList, Map<String, String> keyFillMap, List<String> tableKeys) {
|
||||
private boolean fillVoltagePhaseData(List<SimAndDigNonHarmonicResult> nonHarmList, Map<String, String> keyFillMap, List<String> tableKeys) {
|
||||
String standardMag = "/", standardDur = "/",
|
||||
testMag = "/", testDur = "/",
|
||||
errorMag = "/", errorDur = "/",
|
||||
resultMag = "/", resultDur = "/", result,
|
||||
errorScope, errorScopeMag = "/", errorScopeDur = "/",
|
||||
unitMag = "", unitDur = "";
|
||||
boolean hasData = false;
|
||||
for (SimAndDigNonHarmonicResult adNonHarmonicResult : nonHarmList) {
|
||||
DetectionData tempT = getResultData(adNonHarmonicResult, null, PowerConstant.PHASE_T);
|
||||
if (Objects.isNull(tempT)) {
|
||||
continue;
|
||||
}
|
||||
hasData = true;
|
||||
// 需要判断adNonHarmonicResult是特征幅值还是持续时间
|
||||
String adType = adNonHarmonicResult.getAdType();
|
||||
DictTree temp = dictTreeService.getById(adType);
|
||||
if (temp.getCode().equalsIgnoreCase("MAG")) {
|
||||
// 特征幅值
|
||||
if (Objects.nonNull(tempT)) {
|
||||
standardMag = PubUtils.doubleRoundStr(4, tempT.getResultData()) == null ? "/" : PubUtils.doubleRoundStr(4, tempT.getResultData());
|
||||
testMag = PubUtils.doubleRoundStr(4, tempT.getData()) == null ? "/" : PubUtils.doubleRoundStr(4, tempT.getData());
|
||||
if (Objects.nonNull(tempT.getErrorData())) {
|
||||
errorMag = PubUtils.doubleRoundStr(4, tempT.getErrorData().doubleValue());
|
||||
}
|
||||
resultMag = tempT.getIsData() == 1 ? "合格" : tempT.getIsData() == 2 ? "不合格" : "/";
|
||||
unitMag = tempT.getUnit();
|
||||
errorScopeMag = tempT.getRadius();
|
||||
standardMag = PubUtils.doubleRoundStr(4, tempT.getResultData()) == null ? "/" : PubUtils.doubleRoundStr(4, tempT.getResultData());
|
||||
testMag = PubUtils.doubleRoundStr(4, tempT.getData()) == null ? "/" : PubUtils.doubleRoundStr(4, tempT.getData());
|
||||
if (Objects.nonNull(tempT.getErrorData())) {
|
||||
errorMag = PubUtils.doubleRoundStr(4, tempT.getErrorData().doubleValue());
|
||||
}
|
||||
resultMag = tempT.getIsData() == 1 ? "合格" : tempT.getIsData() == 2 ? "不合格" : "/";
|
||||
unitMag = tempT.getUnit();
|
||||
errorScopeMag = tempT.getRadius();
|
||||
} else if (temp.getCode().equalsIgnoreCase("DUR")) {
|
||||
// 持续时间,需要注意时间单位处理,默认是秒
|
||||
String timeUnit = "s";
|
||||
@@ -1446,6 +1881,9 @@ public class ResultServiceImpl implements IResultService {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!hasData) {
|
||||
return false;
|
||||
}
|
||||
errorScopeMag = dealErrorScope(errorScopeMag).concat(StrUtil.isBlank(unitMag) ? "" : unitMag);
|
||||
errorScopeDur = dealErrorScope(errorScopeDur).concat(StrUtil.isBlank(unitDur) ? "" : unitDur);
|
||||
|
||||
@@ -1471,6 +1909,7 @@ public class ResultServiceImpl implements IResultService {
|
||||
}
|
||||
}
|
||||
keyFillMap.put(ItemReportKeyEnum.RESULT.getKey(), result);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -1523,6 +1962,9 @@ public class ResultServiceImpl implements IResultService {
|
||||
* @param phaseA 相别
|
||||
*/
|
||||
private DetectionData getResultData(Object singleResult, Integer timeInt, String phaseA) {
|
||||
if (Objects.isNull(singleResult)) {
|
||||
return null;
|
||||
}
|
||||
String fieldName = phaseA.toLowerCase().concat("Value");
|
||||
if (Objects.nonNull(timeInt)) {
|
||||
fieldName = fieldName.concat(String.valueOf(timeInt));
|
||||
@@ -1533,6 +1975,9 @@ public class ResultServiceImpl implements IResultService {
|
||||
} catch (Exception exception) {
|
||||
throw new BusinessException("获取对象字段属性失败");
|
||||
}
|
||||
if (StrUtil.isBlank(filedValue)) {
|
||||
return null;
|
||||
}
|
||||
return JSONUtil.toBean(filedValue, DetectionData.class);
|
||||
}
|
||||
|
||||
|
||||
@@ -77,8 +77,8 @@ public class PqScriptDtlsServiceImpl extends ServiceImpl<PqScriptDtlsMapper, PqS
|
||||
private Double fRampOut;
|
||||
// @Value("${Dip.fAfterTime}")
|
||||
// private Double fAfterTime;
|
||||
private static final String waveFluType = "SQU";
|
||||
private static final String waveType = "CPM";
|
||||
private static final String waveFluType = "CPM";
|
||||
private static final String waveType = "SQU";
|
||||
private static final Double fDutyCycle = 50.0;
|
||||
|
||||
|
||||
@@ -398,7 +398,7 @@ public class PqScriptDtlsServiceImpl extends ServiceImpl<PqScriptDtlsMapper, PqS
|
||||
PqScriptDtls dip = setScriptDtls(sourceIssue, i);
|
||||
dip.setValueType(FLICKER);
|
||||
dip.setPhase(phase);
|
||||
if (!"CPM".equals(flickerData.getWaveType())) {
|
||||
if (!"CPM".equals(flickerData.getWaveFluType())) {
|
||||
dip.setChagFre(NumberUtil.round(flickerData.getFChagFre() * 120, -1).doubleValue());
|
||||
} else {
|
||||
dip.setChagFre(flickerData.getFChagFre());
|
||||
@@ -593,10 +593,18 @@ public class PqScriptDtlsServiceImpl extends ServiceImpl<PqScriptDtlsMapper, PqS
|
||||
channelU = channelList.stream().filter(x -> x.getChannelType().contains("U")).collect(Collectors.toList());
|
||||
unbanCheck(info, channelListDTO, channelU, checkArchive);
|
||||
break;
|
||||
case SeqV:
|
||||
channelU = channelList.stream().filter(x -> x.getChannelType().contains("U")).collect(Collectors.toList());
|
||||
unbanSeqCheck(info, channelListDTO, channelU, checkArchive);
|
||||
break;
|
||||
case I_UNBAN:
|
||||
channelI = channelList.stream().filter(x -> x.getChannelType().contains("I")).collect(Collectors.toList());
|
||||
unbanCheck(info, channelListDTO, channelI, checkArchive);
|
||||
break;
|
||||
case SeqA:
|
||||
channelI = channelList.stream().filter(x -> x.getChannelType().contains("I")).collect(Collectors.toList());
|
||||
unbanSeqCheck(info, channelListDTO, channelI, checkArchive);
|
||||
break;
|
||||
case PST:
|
||||
if (CollUtil.isNotEmpty(channelList)) {
|
||||
channelU = channelList.stream().filter(x -> x.getChannelType().contains("U")).collect(Collectors.toList());
|
||||
@@ -826,6 +834,82 @@ public class PqScriptDtlsServiceImpl extends ServiceImpl<PqScriptDtlsMapper, PqS
|
||||
}
|
||||
}
|
||||
|
||||
private void unbanSeqCheck(List<PqScriptDtlsParam.CheckData> info,
|
||||
PqScriptDtlsParam.CheckData channelListDTO,
|
||||
List<PqScriptDtlsParam.ChannelListDTO> list,
|
||||
List<PqScriptCheckData> checkArchive
|
||||
) {
|
||||
PqScriptDtlsParam.CheckData checkData;
|
||||
if (CollUtil.isNotEmpty(list)) {
|
||||
List<PqScriptDtlsParam.ChannelListDTO> channelA = list.stream().filter(x -> x.getChannelType().contains("a")).collect(Collectors.toList());
|
||||
List<PqScriptDtlsParam.ChannelListDTO> channelB = list.stream().filter(x -> x.getChannelType().contains("b")).collect(Collectors.toList());
|
||||
List<PqScriptDtlsParam.ChannelListDTO> channelC = list.stream().filter(x -> x.getChannelType().contains("c")).collect(Collectors.toList());
|
||||
if (CollUtil.isEmpty(checkArchive)) {
|
||||
if (CollUtil.isNotEmpty(channelA) && CollUtil.isNotEmpty(channelB) && CollUtil.isNotEmpty(channelC)) {
|
||||
PqScriptDtlsParam.ChannelListDTO a = channelA.get(0);
|
||||
PqScriptDtlsParam.ChannelListDTO b = channelB.get(0);
|
||||
PqScriptDtlsParam.ChannelListDTO c = channelC.get(0);
|
||||
|
||||
BigDecimal vaMagnitude = new BigDecimal(a.getFAmp());
|
||||
BigDecimal vaAngle = new BigDecimal(a.getFPhase());
|
||||
BigDecimal vbMagnitude = new BigDecimal(b.getFAmp());
|
||||
BigDecimal vbAngle = new BigDecimal(b.getFPhase());
|
||||
BigDecimal vcMagnitude = new BigDecimal(c.getFAmp());
|
||||
BigDecimal vcAngle = new BigDecimal(c.getFPhase());
|
||||
|
||||
// 将幅值和角度转换为复数
|
||||
ThreePhaseUnbalance.Complex va = ThreePhaseUnbalance.fromPolar(vaMagnitude, vaAngle);
|
||||
ThreePhaseUnbalance.Complex vb = ThreePhaseUnbalance.fromPolar(vbMagnitude, vbAngle);
|
||||
ThreePhaseUnbalance.Complex vc = ThreePhaseUnbalance.fromPolar(vcMagnitude, vcAngle);
|
||||
|
||||
// 计算对称分量
|
||||
ThreePhaseUnbalance.Complex[] symmetricalComponents = ThreePhaseUnbalance.calculateSymmetricalComponents(va, vb, vc);
|
||||
ThreePhaseUnbalance.Complex v0 = symmetricalComponents[0]; // 零序分量
|
||||
ThreePhaseUnbalance.Complex v1 = symmetricalComponents[1]; // 正序分量
|
||||
ThreePhaseUnbalance.Complex v2 = symmetricalComponents[2]; // 负序分量
|
||||
|
||||
checkData = new PqScriptDtlsParam.CheckData();
|
||||
checkData.setErrorFlag(channelListDTO.getErrorFlag());
|
||||
checkData.setEnable(channelListDTO.getEnable());
|
||||
checkData.setPhase("A");
|
||||
checkData.setValueType(channelListDTO.getValueType());
|
||||
checkData.setPid(channelListDTO.getPid());
|
||||
checkData.setValue(v1.magnitude().doubleValue());
|
||||
info.add(checkData);
|
||||
|
||||
checkData = new PqScriptDtlsParam.CheckData();
|
||||
checkData.setErrorFlag(channelListDTO.getErrorFlag());
|
||||
checkData.setEnable(channelListDTO.getEnable());
|
||||
checkData.setPhase("B");
|
||||
checkData.setValueType(channelListDTO.getValueType());
|
||||
checkData.setPid(channelListDTO.getPid());
|
||||
checkData.setValue(v2.magnitude().doubleValue());
|
||||
info.add(checkData);
|
||||
|
||||
checkData = new PqScriptDtlsParam.CheckData();
|
||||
checkData.setErrorFlag(channelListDTO.getErrorFlag());
|
||||
checkData.setEnable(channelListDTO.getEnable());
|
||||
checkData.setPhase("C");
|
||||
checkData.setValueType(channelListDTO.getValueType());
|
||||
checkData.setPid(channelListDTO.getPid());
|
||||
checkData.setValue(v0.magnitude().doubleValue());
|
||||
info.add(checkData);
|
||||
}
|
||||
} else {
|
||||
for (PqScriptCheckData checkArciveData : checkArchive) {
|
||||
checkData = new PqScriptDtlsParam.CheckData();
|
||||
checkData.setErrorFlag(checkArciveData.getErrorFlag());
|
||||
checkData.setEnable(checkArciveData.getEnable());
|
||||
checkData.setPhase(checkArciveData.getPhase());
|
||||
checkData.setValueType(checkArciveData.getValueType());
|
||||
checkData.setPid(channelListDTO.getPid());
|
||||
checkData.setValue(checkArciveData.getValue());
|
||||
info.add(checkData);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void rmsCheck(List<PqScriptDtlsParam.CheckData> info,
|
||||
PqScriptDtlsParam.CheckData channelListDTO,
|
||||
List<PqScriptDtlsParam.ChannelListDTO> list,
|
||||
@@ -848,7 +932,18 @@ public class PqScriptDtlsServiceImpl extends ServiceImpl<PqScriptDtlsMapper, PqS
|
||||
if (flyDeltaV) {
|
||||
checkData.setValue(0.0);
|
||||
} else {
|
||||
checkData.setValue(listDTO.getFAmp());
|
||||
// 在这里重新根据谐波、间谐波含有率计算新的幅值
|
||||
Double fAmp = listDTO.getFAmp();
|
||||
if (listDTO.getHarmFlag()) {
|
||||
|
||||
double thd = listDTO.getHarmList().stream().map(harmModel -> harmModel.getFAmp() * harmModel.getFAmp() / 10000).mapToDouble(x -> x).sum();
|
||||
fAmp = Math.sqrt(1 + thd) * fAmp;
|
||||
}
|
||||
if (listDTO.getInHarmFlag()) {
|
||||
double thd = listDTO.getInharmList().stream().map(harmModel -> harmModel.getFAmp() * harmModel.getFAmp() / 10000).mapToDouble(x -> x).sum();
|
||||
fAmp = Math.sqrt(1 + thd) * fAmp;
|
||||
}
|
||||
checkData.setValue(fAmp);
|
||||
}
|
||||
}
|
||||
info.add(checkData);
|
||||
|
||||
@@ -13,7 +13,7 @@ import java.math.RoundingMode;
|
||||
public class ThreePhaseUnbalance {
|
||||
|
||||
// 定义复数类
|
||||
static class Complex {
|
||||
public static class Complex {
|
||||
BigDecimal real; // 复数的实部
|
||||
BigDecimal imag; // 复数的虚部
|
||||
|
||||
@@ -108,10 +108,10 @@ public class ThreePhaseUnbalance {
|
||||
BigDecimal vaMagnitude = new BigDecimal(Ua0);
|
||||
BigDecimal vaAngle = new BigDecimal(thetaA);
|
||||
|
||||
BigDecimal vbMagnitude =new BigDecimal(Ub0);
|
||||
BigDecimal vbMagnitude = new BigDecimal(Ub0);
|
||||
BigDecimal vbAngle = new BigDecimal(thetaB);
|
||||
|
||||
BigDecimal vcMagnitude =new BigDecimal(Uc0);
|
||||
BigDecimal vcMagnitude = new BigDecimal(Uc0);
|
||||
BigDecimal vcAngle = new BigDecimal(thetaC);
|
||||
|
||||
// 将幅值和角度转换为复数
|
||||
@@ -146,12 +146,12 @@ public class ThreePhaseUnbalance {
|
||||
BigDecimal vaAngle = new BigDecimal(0);
|
||||
|
||||
System.out.println("请输入B相电压的幅值(单位:V):");
|
||||
BigDecimal vbMagnitude =new BigDecimal(57.74*0.9);
|
||||
BigDecimal vbMagnitude = new BigDecimal(57.74 * 0.9);
|
||||
System.out.println("请输入B相电压的相位角(单位:度):");
|
||||
BigDecimal vbAngle = new BigDecimal(-122);
|
||||
|
||||
System.out.println("请输入C相电压的幅值(单位:V):");
|
||||
BigDecimal vcMagnitude =new BigDecimal(57.74);
|
||||
BigDecimal vcMagnitude = new BigDecimal(57.74);
|
||||
System.out.println("请输入C相电压的相位角(单位:度):");
|
||||
BigDecimal vcAngle = new BigDecimal(118);
|
||||
|
||||
|
||||
@@ -27,6 +27,11 @@ public class DevTypeParam {
|
||||
@NotBlank(message = DetectionValidMessage.ICD_NOT_BLANK)
|
||||
private String icd;
|
||||
|
||||
@ApiModelProperty(value = "设备关联的PQDIF", required = true)
|
||||
@NotBlank(message = "PQDIF不能为空")
|
||||
@Pattern(regexp = PatternRegex.SYSTEM_ID, message = DetectionValidMessage.ID_FORMAT_ERROR)
|
||||
private String pqdif;
|
||||
|
||||
@ApiModelProperty(value = "工作电源", required = true)
|
||||
@NotBlank(message = DetectionValidMessage.POWER_NOT_BLANK)
|
||||
private String power;
|
||||
|
||||
@@ -37,6 +37,8 @@ public class DevType extends BaseEntity {
|
||||
/**
|
||||
* 工作电源
|
||||
*/
|
||||
private String pqdif;
|
||||
|
||||
private String power;
|
||||
|
||||
/**
|
||||
|
||||
@@ -7,6 +7,8 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.njcn.common.pojo.enums.common.DataStateEnum;
|
||||
import com.njcn.common.pojo.exception.BusinessException;
|
||||
import com.njcn.gather.pqdif.pojo.po.PqPqdifPath;
|
||||
import com.njcn.gather.pqdif.service.IPqPqdifPathService;
|
||||
import com.njcn.gather.pojo.enums.DetectionResponseEnum;
|
||||
import com.njcn.gather.system.dictionary.pojo.po.DictData;
|
||||
import com.njcn.gather.system.dictionary.service.IDictDataService;
|
||||
@@ -36,6 +38,9 @@ public class DevTypeServiceImpl extends ServiceImpl<DevTypeMapper, DevType> impl
|
||||
@Resource
|
||||
private IDictDataService dictDataService;
|
||||
|
||||
@Resource
|
||||
private IPqPqdifPathService pqPqdifPathService;
|
||||
|
||||
|
||||
@Override
|
||||
public List<DevType> listAll() {
|
||||
@@ -76,6 +81,7 @@ public class DevTypeServiceImpl extends ServiceImpl<DevTypeMapper, DevType> impl
|
||||
public boolean addDevType(DevTypeParam addParam) {
|
||||
addParam.setName(addParam.getName().trim());
|
||||
this.checkRepeat(addParam, false);
|
||||
this.checkPqdif(addParam.getPqdif());
|
||||
DevType devType = new DevType();
|
||||
BeanUtil.copyProperties(addParam, devType);
|
||||
devType.setState(DataStateEnum.ENABLE.getCode());
|
||||
@@ -87,6 +93,7 @@ public class DevTypeServiceImpl extends ServiceImpl<DevTypeMapper, DevType> impl
|
||||
public boolean updateDevType(DevTypeParam.UpdateParam updateParam) {
|
||||
updateParam.setName(updateParam.getName().trim());
|
||||
this.checkRepeat(updateParam, true);
|
||||
this.checkPqdif(updateParam.getPqdif());
|
||||
DevType devType = new DevType();
|
||||
BeanUtil.copyProperties(updateParam, devType);
|
||||
return this.updateById(devType);
|
||||
@@ -121,4 +128,14 @@ public class DevTypeServiceImpl extends ServiceImpl<DevTypeMapper, DevType> impl
|
||||
throw new BusinessException(DetectionResponseEnum.DEV_TYPE_NAME_REPEAT);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkPqdif(String pqdifId) {
|
||||
PqPqdifPath pqdif = pqPqdifPathService.lambdaQuery()
|
||||
.eq(PqPqdifPath::getId, pqdifId)
|
||||
.eq(PqPqdifPath::getState, DataStateEnum.ENABLE.getCode())
|
||||
.one();
|
||||
if (Objects.isNull(pqdif)) {
|
||||
throw new BusinessException(DetectionResponseEnum.PQDIF_NOT_EXIST);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
package com.njcn.gather.source.pojo;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.njcn.gather.source.pojo.param.PqSourceParam;
|
||||
import com.njcn.gather.source.pojo.po.PqSource;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import javax.validation.Validation;
|
||||
import javax.validation.Validator;
|
||||
import javax.validation.constraints.DecimalMin;
|
||||
import java.lang.reflect.Field;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
public class PqSourceCapacityFieldsTest {
|
||||
|
||||
private final Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
|
||||
|
||||
@Test
|
||||
public void shouldExposeExplicitCapacityColumnMappings() throws Exception {
|
||||
Field maxVoltage = PqSource.class.getDeclaredField("maxVoltage");
|
||||
Field maxCurrent = PqSource.class.getDeclaredField("maxCurrent");
|
||||
|
||||
Assert.assertEquals("Max_Voltage", maxVoltage.getAnnotation(TableField.class).value());
|
||||
Assert.assertEquals("Max_Current", maxCurrent.getAnnotation(TableField.class).value());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldExposeNonNegativeCapacityValidation() throws Exception {
|
||||
Field maxVoltage = PqSourceParam.class.getDeclaredField("maxVoltage");
|
||||
Field maxCurrent = PqSourceParam.class.getDeclaredField("maxCurrent");
|
||||
|
||||
Assert.assertEquals("0", maxVoltage.getAnnotation(DecimalMin.class).value());
|
||||
Assert.assertEquals("0", maxCurrent.getAnnotation(DecimalMin.class).value());
|
||||
|
||||
PqSourceParam.UpdateParam param = new PqSourceParam.UpdateParam();
|
||||
param.setId("12345678901234567890123456789012");
|
||||
param.setPattern("12345678901234567890123456789012");
|
||||
param.setType("12345678901234567890123456789012");
|
||||
param.setDevType("12345678901234567890123456789012");
|
||||
param.setMaxVoltage(new BigDecimal("-1"));
|
||||
|
||||
Assert.assertFalse(validator.validate(param).isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldCopyCapacityFieldsFromRequestToEntity() {
|
||||
PqSourceParam.UpdateParam param = new PqSourceParam.UpdateParam();
|
||||
param.setMaxVoltage(new BigDecimal("220.00"));
|
||||
param.setMaxCurrent(new BigDecimal("5.00"));
|
||||
|
||||
PqSource source = new PqSource();
|
||||
BeanUtil.copyProperties(param, source);
|
||||
|
||||
Assert.assertEquals(new BigDecimal("220.00"), source.getMaxVoltage());
|
||||
Assert.assertEquals(new BigDecimal("5.00"), source.getMaxCurrent());
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
server:
|
||||
port: 18093
|
||||
port: 18092
|
||||
spring:
|
||||
application:
|
||||
name: entrance
|
||||
@@ -7,9 +7,9 @@ spring:
|
||||
druid:
|
||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||
url: jdbc:mysql://192.168.1.24:13306/pqs9100?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true
|
||||
# username: root
|
||||
# password: njcnpqs
|
||||
# url: jdbc:mysql://127.0.0.1:3306/pqs9100?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true
|
||||
# username: root
|
||||
# password: njcnpqs
|
||||
# url: jdbc:mysql://127.0.0.1:3306/pqs9100?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true
|
||||
username: root
|
||||
password: njcnpqs
|
||||
#初始化建立物理连接的个数、最小、最大连接数
|
||||
@@ -60,12 +60,15 @@ socket:
|
||||
# port: 61000
|
||||
|
||||
webSocket:
|
||||
port: 7777
|
||||
port: 7778
|
||||
|
||||
#源参数下发,暂态数据默认值
|
||||
sntp:
|
||||
port: 123
|
||||
|
||||
Dip:
|
||||
# 暂态前时间(s)
|
||||
# fPreTime: 2f
|
||||
# 暂态前时间(s)
|
||||
# fPreTime: 2f
|
||||
#写入时间(s)
|
||||
fRampIn: 0.001f
|
||||
#写出时间(s)
|
||||
@@ -83,8 +86,8 @@ Dip:
|
||||
# homeDir: D:\logs
|
||||
# commonLevel: info
|
||||
report:
|
||||
# template: D:\template
|
||||
# reportDir: D:\report
|
||||
# template: D:\template
|
||||
# reportDir: D:\report
|
||||
dateFormat: yyyy年MM月dd日
|
||||
#data:
|
||||
# homeDir: D:\data
|
||||
@@ -106,6 +109,9 @@ qr:
|
||||
db:
|
||||
type: mysql
|
||||
|
||||
icd:
|
||||
txt-dir: D:\icd-txt
|
||||
|
||||
|
||||
# 比对录波需要的配置,晚点再做优化
|
||||
# 系统配置
|
||||
@@ -125,3 +131,6 @@ power-quality:
|
||||
activate:
|
||||
private-key: "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCcUyYhVqczGxblL+o/xZzF/8nf+LjrfUE/dS1aRHM7uMDD0cgCArhjtfneFePrMxt+Z7W8yNBzSarub8qsfhaVNikV7Es7oaeTygfjQXTi2n4AFkir3fM07J08RpWhl5M8f8uWTCuvFUYAw00gq55typqmnbkmJa2VIUy/iQf+cMCP7abz4/jNhUzUR3qA7TV4oMRgTdIEDUp63YF8dOC+JH8XxYrCVeHXV6fLCwmesdMzl0lB2VTEKMfLbXhOmF5g7P9y/16VCcN8UBuZlbyYfn+GAxJOSbeHi5HshOKfoSuD7Jz+3WQZpNavOWjIFExKIU38/CvnJCOP7XBCqpSTAgMBAAECggEAYeWokWRE3TpvwiOZnUpR/aVMdVi75a3ROL5XIpqPV61B+t/bU3cEpl0GF9C5pUeiRi0IoStZb3mI9D1KPW/REKyUWkhabQO1gFYbTnRlkNOn6MILzKX4cwJjDaZeeo4EBPU7N+qHyOOXrU6hdH5FfxhMdV983ajm5eeuupxER1C2kAcIklTeVpTX6EKOgZb5LBp5ssOVm2P42pOauvcRozRcvZmqnErXmukv0H4l3EVNt4rHpTn9riHUC63e8JfiYzVaF6zuNUxv6nHEft0/SRMw11XSTnNfDzcKqgjz6ksFBS/6eQQYKESk+ONC53HUuYHFAknkwsPupDCT2W8FIQKBgQDLHT/xCU3nxGr4vFKBDNaO2D5oK20ECbBO4oDvLWWmQG7f+6TsMy8PgVdMnoL4RfqGlwFAKEpS6KVFHnBVqnNEhcdy9uCI7x7Xx8UnyUtxj1EDTm76uta9Ki9OrlqB6tImDM9+Ya3vGktW37ht4WOx2OsJRhG1dbf6RLwFlH7DWwKBgQDFBxvi5I1BR6hg6Tj7xd2SqOT2Y+BED3xuSYENhWbmMhLJDResaB7mjztbxlYaY2mOE0holWm2uDmVFFhMh4jYXik4hYH8nmDzq9mDpZCZ9pyjYqnAP8THoAa8EbgrUWB8A6BPH4iL3KbMnBfBKY0pIr2xrvnjQjNBAgta7KDRKQKBgCe6oe4wxrdF2TKsC2tIqpMoQxS3Icy/ZGgZr+SYuaBKTCWtoDW/UT40K3JGMxIDBhzbXphBCUCsVt9tM8Xd4EwP6tJW7dZ7B0pnve2pVwNwaAVAiz6p2yUHIle+jN+Koe5lZRSwYIg7WW81tWpwwsJfzqFyvjYDP6hJV4mz4ROvAoGAaRcdnKvjXApomShMqJ4lTPChD3q+SA8qg3jZSOj6tZXHx00gb2kp8jg7pPvpOTIFPy6x1Ha9aCRjMk0ju84fA6lVuzwa1S907wOehUVuF3Eeo1cgy9Y3k3KbpPyeixxgpkUY4JslLdSHc2NemD0dee951qhJyRmqVOZOQDUuoeECgYEAqBw2cAFk3vM97WY06TSldGA8ajVHx3BYRjj+zl62NTQthy8fw3tqxb3c5e8toOmZWKjZvDhg2TRLhsDDQWEYg3LZG87REqVIjgEPcpjNLidjygGX8n3JF2o0O5I/EMvl0s/+LVQONfduOBvhwDqr8QNisbLsyneiAq7umewMolo="
|
||||
public-key: "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnFMmIVanMxsW5S/qP8Wcxf/J3/i4631BP3UtWkRzO7jAw9HIAgK4Y7X53hXj6zMbfme1vMjQc0mq7m/KrH4WlTYpFexLO6Gnk8oH40F04tp+ABZIq93zNOydPEaVoZeTPH/LlkwrrxVGAMNNIKuebcqapp25JiWtlSFMv4kH/nDAj+2m8+P4zYVM1Ed6gO01eKDEYE3SBA1Ket2BfHTgviR/F8WKwlXh11enywsJnrHTM5dJQdlUxCjHy214TpheYOz/cv9elQnDfFAbmZW8mH5/hgMSTkm3h4uR7ITin6Erg+yc/t1kGaTWrzloyBRMSiFN/Pwr5yQjj+1wQqqUkwIDAQAB"
|
||||
|
||||
dataCheck:
|
||||
enable: false
|
||||
@@ -1,170 +0,0 @@
|
||||
package com.njcn;
|
||||
|
||||
import com.njcn.gather.tools.comtrade.comparewave.core.model.CompareWaveDTO;
|
||||
import com.njcn.gather.tools.comtrade.comparewave.service.ICompareWaveService;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.InputStream;
|
||||
|
||||
|
||||
/**
|
||||
* 流式文件分析测试
|
||||
* 测试从本地文件读取并转换为流进行分析
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(classes = com.njcn.gather.EntranceApplication.class)
|
||||
public class AnalysisServiceStreamTest {
|
||||
|
||||
@Autowired
|
||||
private ICompareWaveService compareWaveServiceImpl;
|
||||
|
||||
// 测试文件路径 - 请根据实际情况修改
|
||||
private static final String SOURCE_CFG_PATH = "C:\\Users\\Administrator\\Desktop\\wave\\192.168.1.241\\PQ_PQLD2_000177_20251028_112422_833.cfg";
|
||||
private static final String SOURCE_DAT_PATH = "C:\\Users\\Administrator\\Desktop\\wave\\192.168.1.241\\PQ_PQLD2_000177_20251028_112422_833.dat";
|
||||
private static final String TARGET_CFG_PATH = "C:\\Users\\Administrator\\Desktop\\wave\\192.168.1.242\\PQ_PQLD2_000238_20251028_112422_518.cfg";
|
||||
private static final String TARGET_DAT_PATH = "C:\\Users\\Administrator\\Desktop\\wave\\192.168.1.242\\PQ_PQLD2_000238_20251028_112422_518.dat";
|
||||
|
||||
|
||||
// private static final String SOURCE_CFG_PATH = "F:\\hatch\\wavecompare\\数据比对\\统计数据1\\B码\\217\\PQMonitor_PQM1_000006_20200430_115517_889.cfg";
|
||||
// private static final String SOURCE_DAT_PATH = "F:\\hatch\\wavecompare\\数据比对\\统计数据1\\B码\\217\\PQMonitor_PQM1_000006_20200430_115517_889.dat";
|
||||
// private static final String TARGET_CFG_PATH = "F:\\hatch\\wavecompare\\数据比对\\统计数据1\\B码\\216\\PQMonitor_PQM1_000006_20200430_115515_479.cfg";
|
||||
// private static final String TARGET_DAT_PATH = "F:\\hatch\\wavecompare\\数据比对\\统计数据1\\B码\\216\\PQMonitor_PQM1_000006_20200430_115515_479.dat";
|
||||
|
||||
// 输出路径
|
||||
private static final String OUTPUT_PATH = "./test-output/";
|
||||
|
||||
/**
|
||||
* 测试使用文件流进行电能质量分析
|
||||
*/
|
||||
@Test
|
||||
public void testAnalyzeWithStreams() throws Exception {
|
||||
System.out.println("========================================");
|
||||
System.out.println("开始测试流式文件分析");
|
||||
System.out.println("========================================");
|
||||
|
||||
// 验证文件是否存在
|
||||
checkFileExists(SOURCE_CFG_PATH, "源CFG文件");
|
||||
checkFileExists(SOURCE_DAT_PATH, "源DAT文件");
|
||||
checkFileExists(TARGET_CFG_PATH, "目标CFG文件");
|
||||
checkFileExists(TARGET_DAT_PATH, "目标DAT文件");
|
||||
|
||||
// 读取本地文件并创建输入流
|
||||
try (InputStream sourceCfgStream = new FileInputStream(SOURCE_CFG_PATH);
|
||||
InputStream sourceDatStream = new FileInputStream(SOURCE_DAT_PATH);
|
||||
InputStream targetCfgStream = new FileInputStream(TARGET_CFG_PATH);
|
||||
InputStream targetDatStream = new FileInputStream(TARGET_DAT_PATH)) {
|
||||
|
||||
System.out.println("成功创建文件输入流");
|
||||
System.out.println("源CFG文件: " + SOURCE_CFG_PATH);
|
||||
System.out.println("源DAT文件: " + SOURCE_DAT_PATH);
|
||||
System.out.println("目标CFG文件: " + TARGET_CFG_PATH);
|
||||
System.out.println("目标DAT文件: " + TARGET_DAT_PATH);
|
||||
System.out.println("输出路径: " + OUTPUT_PATH);
|
||||
|
||||
// 创建输出目录
|
||||
File outputDir = new File(OUTPUT_PATH);
|
||||
if (!outputDir.exists()) {
|
||||
outputDir.mkdirs();
|
||||
System.out.println("创建输出目录: " + outputDir.getAbsolutePath());
|
||||
}
|
||||
|
||||
// 执行分析,使用星型接线方式(0)
|
||||
System.out.println("\n开始执行电能质量分析(星型接线)...");
|
||||
long startTime = System.currentTimeMillis();
|
||||
|
||||
CompareWaveDTO result = compareWaveServiceImpl.analyzeAndCompareWithStreams(
|
||||
sourceCfgStream,
|
||||
sourceDatStream,
|
||||
targetCfgStream,
|
||||
targetDatStream,
|
||||
// 接线方式: 0=星型接线, 1=V型接线
|
||||
0
|
||||
);
|
||||
|
||||
long endTime = System.currentTimeMillis();
|
||||
long duration = endTime - startTime;
|
||||
|
||||
// 输出分析结果
|
||||
System.out.println("========================================");
|
||||
System.out.println("分析完成!");
|
||||
System.out.println("总耗时: " + duration + " ms (" + String.format("%.2f", duration / 1000.0) + " 秒)");
|
||||
|
||||
|
||||
|
||||
|
||||
System.out.println("========================================");
|
||||
System.out.println("流式文件分析测试完成!");
|
||||
System.out.println("========================================");
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试段信息保留模式的波形分析(新方法)
|
||||
* <p>不进行跨段的统一降采样,保留每个段的原始采样率(超过512才降到256)</p>
|
||||
* <p>跨段的窗口会被丢弃,只计算不跨段的窗口</p>
|
||||
*/
|
||||
@Test
|
||||
public void testAnalyzeWithSegmentPreservation() throws Exception {
|
||||
System.out.println("========================================");
|
||||
System.out.println("开始执行电能质量分析(段信息保留模式)...");
|
||||
System.out.println("========================================");
|
||||
|
||||
// 检查文件是否存在
|
||||
checkFileExists(SOURCE_CFG_PATH, "源CFG文件");
|
||||
checkFileExists(SOURCE_DAT_PATH, "源DAT文件");
|
||||
checkFileExists(TARGET_CFG_PATH, "目标CFG文件");
|
||||
checkFileExists(TARGET_DAT_PATH, "目标DAT文件");
|
||||
|
||||
long startTime = System.currentTimeMillis();
|
||||
|
||||
try (InputStream sourceCfgStream = new FileInputStream(SOURCE_CFG_PATH);
|
||||
InputStream sourceDatStream = new FileInputStream(SOURCE_DAT_PATH);
|
||||
InputStream targetCfgStream = new FileInputStream(TARGET_CFG_PATH);
|
||||
InputStream targetDatStream = new FileInputStream(TARGET_DAT_PATH)) {
|
||||
|
||||
CompareWaveDTO result = compareWaveServiceImpl.analyzeWithSegmentPreservation(
|
||||
sourceCfgStream,
|
||||
sourceDatStream,
|
||||
targetCfgStream,
|
||||
targetDatStream,
|
||||
0 // 接线方式: 0=星型接线, 1=V型接线
|
||||
);
|
||||
|
||||
long endTime = System.currentTimeMillis();
|
||||
long duration = endTime - startTime;
|
||||
|
||||
// 输出分析结果
|
||||
System.out.println("========================================");
|
||||
System.out.println("分析完成!");
|
||||
System.out.println("总耗时: " + duration + " ms (" + String.format("%.2f", duration / 1000.0) + " 秒)");
|
||||
System.out.println("源文件有效窗口数: " + (result.getSourceResults() != null ? result.getSourceResults().size() : 0));
|
||||
System.out.println("目标文件有效窗口数: " + (result.getTargetResults() != null ? result.getTargetResults().size() : 0));
|
||||
System.out.println("========================================");
|
||||
System.out.println("段信息保留模式测试完成!");
|
||||
System.out.println("========================================");
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查文件是否存在
|
||||
*/
|
||||
private void checkFileExists(String filePath, String description) {
|
||||
File file = new File(filePath);
|
||||
if (!file.exists()) {
|
||||
System.err.println("警告: " + description + " 不存在: " + filePath);
|
||||
System.err.println("请确保文件路径正确,或修改测试中的文件路径");
|
||||
} else {
|
||||
System.out.println(description + " 存在: " + filePath);
|
||||
System.out.println(" 文件大小: " + file.length() + " bytes");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
package com.njcn;
|
||||
|
||||
import com.njcn.gather.EntranceApplication;
|
||||
import com.njcn.gather.report.service.IPqReportService;
|
||||
import com.njcn.http.util.RestTemplateUtil;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.test.context.web.WebAppConfiguration;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
|
||||
/**
|
||||
* @author hongawen
|
||||
* @version 1.0.0
|
||||
* @date 2021年12月10日 15:05
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@WebAppConfiguration
|
||||
@SpringBootTest(classes = EntranceApplication.class)
|
||||
public class BaseJunitTest {
|
||||
|
||||
@Autowired
|
||||
private IPqReportService pqReportService;
|
||||
|
||||
@Autowired
|
||||
private RestTemplateUtil restTemplateUtil;
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
File file = new File("D:\\report\\PQS_882B4\\5555.docx");
|
||||
|
||||
try{
|
||||
ResponseEntity<String> stringResponseEntity = restTemplateUtil.uploadFile("http://localhost:18082/api/file/upload",file);
|
||||
}catch (Exception runtimeException){
|
||||
System.out.println(runtimeException.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,175 +0,0 @@
|
||||
package com.njcn;
|
||||
|
||||
import com.njcn.gather.tools.report.util.Docx4jUtil;
|
||||
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
|
||||
import org.docx4j.openpackaging.parts.WordprocessingML.MainDocumentPart;
|
||||
import org.docx4j.wml.ObjectFactory;
|
||||
import org.docx4j.wml.P;
|
||||
import org.docx4j.wml.Tbl;
|
||||
|
||||
import javax.xml.bind.JAXBElement;
|
||||
import java.io.File;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 动态表格生成测试
|
||||
*
|
||||
* @author hongawen
|
||||
* @version 1.0
|
||||
* @date 2025/9/21
|
||||
*/
|
||||
public class DynamicTableTest {
|
||||
|
||||
public static void main(String[] args) {
|
||||
try {
|
||||
// 测试场景1:2个回路,7个检测项目(与result.png一致)
|
||||
testScenario1();
|
||||
|
||||
// 测试场景2:1个回路,只检测电压和频率
|
||||
testScenario2();
|
||||
|
||||
// 测试场景3:4个回路,多个检测项目
|
||||
testScenario3();
|
||||
|
||||
System.out.println("所有测试场景执行完成!");
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试场景1:2个回路,7个检测项目(模拟result.png的数据)
|
||||
*/
|
||||
public static void testScenario1() throws Exception {
|
||||
System.out.println("=== 测试场景1:2个回路,7个检测项目 ===");
|
||||
|
||||
// 创建Word文档
|
||||
WordprocessingMLPackage wordPackage = WordprocessingMLPackage.createPackage();
|
||||
MainDocumentPart mainDocumentPart = wordPackage.getMainDocumentPart();
|
||||
ObjectFactory factory = new ObjectFactory();
|
||||
|
||||
// 1. 添加标题
|
||||
P titleP = factory.createP();
|
||||
Docx4jUtil.createTitle(factory, titleP, "检测结果(场景1:2回路7项目)", 32, true);
|
||||
mainDocumentPart.getContent().add(titleP);
|
||||
|
||||
// 2. 检测项目配置
|
||||
List<String> testItems = Arrays.asList(
|
||||
"电压",
|
||||
"电压不平衡度",
|
||||
"电流不平衡度",
|
||||
"谐波电压",
|
||||
"谐波电流",
|
||||
"间谐波电压",
|
||||
"短时间闪变"
|
||||
);
|
||||
|
||||
// 3. 检测结果数据(模拟result.png中的数据)
|
||||
String[][] testResults = {
|
||||
{"不合格", "不合格"}, // 电压
|
||||
{"无法比较", "无法比较"}, // 电压不平衡度
|
||||
{"合格", "合格"}, // 电流不平衡度
|
||||
{"合格", "合格"}, // 谐波电压
|
||||
{"合格", "合格"}, // 谐波电流
|
||||
{"不合格", "不合格"}, // 间谐波电压
|
||||
{"无法比较", "无法比较"} // 短时间闪变
|
||||
};
|
||||
|
||||
// 4. 定义回路名称
|
||||
List<String> circuitNames = Arrays.asList("测量回路 1", "测量回路 2");
|
||||
|
||||
// 5. 生成动态表格(包含说明内容)
|
||||
JAXBElement<Tbl> table = Docx4jUtil.createDynamicTestResultTable(
|
||||
factory, testItems, circuitNames, testResults, "不合格",
|
||||
"部分值", "200", "去除最大最小值");
|
||||
mainDocumentPart.getContent().add(table);
|
||||
|
||||
// 6. 保存文档
|
||||
File outputFile = new File("检测结果_场景1_2回路7项目.docx");
|
||||
wordPackage.save(outputFile);
|
||||
System.out.println("文档已保存:" + outputFile.getAbsolutePath());
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试场景2:1个回路,只检测电压和频率
|
||||
*/
|
||||
public static void testScenario2() throws Exception {
|
||||
System.out.println("=== 测试场景2:1个回路,2个检测项目 ===");
|
||||
|
||||
WordprocessingMLPackage wordPackage = WordprocessingMLPackage.createPackage();
|
||||
MainDocumentPart mainDocumentPart = wordPackage.getMainDocumentPart();
|
||||
ObjectFactory factory = new ObjectFactory();
|
||||
|
||||
// 标题
|
||||
P titleP = factory.createP();
|
||||
Docx4jUtil.createTitle(factory, titleP, "检测结果(场景2:1回路2项目)", 32, true);
|
||||
mainDocumentPart.getContent().add(titleP);
|
||||
|
||||
// 简单的检测项目
|
||||
List<String> testItems = Arrays.asList("电压", "频率");
|
||||
|
||||
// 1个回路的检测结果
|
||||
String[][] testResults = {
|
||||
{"不合格"}, // 电压
|
||||
{"合格"} // 频率
|
||||
};
|
||||
|
||||
// 定义回路名称
|
||||
List<String> circuitNames = Arrays.asList("#1母线");
|
||||
|
||||
// 生成表格(包含说明内容)
|
||||
JAXBElement<Tbl> table = Docx4jUtil.createDynamicTestResultTable(
|
||||
factory, testItems, circuitNames, testResults, "不合格",
|
||||
"任意值", "100", "取第一个满足条件的数据");
|
||||
mainDocumentPart.getContent().add(table);
|
||||
|
||||
File outputFile = new File("检测结果_场景2_1回路2项目.docx");
|
||||
wordPackage.save(outputFile);
|
||||
System.out.println("文档已保存:" + outputFile.getAbsolutePath());
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试场景3:4个回路,多个检测项目
|
||||
*/
|
||||
public static void testScenario3() throws Exception {
|
||||
System.out.println("=== 测试场景3:4个回路,5个检测项目 ===");
|
||||
|
||||
WordprocessingMLPackage wordPackage = WordprocessingMLPackage.createPackage();
|
||||
MainDocumentPart mainDocumentPart = wordPackage.getMainDocumentPart();
|
||||
ObjectFactory factory = new ObjectFactory();
|
||||
|
||||
// 标题
|
||||
P titleP = factory.createP();
|
||||
Docx4jUtil.createTitle(factory, titleP, "检测结果(场景3:4回路5项目)", 32, true);
|
||||
mainDocumentPart.getContent().add(titleP);
|
||||
|
||||
// 检测项目
|
||||
List<String> testItems = Arrays.asList(
|
||||
"电压", "频率", "电压不平衡度", "谐波电压", "间谐波电压"
|
||||
);
|
||||
|
||||
// 4个回路的检测结果
|
||||
String[][] testResults = {
|
||||
{"不合格", "合格", "合格", "不合格"}, // 电压
|
||||
{"合格", "合格", "合格", "合格"}, // 频率
|
||||
{"无法比较", "无法比较", "合格", "合格"}, // 电压不平衡度
|
||||
{"合格", "不合格", "合格", "合格"}, // 谐波电压
|
||||
{"不合格", "不合格", "不合格", "合格"} // 间谐波电压
|
||||
};
|
||||
|
||||
// 定义回路名称(自定义名称示例)
|
||||
List<String> circuitNames = Arrays.asList("主变高压侧", "主变低压侧", "备用线路1", "备用线路2");
|
||||
|
||||
// 生成表格(包含说明内容)
|
||||
JAXBElement<Tbl> table = Docx4jUtil.createDynamicTestResultTable(
|
||||
factory, testItems, circuitNames, testResults, "不合格",
|
||||
"平均值", "300", "取算术平均值");
|
||||
mainDocumentPart.getContent().add(table);
|
||||
|
||||
File outputFile = new File("检测结果_场景3_4回路5项目.docx");
|
||||
wordPackage.save(outputFile);
|
||||
System.out.println("文档已保存:" + outputFile.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
package com.njcn;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.njcn.gather.detection.pojo.vo.DetectionData;
|
||||
import com.njcn.gather.device.pojo.vo.PqDevVO;
|
||||
import com.njcn.gather.device.service.IPqDevService;
|
||||
import com.njcn.gather.device.service.impl.PqDevServiceImpl;
|
||||
import com.njcn.gather.report.pojo.DevReportParam;
|
||||
import com.njcn.gather.report.pojo.result.ContrastTestResult;
|
||||
import com.njcn.gather.report.service.IPqReportService;
|
||||
import com.njcn.gather.result.pojo.vo.MonitorResultVO;
|
||||
import com.njcn.gather.result.service.impl.ResultServiceImpl;
|
||||
import com.njcn.gather.storage.pojo.po.ContrastHarmonicResult;
|
||||
import com.njcn.gather.system.dictionary.pojo.po.DictTree;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.ArgumentMatchers.*;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* ResultServiceImpl 测试类
|
||||
* 专门测试 getContrastResultHarm 方法
|
||||
*
|
||||
* @author test
|
||||
* @date 2025-01-18
|
||||
*/
|
||||
@Slf4j
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(classes = com.njcn.gather.EntranceApplication.class)
|
||||
public class ResultServiceImplTest extends BaseJunitTest {
|
||||
|
||||
@Autowired
|
||||
private ResultServiceImpl resultService;
|
||||
|
||||
@Autowired
|
||||
private IPqDevService pqDevService;
|
||||
|
||||
@Autowired
|
||||
private IPqReportService pqReportService;
|
||||
|
||||
/**
|
||||
* 测试 getContrastResultHarm 方法 - 正常情况,所有数据合格
|
||||
*/
|
||||
@Test
|
||||
public void testGetContrastResultHarm_AllQualified() throws Exception {
|
||||
log.info("==================== 开始测试:所有数据合格场景 ====================");
|
||||
|
||||
// 准备测试数据
|
||||
DevReportParam devReportParam = new DevReportParam();
|
||||
devReportParam.setPlanId("307a4b57abe84746acec5fd62f58e789");
|
||||
devReportParam.setPlanCode("1");
|
||||
devReportParam.setDevId("11b1a3cadafd4d51986d5c88815c2ece");
|
||||
devReportParam.setDevIdList(Collections.singletonList(devReportParam.getDevId()));
|
||||
// PqDevVO pqDevVO = pqDevService.getPqDevById(devReportParam.getDevId());
|
||||
// Map<Integer, List<ContrastTestResult>> contrastResultHarm = resultService.getContrastResultForReport(devReportParam, pqDevVO);
|
||||
|
||||
|
||||
pqReportService.generateReport(devReportParam);
|
||||
System.out.println(1);
|
||||
System.out.println(1);
|
||||
System.out.println(1);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,140 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration scan="true" scanPeriod="20 seconds" debug="false">
|
||||
<include resource="org/springframework/boot/logging/logback/defaults.xml" />
|
||||
|
||||
<!-- 测试环境下明确指定日志配置,避免IS_UNDEFINED问题 -->
|
||||
<property name="log.projectName" value="entrance"/>
|
||||
<property name="logHomeDir" value="D:\logs"/>
|
||||
<property name="logCommonLevel" value="info"/>
|
||||
|
||||
<conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter" />
|
||||
<conversionRule conversionWord="wex"
|
||||
converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter" />
|
||||
<conversionRule conversionWord="ec"
|
||||
converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter" />
|
||||
|
||||
|
||||
<!--日志输出格式-->
|
||||
<property name="log.pattern"
|
||||
value="|-%d{yyyy-MM-dd HH:mm:ss.SSS} ${LOG_LEVEL_PATTERN:-%level} ${log.projectName} -- %t %logger{100}.%M ==> %m%n${Log_EXCEPTION_CONVERSION_WORD:-%ec}}}"/>
|
||||
<property name="log.maxHistory" value="30"/>
|
||||
|
||||
<!--客户端输出日志-->
|
||||
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
|
||||
<charset>UTF-8</charset>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<!--系统中常规的debug日志-->
|
||||
<!-- 滚动记录文件,先将日志记录到指定文件,当符合某个条件时,将日志记录到其他文件 RollingFileAppender -->
|
||||
<appender name="DEBUG" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<file>
|
||||
${logHomeDir}/${log.projectName}/debug/debug.log
|
||||
</file>
|
||||
<!-- 如果日志级别等于配置级别,过滤器会根据onMath 和 onMismatch接收或拒绝日志。 -->
|
||||
<filter class="ch.qos.logback.classic.filter.LevelFilter">
|
||||
<!-- 设置过滤级别 -->
|
||||
<level>DEBUG</level>
|
||||
<!-- 用于配置符合过滤条件的操作 -->
|
||||
<onMatch>ACCEPT</onMatch>
|
||||
<!-- 用于配置不符合过滤条件的操作 -->
|
||||
<onMismatch>DENY</onMismatch>
|
||||
</filter>
|
||||
<!-- 最常用的滚动策略,它根据时间来制定滚动策略.既负责滚动也负责触发滚动 SizeAndTimeBasedRollingPolicy-->
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
|
||||
<!--日志输出位置 可相对、和绝对路径 -->
|
||||
<fileNamePattern>
|
||||
${logHomeDir}/${log.projectName}/debug/debug.log.%d{yyyy-MM-dd}.%i.log
|
||||
</fileNamePattern>
|
||||
<maxFileSize>10MB</maxFileSize>
|
||||
<!-- 可选节点,控制保留的归档文件的最大数量,超出数量就删除旧文件,假设设置每个月滚动,且<maxHistory>是6,
|
||||
则只保存最近6个月的文件,删除之前的旧文件。注意,删除旧文件是,那些为了归档而创建的目录也会被删除 -->
|
||||
<maxHistory>${log.maxHistory:-30}</maxHistory>
|
||||
<!--重启清理日志文件-->
|
||||
<!-- <cleanHistoryOnStart>true</cleanHistoryOnStart>-->
|
||||
<!--每个文件最多100MB,保留N天的历史记录,但最多20GB-->
|
||||
<!--<totalSizeCap>20GB</totalSizeCap>-->
|
||||
<!--日志文件最大的大小-->
|
||||
<!--<MaxFileSize>${log.maxSize}</MaxFileSize>-->
|
||||
</rollingPolicy>
|
||||
<encoder>
|
||||
<pattern>
|
||||
${log.pattern}
|
||||
</pattern>
|
||||
<charset>UTF-8</charset>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<!--系统中常规的info日志-->
|
||||
<appender name="INFO" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<filter class="ch.qos.logback.classic.filter.LevelFilter">
|
||||
<level>INFO</level>
|
||||
<onMatch>ACCEPT</onMatch>
|
||||
<onMismatch>DENY</onMismatch>
|
||||
</filter>
|
||||
<file>
|
||||
${logHomeDir}/${log.projectName}/info/info.log
|
||||
</file>
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
|
||||
<fileNamePattern>
|
||||
${logHomeDir}/${log.projectName}/info/info.log.%d{yyyy-MM-dd}.%i.log
|
||||
</fileNamePattern>
|
||||
<maxFileSize>10MB</maxFileSize>
|
||||
<maxHistory>${log.maxHistory:-30}</maxHistory>
|
||||
</rollingPolicy>
|
||||
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
|
||||
<pattern>
|
||||
${log.pattern}
|
||||
</pattern>
|
||||
<charset>UTF-8</charset>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
|
||||
<!--系统中常规的error日志-->
|
||||
<appender name="ERROR" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<file>
|
||||
${logHomeDir}/${log.projectName}/error/error.log
|
||||
</file>
|
||||
<filter class="ch.qos.logback.classic.filter.LevelFilter">
|
||||
<level>ERROR</level>
|
||||
<onMatch>ACCEPT</onMatch>
|
||||
<onMismatch>DENY</onMismatch>
|
||||
</filter>
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
|
||||
<fileNamePattern>
|
||||
${logHomeDir}/${log.projectName}/error/error.log.%d{yyyy-MM-dd}.%i.log
|
||||
</fileNamePattern>
|
||||
<maxFileSize>10MB</maxFileSize>
|
||||
<maxHistory>${log.maxHistory:-30}</maxHistory>
|
||||
</rollingPolicy>
|
||||
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
|
||||
<pattern>
|
||||
${log.pattern}
|
||||
</pattern>
|
||||
<charset>UTF-8</charset>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<logger name="org.apache.catalina.startup.DigesterFactory" level="ERROR"/>
|
||||
<logger name="org.apache.catalina.util.LifecycleBase" level="ERROR"/>
|
||||
<logger name="org.apache.coyote.http11.Http11NioProtocol" level="WARN"/>
|
||||
|
||||
|
||||
|
||||
<logger name="com.njcn" level="INFO" additivity="false">
|
||||
<appender-ref ref="CONSOLE"/>
|
||||
<appender-ref ref="DEBUG"/>
|
||||
<appender-ref ref="INFO"/>
|
||||
<appender-ref ref="ERROR"/>
|
||||
</logger>
|
||||
|
||||
<root level="${logCommonLevel}">
|
||||
<appender-ref ref="CONSOLE"/>
|
||||
<appender-ref ref="DEBUG"/>
|
||||
<appender-ref ref="INFO"/>
|
||||
<appender-ref ref="ERROR"/>
|
||||
</root>
|
||||
</configuration>
|
||||
@@ -73,6 +73,12 @@ public class StorageUtil {
|
||||
case "IMBA":
|
||||
unit = "%";
|
||||
break;
|
||||
case "SeqV":
|
||||
unit = "V";
|
||||
break;
|
||||
case "SeqA":
|
||||
unit = "A";
|
||||
break;
|
||||
/**
|
||||
* 谐波有功功率
|
||||
*/
|
||||
|
||||
@@ -80,12 +80,12 @@ public class BookmarkUtil {
|
||||
P p = (P) element;
|
||||
String textFromP = Docx4jUtil.getTextFromP(p);
|
||||
if (textFromP.contains("测量回路")) {
|
||||
if (!textFromP.contains("1")) {
|
||||
// 另起一页
|
||||
P pagePara = Docx4jUtil.getPageBreak();
|
||||
idx = idx + 1;
|
||||
parentContent.add(idx, pagePara);
|
||||
}
|
||||
// if (!textFromP.contains("1")) {
|
||||
// // 另起一页
|
||||
// P pagePara = Docx4jUtil.getPageBreak();
|
||||
// idx = idx + 1;
|
||||
// parentContent.add(idx, pagePara);
|
||||
// }
|
||||
idx = idx + 1;
|
||||
parentContent.add(idx, p);
|
||||
}
|
||||
@@ -169,4 +169,4 @@ public class BookmarkUtil {
|
||||
}
|
||||
return bookmarkInfo;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user