修复2次关源指令bug
完善数据校验结果接口调用 pqdif功补充
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -53,3 +53,4 @@ rebel.xml
|
|||||||
CLAUDE.md
|
CLAUDE.md
|
||||||
docs/
|
docs/
|
||||||
data/
|
data/
|
||||||
|
.m2
|
||||||
@@ -150,6 +150,10 @@
|
|||||||
<artifactId>spring-boot-starter-test</artifactId>
|
<artifactId>spring-boot-starter-test</artifactId>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-data-redis</artifactId>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import com.njcn.gather.detection.pojo.po.DevData;
|
|||||||
import com.njcn.gather.detection.pojo.po.IcdCheckData;
|
import com.njcn.gather.detection.pojo.po.IcdCheckData;
|
||||||
import com.njcn.gather.detection.pojo.po.SourceCompareDev;
|
import com.njcn.gather.detection.pojo.po.SourceCompareDev;
|
||||||
import com.njcn.gather.detection.pojo.vo.*;
|
import com.njcn.gather.detection.pojo.vo.*;
|
||||||
|
import com.njcn.gather.detection.service.FormalTestEventPublisher;
|
||||||
import com.njcn.gather.detection.service.impl.DetectionServiceImpl;
|
import com.njcn.gather.detection.service.impl.DetectionServiceImpl;
|
||||||
import com.njcn.gather.detection.util.DetectionUtil;
|
import com.njcn.gather.detection.util.DetectionUtil;
|
||||||
import com.njcn.gather.detection.util.socket.*;
|
import com.njcn.gather.detection.util.socket.*;
|
||||||
@@ -83,6 +84,7 @@ public class SocketDevResponseService {
|
|||||||
private final IDictDataService dictDataService;
|
private final IDictDataService dictDataService;
|
||||||
private final IPqSourceService pqSourceService;
|
private final IPqSourceService pqSourceService;
|
||||||
private final IResultService resultService;
|
private final IResultService resultService;
|
||||||
|
private final FormalTestEventPublisher formalTestEventPublisher;
|
||||||
|
|
||||||
@Value("${dataCheck.enable}")
|
@Value("${dataCheck.enable}")
|
||||||
private Boolean dataCheck;
|
private Boolean dataCheck;
|
||||||
@@ -1229,6 +1231,7 @@ public class SocketDevResponseService {
|
|||||||
socketMsg.setOperateCode(SourceOperateCodeEnum.OPER_GATHER.getValue());
|
socketMsg.setOperateCode(SourceOperateCodeEnum.OPER_GATHER.getValue());
|
||||||
SocketManager.sendMsg(param.getUserPageId() + CnSocketUtil.SOURCE_TAG, JSON.toJSONString(socketMsg));
|
SocketManager.sendMsg(param.getUserPageId() + CnSocketUtil.SOURCE_TAG, JSON.toJSONString(socketMsg));
|
||||||
|
|
||||||
|
this.publishFormalStartIfNeeded();
|
||||||
webSocketVO.setDesc(null);
|
webSocketVO.setDesc(null);
|
||||||
WebServiceManager.sendMsg(param.getUserPageId(), JSON.toJSONString(webSocketVO));
|
WebServiceManager.sendMsg(param.getUserPageId(), JSON.toJSONString(webSocketVO));
|
||||||
} else {
|
} else {
|
||||||
@@ -1386,7 +1389,7 @@ public class SocketDevResponseService {
|
|||||||
List<String> valueType = iPqScriptCheckDataService.getValueType(checkDataParam);
|
List<String> valueType = iPqScriptCheckDataService.getValueType(checkDataParam);
|
||||||
|
|
||||||
iPqDevService.updateResult(param.getDevIds(), valueType, param.getCode(), param.getUserId(), param.getTemperature(), param.getHumidity(), true);
|
iPqDevService.updateResult(param.getDevIds(), valueType, param.getCode(), param.getUserId(), param.getTemperature(), param.getHumidity(), true);
|
||||||
if (dataCheck) {
|
if (Boolean.TRUE.equals(dataCheck)) {
|
||||||
resultService.tryNotifyThirdPartyAfterFormalTest(param);
|
resultService.tryNotifyThirdPartyAfterFormalTest(param);
|
||||||
}
|
}
|
||||||
CnSocketUtil.quitSend(param);
|
CnSocketUtil.quitSend(param);
|
||||||
@@ -1527,6 +1530,7 @@ public class SocketDevResponseService {
|
|||||||
switch (Objects.requireNonNull(operateCodeEnum)) {
|
switch (Objects.requireNonNull(operateCodeEnum)) {
|
||||||
case QUIT_INIT_01:
|
case QUIT_INIT_01:
|
||||||
//关闭所有
|
//关闭所有
|
||||||
|
publishFormalEndIfNeeded();
|
||||||
SocketManager.removeUser(s);
|
SocketManager.removeUser(s);
|
||||||
CnSocketUtil.quitSendSource(param);
|
CnSocketUtil.quitSendSource(param);
|
||||||
break;
|
break;
|
||||||
@@ -1549,6 +1553,7 @@ public class SocketDevResponseService {
|
|||||||
case NO_INIT_DEV:
|
case NO_INIT_DEV:
|
||||||
switch (operateCodeEnum) {
|
switch (operateCodeEnum) {
|
||||||
case QUIT_INIT_01:
|
case QUIT_INIT_01:
|
||||||
|
publishFormalEndIfNeeded();
|
||||||
SocketManager.removeUser(s);
|
SocketManager.removeUser(s);
|
||||||
// CnSocketUtil.quitSendSource(param);
|
// CnSocketUtil.quitSendSource(param);
|
||||||
break;
|
break;
|
||||||
@@ -1571,6 +1576,22 @@ public class SocketDevResponseService {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void publishFormalEndIfNeeded() {
|
||||||
|
if (FormalTestManager.endEventPublished && !Boolean.TRUE.equals(dataCheck)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
formalTestEventPublisher.publishEnd(FormalTestManager.sessionId, FormalTestManager.devList);
|
||||||
|
FormalTestManager.endEventPublished = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void publishFormalStartIfNeeded() {
|
||||||
|
if (FormalTestManager.startEventPublished || ObjectUtil.isNull(FormalTestManager.sessionId) && !Boolean.TRUE.equals(dataCheck)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
formalTestEventPublisher.publishStart(FormalTestManager.sessionId, FormalTestManager.devList);
|
||||||
|
FormalTestManager.startEventPublished = true;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param issue
|
* @param issue
|
||||||
* @return key为V或I,value为对应的源下发信息
|
* @return key为V或I,value为对应的源下发信息
|
||||||
@@ -1816,6 +1837,9 @@ public class SocketDevResponseService {
|
|||||||
FormalTestManager.overload = getOverloadResult(param);
|
FormalTestManager.overload = getOverloadResult(param);
|
||||||
FormalTestManager.checkStartTime = LocalDateTime.now();
|
FormalTestManager.checkStartTime = LocalDateTime.now();
|
||||||
FormalTestManager.reCheckType = param.getReCheckType();
|
FormalTestManager.reCheckType = param.getReCheckType();
|
||||||
|
FormalTestManager.sessionId = UUID.randomUUID().toString().replace("-", "");
|
||||||
|
FormalTestManager.startEventPublished = false;
|
||||||
|
FormalTestManager.endEventPublished = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package com.njcn.gather.detection.pojo.vo;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class FormalTestDevicePayload {
|
||||||
|
|
||||||
|
private String deviceId;
|
||||||
|
|
||||||
|
private String monitorId;
|
||||||
|
|
||||||
|
private String deviceIp;
|
||||||
|
|
||||||
|
private String deviceType;
|
||||||
|
|
||||||
|
private String icdMappingName;
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package com.njcn.gather.detection.pojo.vo;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class FormalTestEventPayload {
|
||||||
|
|
||||||
|
private String eventType;
|
||||||
|
|
||||||
|
private LocalDateTime eventTime;
|
||||||
|
|
||||||
|
private String sessionId;
|
||||||
|
|
||||||
|
private List<FormalTestDevicePayload> devices;
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package com.njcn.gather.detection.service;
|
||||||
|
|
||||||
|
import com.njcn.gather.device.pojo.vo.PreDetection;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public interface FormalTestEventPublisher {
|
||||||
|
|
||||||
|
void publishStart(String sessionId, List<PreDetection> devices);
|
||||||
|
|
||||||
|
void publishEnd(String sessionId, List<PreDetection> devices);
|
||||||
|
}
|
||||||
@@ -0,0 +1,95 @@
|
|||||||
|
package com.njcn.gather.detection.service.impl;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson.JSON;
|
||||||
|
import com.njcn.gather.detection.pojo.vo.FormalTestDevicePayload;
|
||||||
|
import com.njcn.gather.detection.pojo.vo.FormalTestEventPayload;
|
||||||
|
import com.njcn.gather.detection.service.FormalTestEventPublisher;
|
||||||
|
import com.njcn.gather.device.pojo.vo.PreDetection;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class FormalTestEventPublisherImpl implements FormalTestEventPublisher {
|
||||||
|
|
||||||
|
private final StringRedisTemplate stringRedisTemplate;
|
||||||
|
|
||||||
|
private static String msgChannel = "formal-test-msg-channel";
|
||||||
|
|
||||||
|
private final Set<String> publishedStartSessions =
|
||||||
|
Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>());
|
||||||
|
|
||||||
|
private final Set<String> publishedEndSessions =
|
||||||
|
Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>());
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void publishStart(String sessionId, List<PreDetection> devices) {
|
||||||
|
publish("start", sessionId, devices);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void publishEnd(String sessionId, List<PreDetection> devices) {
|
||||||
|
publish("end", sessionId, devices);
|
||||||
|
}
|
||||||
|
|
||||||
|
public FormalTestEventPayload buildPayload(String eventType, String sessionId, List<PreDetection> devices) {
|
||||||
|
FormalTestEventPayload payload = new FormalTestEventPayload();
|
||||||
|
payload.setEventType(eventType);
|
||||||
|
payload.setEventTime(LocalDateTime.now());
|
||||||
|
payload.setSessionId(sessionId);
|
||||||
|
payload.setDevices(buildDevices(devices));
|
||||||
|
return payload;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void publish(String eventType, String sessionId, List<PreDetection> devices) {
|
||||||
|
if (sessionId == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Set<String> dedupSet = "start".equals(eventType) ? publishedStartSessions : publishedEndSessions;
|
||||||
|
if (!dedupSet.add(sessionId)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
FormalTestEventPayload payload = buildPayload(eventType, sessionId, devices);
|
||||||
|
stringRedisTemplate.convertAndSend(msgChannel, JSON.toJSONString(payload));
|
||||||
|
} catch (Exception ex) {
|
||||||
|
log.error("publish formal test event failed, eventType={}, sessionId={}, deviceCount={}",
|
||||||
|
eventType, sessionId, devices == null ? 0 : devices.size(), ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<FormalTestDevicePayload> buildDevices(List<PreDetection> devices) {
|
||||||
|
List<FormalTestDevicePayload> results = new ArrayList<>();
|
||||||
|
if (devices == null) {
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
for (PreDetection device : devices) {
|
||||||
|
if (device == null || device.getMonitorList() == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (PreDetection.MonitorListDTO monitor : device.getMonitorList()) {
|
||||||
|
if (monitor == null || monitor.getLine() == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
FormalTestDevicePayload payload = new FormalTestDevicePayload();
|
||||||
|
payload.setDeviceId(device.getDevId());
|
||||||
|
payload.setMonitorId(device.getDevId() + "_" + monitor.getLine());
|
||||||
|
payload.setDeviceIp(device.getDevIP());
|
||||||
|
payload.setDeviceType(device.getDevType());
|
||||||
|
payload.setIcdMappingName(device.getIcdType());
|
||||||
|
results.add(payload);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -49,7 +49,11 @@ public class CnSocketUtil {
|
|||||||
JSONObject jsonObject = new JSONObject();
|
JSONObject jsonObject = new JSONObject();
|
||||||
jsonObject.put("sourceId", param.getSourceName());
|
jsonObject.put("sourceId", param.getSourceName());
|
||||||
socketMsg.setData(jsonObject.toJSONString());
|
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());
|
WebServiceManager.removePreDetectionParam(param.getUserPageId());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -220,6 +220,12 @@ public class FormalTestManager {
|
|||||||
*/
|
*/
|
||||||
public static LocalDateTime checkStartTime;
|
public static LocalDateTime checkStartTime;
|
||||||
|
|
||||||
|
public static String sessionId;
|
||||||
|
|
||||||
|
public static boolean startEventPublished;
|
||||||
|
|
||||||
|
public static boolean endEventPublished;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 数模式 检测类型"1"-"全部检测" , "2"-"不合格项复检"
|
* 数模式 检测类型"1"-"全部检测" , "2"-"不合格项复检"
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -55,6 +55,11 @@ public class SocketManager {
|
|||||||
*/
|
*/
|
||||||
private static final Map<String, NioEventLoopGroup> socketGroup = new ConcurrentHashMap<>();
|
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) {
|
public static void addUser(String userId, Channel channel) {
|
||||||
socketSessions.put(userId, channel);
|
socketSessions.put(userId, channel);
|
||||||
}
|
}
|
||||||
@@ -65,20 +70,31 @@ public class SocketManager {
|
|||||||
|
|
||||||
public static void removeUser(String userId) {
|
public static void removeUser(String userId) {
|
||||||
Channel channel = socketSessions.get(userId);
|
Channel channel = socketSessions.get(userId);
|
||||||
|
removeMapping(userId, false);
|
||||||
if (ObjectUtil.isNotNull(channel)) {
|
if (ObjectUtil.isNotNull(channel)) {
|
||||||
try {
|
try {
|
||||||
|
markActiveClose(userId);
|
||||||
channel.close().sync();
|
channel.close().sync();
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
e.printStackTrace();
|
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);
|
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) {
|
public static Channel getChannelByUserId(String userId) {
|
||||||
@@ -89,6 +105,20 @@ public class SocketManager {
|
|||||||
return socketGroup.get(userId);
|
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) {
|
public static void sendMsg(String userId, String msg) {
|
||||||
Channel channel = socketSessions.get(userId);
|
Channel channel = socketSessions.get(userId);
|
||||||
if (ObjectUtil.isNotNull(channel)) {
|
if (ObjectUtil.isNotNull(channel)) {
|
||||||
|
|||||||
@@ -115,7 +115,11 @@ public class NettyDevClientHandler extends SimpleChannelInboundHandler<String> {
|
|||||||
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
|
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
|
||||||
log.warn("设备通讯客户端断线");
|
log.warn("设备通讯客户端断线");
|
||||||
ctx.close();
|
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);
|
CnSocketUtil.quitSendSource(param);
|
||||||
// 设备主动断开 → 本次检测视为结束,释放检测锁
|
// 设备主动断开 → 本次检测视为结束,释放检测锁
|
||||||
DetectionLockManager.getInstance()
|
DetectionLockManager.getInstance()
|
||||||
|
|||||||
@@ -70,7 +70,12 @@ public class NettySourceClientHandler extends SimpleChannelInboundHandler<String
|
|||||||
ctx.close();
|
ctx.close();
|
||||||
// 从Socket管理器中移除用户通道映射
|
// 从Socket管理器中移除用户通道映射
|
||||||
if (webUser != null && StrUtil.isNotBlank(userId)) {
|
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()
|
DetectionLockManager.getInstance()
|
||||||
.releaseIfMatchPage(userId, "SOURCE_CHANNEL_INACTIVE");
|
.releaseIfMatchPage(userId, "SOURCE_CHANNEL_INACTIVE");
|
||||||
@@ -120,6 +125,11 @@ public class NettySourceClientHandler extends SimpleChannelInboundHandler<String
|
|||||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
|
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
|
||||||
String userId = webUser != null ? webUser.getUserPageId() : "unknown";
|
String userId = webUser != null ? webUser.getUserPageId() : "unknown";
|
||||||
String channelId = ctx.channel().id().toString();
|
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) {
|
if (cause instanceof ConnectException) {
|
||||||
|
|||||||
@@ -75,7 +75,8 @@ public enum DetectionResponseEnum {
|
|||||||
PLAN_REPEATED_IN_SAME_LEVEL("A02096", "该父计划下存在同名的子计划"),
|
PLAN_REPEATED_IN_SAME_LEVEL("A02096", "该父计划下存在同名的子计划"),
|
||||||
PLEASE_UNASSIGN_STANDARD_DEV("A02097", "存在已分配给子计划的标准设备,请先解除分配"),
|
PLEASE_UNASSIGN_STANDARD_DEV("A02097", "存在已分配给子计划的标准设备,请先解除分配"),
|
||||||
PLEASE_UNASSIGN_DEVICE("A02098", "存在已分配给计划的被检设备,请先解除分配"),
|
PLEASE_UNASSIGN_DEVICE("A02098", "存在已分配给计划的被检设备,请先解除分配"),
|
||||||
DEV_IP_PORT_EXIST("A02099", "存在重复被检设备");
|
DEV_IP_PORT_EXIST("A02099", "存在重复被检设备"),
|
||||||
|
PQDIF_NOT_EXIST("A02100", "PQDIF不存在");
|
||||||
|
|
||||||
private final String code;
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -164,7 +164,7 @@ public class ResultController extends BaseController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
|
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
|
||||||
@PostMapping("/createChecksquareTask")
|
@GetMapping("/createChecksquareTask")
|
||||||
@ApiOperation("调用第三方数模数据检测接口")
|
@ApiOperation("调用第三方数模数据检测接口")
|
||||||
@ApiImplicitParam(name = "devId", value = "设备id", required = true)
|
@ApiImplicitParam(name = "devId", value = "设备id", required = true)
|
||||||
public HttpResult<String> createChecksquareTask(@RequestParam("devId") String devId) {
|
public HttpResult<String> createChecksquareTask(@RequestParam("devId") String devId) {
|
||||||
|
|||||||
@@ -206,19 +206,23 @@ public class ResultServiceImpl implements IResultService {
|
|||||||
throw new BusinessException(CommonResponseEnum.FAIL, "该设备监测点不存在");
|
throw new BusinessException(CommonResponseEnum.FAIL, "该设备监测点不存在");
|
||||||
}
|
}
|
||||||
|
|
||||||
PqDevSub devSub = pqDevSubService.getOne(new LambdaQueryWrapper<PqDevSub>()
|
// PqDevSub devSub = pqDevSubService.getOne(new LambdaQueryWrapper<PqDevSub>()
|
||||||
.eq(PqDevSub::getDevId, devId), false);
|
// .eq(PqDevSub::getDevId, devId), false);
|
||||||
if (ObjectUtil.isNull(devSub)
|
// if (ObjectUtil.isNull(devSub)
|
||||||
|| ObjectUtil.isNull(devSub.getCheckStartTime())
|
// || ObjectUtil.isNull(devSub.getCheckStartTime())
|
||||||
|| ObjectUtil.isNull(devSub.getCheckEndTime())) {
|
// || ObjectUtil.isNull(devSub.getCheckEndTime())) {
|
||||||
throw new BusinessException(CommonResponseEnum.FAIL, "该设备检测开始时间或结束时间为空");
|
// throw new BusinessException(CommonResponseEnum.FAIL, "该设备检测开始时间或结束时间为空");
|
||||||
}
|
// }
|
||||||
|
|
||||||
DataCheckRequest request = new DataCheckRequest();
|
DataCheckRequest request = new DataCheckRequest();
|
||||||
request.setLineIds(monitorList.stream().map(PqMonitor::getId).collect(Collectors.toList()));
|
// 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(Collections.emptyList());
|
request.setIndicatorCodes(Collections.emptyList());
|
||||||
request.setTimeStart(CHECKSQUARE_TIME_FORMATTER.format(devSub.getCheckStartTime()));
|
request.setTimeStart("2026-05-22 09:00:00");
|
||||||
request.setTimeEnd(CHECKSQUARE_TIME_FORMATTER.format(devSub.getCheckEndTime()));
|
request.setTimeEnd("2026-05-22 12:00:00");
|
||||||
return restTemplateUtil.postJson(CHECKSQUARE_CREATE_URL, request, String.class);
|
return restTemplateUtil.postJson(CHECKSQUARE_CREATE_URL, request, String.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -27,6 +27,11 @@ public class DevTypeParam {
|
|||||||
@NotBlank(message = DetectionValidMessage.ICD_NOT_BLANK)
|
@NotBlank(message = DetectionValidMessage.ICD_NOT_BLANK)
|
||||||
private String icd;
|
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)
|
@ApiModelProperty(value = "工作电源", required = true)
|
||||||
@NotBlank(message = DetectionValidMessage.POWER_NOT_BLANK)
|
@NotBlank(message = DetectionValidMessage.POWER_NOT_BLANK)
|
||||||
private String power;
|
private String power;
|
||||||
|
|||||||
@@ -37,6 +37,8 @@ public class DevType extends BaseEntity {
|
|||||||
/**
|
/**
|
||||||
* 工作电源
|
* 工作电源
|
||||||
*/
|
*/
|
||||||
|
private String pqdif;
|
||||||
|
|
||||||
private String power;
|
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.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
import com.njcn.common.pojo.enums.common.DataStateEnum;
|
import com.njcn.common.pojo.enums.common.DataStateEnum;
|
||||||
import com.njcn.common.pojo.exception.BusinessException;
|
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.pojo.enums.DetectionResponseEnum;
|
||||||
import com.njcn.gather.system.dictionary.pojo.po.DictData;
|
import com.njcn.gather.system.dictionary.pojo.po.DictData;
|
||||||
import com.njcn.gather.system.dictionary.service.IDictDataService;
|
import com.njcn.gather.system.dictionary.service.IDictDataService;
|
||||||
@@ -36,6 +38,9 @@ public class DevTypeServiceImpl extends ServiceImpl<DevTypeMapper, DevType> impl
|
|||||||
@Resource
|
@Resource
|
||||||
private IDictDataService dictDataService;
|
private IDictDataService dictDataService;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private IPqPqdifPathService pqPqdifPathService;
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<DevType> listAll() {
|
public List<DevType> listAll() {
|
||||||
@@ -76,6 +81,7 @@ public class DevTypeServiceImpl extends ServiceImpl<DevTypeMapper, DevType> impl
|
|||||||
public boolean addDevType(DevTypeParam addParam) {
|
public boolean addDevType(DevTypeParam addParam) {
|
||||||
addParam.setName(addParam.getName().trim());
|
addParam.setName(addParam.getName().trim());
|
||||||
this.checkRepeat(addParam, false);
|
this.checkRepeat(addParam, false);
|
||||||
|
this.checkPqdif(addParam.getPqdif());
|
||||||
DevType devType = new DevType();
|
DevType devType = new DevType();
|
||||||
BeanUtil.copyProperties(addParam, devType);
|
BeanUtil.copyProperties(addParam, devType);
|
||||||
devType.setState(DataStateEnum.ENABLE.getCode());
|
devType.setState(DataStateEnum.ENABLE.getCode());
|
||||||
@@ -87,6 +93,7 @@ public class DevTypeServiceImpl extends ServiceImpl<DevTypeMapper, DevType> impl
|
|||||||
public boolean updateDevType(DevTypeParam.UpdateParam updateParam) {
|
public boolean updateDevType(DevTypeParam.UpdateParam updateParam) {
|
||||||
updateParam.setName(updateParam.getName().trim());
|
updateParam.setName(updateParam.getName().trim());
|
||||||
this.checkRepeat(updateParam, true);
|
this.checkRepeat(updateParam, true);
|
||||||
|
this.checkPqdif(updateParam.getPqdif());
|
||||||
DevType devType = new DevType();
|
DevType devType = new DevType();
|
||||||
BeanUtil.copyProperties(updateParam, devType);
|
BeanUtil.copyProperties(updateParam, devType);
|
||||||
return this.updateById(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);
|
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());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -3,13 +3,16 @@ server:
|
|||||||
spring:
|
spring:
|
||||||
application:
|
application:
|
||||||
name: entrance
|
name: entrance
|
||||||
|
redis:
|
||||||
|
host: localhost
|
||||||
|
port: 16379
|
||||||
datasource:
|
datasource:
|
||||||
druid:
|
druid:
|
||||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
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
|
url: jdbc:mysql://192.168.1.24:13306/pqs9100?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true
|
||||||
# username: root
|
# username: root
|
||||||
# password: njcnpqs
|
# password: njcnpqs
|
||||||
# url: jdbc:mysql://127.0.0.1:3306/pqs9100?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true
|
# url: jdbc:mysql://127.0.0.1:3306/pqs9100?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true
|
||||||
username: root
|
username: root
|
||||||
password: njcnpqs
|
password: njcnpqs
|
||||||
#初始化建立物理连接的个数、最小、最大连接数
|
#初始化建立物理连接的个数、最小、最大连接数
|
||||||
@@ -67,8 +70,8 @@ sntp:
|
|||||||
port: 123
|
port: 123
|
||||||
|
|
||||||
Dip:
|
Dip:
|
||||||
# 暂态前时间(s)
|
# 暂态前时间(s)
|
||||||
# fPreTime: 2f
|
# fPreTime: 2f
|
||||||
#写入时间(s)
|
#写入时间(s)
|
||||||
fRampIn: 0.001f
|
fRampIn: 0.001f
|
||||||
#写出时间(s)
|
#写出时间(s)
|
||||||
@@ -86,8 +89,8 @@ Dip:
|
|||||||
# homeDir: D:\logs
|
# homeDir: D:\logs
|
||||||
# commonLevel: info
|
# commonLevel: info
|
||||||
report:
|
report:
|
||||||
# template: D:\template
|
# template: D:\template
|
||||||
# reportDir: D:\report
|
# reportDir: D:\report
|
||||||
dateFormat: yyyy年MM月dd日
|
dateFormat: yyyy年MM月dd日
|
||||||
#data:
|
#data:
|
||||||
# homeDir: D:\data
|
# homeDir: D:\data
|
||||||
@@ -133,4 +136,4 @@ activate:
|
|||||||
public-key: "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnFMmIVanMxsW5S/qP8Wcxf/J3/i4631BP3UtWkRzO7jAw9HIAgK4Y7X53hXj6zMbfme1vMjQc0mq7m/KrH4WlTYpFexLO6Gnk8oH40F04tp+ABZIq93zNOydPEaVoZeTPH/LlkwrrxVGAMNNIKuebcqapp25JiWtlSFMv4kH/nDAj+2m8+P4zYVM1Ed6gO01eKDEYE3SBA1Ket2BfHTgviR/F8WKwlXh11enywsJnrHTM5dJQdlUxCjHy214TpheYOz/cv9elQnDfFAbmZW8mH5/hgMSTkm3h4uR7ITin6Erg+yc/t1kGaTWrzloyBRMSiFN/Pwr5yQjj+1wQqqUkwIDAQAB"
|
public-key: "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnFMmIVanMxsW5S/qP8Wcxf/J3/i4631BP3UtWkRzO7jAw9HIAgK4Y7X53hXj6zMbfme1vMjQc0mq7m/KrH4WlTYpFexLO6Gnk8oH40F04tp+ABZIq93zNOydPEaVoZeTPH/LlkwrrxVGAMNNIKuebcqapp25JiWtlSFMv4kH/nDAj+2m8+P4zYVM1Ed6gO01eKDEYE3SBA1Ket2BfHTgviR/F8WKwlXh11enywsJnrHTM5dJQdlUxCjHy214TpheYOz/cv9elQnDfFAbmZW8mH5/hgMSTkm3h4uR7ITin6Erg+yc/t1kGaTWrzloyBRMSiFN/Pwr5yQjj+1wQqqUkwIDAQAB"
|
||||||
|
|
||||||
dataCheck:
|
dataCheck:
|
||||||
enable: true
|
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,66 +0,0 @@
|
|||||||
package com.njcn;
|
|
||||||
|
|
||||||
import com.njcn.gather.device.pojo.po.PqDevSub;
|
|
||||||
import com.njcn.gather.device.service.IPqDevSubService;
|
|
||||||
import com.njcn.gather.monitor.pojo.po.PqMonitor;
|
|
||||||
import com.njcn.gather.monitor.service.IPqMonitorService;
|
|
||||||
import com.njcn.gather.result.pojo.param.DataCheckRequest;
|
|
||||||
import com.njcn.gather.result.service.impl.ResultServiceImpl;
|
|
||||||
import com.njcn.http.util.RestTemplateUtil;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
import org.mockito.ArgumentCaptor;
|
|
||||||
import org.mockito.Mock;
|
|
||||||
import org.mockito.junit.MockitoJUnitRunner;
|
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collections;
|
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
|
||||||
import static org.junit.Assert.assertTrue;
|
|
||||||
import static org.mockito.ArgumentMatchers.eq;
|
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
|
||||||
import static org.mockito.Mockito.when;
|
|
||||||
|
|
||||||
@RunWith(MockitoJUnitRunner.class)
|
|
||||||
public class ResultChecksquareCreateTest {
|
|
||||||
|
|
||||||
@Mock
|
|
||||||
private IPqMonitorService pqMonitorService;
|
|
||||||
@Mock
|
|
||||||
private IPqDevSubService pqDevSubService;
|
|
||||||
@Mock
|
|
||||||
private RestTemplateUtil restTemplateUtil;
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void createChecksquareTaskByDevIdBuildsRequestAndReturnsThirdPartyResponse() {
|
|
||||||
String devId = "dev-1";
|
|
||||||
PqMonitor monitor1 = new PqMonitor();
|
|
||||||
monitor1.setId("line-1");
|
|
||||||
PqMonitor monitor2 = new PqMonitor();
|
|
||||||
monitor2.setId("line-2");
|
|
||||||
PqDevSub devSub = new PqDevSub();
|
|
||||||
devSub.setCheckStartTime(LocalDateTime.of(2026, 6, 22, 10, 1, 2));
|
|
||||||
devSub.setCheckEndTime(LocalDateTime.of(2026, 6, 22, 11, 3, 4));
|
|
||||||
|
|
||||||
when(pqMonitorService.listPqMonitorByDevIds(Collections.singletonList(devId))).thenReturn(Arrays.asList(monitor1, monitor2));
|
|
||||||
when(pqDevSubService.getOne(any(), eq(false))).thenReturn(devSub);
|
|
||||||
ArgumentCaptor<DataCheckRequest> requestCaptor = ArgumentCaptor.forClass(DataCheckRequest.class);
|
|
||||||
when(restTemplateUtil.postJson(eq(ResultServiceImpl.CHECKSQUARE_CREATE_URL), requestCaptor.capture(), eq(String.class)))
|
|
||||||
.thenReturn("{\"code\":0}");
|
|
||||||
|
|
||||||
ResultServiceImpl resultService = new ResultServiceImpl(null, null, null, null, null, null, null, null,
|
|
||||||
null, null, null, null, null, null, null, null, null, null, pqMonitorService, null, null,
|
|
||||||
pqDevSubService, null, restTemplateUtil);
|
|
||||||
|
|
||||||
String response = resultService.createChecksquareTaskByDevId(devId);
|
|
||||||
|
|
||||||
DataCheckRequest request = requestCaptor.getValue();
|
|
||||||
assertEquals("{\"code\":0}", response);
|
|
||||||
assertEquals(Arrays.asList("line-1", "line-2"), request.getLineIds());
|
|
||||||
assertTrue(request.getIndicatorCodes().isEmpty());
|
|
||||||
assertEquals("2026-06-22 10:01:02", request.getTimeStart());
|
|
||||||
assertEquals("2026-06-22 11:03:04", request.getTimeEnd());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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,151 +0,0 @@
|
|||||||
package com.njcn.gather.detection.lock;
|
|
||||||
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.util.concurrent.CountDownLatch;
|
|
||||||
import java.util.concurrent.ExecutorService;
|
|
||||||
import java.util.concurrent.Executors;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
|
||||||
|
|
||||||
public class DetectionLockManagerTest {
|
|
||||||
|
|
||||||
private DetectionLockManager manager;
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void setUp() throws Exception {
|
|
||||||
manager = DetectionLockManager.getInstance();
|
|
||||||
// 通过反射把 current 清零,避免不同测试方法间状态污染
|
|
||||||
Field f = DetectionLockManager.class.getDeclaredField("current");
|
|
||||||
f.setAccessible(true);
|
|
||||||
((AtomicReference<?>) f.get(manager)).set(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void tryAcquire_whenEmpty_returnsOk() {
|
|
||||||
DetectionLockManager.AcquireResult r = manager.tryAcquire("u1", "alice", "page-1");
|
|
||||||
assertTrue(r.isOk());
|
|
||||||
assertNull(r.getHolder());
|
|
||||||
assertEquals("u1", manager.getCurrent().getUserId());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void tryAcquire_whenHeldByOther_returnsBusyWithHolder() {
|
|
||||||
manager.tryAcquire("u1", "alice", "page-1");
|
|
||||||
DetectionLockManager.AcquireResult r = manager.tryAcquire("u2", "bob", "page-2");
|
|
||||||
assertFalse(r.isOk());
|
|
||||||
assertNotNull(r.getHolder());
|
|
||||||
assertEquals("u1", r.getHolder().getHolderUserId());
|
|
||||||
assertEquals("alice", r.getHolder().getHolderUserName());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void tryAcquire_reentrantSameUser_refreshesPageAndExpireAt() throws Exception {
|
|
||||||
manager.tryAcquire("u1", "alice", "page-1");
|
|
||||||
long oldAcquire = manager.getCurrent().getAcquireTime();
|
|
||||||
long oldExpire = manager.getCurrent().getExpireAt();
|
|
||||||
// sleep 50ms(远超 Windows 系统时钟 ~15ms 精度),保证时间戳推进
|
|
||||||
Thread.sleep(50);
|
|
||||||
DetectionLockManager.AcquireResult r = manager.tryAcquire("u1", "alice", "page-2");
|
|
||||||
assertTrue(r.isOk());
|
|
||||||
assertEquals("page-2", manager.getCurrent().getUserPageId());
|
|
||||||
assertTrue("acquireTime 应推进", manager.getCurrent().getAcquireTime() > oldAcquire);
|
|
||||||
assertTrue("expireAt 应推进", manager.getCurrent().getExpireAt() > oldExpire);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void tryAcquire_whenExpired_anyUserCanAcquire() throws Exception {
|
|
||||||
// 直接构造一个 expireAt 在过去的 lock 写进去
|
|
||||||
Field f = DetectionLockManager.class.getDeclaredField("current");
|
|
||||||
f.setAccessible(true);
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
AtomicReference<DetectionLock> ref = (AtomicReference<DetectionLock>) f.get(manager);
|
|
||||||
long past = System.currentTimeMillis() - 1000;
|
|
||||||
ref.set(new DetectionLock("u1", "alice", "page-1", past - 1000, past));
|
|
||||||
|
|
||||||
DetectionLockManager.AcquireResult r = manager.tryAcquire("u2", "bob", "page-2");
|
|
||||||
assertTrue(r.isOk());
|
|
||||||
assertEquals("u2", manager.getCurrent().getUserId());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void releaseIfHeldBy_matchingUser_clears() {
|
|
||||||
manager.tryAcquire("u1", "alice", "page-1");
|
|
||||||
manager.releaseIfHeldBy("u1", "TEST");
|
|
||||||
assertNull(manager.getCurrent());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void releaseIfHeldBy_nonMatchingUser_isNoOp() {
|
|
||||||
manager.tryAcquire("u1", "alice", "page-1");
|
|
||||||
manager.releaseIfHeldBy("u2", "TEST");
|
|
||||||
assertNotNull(manager.getCurrent());
|
|
||||||
assertEquals("u1", manager.getCurrent().getUserId());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void releaseIfHeldBy_whenEmpty_isNoOp() {
|
|
||||||
manager.releaseIfHeldBy("u1", "TEST");
|
|
||||||
assertNull(manager.getCurrent());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void releaseIfMatchPage_matchingPage_clears() {
|
|
||||||
manager.tryAcquire("u1", "alice", "page-1");
|
|
||||||
manager.releaseIfMatchPage("page-1", "TEST");
|
|
||||||
assertNull(manager.getCurrent());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void releaseIfMatchPage_nonMatchingPage_isNoOp() {
|
|
||||||
manager.tryAcquire("u1", "alice", "page-1");
|
|
||||||
manager.releaseIfMatchPage("page-2", "TEST");
|
|
||||||
assertNotNull(manager.getCurrent());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void forceRelease_alwaysClears() {
|
|
||||||
manager.tryAcquire("u1", "alice", "page-1");
|
|
||||||
manager.forceRelease("admin", "TEST");
|
|
||||||
assertNull(manager.getCurrent());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void concurrentTryAcquire_onlyOneWins() throws Exception {
|
|
||||||
int n = 100;
|
|
||||||
ExecutorService pool = Executors.newFixedThreadPool(16);
|
|
||||||
CountDownLatch start = new CountDownLatch(1);
|
|
||||||
CountDownLatch done = new CountDownLatch(n);
|
|
||||||
AtomicInteger okCount = new AtomicInteger(0);
|
|
||||||
// 记下胜出线程的 userId,便于断言 holder 身份与胜出者一致
|
|
||||||
AtomicReference<String> winnerUserId = new AtomicReference<>(null);
|
|
||||||
for (int i = 0; i < n; i++) {
|
|
||||||
final String uid = "u" + i;
|
|
||||||
pool.submit(() -> {
|
|
||||||
try {
|
|
||||||
start.await();
|
|
||||||
DetectionLockManager.AcquireResult r = manager.tryAcquire(uid, uid, "page-" + uid);
|
|
||||||
if (r.isOk()) {
|
|
||||||
okCount.incrementAndGet();
|
|
||||||
winnerUserId.set(uid);
|
|
||||||
}
|
|
||||||
} catch (InterruptedException ignored) {
|
|
||||||
} finally {
|
|
||||||
done.countDown();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
start.countDown();
|
|
||||||
done.await(5, TimeUnit.SECONDS);
|
|
||||||
pool.shutdownNow();
|
|
||||||
assertEquals("100 个不同账号并发只能有 1 个抢到锁", 1, okCount.get());
|
|
||||||
assertNotNull(manager.getCurrent());
|
|
||||||
// 持锁者必须就是宣称 ok 的那个线程,不能是别人
|
|
||||||
assertEquals("holder 身份必须等于胜出线程的 userId", winnerUserId.get(), manager.getCurrent().getUserId());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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>
|
|
||||||
Reference in New Issue
Block a user