Merge branch 'main' of http://182.92.203.107:3000/libingkun/skyeyesystem
This commit is contained in:
commit
418b81fa54
@ -13,9 +13,8 @@ import com.zhangy.skyeye.jm.dto.JmJobQueryDTO;
|
|||||||
import com.zhangy.skyeye.jm.dto.JmJobUpdDTO;
|
import com.zhangy.skyeye.jm.dto.JmJobUpdDTO;
|
||||||
import com.zhangy.skyeye.jm.entity.JmJob;
|
import com.zhangy.skyeye.jm.entity.JmJob;
|
||||||
import com.zhangy.skyeye.jm.service.JmJobService;
|
import com.zhangy.skyeye.jm.service.JmJobService;
|
||||||
import com.zhangy.skyeye.jm.service.JmJobStatusService;
|
|
||||||
import com.zhangy.skyeye.publics.consts.ExecStatusEnum;
|
import com.zhangy.skyeye.publics.consts.ExecStatusEnum;
|
||||||
import com.zhangy.skyeye.sar.service.ISarControlService;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
import org.springframework.validation.annotation.Validated;
|
import org.springframework.validation.annotation.Validated;
|
||||||
@ -28,17 +27,12 @@ import java.util.Collections;
|
|||||||
@Validated
|
@Validated
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/sar/job")
|
@RequestMapping("/sar/job")
|
||||||
|
@Slf4j
|
||||||
public class JmJobController {
|
public class JmJobController {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private JmJobService jobService;
|
private JmJobService jobService;
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private ISarControlService controlInfoService;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private JmJobStatusService sarJobStatusService;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 分页查询
|
* 分页查询
|
||||||
*/
|
*/
|
||||||
@ -93,7 +87,6 @@ public class JmJobController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 修改
|
|
||||||
*/
|
*/
|
||||||
@PostMapping("/update")
|
@PostMapping("/update")
|
||||||
public Object update(@Valid @RequestBody JmJobUpdDTO param) {
|
public Object update(@Valid @RequestBody JmJobUpdDTO param) {
|
||||||
@ -119,6 +112,7 @@ public class JmJobController {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 开始执行任务
|
* 开始执行任务
|
||||||
|
*
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
@GetMapping("/start")
|
@GetMapping("/start")
|
||||||
@ -126,7 +120,7 @@ public class JmJobController {
|
|||||||
// 查询执行任务的无人机
|
// 查询执行任务的无人机
|
||||||
JmJobDTO job = jobService.selectDetail(id);
|
JmJobDTO job = jobService.selectDetail(id);
|
||||||
if (job == null) {
|
if (job == null) {
|
||||||
throw ServiceException.noLog("找不到任务,id=" + job.getId());
|
throw ServiceException.noLog("找不到任务,id=" + id);
|
||||||
} else if (EnumUtil.parseEx(ExecStatusEnum.class, job.getStatus()) != ExecStatusEnum.NOT) {
|
} else if (EnumUtil.parseEx(ExecStatusEnum.class, job.getStatus()) != ExecStatusEnum.NOT) {
|
||||||
throw ServiceException.noLog("任务状态不是未执行,无法起飞");
|
throw ServiceException.noLog("任务状态不是未执行,无法起飞");
|
||||||
}
|
}
|
||||||
@ -136,6 +130,7 @@ public class JmJobController {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 保存前清理ID,重新生成
|
* 保存前清理ID,重新生成
|
||||||
|
*
|
||||||
* @param e
|
* @param e
|
||||||
*/
|
*/
|
||||||
private void clearId(JmJobDTO e) {
|
private void clearId(JmJobDTO e) {
|
||||||
|
|||||||
@ -14,7 +14,10 @@ import com.zhangy.skyeye.jm.entity.JmJobPayload;
|
|||||||
import com.zhangy.skyeye.jm.entity.JmJobPoint;
|
import com.zhangy.skyeye.jm.entity.JmJobPoint;
|
||||||
import com.zhangy.skyeye.jm.entity.JmJobUav;
|
import com.zhangy.skyeye.jm.entity.JmJobUav;
|
||||||
import com.zhangy.skyeye.jm.service.JmAirlinePlanService;
|
import com.zhangy.skyeye.jm.service.JmAirlinePlanService;
|
||||||
import com.zhangy.skyeye.py.dto.*;
|
import com.zhangy.skyeye.py.dto.PyAirlineParamDTO;
|
||||||
|
import com.zhangy.skyeye.py.dto.PyAirlinePayloadDTO;
|
||||||
|
import com.zhangy.skyeye.py.dto.PyAirlineTargetDTO;
|
||||||
|
import com.zhangy.skyeye.py.dto.PyAirlineUavDTO;
|
||||||
import com.zhangy.skyeye.py.service.IPyAirlineService;
|
import com.zhangy.skyeye.py.service.IPyAirlineService;
|
||||||
import com.zhangy.skyeye.sar.consts.SarImageModeEnum;
|
import com.zhangy.skyeye.sar.consts.SarImageModeEnum;
|
||||||
import com.zhangy.skyeye.sar.dto.SarFlightPlanDTO;
|
import com.zhangy.skyeye.sar.dto.SarFlightPlanDTO;
|
||||||
@ -23,7 +26,10 @@ import lombok.extern.slf4j.Slf4j;
|
|||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -46,7 +52,6 @@ public class JmAirlinePlanServiceImpl implements JmAirlinePlanService {
|
|||||||
JSON.toJSONString(uavList), JSON.toJSONString(pointList));
|
JSON.toJSONString(uavList), JSON.toJSONString(pointList));
|
||||||
checkParam(jobMode, imageMode, targetType);
|
checkParam(jobMode, imageMode, targetType);
|
||||||
// 非航线模式需要调算法生成航线,需要从缓存取sar坐标
|
// 非航线模式需要调算法生成航线,需要从缓存取sar坐标
|
||||||
Map<Long, List<JmAirline>> airlineGroup = null;
|
|
||||||
// 1)聚束
|
// 1)聚束
|
||||||
if (imageMode == SarImageModeEnum.JS) {
|
if (imageMode == SarImageModeEnum.JS) {
|
||||||
return planJs(uavList.get(0), pointList);
|
return planJs(uavList.get(0), pointList);
|
||||||
@ -176,7 +181,6 @@ public class JmAirlinePlanServiceImpl implements JmAirlinePlanService {
|
|||||||
.map(p -> new double[]{p.getLongitude(), p.getLatitude()})
|
.map(p -> new double[]{p.getLongitude(), p.getLatitude()})
|
||||||
.toArray(double[][]::new);
|
.toArray(double[][]::new);
|
||||||
log.info("提取到 {} 个目标点坐标", coords.length);
|
log.info("提取到 {} 个目标点坐标", coords.length);
|
||||||
|
|
||||||
List<PyAirlineTargetDTO> pyTargets = new ArrayList<>();
|
List<PyAirlineTargetDTO> pyTargets = new ArrayList<>();
|
||||||
List<PyAirlineUavDTO> pyUavList = new ArrayList<>();
|
List<PyAirlineUavDTO> pyUavList = new ArrayList<>();
|
||||||
for (int i = 0; i < uavList.size(); i++) {
|
for (int i = 0; i < uavList.size(); i++) {
|
||||||
@ -221,6 +225,7 @@ public class JmAirlinePlanServiceImpl implements JmAirlinePlanService {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 从无人机载荷列表中查找 SAR 类型载荷,并设置 IP
|
* 从无人机载荷列表中查找 SAR 类型载荷,并设置 IP
|
||||||
|
*
|
||||||
* @param uav 无人机对象
|
* @param uav 无人机对象
|
||||||
* @return SAR 载荷
|
* @return SAR 载荷
|
||||||
* @throws ServiceException 如果没有找到 SAR 载荷
|
* @throws ServiceException 如果没有找到 SAR 载荷
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
package com.zhangy.skyeye.py.service.impl;
|
package com.zhangy.skyeye.py.service.impl;
|
||||||
|
|
||||||
import cn.hutool.core.bean.BeanUtil;
|
import cn.hutool.core.bean.BeanUtil;
|
||||||
|
import com.alibaba.fastjson2.JSON;
|
||||||
import com.zhangy.skyeye.common.extend.exception.ServiceException;
|
import com.zhangy.skyeye.common.extend.exception.ServiceException;
|
||||||
import com.zhangy.skyeye.common.extend.util.HttpUtil;
|
import com.zhangy.skyeye.common.extend.util.HttpUtil;
|
||||||
import com.zhangy.skyeye.common.extend.util.JsonUtil;
|
import com.zhangy.skyeye.common.extend.util.JsonUtil;
|
||||||
@ -13,16 +14,16 @@ import lombok.Setter;
|
|||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import com.alibaba.fastjson2.*;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.ConnectException;
|
import java.net.ConnectException;
|
||||||
import java.net.http.HttpResponse;
|
import java.net.http.HttpResponse;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.function.Consumer;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 航线规划
|
* 航线规划
|
||||||
@ -76,91 +77,132 @@ public class PyAirlineServiceImpl implements IPyAirlineService {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<Long, List<JmAirline>> getAirline(PyAirlineParamDTO param) {
|
public Map<Long, List<JmAirline>> getAirline(PyAirlineParamDTO param) {
|
||||||
log.info("请求航线规划算法服务参数:{}", JSON.toJSONString(param));
|
log.info("请求航线规划算法 | param: {}", JSON.toJSONString(param));
|
||||||
// HttpResponse<String> post;
|
// 1. 调用Python算法服务
|
||||||
// try {
|
HttpResponse<String> httpResponse = callPythonAirlineService(param);
|
||||||
// post = HttpUtil.post(javaUrl, null, param);
|
// 2. 解析响应
|
||||||
// } catch (IOException e) {
|
PyAirlineResponse response = parseResponse(httpResponse);
|
||||||
// throw new RuntimeException(e);
|
// 3. 检查HTTP状态码
|
||||||
// }
|
int statusCode = httpResponse.statusCode();
|
||||||
// PyAirlineResponse pyAirlineResponse;
|
if (statusCode >= 400) {
|
||||||
// pyAirlineResponse = JsonUtil.parse(post.body(), PyAirlineResponse.class);
|
String errMsg = String.format("算法服务返回错误[%d]: %s", statusCode, response.getMessage());
|
||||||
// log.info("请求航线规划算法服务响应:{}",JSON.toJSONString(pyAirlineResponse));
|
log.warn("调用航线规划算法失败 | httpStatus={}, msg={}, param={}",
|
||||||
HttpResponse<String> httpResponse;
|
statusCode, response.getMessage(), JSON.toJSONString(param));
|
||||||
try {
|
throw ServiceException.noLog(errMsg);
|
||||||
httpResponse = HttpUtil.post(url, null, param);
|
|
||||||
} catch (ConnectException ex) {
|
|
||||||
throw ServiceException.noLog("航线规划服务未启动!");
|
|
||||||
} catch (IOException ex) {
|
|
||||||
throw new ServiceException("Post请求错误:url=[" + url + "] " + ex.getMessage(), ex);
|
|
||||||
}
|
|
||||||
PyAirlineResponse response;
|
|
||||||
response = JsonUtil.parse(httpResponse.body(), PyAirlineResponse.class);
|
|
||||||
log.info("请求航线规划算法服务响应:{}", JSON.toJSONString(response));
|
|
||||||
int responseCode = httpResponse.statusCode();
|
|
||||||
if (responseCode >= 400) {
|
|
||||||
log.warn("调用航线规划算法错误[{}]:{},参数:{}", responseCode, response.getMessage(), JsonUtil.toString(param));
|
|
||||||
throw ServiceException.noLog("调用航线规划算法错误[" + responseCode + "]:" + response.getMessage());
|
|
||||||
}
|
}
|
||||||
|
log.info("算法服务响应 | {}", JSON.toJSONString(response));
|
||||||
|
// 4. 获取核心数据
|
||||||
Map<Long, PyAirlineDTO[]> uavMap = response.getData();
|
Map<Long, PyAirlineDTO[]> uavMap = response.getData();
|
||||||
log.info("航线规划算法结果:{}", JSON.toJSONString(uavMap));
|
// 5. 空结果的特殊处理
|
||||||
if (checkEmpty(uavMap)) {
|
if (isEmpty(uavMap)) {
|
||||||
if ("success".endsWith(response.getMessage())) {
|
handleEmptyResult(statusCode, response.getMessage(), param);
|
||||||
|
}
|
||||||
|
log.info("算法返回航线数据 | uavCount={}, rawData={}",
|
||||||
|
uavMap.size(), JSON.toJSONString(uavMap));
|
||||||
|
// 6. 转换为业务对象
|
||||||
|
return convertToJmAirlineMap(uavMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 调用Python航线规划服务
|
||||||
|
*/
|
||||||
|
private HttpResponse<String> callPythonAirlineService(PyAirlineParamDTO param) {
|
||||||
|
try {
|
||||||
|
return HttpUtil.post(url, null, param);
|
||||||
|
} catch (ConnectException e) {
|
||||||
|
throw ServiceException.noLog("航线规划服务未启动");
|
||||||
|
} catch (IOException e) {
|
||||||
|
String err = String.format("Post请求失败 url=[%s] %s", url, e.getMessage());
|
||||||
|
throw new ServiceException(err, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析HTTP响应体为对象
|
||||||
|
*/
|
||||||
|
private PyAirlineResponse parseResponse(HttpResponse<String> httpResponse) {
|
||||||
|
try {
|
||||||
|
return JsonUtil.parse(httpResponse.body(), PyAirlineResponse.class);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("算法响应JSON解析失败 | body={}", httpResponse.body(), e);
|
||||||
|
throw new ServiceException("算法返回数据格式错误", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理算法返回空数据的情况(兼容历史特殊逻辑)
|
||||||
|
*/
|
||||||
|
private void handleEmptyResult(int statusCode, String message, PyAirlineParamDTO param) {
|
||||||
|
String paramJson = JSON.toJSONString(param);
|
||||||
|
if ("success".equals(message)) {
|
||||||
|
log.warn("算法返回空结果但message=success,可能区域过大 | param={}", paramJson);
|
||||||
throw ServiceException.noLog("无法生成航线,可能飞行区域超过25平方公里");
|
throw ServiceException.noLog("无法生成航线,可能飞行区域超过25平方公里");
|
||||||
}
|
}
|
||||||
log.warn("调用航线规划算法未返回数据[{}]:{},参数:{}", responseCode, response.getMessage(), JsonUtil.toString(param));
|
String errMsg = String.format("算法未返回有效数据[%d]: %s", statusCode, message);
|
||||||
throw ServiceException.noLog("调用航线规划算法未返回数据[" + responseCode + "]:" + response.getMessage());
|
log.warn("算法返回空数据 | httpStatus={}, msg={}, param={}", statusCode, message, paramJson);
|
||||||
|
throw ServiceException.noLog(errMsg);
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<Long, List<JmAirline>> map = new HashMap<>();
|
/**
|
||||||
|
* 将算法返回的DTO数组转换为业务对象Map
|
||||||
|
* 注意:算法可能出现多架无人机只对应一条航线的情况(其他无人机返回空数组)
|
||||||
|
*/
|
||||||
|
private Map<Long, List<JmAirline>> convertToJmAirlineMap(Map<Long, PyAirlineDTO[]> uavMap) {
|
||||||
|
Map<Long, List<JmAirline>> result = new HashMap<>();
|
||||||
for (Map.Entry<Long, PyAirlineDTO[]> entry : uavMap.entrySet()) {
|
for (Map.Entry<Long, PyAirlineDTO[]> entry : uavMap.entrySet()) {
|
||||||
long uavId = entry.getKey();
|
Long uavId = entry.getKey();
|
||||||
PyAirlineDTO[] arr = entry.getValue();
|
PyAirlineDTO[] airlines = entry.getValue();
|
||||||
// 当多架无人机对应一条航线,则只有一架无人机有航线数据,其余为空数组
|
// 跳过空数组的无人机(常见于多机共享一条航线的情况)
|
||||||
if (arr == null || arr.length == 0) {
|
if (airlines == null || airlines.length == 0) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
map.put(uavId, Stream.of(arr)
|
List<JmAirline> jmAirlines = Arrays.stream(airlines)
|
||||||
.map(airlineDTO -> {
|
.map(this::convertPyAirlineToJmAirline)
|
||||||
// 算法返回高度为相对高度,需要加起飞点高度为海拔高度
|
.collect(Collectors.toList());
|
||||||
JmAirline airline = BeanUtil.copyProperties(airlineDTO, JmAirline.class);
|
result.put(uavId, jmAirlines);
|
||||||
double[] flightStart = airlineDTO.getFlightStart();
|
|
||||||
airline.setFlightStartLon(flightStart[0]);
|
|
||||||
airline.setFlightStartLat(flightStart[1]);
|
|
||||||
airline.setFlightStartHeight(flightStart[2]); // 相对起飞点高度
|
|
||||||
|
|
||||||
double[] flightEnd = airlineDTO.getFlightEnd();
|
|
||||||
airline.setFlightEndLon(flightEnd[0]);
|
|
||||||
airline.setFlightEndLat(flightEnd[1]);
|
|
||||||
airline.setFlightEndHeight(flightEnd[2]); // 相对起飞点高度
|
|
||||||
|
|
||||||
double[] groundStart = airlineDTO.getGroundStart();
|
|
||||||
airline.setGroundStartLon(groundStart[0]);
|
|
||||||
airline.setGroundStartLat(groundStart[1]);
|
|
||||||
airline.setGroundStartHeight(groundStart[2]); // 相对起飞点高度
|
|
||||||
|
|
||||||
double[] targetCentroid = airlineDTO.getTargetCentroid();
|
|
||||||
airline.setTargetCentroidLon(targetCentroid[0]);
|
|
||||||
airline.setTargetCentroidLat(targetCentroid[1]);
|
|
||||||
airline.setTargetCentroidHeight(targetCentroid[2]); // 相对起飞点高度
|
|
||||||
|
|
||||||
airline.setTargetWidth(airlineDTO.getTargetWidth());
|
|
||||||
airline.setTargetLength(airlineDTO.getTargetLength());
|
|
||||||
airline.setTargetHeading(airlineDTO.getTargetHeading());
|
|
||||||
|
|
||||||
double[] start = airlineDTO.getStart();
|
|
||||||
airline.setStartLon(start[0]);
|
|
||||||
airline.setStartLat(start[1]);
|
|
||||||
airline.setStartHeight(start[2]); // 相对起飞点高度
|
|
||||||
|
|
||||||
double[] end = airlineDTO.getEnd();
|
|
||||||
airline.setEndLon(end[0]);
|
|
||||||
airline.setEndLat(end[1]);
|
|
||||||
airline.setEndHeight(end[2]); // 相对起飞点高度
|
|
||||||
return airline;
|
|
||||||
})
|
|
||||||
.collect(Collectors.toList()));
|
|
||||||
}
|
}
|
||||||
return map;
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 单条航线DTO → 业务对象转换
|
||||||
|
* 核心点:算法返回的是相对高度,需要结合起飞点高度理解(但本方法不做高度修正,仅字段映射)
|
||||||
|
*/
|
||||||
|
private JmAirline convertPyAirlineToJmAirline(PyAirlineDTO dto) {
|
||||||
|
JmAirline airline = BeanUtil.copyProperties(dto, JmAirline.class);
|
||||||
|
// 起飞点(起点)
|
||||||
|
setCoordinate(airline::setFlightStartLon, airline::setFlightStartLat, airline::setFlightStartHeight,
|
||||||
|
dto.getFlightStart());
|
||||||
|
// 结束点
|
||||||
|
setCoordinate(airline::setFlightEndLon, airline::setFlightEndLat, airline::setFlightEndHeight,
|
||||||
|
dto.getFlightEnd());
|
||||||
|
// 离地起点(起飞段)
|
||||||
|
setCoordinate(airline::setGroundStartLon, airline::setGroundStartLat, airline::setGroundStartHeight,
|
||||||
|
dto.getGroundStart());
|
||||||
|
// 目标区域中心
|
||||||
|
setCoordinate(airline::setTargetCentroidLon, airline::setTargetCentroidLat, airline::setTargetCentroidHeight,
|
||||||
|
dto.getTargetCentroid());
|
||||||
|
// 目标区域尺寸 & 朝向
|
||||||
|
airline.setTargetWidth(dto.getTargetWidth());
|
||||||
|
airline.setTargetLength(dto.getTargetLength());
|
||||||
|
airline.setTargetHeading(dto.getTargetHeading());
|
||||||
|
// 航线规划起点/终点(通常是路径规划后的首末点)
|
||||||
|
setCoordinate(airline::setStartLon, airline::setStartLat, airline::setStartHeight, dto.getStart());
|
||||||
|
setCoordinate(airline::setEndLon, airline::setEndLat, airline::setEndHeight, dto.getEnd());
|
||||||
|
return airline;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setCoordinate(Consumer<Double> lonSetter, Consumer<Double> latSetter, Consumer<Double> heightSetter,
|
||||||
|
double[] coord) {
|
||||||
|
if (coord != null && coord.length == 3) {
|
||||||
|
lonSetter.accept(coord[0]);
|
||||||
|
latSetter.accept(coord[1]);
|
||||||
|
heightSetter.accept(coord[2]); // 算法返回的是相对高度
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isEmpty(Map<Long, PyAirlineDTO[]> map) {
|
||||||
|
return map == null || map.isEmpty() ||
|
||||||
|
map.values().stream().allMatch(arr -> arr == null || arr.length == 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user