Преглед на файлове

Merge remote-tracking branch 'origin/master'

lipenghui преди 3 седмици
родител
ревизия
62860b7bd1

+ 8 - 0
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/iotmainworkorder/IotMainWorkOrderController.java

@@ -145,6 +145,14 @@ public class IotMainWorkOrderController {
         return success(new PageResult<>(buildDeviceDistanceList(pageResult.getList()), pageResult.getTotal()));
     }
 
+    @GetMapping("/maintenanceSearch")
+    @Operation(summary = "以设备为维度统计所有保养计划明细中最近的保养数据 里程/时间/自然日期 可能以设备BOM分组")
+    @PreAuthorize("@ss.hasPermission('pms:iot-main-work-order:query')")
+    public CommonResult<PageResult<IotDeviceRespVO>> maintenanceSearch(@Valid IotMainWorkOrderPageReqVO pageReqVO) {
+        PageResult<IotDeviceRespVO> pageResult = iotMainWorkOrderService.maintenanceSearch(pageReqVO);
+        return success(new PageResult<>(buildDeviceDistanceList(pageResult.getList()), pageResult.getTotal()));
+    }
+
     @GetMapping("/deviceOrderPage")
     @Operation(summary = "根据设备id获得保养工单分页")
     @PreAuthorize("@ss.hasPermission('pms:iot-main-work-order:query')")

+ 4 - 0
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/vo/IotDeviceRespVO.java

@@ -1,6 +1,7 @@
 package cn.iocoder.yudao.module.pms.controller.admin.vo;
 
 import cn.iocoder.yudao.module.pms.controller.admin.iotdevicerunlog.vo.IotDeviceRunLogRespVO;
+import cn.iocoder.yudao.module.pms.controller.admin.iotmaintenancebom.vo.IotMaintenanceBomRespVO;
 import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
 import com.alibaba.excel.annotation.ExcelProperty;
 import io.swagger.v3.oas.annotations.media.Schema;
@@ -207,4 +208,7 @@ public class IotDeviceRespVO {
 
     @Schema(description = "启用日期")
     private String enableDate;
+
+    @Schema(description = "设备对应的 BOM分组 最短保养距离集合")
+    private List<IotMaintenanceBomRespVO> groupBomDistances;
 }

+ 8 - 0
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/service/iotmainworkorder/IotMainWorkOrderService.java

