parent
0667a3d889
commit
b9bfbfd468
19
pom.xml
19
pom.xml
|
@ -34,6 +34,10 @@
|
|||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-undertow</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.tomcat.embed</groupId>
|
||||
<artifactId>tomcat-embed-core</artifactId>
|
||||
|
@ -85,6 +89,7 @@
|
|||
<dependency>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
<version>8.0.21</version>
|
||||
</dependency>
|
||||
<!--mybatis -->
|
||||
<dependency>
|
||||
|
@ -216,20 +221,6 @@
|
|||
<artifactId>ipaddress</artifactId>
|
||||
<version>5.2.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-tomcat</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-undertow</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
package com.dispose.ability;
|
||||
|
||||
import com.dispose.common.DDoSAttackType;
|
||||
import com.dispose.common.DisposeCapacityType;
|
||||
import com.dispose.common.ErrorCode;
|
||||
import com.dispose.common.NetflowDirection;
|
||||
import com.dispose.pojo.po.MulReturnType;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* The interface Dispose ability.
|
||||
*
|
||||
* @author <huangxin@cmhi.chinamoblie.com>
|
||||
*/
|
||||
public interface DisposeAbility {
|
||||
|
||||
/**
|
||||
* Init device env error code.
|
||||
*
|
||||
* @param urlPath the url path
|
||||
* @param username the username
|
||||
* @param password the password
|
||||
* @return the error code
|
||||
*/
|
||||
ErrorCode initDeviceEnv(String urlPath, String username, String password);
|
||||
|
||||
/**
|
||||
* Run dispose mul return type.
|
||||
*
|
||||
* @param ip the ip
|
||||
* @param capType the cap type
|
||||
* @param nfDirection the nf direction
|
||||
* @param attackType the attack type
|
||||
* @param duration the duration
|
||||
* @return the mul return type
|
||||
*/
|
||||
MulReturnType<ErrorCode, Long> runDispose(String ip, DisposeCapacityType capType,
|
||||
@Nullable NetflowDirection[] nfDirection,
|
||||
@Nullable DDoSAttackType[] attackType,
|
||||
@Nullable Long duration);
|
||||
|
||||
/**
|
||||
* Stop dispose mul return type.
|
||||
*
|
||||
* @param ip the ip
|
||||
* @param capType the cap type
|
||||
* @return the mul return type
|
||||
*/
|
||||
MulReturnType<ErrorCode, Long> stopDispose(String ip, DisposeCapacityType capType);
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,144 @@
|
|||
package com.dispose.ability.impl;
|
||||
|
||||
import com.dispose.ability.DisposeAbility;
|
||||
import com.dispose.common.CommonEnumHandler;
|
||||
import com.dispose.common.DDoSAttackType;
|
||||
import com.dispose.common.DisposeCapacityType;
|
||||
import com.dispose.common.DpTechAttackType;
|
||||
import com.dispose.common.DpTechConfigValue;
|
||||
import com.dispose.common.ErrorCode;
|
||||
import com.dispose.common.NetflowDirection;
|
||||
import com.dispose.pojo.po.MulReturnType;
|
||||
import com.dptech.dispose.AbnormalFlowCleaningServicePortType;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.cxf.endpoint.Client;
|
||||
import org.apache.cxf.frontend.ClientProxy;
|
||||
import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;
|
||||
import org.apache.cxf.transport.http.HTTPConduit;
|
||||
import org.apache.cxf.transports.http.configuration.HTTPClientPolicy;
|
||||
import org.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor;
|
||||
import org.apache.wss4j.dom.WSConstants;
|
||||
import org.apache.wss4j.dom.handler.WSHandlerConstants;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.xml.ws.BindingProvider;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* The type Dp tech ability.
|
||||
*
|
||||
* @author <huangxin@cmhi.chinamoblie.com>
|
||||
*/
|
||||
@Component
|
||||
@Slf4j
|
||||
public class DpTechAbilityImpl implements DisposeAbility {
|
||||
|
||||
private AbnormalFlowCleaningServicePortType cleanTypePort;
|
||||
|
||||
@Override
|
||||
public ErrorCode initDeviceEnv(String urlPath, String username, String password) {
|
||||
JaxWsProxyFactoryBean jaxWsProxyFactoryBean = new JaxWsProxyFactoryBean();
|
||||
jaxWsProxyFactoryBean.setServiceClass(AbnormalFlowCleaningServicePortType.class);
|
||||
jaxWsProxyFactoryBean.setAddress(urlPath);
|
||||
|
||||
//WS-Security Head
|
||||
Map<String, Object> outProps = new HashMap<>();
|
||||
outProps.put(WSHandlerConstants.ACTION, WSHandlerConstants.USERNAME_TOKEN);
|
||||
|
||||
// 配置用户名,密码类型
|
||||
outProps.put(WSHandlerConstants.USER, username);
|
||||
outProps.put(WSHandlerConstants.PASSWORD_TYPE, WSConstants.PW_TEXT);
|
||||
|
||||
// 添加WSSecure头部验证信息
|
||||
jaxWsProxyFactoryBean.getOutInterceptors().add(new WSS4JOutInterceptor(outProps));
|
||||
|
||||
this.cleanTypePort = (AbnormalFlowCleaningServicePortType) jaxWsProxyFactoryBean.create();
|
||||
|
||||
Map<String, Object> ctx = ((BindingProvider) this.cleanTypePort).getRequestContext();
|
||||
ctx.put("password", username);
|
||||
ctx.put("username", password);
|
||||
|
||||
// 配置连接,访问超时时间
|
||||
Client proxy = ClientProxy.getClient(this.cleanTypePort);
|
||||
HTTPConduit conduit = (HTTPConduit) proxy.getConduit();
|
||||
HTTPClientPolicy policy = new HTTPClientPolicy();
|
||||
// 请求超时
|
||||
policy.setConnectionTimeout(DpTechConfigValue.SOAP_CONNECT_TIMEOUT_SECOND);
|
||||
//读取超时
|
||||
policy.setReceiveTimeout(DpTechConfigValue.SOAP_RECEIVE_TIMEOUT_SECOND);
|
||||
conduit.setClient(policy);
|
||||
|
||||
return ErrorCode.ERR_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Run dispose mul return type.
|
||||
*
|
||||
* @param ip the ip
|
||||
* @param capType the cap type
|
||||
* @return the mul return type
|
||||
*/
|
||||
@Override
|
||||
public MulReturnType<ErrorCode, Long> runDispose(String ip, DisposeCapacityType capType,
|
||||
@Nullable NetflowDirection[] nfDirection,
|
||||
@Nullable DDoSAttackType[] attackType,
|
||||
@Nullable Long duration) {
|
||||
ErrorCode err = ErrorCode.ERR_OK;
|
||||
|
||||
try {
|
||||
log.info("++++Begging DPTech Start Cleanup Task: {}", ip);
|
||||
|
||||
// 查找需要处置的流量方向集合
|
||||
List<NetflowDirection> dirList = Arrays.stream(NetflowDirection.values())
|
||||
.filter(d -> nfDirection == null || nfDirection.length == 0 || Arrays.asList(nfDirection).contains(d))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// 查找需要处理的攻击类型集合
|
||||
List<DpTechAttackType> typeList = Arrays.stream(DDoSAttackType.values())
|
||||
.filter(t -> attackType == null || attackType.length == 0 || Arrays.asList(attackType).contains(t))
|
||||
.map(t -> CommonEnumHandler.codeOf(DpTechAttackType.class,
|
||||
DpTechAttackType.fromDdosAttackTypeValue(t)))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
dirList.forEach(d -> typeList.forEach(t -> CompletableFuture.supplyAsync(() ->
|
||||
cleanTypePort.startAbnormalTaskForUMC(ip, t.getValue(), d.getCode()))
|
||||
.whenComplete((v, ex) -> {
|
||||
if (ex != null) {
|
||||
log.error("DPTech run dispose: {}, {}, error:{}", ip, t.getValue(),
|
||||
ex.getMessage());
|
||||
} else {
|
||||
log.debug("Cleanup: {} --> {}:{}", d, t.getDescription(), t.getValue());
|
||||
if (v.getResultRetVal() != ErrorCode.ERR_OK.getCode()) {
|
||||
log.error("DPTech run dispose {} error: {}", ip, v.getResultInfo());
|
||||
}
|
||||
}
|
||||
})));
|
||||
|
||||
log.info("----Finish DPTech Start Cleanup Task: {}", ip);
|
||||
} catch (Exception ex) {
|
||||
log.error(ex.getMessage());
|
||||
log.error("----Error DPTech Start Cleanup Task: {}", ip);
|
||||
err = ErrorCode.ERR_SYSTEMEXCEPTION;
|
||||
}
|
||||
|
||||
return new MulReturnType<>(err, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop dispose mul return type.
|
||||
*
|
||||
* @param ip the ip
|
||||
* @param capType the cap type
|
||||
* @return the mul return type
|
||||
*/
|
||||
@Override
|
||||
public MulReturnType<ErrorCode, Long> stopDispose(String ip, DisposeCapacityType capType) {
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,97 @@
|
|||
package com.dispose.common;
|
||||
|
||||
/**
|
||||
* The enum D do s attack type.
|
||||
*
|
||||
* @author <huangxin@cmhi.chinamoblie.com>
|
||||
*/
|
||||
public enum DDoSAttackType implements BaseEnum {
|
||||
/**
|
||||
* The Tcp syn flood.
|
||||
*/
|
||||
TCP_SYN_FLOOD(0, "TCP SYN Flood"),
|
||||
/**
|
||||
* The Udp flood.
|
||||
*/
|
||||
UDP_FLOOD(1, "UDP Flood"),
|
||||
/**
|
||||
* The Icmp flood.
|
||||
*/
|
||||
ICMP_FLOOD(2, "ICMP Flood"),
|
||||
/**
|
||||
* The Tcp syn ack flood.
|
||||
*/
|
||||
TCP_SYN_ACK_FLOOD(3, "TCP SYN-ACK Flood"),
|
||||
/**
|
||||
* The Tcp fin flood.
|
||||
*/
|
||||
TCP_FIN_FLOOD(4, "TCP FIN Flood"),
|
||||
/**
|
||||
* The Ip fragment flood.
|
||||
*/
|
||||
IP_FRAGMENT_FLOOD(5, "IP Fragment Flood"),
|
||||
/**
|
||||
* The Tcp ack flood.
|
||||
*/
|
||||
TCP_ACK_FLOOD(6, "TCP ACK Flood"),
|
||||
/**
|
||||
* The Cc flood.
|
||||
*/
|
||||
CC_FLOOD(7, "CC Flood"),
|
||||
/**
|
||||
* The Http flood.
|
||||
*/
|
||||
HTTP_FLOOD(8, "HTTP Flood"),
|
||||
/**
|
||||
* The Dns query flood.
|
||||
*/
|
||||
DNS_QUERY_FLOOD(9, "DNS Query Flood"),
|
||||
/**
|
||||
* The Dns reply flood.
|
||||
*/
|
||||
DNS_REPLY_FLOOD(10, "DNS Reply Flood"),
|
||||
/**
|
||||
* The Host total traffic.
|
||||
*/
|
||||
HOST_TOTAL_TRAFFIC(11, "Host Total Traffic");
|
||||
|
||||
/**
|
||||
* The Code.
|
||||
*/
|
||||
private final Integer code;
|
||||
/**
|
||||
* The Readme.
|
||||
*/
|
||||
private final String readme;
|
||||
|
||||
/**
|
||||
* Instantiates a new D do s attack type.
|
||||
*
|
||||
* @param code the code
|
||||
* @param readme the readme
|
||||
*/
|
||||
DDoSAttackType(int code, String readme) {
|
||||
this.code = code;
|
||||
this.readme = readme;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets value.
|
||||
*
|
||||
* @return the value
|
||||
*/
|
||||
@Override
|
||||
public Integer getValue() {
|
||||
return this.code;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets description.
|
||||
*
|
||||
* @return the description
|
||||
*/
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return this.readme;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,107 @@
|
|||
package com.dispose.common;
|
||||
|
||||
/**
|
||||
* The enum Dp tech attack type.
|
||||
*
|
||||
* @author <huangxin@cmhi.chinamoblie.com>
|
||||
*/
|
||||
public enum DpTechAttackType implements BaseEnum {
|
||||
/**
|
||||
* The Tcp syn flood.
|
||||
*/
|
||||
TCP_SYN_FLOOD(1, "TCP SYN Flood"),
|
||||
/**
|
||||
* The Udp flood.
|
||||
*/
|
||||
UDP_FLOOD(2, "UDP Flood"),
|
||||
/**
|
||||
* The Icmp flood.
|
||||
*/
|
||||
ICMP_FLOOD(3, "ICMP Flood"),
|
||||
/**
|
||||
* The Tcp syn ack flood.
|
||||
*/
|
||||
TCP_SYN_ACK_FLOOD(6, "TCP SYN-ACK Flood"),
|
||||
/**
|
||||
* The Tcp fin flood.
|
||||
*/
|
||||
TCP_FIN_FLOOD(7, "TCP FIN Flood"),
|
||||
/**
|
||||
* The Ip fragment flood.
|
||||
*/
|
||||
IP_FRAGMENT_FLOOD(8, "IP Fragment Flood"),
|
||||
/**
|
||||
* The Tcp ack flood.
|
||||
*/
|
||||
TCP_ACK_FLOOD(13, "TCP ACK Flood"),
|
||||
/**
|
||||
* The Cc flood.
|
||||
*/
|
||||
CC_FLOOD(15, "CC Flood"),
|
||||
/**
|
||||
* The Http flood.
|
||||
*/
|
||||
HTTP_FLOOD(14, "HTTP Flood"),
|
||||
/**
|
||||
* The Dns query flood.
|
||||
*/
|
||||
DNS_QUERY_FLOOD(16, "DNS Query Flood"),
|
||||
/**
|
||||
* The Dns reply flood.
|
||||
*/
|
||||
DNS_REPLY_FLOOD(33, "DNS Reply Flood"),
|
||||
/**
|
||||
* The Host total traffic.
|
||||
*/
|
||||
HOST_TOTAL_TRAFFIC(31, "Host Total Traffic");
|
||||
|
||||
/**
|
||||
* The Code.
|
||||
*/
|
||||
private final Integer code;
|
||||
/**
|
||||
* The Readme.
|
||||
*/
|
||||
private final String readme;
|
||||
|
||||
/**
|
||||
* Instantiates a new Dp tech attack type.
|
||||
*
|
||||
* @param code the code
|
||||
* @param readme the readme
|
||||
*/
|
||||
DpTechAttackType(int code, String readme) {
|
||||
this.code = code;
|
||||
this.readme = readme;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets value.
|
||||
*
|
||||
* @return the value
|
||||
*/
|
||||
@Override
|
||||
public Integer getValue() {
|
||||
return this.code;
|
||||
}
|
||||
|
||||
/**
|
||||
* From ddos attack type value integer.
|
||||
*
|
||||
* @param type the type
|
||||
* @return the integer
|
||||
*/
|
||||
public static Integer fromDdosAttackTypeValue(DDoSAttackType type) {
|
||||
return type.getValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets description.
|
||||
*
|
||||
* @return the description
|
||||
*/
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return this.readme;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
package com.dispose.common;
|
||||
|
||||
/**
|
||||
* The enum Netflow direction.
|
||||
*
|
||||
* @author <huangxin@cmhi.chinamoblie.com>
|
||||
*/
|
||||
public enum NetflowDirection {
|
||||
/**
|
||||
* Direction in netflow direction.
|
||||
*/
|
||||
DIRECTION_IN(0, "入方向"),
|
||||
/**
|
||||
* Direction out netflow direction.
|
||||
*/
|
||||
DIRECTION_OUT(1, "出方向");
|
||||
/**
|
||||
* The Code.
|
||||
*/
|
||||
private final int code;
|
||||
/**
|
||||
* The Readme.
|
||||
*/
|
||||
private final String readme;
|
||||
|
||||
/**
|
||||
* Instantiates a new Netflow direction.
|
||||
*
|
||||
* @param code the code
|
||||
* @param readme the readme
|
||||
*/
|
||||
NetflowDirection(int code, String readme) {
|
||||
this.code = code;
|
||||
this.readme = readme;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets code.
|
||||
*
|
||||
* @return the code
|
||||
*/
|
||||
public int getCode() {
|
||||
return this.code;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets readme.
|
||||
*
|
||||
* @return the readme
|
||||
*/
|
||||
public String getReadme() {
|
||||
return this.readme;
|
||||
}
|
||||
}
|
|
@ -247,13 +247,17 @@ public class DisposeDeviceManagerController {
|
|||
|
||||
GetDeviceRsp rspInfo = new GetDeviceRsp();
|
||||
|
||||
// 拼装返回数据
|
||||
rspInfo.setStatus(ErrorCode.ERR_OK.getCode());
|
||||
rspInfo.setMessage(new String[]{ErrorCode.ERR_OK.getMsg()});
|
||||
|
||||
// 分页信息
|
||||
rspInfo.setCurPageNumber(ret.getFirstParam().getPageNum());
|
||||
rspInfo.setPageSize(ret.getFirstParam().getPageSize());
|
||||
rspInfo.setTotalItems((int)ret.getFirstParam().getTotal());
|
||||
rspInfo.setTotalPages(ret.getFirstParam().getPages());
|
||||
|
||||
// 设备信息
|
||||
ret.getSecondParam().forEach(v -> {
|
||||
GetDeviceDetail devInfo = new GetDeviceDetail();
|
||||
devInfo.setId(v.getId().toString());
|
||||
|
|
|
@ -16,6 +16,7 @@ import org.springframework.stereotype.Component;
|
|||
import javax.annotation.Resource;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
|
@ -219,6 +220,10 @@ public class DisposeDeviceManagerImpl implements DisposeDeviceManager {
|
|||
// 获取分页数据
|
||||
List<DisposeDevice> devList = disposeDeviceMapper.selectAll();
|
||||
|
||||
if(devList == null) {
|
||||
devList = new ArrayList<>();
|
||||
}
|
||||
|
||||
// 获取分页信息
|
||||
PageInfo<DisposeDevice> pageInfo = new PageInfo<>(devList);
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ import lombok.NoArgsConstructor;
|
|||
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.validation.constraints.Pattern;
|
||||
import javax.validation.constraints.PositiveOrZero;
|
||||
import javax.validation.constraints.Positive;
|
||||
|
||||
/**
|
||||
* The type Get device req.
|
||||
|
@ -25,7 +25,7 @@ public class GetDeviceReq {
|
|||
/**
|
||||
* The Start page.
|
||||
*/
|
||||
@PositiveOrZero(message = "startPage 字段值不能小于0", groups = ValidGroups.ProtocolCommonValid.class)
|
||||
@Positive(message = "startPage 字段值必须大于0", groups = ValidGroups.ProtocolCommonValid.class)
|
||||
@NotNull(message = "startPage 字段不能为空", groups = ValidGroups.ProtocolCommonValid.class)
|
||||
private Integer startPage;
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ import org.springframework.test.context.junit4.SpringRunner;
|
|||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
|
@ -61,7 +61,7 @@ public class DisposeDeviceManagerTest {
|
|||
* A 1 add dispose device.
|
||||
*/
|
||||
@Test
|
||||
public void a1_addDisposeDevice() throws IllegalAccessException, NoSuchMethodException, InvocationTargetException {
|
||||
public void a1_addDisposeDevice() {
|
||||
|
||||
for (int i = 1; i < 3; i++) {
|
||||
String ipAddr = "192.168.0." + i;
|
||||
|
@ -107,11 +107,12 @@ public class DisposeDeviceManagerTest {
|
|||
public void g1_getDisposeDevice() {
|
||||
PageHelper.startPage(0, 20);
|
||||
|
||||
List<DisposeDevice> devList = disposeDeviceMapper.selectAll();
|
||||
//List<DisposeDevice> devList = disposeDeviceMapper.selectAll();
|
||||
|
||||
List<DisposeDevice> devList = new ArrayList<>();
|
||||
|
||||
PageInfo<DisposeDevice> pageInfo = new PageInfo<>(devList);
|
||||
|
||||
|
||||
log.debug("Get {} device items", devList.size());
|
||||
log.debug("Page Info: total {}, page {}", pageInfo.getTotal(), pageInfo.getPageNum());
|
||||
}
|
||||
|
@ -120,7 +121,7 @@ public class DisposeDeviceManagerTest {
|
|||
* A 2 upgrade dispose device.
|
||||
*/
|
||||
@Test
|
||||
public void a2_upgradeDisposeDevice() throws IllegalAccessException, NoSuchMethodException, InvocationTargetException, JsonProcessingException {
|
||||
public void a2_upgradeDisposeDevice() throws JsonProcessingException {
|
||||
|
||||
for (int i = 1; i < 3; i++) {
|
||||
String ipAddr = "192.168.0." + i;
|
||||
|
@ -169,7 +170,7 @@ public class DisposeDeviceManagerTest {
|
|||
* A 3 change dispose device status.
|
||||
*/
|
||||
@Test
|
||||
public void a3_changeDisposeDeviceStatus() throws IllegalAccessException, NoSuchMethodException, InvocationTargetException, JsonProcessingException {
|
||||
public void a3_changeDisposeDeviceStatus() {
|
||||
|
||||
disposeDeviceMapper.selectAll().forEach(v -> {
|
||||
for (ObjectStatus obj : ObjectStatus.values()
|
||||
|
|
Loading…
Reference in New Issue