@@ -75,6 +75,14 @@ public interface IotMainWorkOrderService {
      */
     PageResult<IotDeviceRespVO> deviceMainDistances(IotMainWorkOrderPageReqVO pageReqVO);
 
+    /**
+     * 以设备为维度统计所有保养计划明细中最近的保养数据 里程/时间/自然日期 可能以设备BOM分组
+     *
+     * @param pageReqVO 分页查询
+     * @return 保养工单分页
+     */
+    PageResult<IotDeviceRespVO> maintenanceSearch(IotMainWorkOrderPageReqVO pageReqVO);
+
     /**
      * 查询工单表中最大的id
      *

+ 408 - 1
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/service/iotmainworkorder/IotMainWorkOrderServiceImpl.java

@@ -11,6 +11,7 @@ import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
 import cn.iocoder.yudao.module.pms.controller.admin.iotdevicerunlog.vo.IotDeviceRunLogRespVO;
 import cn.iocoder.yudao.module.pms.controller.admin.iotlockstock.vo.IotLockStockPageReqVO;
 import cn.iocoder.yudao.module.pms.controller.admin.iotmaintenancebom.vo.IotMaintenanceBomPageReqVO;
+import cn.iocoder.yudao.module.pms.controller.admin.iotmaintenancebom.vo.IotMaintenanceBomRespVO;
 import cn.iocoder.yudao.module.pms.controller.admin.iotmainworkorder.vo.IotMainWorkOrderPageReqVO;
 import cn.iocoder.yudao.module.pms.controller.admin.iotmainworkorder.vo.IotMainWorkOrderRespVO;
 import cn.iocoder.yudao.module.pms.controller.admin.iotmainworkorder.vo.IotMainWorkOrderSaveReqVO;
@@ -254,6 +255,7 @@ public class IotMainWorkOrderServiceImpl implements IotMainWorkOrderService {
                 }
                 // 找出距离0最近的数,作为工单的最近保养距离数据
                 Object closet = chooseNearestDistance(runningTimeDistance, runningKiloDistance, naturalDateDistance);
+                // key距离单位(小时 公里 天)   value最小的距离值
                 Map<String, Object> tempDistance = new HashMap<>();
                 if (closet == runningTimeDistance) {
                     tempDistance.put("H", closet);
@@ -577,6 +579,366 @@ public class IotMainWorkOrderServiceImpl implements IotMainWorkOrderService {
         }
     }
 
+    @Override
+    public PageResult<IotDeviceRespVO> maintenanceSearch(IotMainWorkOrderPageReqVO pageReqVO) {
+        // 所有保养计划 明细中待保养的最近距离 里程/时间/自然日
+        Set<Long> deviceIds = new HashSet<>();
+        Set<Long> orderDeviceIds = new HashSet<>();
+        // 查询保养计划明细中不包含 deviceIds 的设备id集合
+        List<IotMaintenanceBomDO> mainBomS = new ArrayList<>();
+        Set<Long> mainBomDeviceIds = new HashSet<>();
+
+        IotMaintenanceBomPageReqVO mainBomReqVO = new IotMaintenanceBomPageReqVO();
+        mainBomS = iotMaintenanceBomMapper.selectList(mainBomReqVO);
+        Set<String> boundedMultiAttrNames = new HashSet<>();
+        if (CollUtil.isNotEmpty(mainBomS)) {
+            mainBomDeviceIds = CollUtil.isEmpty(mainBomS)
+                    ? Collections.emptySet()
+                    : mainBomS.stream()
+                    .map(IotMaintenanceBomDO::getDeviceId) // 获取 deviceId
+                    .filter(Objects::nonNull)      // 过滤非空值
+                    .collect(Collectors.toSet());  // 收集到 Set 中
+            // 查询所有保养计划 保养项 中已经绑定的 多个累计时长 公里数 属性名称值
+            mainBomS.forEach(bom -> {
+                if (StrUtil.isNotBlank(bom.getType())) {
+                    // 累计公里数属性
+                    boundedMultiAttrNames.add(bom.getType());
+                }
+                if (StrUtil.isNotBlank(bom.getCode())) {
+                    // 累计时长属性
+                    boundedMultiAttrNames.add(bom.getCode());
+                }
+            });
+        }
+        deviceIds.addAll(mainBomDeviceIds);
+        if (CollUtil.isEmpty(deviceIds)){
+            // 没有设备信息,返回空列表
+            return PageResult.empty();
+        }
+        // 查询 运行记录模板中包含多个累计 时长 公里数 属性的集合
+        List<IotDeviceRunLogRespVO> multipleAccumulatedData = new ArrayList<>();
+        if (CollUtil.isNotEmpty(deviceIds) && CollUtil.isNotEmpty(boundedMultiAttrNames)) {
+            multipleAccumulatedData = iotDeviceRunLogService.multipleAccumulatedData(deviceIds, boundedMultiAttrNames);
+        }
+        // key(设备id-累计时长属性名称)    value时长属性累计时长数值
+        Map<String, BigDecimal> tempTotalRunDataPair = new HashMap<>();
+        if (CollUtil.isNotEmpty(multipleAccumulatedData)) {
+            multipleAccumulatedData.forEach(data -> {
+                String uniqueKey = StrUtil.join("-", data.getDeviceId(), data.getPointName());
+                tempTotalRunDataPair.put(uniqueKey, data.getTotalRunTime());
+            });
+        }
+
+        // 查询 运行记录模板中 正常的累计时长 公里数集合
+        Map<Long, IotDeviceRunLogRespVO> deviceRunLogMap = iotDeviceRunLogService.getDeviceRunLogMap(new ArrayList<>(deviceIds));
+        // 以设备为维度统计每个设备相关的保养项的最近保养距离 key设备id    value设备下每个保养项的的最小保养距离集合
+        Map<Long, List<Map<String, Object>>> orderDistancePair = new HashMap<>();
+        // 设备保养明细 key设备id  value设备保养工单明细下所有保养规则数据最小值
+        Map<Long, String> resultMap = new HashMap<>();
+        // 设备保养明细 key设备id-保养项分组名称  value设备保养项分组下最近的保养距离
+        Map<String, String> groupResultMap = new HashMap<>();
+        // 以设备为维度统计每个设备相关的保养项(分组)的最近保养距离 key设备id-保养项分组名称    value设备下每个保养项的的最小保养距离集合
+        Map<String, List<Map<String, Object>>> minBomGroupDistancePair = new HashMap<>();
+        // 以“设备id-分组bom名称-bomNodeId”为维度统计每个设备相关的保养项的最近保养距离关联的对象 key设备id-分组bom名称-bomNodeId
+        // value设备下每个保养项的的最小保养距离关联的保养项对象
+        Map<String, IotMaintenanceBomDO> minBomDistancePair = new HashMap<>();
+        // 以“设备id-分组bom名称-bomNodeId”为维度统计每个设备相关的保养项的最近保养距离关联的对象 key设备id-分组bom名称-bomNodeId
+        // value设备下每个保养项的的最小保养距离 数值
+        Map<String, String> bomDistancePair = new HashMap<>();
+        // key设备id   value设备对应的各个bom分组及分组下的最短保养距离
+        Map<Long, List<IotMaintenanceBomDO>> deviceBomGroupPair = new HashMap<>();
+        // key设备id   value设备对应的各个bom分组及分组下的最短保养距离
+        Map<Long, List<IotMaintenanceBomRespVO>> resultDeviceBomGroupPair = new HashMap<>();
+        // key设备id-bomNodeId   value保养项的累计运行时长/里程
+        Map<String, IotMaintenanceBomRespVO> maintenanceBomRespPair = new HashMap<>();
+        if (CollUtil.isNotEmpty(mainBomS)) {
+            // 遍历保养计划明细 计算 保养计划中的设备 的最近的保养时间
+            mainBomS.forEach(bom -> {
+                // 拷贝一份保养明细对象 设置 累计运行时长/里程 使用
+                IotMaintenanceBomRespVO bomResp = new IotMaintenanceBomRespVO();
+                BeanUtils.copyProperties(bom, bomResp);
+                BigDecimal runningTimeDistance = null;
+                BigDecimal runningKiloDistance = null;
+                BigDecimal naturalDateDistance = null;
+                // 计算每个保养项 每个保养规则下的 距离保养时间 单位 小时
+                if (ObjUtil.isNotEmpty(bom.getRunningTimeRule()) && 0 == bom.getRunningTimeRule()) {
+                    // 运行时间保养规则
+                    BigDecimal totalRunTime = BigDecimal.ZERO;
+                    if (deviceRunLogMap.containsKey(bom.getDeviceId())) {
+                        totalRunTime = deviceRunLogMap.get(bom.getDeviceId()).getTotalRunTime();
+                    } else {
+                        // 运行记录模板中包含多个累计时长 公里数类型的属性
+                        String uniqueKey = StrUtil.join("-", bom.getDeviceId(), bom.getCode());
+                        if (tempTotalRunDataPair.containsKey(uniqueKey)) {
+                            totalRunTime = tempTotalRunDataPair.get(uniqueKey);
+                        }
+                    }
+                    BigDecimal lastRunningTime = bom.getLastRunningTime();      // 上次保养运行时间
+                    BigDecimal runningTimePeriod = bom.getNextRunningTime();    // 运行时间周期
+                    BigDecimal timePeriodLead = bom.getTimePeriodLead();    // 运行时间周期提前量
+                    runningTimeDistance = runningTimePeriod.subtract(totalRunTime.subtract(lastRunningTime));
+                    bomResp.setTotalRunTime(totalRunTime);
+                }
+                if (ObjUtil.isNotEmpty(bom.getMileageRule()) && 0 == bom.getMileageRule()) {
+                    // 运行里程保养规则 累计运行里程规则 累计运行里程 >= (上次保养运行里程+运行里程周期-提前量)
+                    BigDecimal totalMileage = BigDecimal.ZERO;
+                    if (deviceRunLogMap.containsKey(bom.getDeviceId())) {
+                        totalMileage = deviceRunLogMap.get(bom.getDeviceId()).getTotalMileage();
+                    } else {
+                        // 运行记录模板中包含多个累计时长 公里数类型的属性
+                        String uniqueKey = StrUtil.join("-", bom.getDeviceId(), bom.getType());
+                        if (tempTotalRunDataPair.containsKey(uniqueKey)) {
+                            totalMileage = tempTotalRunDataPair.get(uniqueKey);
+                        }
+                    }
+                    BigDecimal lastRunningKilo = bom.getLastRunningKilometers();      // 上次保养运行里程
+                    BigDecimal runningKiloPeriod = bom.getNextRunningKilometers();    // 运行里程周期
+                    BigDecimal kiloCycleLead = bom.getKiloCycleLead();    // 运行里程周期提前量
+                    runningKiloDistance = runningKiloPeriod.subtract(totalMileage.subtract(lastRunningKilo));
+                    bomResp.setTotalMileage(totalMileage);
+                }
+                if (ObjUtil.isNotEmpty(bom.getNaturalDateRule()) && 0 == bom.getNaturalDateRule()) {
+                    // 自然日期保养规则
+                    LocalDateTime lastNaturalDate = bom.getLastNaturalDate();      // 上次保养自然日期
+                    BigDecimal naturalDatePeriod = bom.getNextNaturalDate();        // 自然日周期
+                    BigDecimal natualDateLead = bom.getNaturalDatePeriodLead();    // 自然日周期提前量
+                    if (ObjUtil.isNotEmpty(lastNaturalDate) && ObjUtil.isNotEmpty(naturalDatePeriod) && ObjUtil.isNotEmpty(natualDateLead)) {
+                        // 计算有效天数 = 自然日周期 - 提前量
+                        long days = naturalDatePeriod.longValue(); // 转为长整型天数
+                        // 计算目标日期:上次保养日期 + 有效天数(注意:LocalDateTime加天数会自动处理日期进位)
+                        LocalDateTime targetDate = lastNaturalDate.plusDays(days);
+                        // 获取当前日期时间
+                        LocalDateTime now = LocalDateTime.now();
+                        // 计算日期差值(以天为单位)
+                        naturalDateDistance = new BigDecimal(ChronoUnit.DAYS.between(now, targetDate));
+                    }
+                }
+                // 按照保养项的父BOM节点分组统计 最短保养距离
+                // 先按照 保养项目 层级分组 电驱空压机->油气分离器滤芯
+                String bomNodeName = bom.getName();
+                Long bomNodeId = bom.getBomNodeId();
+                String uniqueKey = StrUtil.EMPTY;
+                String uniqueBomKey = StrUtil.EMPTY;
+                String uniqueBomNodeIdKey = StrUtil.EMPTY;
+                if (StrUtil.isNotBlank(bomNodeName)) {
+                    String[] bomNodeNameAttr = bomNodeName.split("->");
+                    if (ObjUtil.isNotEmpty(bomNodeNameAttr) && ObjUtil.isNotEmpty(bomNodeId) && bomNodeNameAttr.length > 1) {
+                        // 当前保养项是 多级 BOM 可以分组统计 每个分组下的 最短保养距离   key"设备id-电驱空压机"
+                        uniqueKey = StrUtil.join("-", bom.getDeviceId(), bomNodeNameAttr[0]);
+                        uniqueBomKey = StrUtil.join("-", bom.getDeviceId(), bomNodeNameAttr[0], bomNodeId);
+                        uniqueBomNodeIdKey = StrUtil.join("-", bom.getDeviceId(), bom.getBomNodeId());
+                    }
+                }
+                // 设置每个保养项的 累计运行时长/里程
+                maintenanceBomRespPair.put(uniqueBomNodeIdKey, bomResp);
+
+                IotMaintenanceBomDO tempBom = new IotMaintenanceBomDO();
+                tempBom.setDeviceId(bom.getDeviceId());
+                tempBom.setBomNodeId(bom.getBomNodeId());
+                tempBom.setName(bom.getName());
+                tempBom.setRunningTimeRule(bom.getRunningTimeRule());
+                tempBom.setMileageRule(bom.getMileageRule());
+                tempBom.setNaturalDateRule(bom.getNaturalDateRule());
+                // 找出距离0最近的数,作为工单的最近保养距离数据
+                Object closet = chooseNearestDistance(runningTimeDistance, runningKiloDistance, naturalDateDistance);
+                String tempMinDistance = StrUtil.EMPTY;
+                Map<String, Object> tempDistance = new HashMap<>();
+                if (closet == runningTimeDistance) {
+                    tempDistance.put("H", closet);
+                    tempMinDistance = StrUtil.join(" ", closet, "H");
+                    // 运行时长
+                    tempBom.setLastRunningTime(bom.getLastRunningTime());
+                    tempBom.setNextRunningTime(bom.getNextRunningTime());
+                    tempBom.setTimePeriodLead(bom.getTimePeriodLead());
+                } else if (closet == runningKiloDistance) {
+                    tempDistance.put("KM", closet);
+                    tempMinDistance = StrUtil.join(" ", closet, "KM");
+                    // 运行里程
+                    tempBom.setLastRunningKilometers(bom.getLastRunningKilometers());
+                    tempBom.setNextRunningKilometers(bom.getNextRunningKilometers());
+                    tempBom.setKiloCycleLead(bom.getKiloCycleLead());
+                } else if (closet == naturalDateDistance) {
+                    tempDistance.put("D", closet);
+                    tempMinDistance = StrUtil.join(" ", closet, "D");
+                    // 自然日期
+                    tempBom.setLastNaturalDate(bom.getLastNaturalDate());
+                    tempBom.setNextNaturalDate(bom.getNextNaturalDate());
+                    tempBom.setNaturalDatePeriodLead(bom.getNaturalDatePeriodLead());
+                }
+                // 保存每个保养项对象 之后与 每个保养项分组的 最短保养距离匹配
+                minBomDistancePair.put(uniqueBomKey, tempBom);
+                // 保存每个保养项的最短距离 之后与 每个保养项分组的 最短保养距离匹配
+                bomDistancePair.put(uniqueBomKey, tempMinDistance);
+                if (orderDistancePair.containsKey(bom.getDeviceId())) {
+                    List<Map<String, Object>> tempDistances = orderDistancePair.get(bom.getDeviceId());
+                    tempDistances.add(tempDistance);
+                    orderDistancePair.put(bom.getDeviceId(), tempDistances);
+                } else {
+                    List<Map<String, Object>> tempDistances = new ArrayList<>();
+                    tempDistances.add(tempDistance);
+                    orderDistancePair.put(bom.getDeviceId(), tempDistances);
+                }
+                // 按照保养项的父BOM节点分组统计 最短保养距离
+                // 先按照 保养项目 层级分组 电驱空压机->油气分离器滤芯
+                if (StrUtil.isNotBlank(uniqueKey)) {
+                    if (minBomGroupDistancePair.containsKey(uniqueKey)) {
+                        List<Map<String, Object>> tempDistances = minBomGroupDistancePair.get(uniqueKey);
+                        tempDistances.add(tempDistance);
+                        minBomGroupDistancePair.put(uniqueKey, tempDistances);
+                    } else {
+                        List<Map<String, Object>> tempDistances = new ArrayList<>();
+                        tempDistances.add(tempDistance);
+                        minBomGroupDistancePair.put(uniqueKey, tempDistances);
+                    }
+                }
+            });
+        }
+        // 以“设备id-保养项分组” 为维度 统计每个设备保养项分组下 距离最近的保养数据
+        groupResultMap = findGroupClosestToZero(minBomGroupDistancePair);
+        List<Map.Entry<String, String>> keys = groupResultMap.entrySet().stream().filter(e -> e.getKey().contains("530-")).collect(Collectors.toList());
+        // minBomDistancePair  bomDistancePair 循环这2个对象 组装每个分组需要显示的最短保养距离 BOM对象
+        if (CollUtil.isNotEmpty(bomDistancePair)) {
+            Map<String, String> finalGroupResultMap = groupResultMap;
+            bomDistancePair.forEach((k, v) -> {
+                // k设备id-分组bom名称-bomNodeId   v设备下每个保养项的的最小保养距离 数值
+                // 分割出 k 的前部分 “设备id-分组bom名称”
+                String[] firstKeyAttr = splitAtSecondHyphen(k);
+                if (ObjUtil.isNotEmpty(firstKeyAttr) && firstKeyAttr.length > 1) {
+                    String firstKeyStr = firstKeyAttr[0];
+                    Long deviceId = 0l;
+                    if (StrUtil.isNotBlank(firstKeyStr)) {
+                        String[] deviceIdAttr = firstKeyStr.split("-");
+                        if (ObjUtil.isNotEmpty(deviceIdAttr) && deviceIdAttr.length > 1) {
+                            deviceId = Long.valueOf(deviceIdAttr[0]);
+                        }
+                    }
+                    if (finalGroupResultMap.containsKey(firstKeyAttr[0])) {
+                        // "-2249.00 H"
+                        String groupMinDistance = finalGroupResultMap.get(firstKeyAttr[0]);
+                        // 去掉 距离单位 获得 数值
+                        if (StrUtil.isNotBlank(groupMinDistance)) {
+                            if (StrUtil.equals(groupMinDistance, v)) {
+                                // 根据 k 找到 minBomDistancePair 中对应的 最小距离 保养对象
+                                if (minBomDistancePair.containsKey(k)) {
+                                    IotMaintenanceBomDO tempBom = minBomDistancePair.get(k);
+                                    if (deviceBomGroupPair.containsKey(deviceId)) {
+                                        List<IotMaintenanceBomDO> tempBomS = deviceBomGroupPair.get(deviceId);
+                                        tempBomS.add(tempBom);
+                                        deviceBomGroupPair.put(deviceId, tempBomS);
+                                    } else {
+                                        List<IotMaintenanceBomDO> tempBomS = new ArrayList<>();
+                                        tempBomS.add(tempBom);
+                                        deviceBomGroupPair.put(deviceId, tempBomS);
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            });
+        }
+
+        // 重新整理 deviceBomGroupPair 集合 每个 设备的保养项分组只保留1个对象 因为存在 相同保养距离的保养项
+        if (CollUtil.isNotEmpty(deviceBomGroupPair)) {
+            deviceBomGroupPair.forEach((k,v) -> {
+                // k设备id    v分组bom中距离保养最近的对象
+                Set<String> bomNames = new HashSet<>();
+                List<IotMaintenanceBomRespVO> copyBomS = new ArrayList<>();
+                v.forEach(bom -> {
+                    String leveledBomName = bom.getName();
+                    String[] bomNameAttr = leveledBomName.split("->");
+                    String uniqueKey = StrUtil.join("-", bom.getDeviceId(), bom.getBomNodeId());
+                    if (ObjUtil.isNotEmpty(bomNameAttr) && bomNameAttr.length > 1) {
+                        if (!bomNames.contains(bomNameAttr[0])) {
+                            IotMaintenanceBomRespVO bomResp = new IotMaintenanceBomRespVO();
+                            BeanUtils.copyProperties(bom, bomResp);
+                            if (maintenanceBomRespPair.containsKey(uniqueKey)) {
+                                IotMaintenanceBomRespVO tempBomResp = maintenanceBomRespPair.get(uniqueKey);
+                                bomResp.setTotalRunTime(tempBomResp.getTotalRunTime());
+                                bomResp.setTotalMileage(tempBomResp.getTotalMileage());
+                            }
+                            copyBomS.add(bomResp);
+                        }
+                        bomNames.add(bomNameAttr[0]);
+                    }
+                });
+                resultDeviceBomGroupPair.put(k, copyBomS);
+            });
+        }
+
+        // 以设备id 为维度 统计每个保养计划明细中 距离最近的保养数据
+        resultMap = findClosestToZero(orderDistancePair);
+        // 对集合 resultMap 中所有数据进行排序 按照 map 的value值 去除后面的 字符后 升序排列
+        // 排序后输出一个 List<Long> 类型的集合,排序对应上面的排序规则
+        List<Long> sortedDeviceIds = sortByNumericValue(resultMap);
+        try {
+            // 左侧组织树 组织层级查询
+            Set<Long> ids = new HashSet<>();
+            if (Objects.nonNull(pageReqVO.getDeptId())) {
+                ids = deptService.getChildDeptIdListFromCache(pageReqVO.getDeptId());
+                ids.add(pageReqVO.getDeptId());
+            }
+            // 不查询保养工单表 暂时传一个不会出现的设备id
+            orderDeviceIds.add(Long.MIN_VALUE);
+            List<IotDeviceRespVO> alarmDevices = iotDeviceMapper.deviceAlarmDistances(sortedDeviceIds, orderDeviceIds, mainBomDeviceIds);
+            // 处理当前分页数据 拼接上已经排序的 筛选出的设备保养项 最小保养距离
+            List<Long> alarmDeviceIds = new ArrayList<>();
+            Map<Long, IotDeviceRespVO> alarmDevicePair = new HashMap<>();
+            if (CollUtil.isNotEmpty(alarmDevices)) {
+                for (IotDeviceRespVO device : alarmDevices) {
+                    if (resultMap.containsKey(device.getId())) {
+                        device.setMainDistance(resultMap.get(device.getId()));
+                        alarmDeviceIds.add(device.getId());
+                    }
+                    alarmDevicePair.put(device.getId(), device);
+                }
+            }
+            // 查询所有设备列表 通过SQL形式 使用 FIELD 字段
+            IPage<IotDeviceRespVO> page = iotDeviceMapper.allDeviceDistances(
+                    new Page<>(pageReqVO.getPageNo(), pageReqVO.getPageSize()), pageReqVO, alarmDeviceIds, ids);
+            if (CollUtil.isNotEmpty(page.getRecords())) {
+                page.getRecords().forEach(device -> {
+                    if (alarmDevicePair.containsKey(device.getId())) {
+                        device.setMainDistance(alarmDevicePair.get(device.getId()).getMainDistance());
+                        device.setPlanId(alarmDevicePair.get(device.getId()).getPlanId());
+                        device.setWorkOrderId(alarmDevicePair.get(device.getId()).getWorkOrderId());
+                    }
+                    // 查询 每个设备对应的 保养项 分组 集合 已经设置了 最短距离
+                    if (resultDeviceBomGroupPair.containsKey(device.getId())) {
+                        List<IotMaintenanceBomRespVO> tempBomS = resultDeviceBomGroupPair.get(device.getId());
+                        device.setGroupBomDistances(tempBomS);
+                    }
+                });
+            }
+            return new PageResult<>(page.getRecords(), page.getTotal());
+        } catch (Exception exception) {
+            if (exception.getMessage().contains("Table does not exist")) {
+                return PageResult.empty();
+            }
+            throw exception;
+        }
+    }
+
+    private String[] splitAtSecondHyphen(String str) {
+        int firstIndex = str.indexOf('-');
+        if (firstIndex == -1) {
+            // 没有找到任何 '-',返回整个字符串和空字符串
+            return new String[]{str, ""};
+        }
+
+        // 从第一个 '-' 后开始查找第二个 '-'
+        int secondIndex = str.indexOf('-', firstIndex + 1);
+        if (secondIndex == -1) {
+            // 没有找到第二个 '-',返回整个字符串和空字符串
+            return new String[]{str, ""};
+        }
+
+        // 分割字符串
+        String part1 = str.substring(0, secondIndex);
+        String part2 = str.substring(secondIndex + 1);
+        return new String[]{part1, part2};
+    }
+
     /**
      * 排序 Map<Long, String> 中的元素 删除 类似 D、 H、 KM 后进行升序排列
      * @param map
@@ -654,6 +1016,52 @@ public class IotMainWorkOrderServiceImpl implements IotMainWorkOrderService {
         return result;
     }
 
+    /**
+     * 找出每个设备对应的bom保养项(层级分组)集合中 距离保养最短的 数据
+     *
+     * @param minBomGroupDistancePair key设备id-保养项一级分组名称
+     */
+    private Map<String, String> findGroupClosestToZero(Map<String, List<Map<String, Object>>> minBomGroupDistancePair) {
+        Map<String, String> result = new HashMap<>();
+        for (Map.Entry<String, List<Map<String, Object>>> entry : minBomGroupDistancePair.entrySet()) {
+            String uniqueKey = entry.getKey();  // 设备id-保养项节点第一层级名称
+            List<Map<String, Object>> mapList = entry.getValue();
+            // 存储所有有效值(原始值、单位、转换值)
+            List<Triple<BigDecimal, String, BigDecimal>> allValues = new ArrayList<>();
+            // 收集所有值并进行单位转换
+            for (Map<String, Object> map : mapList) {
+                for (Map.Entry<String, Object> valueEntry : map.entrySet()) {
+                    if (valueEntry.getValue() instanceof BigDecimal) {
+                        BigDecimal value = (BigDecimal) valueEntry.getValue();
+                        String unit = valueEntry.getKey();
+                        BigDecimal convertedValue;
+                        // 小时转换为天
+                        if ("H".equals(unit)) {
+                            convertedValue = value.divide(BigDecimal.valueOf(24), 2, BigDecimal.ROUND_HALF_UP);
+                        } else {
+                            convertedValue = value;
+                        }
+                        allValues.add(new Triple<>(value, unit, convertedValue));
+                    }
+                }
+            }
+            // 如果没有有效值,跳过
+            if (allValues.isEmpty()) {
+                continue;
+            }
+            // 找到转换后值最小的值
+            Triple<BigDecimal, String, BigDecimal> minTriple = allValues.get(0);
+            for (Triple<BigDecimal, String, BigDecimal> triple : allValues) {
+                if (triple.getThird().compareTo(minTriple.getThird()) < 0) {
+                    minTriple = triple;
+                }
+            }
+            // 格式化为字符串: "原始值 单位"
+            result.put(uniqueKey, minTriple.getFirst() + " " + minTriple.getSecond());
+        }
+        return result;
+    }
+
     /**
      * 将Map转换为String格式:value1 key1; value2 key2
      */
@@ -971,7 +1379,6 @@ public class IotMainWorkOrderServiceImpl implements IotMainWorkOrderService {
         }
         // 如果在填报工单时 填写了 保养项 中的 ‘运行时间周期(H)’ 保存时需要同步到 保养工单 关联的 保养计划的 对应的保养项的 ‘运行时间周期(H)’
         // 包含 延时保养 的情况
-
         // 只扣减本地库存 不处理SAP库存
         if (CollUtil.isNotEmpty(factoryIds) && CollUtil.isNotEmpty(costCenterIds) && CollUtil.isNotEmpty(materialCodes)) {
             IotLockStockPageReqVO reqVO = new IotLockStockPageReqVO();