Pārlūkot izejas kodu

Merge remote-tracking branch 'origin/master'

lipenghui 2 mēneši atpakaļ
vecāks
revīzija
95563d69b2

+ 6 - 0
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/config/PmsDataPermissionConfiguration.java

@@ -10,8 +10,10 @@ import cn.iocoder.yudao.module.pms.dal.dataobject.inspect.IotInspectItemDO;
 import cn.iocoder.yudao.module.pms.dal.dataobject.inspect.IotInspectOrderDO;
 import cn.iocoder.yudao.module.pms.dal.dataobject.inspect.IotInspectPlanDO;
 import cn.iocoder.yudao.module.pms.dal.dataobject.inspect.IotInspectRouteDO;
+import cn.iocoder.yudao.module.pms.dal.dataobject.iotlockstock.IotLockStockDO;
 import cn.iocoder.yudao.module.pms.dal.dataobject.iotmainworkorder.IotMainWorkOrderDO;
 import cn.iocoder.yudao.module.pms.dal.dataobject.maintain.IotMaintainDO;
+import cn.iocoder.yudao.module.pms.dal.dataobject.maintenance.IotMaintenancePlanDO;
 import cn.iocoder.yudao.module.supplier.dal.dataobject.product.SupplierDO;
 import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO;
 import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
@@ -44,6 +46,10 @@ public class PmsDataPermissionConfiguration {
             rule.addDeptColumn(IotInspectRouteDO.class,"dept_id");
             rule.addDeptColumn(IotInspectPlanDO.class, "dept_id");
             rule.addDeptColumn(IotInspectOrderDO.class, "dept_id");
+            rule.addDeptColumn(IotLockStockDO.class, "dept_id");
+            rule.addDeptColumn(IotMainWorkOrderDO.class, "dept_id");
+            rule.addDeptColumn(IotMaintenancePlanDO.class, "dept_id");
+
             // user
             rule.addUserColumn(SupplierDO.class);
             rule.addUserColumn(AdminUserDO.class, "id");

+ 0 - 1
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/IotDeviceController.java

@@ -14,7 +14,6 @@ import cn.iocoder.yudao.module.pms.controller.admin.iotdeviceperson.vo.IotDevice
 import cn.iocoder.yudao.module.pms.controller.admin.vo.*;
 import cn.iocoder.yudao.module.pms.dal.dataobject.IotDeviceDO;
 import cn.iocoder.yudao.module.pms.dal.dataobject.IotProductClassifyDO;
-import cn.iocoder.yudao.module.pms.dal.dataobject.yanfan.ThingsModelDO;
 import cn.iocoder.yudao.module.pms.dal.dataobject.yanfan.YfDeviceDO;
 import cn.iocoder.yudao.module.pms.dal.mysql.TDDeviceMapper;
 import cn.iocoder.yudao.module.pms.enums.common.IotDeviceStatusEnum;

+ 3 - 1
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/dal/mysql/IotDeviceMapper.java

@@ -89,8 +89,10 @@ public interface IotDeviceMapper extends BaseMapperX<IotDeviceDO> {
                 .eqIfPresent(IotDeviceDO::getBomSyncStatus, reqVO.getBomSyncStatus()));
     }
 
-    default List<IotDeviceDO> selectSimpleList(Collection<Long> deptIds) {
+    default List<IotDeviceDO> selectSimpleList(IotDevicePageReqVO reqVO, Collection<Long> deptIds) {
         return selectList(new LambdaQueryWrapperX<IotDeviceDO>()
+                .likeIfPresent(IotDeviceDO::getDeviceCode, reqVO.getDeviceCode())
+                .likeIfPresent(IotDeviceDO::getDeviceName, reqVO.getDeviceName())
                 .inIfPresent(IotDeviceDO::getDeptId, deptIds));
     }
 

+ 1 - 5
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/service/IotDeviceServiceImpl.java

@@ -10,7 +10,6 @@ import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
 import cn.iocoder.yudao.framework.common.util.object.PageUtils;
 import cn.iocoder.yudao.module.pms.ThingsModelDTO;
 import cn.iocoder.yudao.module.pms.controller.admin.TableDataInfo;
-import cn.iocoder.yudao.module.pms.controller.admin.vo.DeviceVO;
 import cn.iocoder.yudao.module.pms.controller.admin.vo.IotDevicePageReqVO;
 import cn.iocoder.yudao.module.pms.controller.admin.vo.IotDeviceRespVO;
 import cn.iocoder.yudao.module.pms.controller.admin.vo.IotDeviceSaveReqVO;
@@ -18,8 +17,6 @@ import cn.iocoder.yudao.module.pms.dal.dataobject.IotDeviceDO;
 import cn.iocoder.yudao.module.pms.dal.dataobject.IotInfoClassifyDO;
 import cn.iocoder.yudao.module.pms.dal.dataobject.IotTreeDO;
 import cn.iocoder.yudao.module.pms.dal.dataobject.iotmodel.IotModelDO;
-import cn.iocoder.yudao.module.pms.dal.dataobject.yanfan.ThingsModelDO;
-import cn.iocoder.yudao.module.pms.dal.dataobject.yanfan.YfDeviceDO;
 import cn.iocoder.yudao.module.pms.dal.mysql.IotDeviceMapper;
 import cn.iocoder.yudao.module.pms.dal.mysql.IotInfoClassifyMapper;
 import cn.iocoder.yudao.module.pms.dal.mysql.IotTreeMapper;
@@ -33,7 +30,6 @@ import cn.iocoder.yudao.module.system.enums.ErrorCodeConstants;
 import cn.iocoder.yudao.module.system.service.dept.DeptService;
 import cn.iocoder.yudao.module.system.service.dict.DictDataService;
 import com.alibaba.fastjson.JSON;
-import com.alibaba.fastjson.JSONObject;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.google.common.collect.ImmutableMap;
@@ -224,7 +220,7 @@ public class IotDeviceServiceImpl implements IotDeviceService {
             ids = deptService.getChildDeptIdListFromCache(reqVO.getDeptId());
             ids.add(reqVO.getDeptId());
         }
-        return iotDeviceMapper.selectSimpleList(ids);
+        return iotDeviceMapper.selectSimpleList(reqVO, ids);
     }
 
     @Override

+ 5 - 2
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/service/iotdeviceperson/IotDevicePersonServiceImpl.java

@@ -1,6 +1,8 @@
 package cn.iocoder.yudao.module.pms.service.iotdeviceperson;
 
 import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.ObjUtil;
+import cn.hutool.core.util.StrUtil;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
 import cn.iocoder.yudao.module.pms.controller.admin.iotdeviceperson.vo.IotDevicePersonPageReqVO;
@@ -106,14 +108,15 @@ public class IotDevicePersonServiceImpl implements IotDevicePersonService {
         // 设备和人员姓名匹配集合
         Map<Long, String> devicePersonNames = new HashMap<>();
         for (IotDevicePersonDO devicePerson : devicePersons) {
+            AdminUserRespDTO user = users.get(devicePerson.getPersonId());
             if (devicePersonNames.containsKey(devicePerson.getDeviceId())) {
                 String tempNames = devicePersonNames.get(devicePerson.getDeviceId());
                 Set<String> tempNameList = new HashSet<>(Arrays.asList(tempNames.split(",")));
-                String currentUserName = users.get(devicePerson.getPersonId()).getNickname();
+                String currentUserName = ObjUtil.isEmpty(user) ? StrUtil.EMPTY : user.getNickname();
                 tempNameList.add(currentUserName);
                 devicePersonNames.put(devicePerson.getDeviceId(), StringUtils.join(tempNameList, ","));
             } else {
-                devicePersonNames.put(devicePerson.getDeviceId(), users.get(devicePerson.getPersonId()).getNickname());
+                devicePersonNames.put(devicePerson.getDeviceId(), ObjUtil.isEmpty(user) ? StrUtil.EMPTY : user.getNickname());
             }
         }
         return devicePersonNames;

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

@@ -192,7 +192,8 @@ public class IotMainWorkOrderServiceImpl implements IotMainWorkOrderService {
                         BigDecimal lastRunningTime = bom.getLastRunningTime();      // 上次保养运行时间
                         BigDecimal runningTimePeriod = bom.getNextRunningTime();    // 运行时间周期
                         BigDecimal timePeriodLead = bom.getTimePeriodLead();    // 运行时间周期提前量
-                        runningTimeDistance = (lastRunningTime.add(runningTimePeriod).subtract(timePeriodLead)).subtract(totalRunTime);
+                        // runningTimeDistance = (lastRunningTime.add(runningTimePeriod).subtract(timePeriodLead)).subtract(totalRunTime);
+                        runningTimeDistance = runningTimePeriod.subtract(totalRunTime.subtract(lastRunningTime));
                     }
                 }
                 if (0 == bom.getMileageRule()) {
@@ -202,7 +203,8 @@ public class IotMainWorkOrderServiceImpl implements IotMainWorkOrderService {
                         BigDecimal lastRunningKilo = bom.getLastRunningKilometers();      // 上次保养运行里程
                         BigDecimal runningKiloPeriod = bom.getNextRunningKilometers();    // 运行里程周期
                         BigDecimal kiloCycleLead = bom.getKiloCycleLead();    // 运行里程周期提前量
-                        runningKiloDistance = (lastRunningKilo.add(runningKiloPeriod).subtract(kiloCycleLead)).subtract(totalMileage);
+                        // runningKiloDistance = (lastRunningKilo.add(runningKiloPeriod).subtract(kiloCycleLead)).subtract(totalMileage);
+                        runningKiloDistance = runningKiloPeriod.subtract(totalMileage.subtract(lastRunningKilo));
                     }
                 }
                 if (0 == bom.getNaturalDateRule()) {
@@ -212,13 +214,13 @@ public class IotMainWorkOrderServiceImpl implements IotMainWorkOrderService {
                     BigDecimal natualDateLead = bom.getNaturalDatePeriodLead();    // 自然日周期提前量
                     if (ObjUtil.isNotEmpty(lastNaturalDate) && ObjUtil.isNotEmpty(naturalDatePeriod) && ObjUtil.isNotEmpty(natualDateLead)) {
                         // 计算有效天数 = 自然日周期 - 提前量
-                        long days = naturalDatePeriod.subtract(natualDateLead).longValue(); // 转为长整型天数
+                        long days = naturalDatePeriod.longValue(); // 转为长整型天数
                         // 计算目标日期:上次保养日期 + 有效天数(注意:LocalDateTime加天数会自动处理日期进位)
                         LocalDateTime targetDate = lastNaturalDate.plusDays(days);
                         // 获取当前日期时间
                         LocalDateTime now = LocalDateTime.now();
                         // 计算日期差值(以天为单位)
-                        naturalDateDistance = new BigDecimal(ChronoUnit.DAYS.between(targetDate, now));
+                        naturalDateDistance = new BigDecimal(ChronoUnit.DAYS.between(now, targetDate));
                     }
                 }
                 // 找出距离0最近的数,作为工单的最近保养距离数据
@@ -256,7 +258,8 @@ public class IotMainWorkOrderServiceImpl implements IotMainWorkOrderService {
                         BigDecimal lastRunningTime = bom.getLastRunningTime();      // 上次保养运行时间
                         BigDecimal runningTimePeriod = bom.getNextRunningTime();    // 运行时间周期
                         BigDecimal timePeriodLead = bom.getTimePeriodLead();    // 运行时间周期提前量
-                        runningTimeDistance = (lastRunningTime.add(runningTimePeriod).subtract(timePeriodLead)).subtract(totalRunTime);
+                        // runningTimeDistance = (lastRunningTime.add(runningTimePeriod).subtract(timePeriodLead)).subtract(totalRunTime);
+                        runningTimeDistance = runningTimePeriod.subtract(totalRunTime.subtract(lastRunningTime));
                     }
                 }
                 if (0 == bom.getMileageRule()) {
@@ -267,7 +270,8 @@ public class IotMainWorkOrderServiceImpl implements IotMainWorkOrderService {
                         BigDecimal lastRunningKilo = bom.getLastRunningKilometers();      // 上次保养运行里程
                         BigDecimal runningKiloPeriod = bom.getNextRunningKilometers();    // 运行里程周期
                         BigDecimal kiloCycleLead = bom.getKiloCycleLead();    // 运行里程周期提前量
-                        runningKiloDistance = (lastRunningKilo.add(runningKiloPeriod).subtract(kiloCycleLead)).subtract(totalMileage);
+                        // runningKiloDistance = (lastRunningKilo.add(runningKiloPeriod).subtract(kiloCycleLead)).subtract(totalMileage);
+                        runningKiloDistance = runningKiloPeriod.subtract(totalMileage.subtract(lastRunningKilo));
                     }
                 }
                 if (0 == bom.getNaturalDateRule()) {
@@ -277,13 +281,13 @@ public class IotMainWorkOrderServiceImpl implements IotMainWorkOrderService {
                     BigDecimal natualDateLead = bom.getNaturalDatePeriodLead();    // 自然日周期提前量
                     if (ObjUtil.isNotEmpty(lastNaturalDate) && ObjUtil.isNotEmpty(naturalDatePeriod) && ObjUtil.isNotEmpty(natualDateLead)) {
                         // 计算有效天数 = 自然日周期 - 提前量
-                        long days = naturalDatePeriod.subtract(natualDateLead).longValue(); // 转为长整型天数
+                        long days = naturalDatePeriod.longValue(); // 转为长整型天数
                         // 计算目标日期:上次保养日期 + 有效天数(注意:LocalDateTime加天数会自动处理日期进位)
                         LocalDateTime targetDate = lastNaturalDate.plusDays(days);
                         // 获取当前日期时间
                         LocalDateTime now = LocalDateTime.now();
                         // 计算日期差值(以天为单位)
-                        naturalDateDistance = new BigDecimal(ChronoUnit.DAYS.between(targetDate, now));
+                        naturalDateDistance = new BigDecimal(ChronoUnit.DAYS.between(now, targetDate));
                     }
                 }
                 // 找出距离0最近的数,作为工单的最近保养距离数据
@@ -363,7 +367,7 @@ public class IotMainWorkOrderServiceImpl implements IotMainWorkOrderService {
      * @return
      */
     private List<Long> sortByNumericValue(Map<Long, String> map) {
-        return map.entrySet().stream()
+        /* return map.entrySet().stream()
                 // 转换数值并保留原始entry
                 .map(entry -> {
                     try {
@@ -385,6 +389,30 @@ public class IotMainWorkOrderServiceImpl implements IotMainWorkOrderService {
                 )
                 // 提取排序后的key
                 .map(AbstractMap.SimpleEntry::getKey)
+                .collect(Collectors.toList()); */
+        return map.entrySet().stream()
+                .map(entry -> {
+                    String[] parts = entry.getValue().split("\\s+");
+                    if (parts.length < 2) {
+                        return new AbstractMap.SimpleEntry<>(entry.getKey(), Double.MAX_VALUE);
+                    }
+
+                    try {
+                        BigDecimal value = new BigDecimal(parts[0]);
+                        // 单位转换:H→天,KM/D保持不变
+                        if ("H".equals(parts[1])) {
+                            value = value.divide(BigDecimal.valueOf(24), 2, BigDecimal.ROUND_HALF_UP);
+                        }
+                        return new AbstractMap.SimpleEntry<>(entry.getKey(), value.doubleValue());
+                    } catch (NumberFormatException e) {
+                        return new AbstractMap.SimpleEntry<>(entry.getKey(), Double.MAX_VALUE);
+                    }
+                })
+                .sorted(Comparator
+                        .<AbstractMap.SimpleEntry<Long, Double>>comparingDouble(AbstractMap.SimpleEntry::getValue) // 按转换值升序
+                        .thenComparingLong(AbstractMap.SimpleEntry::getKey) // 值相同时按键升序
+                )
+                .map(AbstractMap.SimpleEntry::getKey)
                 .collect(Collectors.toList());
     }
 
@@ -395,30 +423,40 @@ public class IotMainWorkOrderServiceImpl implements IotMainWorkOrderService {
     private Map<Long, String> findClosestToZero(Map<Long, List<Map<String, Object>>> orderDistancePair) {
         Map<Long, String> result = new HashMap<>();
         for (Map.Entry<Long, List<Map<String, Object>>> entry : orderDistancePair.entrySet()) {
-            Long key = entry.getKey();
+            Long workOrderId = entry.getKey();
             List<Map<String, Object>> mapList = entry.getValue();
-
-            // 初始化最小距离和对应的Map
-            BigDecimal minAbsDistance = null;
-            Map<String, Object> closestMap = null;
-
-            for (Map<String, Object> currentMap : mapList) {
-                // 计算当前Map所有值的最小绝对值
-                BigDecimal currentMinAbs = calculateMinAbsolute(currentMap);
-
-                // 跳过无效Map(无有效值)
-                if (currentMinAbs == null) continue;
-
-                // 比较并更新最小值
-                if (minAbsDistance == null || currentMinAbs.compareTo(minAbsDistance) < 0) {
-                    minAbsDistance = currentMinAbs;
-                    closestMap = currentMap;
+            // 存储所有有效值(原始值、单位、转换值)
+            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));
+                    }
                 }
             }
-            // 将有效结果放入最终Map
-            if (closestMap != null) {
-                result.put(key, convertMapToString(closestMap));
+            // 如果没有有效值,跳过
+            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(workOrderId, minTriple.getFirst() + " " + minTriple.getSecond());
         }
         return result;
     }
@@ -473,50 +511,32 @@ public class IotMainWorkOrderServiceImpl implements IotMainWorkOrderService {
      * @return
      */
     private BigDecimal chooseNearestDistance(BigDecimal runningTimeDistance, BigDecimal runningKiloDistance, BigDecimal naturalDateDistance) {
-        // 存储当前找到的最小绝对值的变量
-        BigDecimal closest = null;
-        // 存储当前最小绝对值(BigDecimal 类型)
-        BigDecimal minAbs = null;
-        // 比较第一个变量
+        List<Triple<BigDecimal, String, BigDecimal>> values = new ArrayList<>();
         if (runningTimeDistance != null) {
-            closest = runningTimeDistance;
-            minAbs = runningTimeDistance.abs();
+            // 转换为天数用于比较,但保留原始值
+            BigDecimal converted = runningTimeDistance.divide(BigDecimal.valueOf(24), 2, BigDecimal.ROUND_HALF_UP);
+            values.add(new Triple<>(runningTimeDistance, "H", converted));
         }
-        // 比较第二个变量
         if (runningKiloDistance != null) {
-            if (minAbs == null) {
-                // 如果之前没有有效值,直接使用 b
-                closest = runningKiloDistance;
-                minAbs = runningKiloDistance.abs();
-            } else {
-                // 比较绝对值大小
-                BigDecimal absB = runningKiloDistance.abs();
-                int cmp = absB.compareTo(minAbs);
-                if (cmp < 0 || (cmp == 0 && runningKiloDistance.compareTo(closest) > 0)) {
-                    // 如果 b 的绝对值更小,或绝对值相等但 b 是正数(更大值表示正数)
-                    closest = runningKiloDistance;
-                    minAbs = absB;
-                }
-            }
+            // 公里值不转换
+            values.add(new Triple<>(runningKiloDistance, "KM", runningKiloDistance));
         }
-        // 比较第三个变量
         if (naturalDateDistance != null) {
-            if (minAbs == null) {
-                // 如果之前没有有效值,直接使用 c
-                closest = naturalDateDistance;
-                minAbs = naturalDateDistance.abs();
-            } else {
-                // 比较绝对值大小
-                BigDecimal absC = naturalDateDistance.abs();
-                int cmp = absC.compareTo(minAbs);
-                if (cmp < 0 || (cmp == 0 && naturalDateDistance.compareTo(closest) > 0)) {
-                    // 如果 c 的绝对值更小,或绝对值相等但 c 是正数
-                    closest = naturalDateDistance;
-                    minAbs = absC;
-                }
+            // 自然日值不转换
+            values.add(new Triple<>(naturalDateDistance, "D", naturalDateDistance));
+        }
+        // 如果没有有效值,返回null
+        if (values.isEmpty()) {
+            return null;
+        }
+        // 找到转换后值最小的原始值
+        Triple<BigDecimal, String, BigDecimal> minTriple = values.get(0);
+        for (Triple<BigDecimal, String, BigDecimal> triple : values) {
+            if (triple.getThird().compareTo(minTriple.getThird()) < 0) {
+                minTriple = triple;
             }
         }
-        return closest; // 可能返回 null(当所有输入均为 null 时)
+        return minTriple.getFirst(); // 返回原始值
     }
 
     @Override
@@ -612,7 +632,7 @@ public class IotMainWorkOrderServiceImpl implements IotMainWorkOrderService {
                 mainCompleted.set(false);
             }
             if (bom.getDelayKilometers().compareTo(BigDecimal.ZERO)==0
-                    || bom.getDelayDuration().compareTo(BigDecimal.ZERO)==0 || bom.getDelayNaturalDate().compareTo(BigDecimal.ZERO)==0) {
+                    && bom.getDelayDuration().compareTo(BigDecimal.ZERO)==0 && bom.getDelayNaturalDate().compareTo(BigDecimal.ZERO)==0) {
                 // 如果保养项没有设置延时保养 则查询当前设备的 累计运行公里数 累计运行时间 当前时间日期 赋值到 关联保养计划 明细 的 对应保养规则数据上
                 deviceIds.add(bom.getDeviceId());
                 // 保养项下如果已经添加了物料 说明该保养项已经保养完成
@@ -672,26 +692,28 @@ public class IotMainWorkOrderServiceImpl implements IotMainWorkOrderService {
                 }
             }
         }
-        // 将当前工单的关联工单 一并关闭 是否需要将当前工单选择的物料及费用也复制到 关联工单?
-        IotMainWorkOrderPageReqVO orderReqVO = new IotMainWorkOrderPageReqVO();
-        orderReqVO.setOrderGroupId(updateObj.getOrderGroupId());
-        List<IotMainWorkOrderDO> associateOrders = iotMainWorkOrderMapper.selectList(orderReqVO);
-        List<IotMainWorkOrderDO> filteredOrders = associateOrders.stream()
-                .filter(order -> !order.getId().equals(updateObj.getId()))
-                .collect(Collectors.toList());
-        if (CollUtil.isNotEmpty(filteredOrders)) {
-            // 将关联工单设置为 已执行 直接删除
-            filteredOrders.forEach(order -> {order.setResult(2);  order.setDeleted(true);});
-            iotMainWorkOrderMapper.updateBatch(filteredOrders);
-            List<Long> workOrderIds = filteredOrders.stream()
-                    .map(IotMainWorkOrderDO::getId)
+        // 如果当前工单已经关闭,需要同时将当前工单的关联工单 一并关闭 是否需要将当前工单选择的物料及费用也复制到 关联工单?
+        if (2 == updateObj.getResult()) {
+            IotMainWorkOrderPageReqVO orderReqVO = new IotMainWorkOrderPageReqVO();
+            orderReqVO.setOrderGroupId(updateObj.getOrderGroupId());
+            List<IotMainWorkOrderDO> associateOrders = iotMainWorkOrderMapper.selectList(orderReqVO);
+            List<IotMainWorkOrderDO> filteredOrders = associateOrders.stream()
+                    .filter(order -> !order.getId().equals(updateObj.getId()))
                     .collect(Collectors.toList());
-            // 将关联工单的明细 status 设置为 1
-            IotMainWorkOrderBomPageReqVO reqVO = new IotMainWorkOrderBomPageReqVO();
-            reqVO.setWorkOrderIds(workOrderIds);
-            List<IotMainWorkOrderBomDO> associateOrderBomS = iotMainWorkOrderBomMapper.selectList(reqVO);
-            associateOrderBomS.forEach(order -> order.setStatus(1));
-            iotMainWorkOrderBomMapper.updateBatch(associateOrderBomS);
+            if (CollUtil.isNotEmpty(filteredOrders)) {
+                // 将关联工单设置为 已执行 直接删除
+                filteredOrders.forEach(order -> {order.setResult(2);  order.setDeleted(true);});
+                iotMainWorkOrderMapper.updateBatch(filteredOrders);
+                List<Long> workOrderIds = filteredOrders.stream()
+                        .map(IotMainWorkOrderDO::getId)
+                        .collect(Collectors.toList());
+                // 将关联工单的明细 status 设置为 1
+                IotMainWorkOrderBomPageReqVO reqVO = new IotMainWorkOrderBomPageReqVO();
+                reqVO.setWorkOrderIds(workOrderIds);
+                List<IotMainWorkOrderBomDO> associateOrderBomS = iotMainWorkOrderBomMapper.selectList(reqVO);
+                associateOrderBomS.forEach(order -> order.setStatus(1));
+                iotMainWorkOrderBomMapper.updateBatch(associateOrderBomS);
+            }
         }
         // 只扣减本地库存 不处理SAP库存
         if (CollUtil.isNotEmpty(factoryIds) && CollUtil.isNotEmpty(costCenterIds) && CollUtil.isNotEmpty(materialCodes)) {
@@ -859,4 +881,27 @@ public class IotMainWorkOrderServiceImpl implements IotMainWorkOrderService {
         return workOrderResp;
     }
 
+    private static class Triple<A, B, C> {
+        private final A first;
+        private final B second;
+        private final C third;
+
+        public Triple(A first, B second, C third) {
+            this.first = first;
+            this.second = second;
+            this.third = third;
+        }
+
+        public A getFirst() {
+            return first;
+        }
+
+        public B getSecond() {
+            return second;
+        }
+
+        public C getThird() {
+            return third;
+        }
+    }
 }

+ 150 - 15
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/service/iotmainworkorderbom/IotMainWorkOrderBomServiceImpl.java

@@ -108,8 +108,10 @@ public class IotMainWorkOrderBomServiceImpl implements IotMainWorkOrderBomServic
                         BigDecimal totalRunTime = deviceRunLogMap.get(bom.getDeviceId()).getTotalRunTime();
                         BigDecimal lastRunningTime = bom.getLastRunningTime();      // 上次保养运行时间
                         BigDecimal runningTimePeriod = bom.getNextRunningTime();    // 运行时间周期
-                        BigDecimal timePeriodLead = bom.getTimePeriodLead();    // 运行时间周期提前量
-                        runningTimeDistance = (lastRunningTime.add(runningTimePeriod).subtract(timePeriodLead)).subtract(totalRunTime);
+                        BigDecimal timePeriodLead = bom.getTimePeriodLead();        // 运行时间周期提前量
+                        BigDecimal delayDuration = bom.getDelayDuration();          // 推迟运行时长H
+                        // runningTimeDistance = (lastRunningTime.add(runningTimePeriod).subtract(timePeriodLead)).subtract(totalRunTime);
+                        runningTimeDistance = (runningTimePeriod.subtract(totalRunTime.subtract(lastRunningTime)).subtract(timePeriodLead)).add(delayDuration);
                     }
                 }
                 if (0 == bom.getMileageRule()) {
@@ -119,24 +121,27 @@ public class IotMainWorkOrderBomServiceImpl implements IotMainWorkOrderBomServic
                         BigDecimal totalMileage = deviceRunLogMap.get(bom.getDeviceId()).getTotalMileage();
                         BigDecimal lastRunningKilo = bom.getLastRunningKilometers();      // 上次保养运行里程
                         BigDecimal runningKiloPeriod = bom.getNextRunningKilometers();    // 运行里程周期
-                        BigDecimal kiloCycleLead = bom.getKiloCycleLead();    // 运行里程周期提前量
-                        runningKiloDistance = (lastRunningKilo.add(runningKiloPeriod).subtract(kiloCycleLead)).subtract(totalMileage);
+                        BigDecimal kiloCycleLead = bom.getKiloCycleLead();                // 运行里程周期提前量
+                        BigDecimal delayKilometers = bom.getDelayKilometers();            // 推迟运行公里数KM
+                        // runningKiloDistance = (lastRunningKilo.add(runningKiloPeriod).subtract(kiloCycleLead)).subtract(totalMileage);
+                        runningKiloDistance = (runningKiloPeriod.subtract(totalMileage.subtract(lastRunningKilo)).subtract(kiloCycleLead)).add(delayKilometers);
                     }
                 }
                 if (0 == bom.getNaturalDateRule()) {
                     // 自然日期保养规则
                     LocalDateTime lastNaturalDate = bom.getLastNaturalDate();      // 上次保养自然日期
-                    BigDecimal naturalDatePeriod = bom.getNextNaturalDate();        // 自然日周期
+                    BigDecimal naturalDatePeriod = bom.getNextNaturalDate();       // 自然日周期
                     BigDecimal natualDateLead = bom.getNaturalDatePeriodLead();    // 自然日周期提前量
+                    BigDecimal delayNaturalDate = bom.getDelayNaturalDate();       // 推迟自然日期D
                     if (ObjUtil.isNotEmpty(lastNaturalDate) && ObjUtil.isNotEmpty(naturalDatePeriod) && ObjUtil.isNotEmpty(natualDateLead)) {
-                        // 计算有效天数 = 自然日周期 - 提前量
-                        long days = naturalDatePeriod.subtract(natualDateLead).longValue(); // 转为长整型天数
+                        // 计算有效天数 = 自然日周期 - 提前量 + 推迟自然日期D
+                        long days = (naturalDatePeriod.subtract(natualDateLead)).add(delayNaturalDate).longValue();     // 转为长整型天数
                         // 计算目标日期:上次保养日期 + 有效天数(注意:LocalDateTime加天数会自动处理日期进位)
                         LocalDateTime targetDate = lastNaturalDate.plusDays(days);
                         // 获取当前日期时间
                         LocalDateTime now = LocalDateTime.now();
                         // 计算日期差值(以天为单位)
-                        naturalDateDistance = new BigDecimal(ChronoUnit.DAYS.between(targetDate, now));
+                        naturalDateDistance = new BigDecimal(ChronoUnit.DAYS.between(now, targetDate));
                     }
                 }
                 // 找出距离0最近的数,作为工单的最近保养距离数据
@@ -171,21 +176,17 @@ public class IotMainWorkOrderBomServiceImpl implements IotMainWorkOrderBomServic
      */
     private Map<Long, String> findClosestToZero(Map<Long, List<Map<String, Object>>> orderDistancePair){
         Map<Long, String> result = new HashMap<>();
-        for (Map.Entry<Long, List<Map<String, Object>>> entry : orderDistancePair.entrySet()) {
+        /* for (Map.Entry<Long, List<Map<String, Object>>> entry : orderDistancePair.entrySet()) {
             Long key = entry.getKey();
             List<Map<String, Object>> mapList = entry.getValue();
-
             // 初始化最小距离和对应的Map
             BigDecimal minAbsDistance = null;
             Map<String, Object> closestMap = null;
-
             for (Map<String, Object> currentMap : mapList) {
                 // 计算当前Map所有值的最小绝对值
                 BigDecimal currentMinAbs = calculateMinAbsolute(currentMap);
-
                 // 跳过无效Map(无有效值)
                 if (currentMinAbs == null) continue;
-
                 // 比较并更新最小值
                 if (minAbsDistance == null || currentMinAbs.compareTo(minAbsDistance) < 0) {
                     minAbsDistance = currentMinAbs;
@@ -197,6 +198,72 @@ public class IotMainWorkOrderBomServiceImpl implements IotMainWorkOrderBomServic
                 result.put(key, convertMapToString(closestMap));
             }
         }
+        return result; */
+        /* for (Map.Entry<Long, List<Map<String, Object>>> entry : orderDistancePair.entrySet()) {
+            Long workOrderId = entry.getKey();
+            List<Map<String, Object>> mapList = entry.getValue();
+            // 存储所有有效值及其单位
+            List<Pair<BigDecimal, String>> 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();
+                        allValues.add(new Pair<>(value, valueEntry.getKey()));
+                    }
+                }
+            }
+            // 如果没有有效值,跳过
+            if (allValues.isEmpty()) {
+                continue;
+            }
+            // 找到实际数值最小的值
+            Pair<BigDecimal, String> minPair = allValues.get(0);
+            for (Pair<BigDecimal, String> pair : allValues) {
+                if (pair.getKey().compareTo(minPair.getKey()) < 0) {
+                    minPair = pair;
+                }
+            }
+            // 格式化为字符串: "数值 单位"
+            result.put(workOrderId, minPair.getKey() + " " + minPair.getValue());
+        }
+        return result; */
+        for (Map.Entry<Long, List<Map<String, Object>>> entry : orderDistancePair.entrySet()) {
+            Long workOrderId = entry.getKey();
+            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(workOrderId, minTriple.getFirst() + " " + minTriple.getSecond());
+        }
         return result;
     }
 
@@ -244,7 +311,7 @@ public class IotMainWorkOrderBomServiceImpl implements IotMainWorkOrderBomServic
 
     private BigDecimal chooseNearestDistance(BigDecimal runningTimeDistance, BigDecimal runningKiloDistance, BigDecimal naturalDateDistance){
         // 存储当前找到的最小绝对值的变量
-        BigDecimal closest = null;
+        /* BigDecimal closest = null;
         // 存储当前最小绝对值(BigDecimal 类型)
         BigDecimal minAbs = null;
         // 比较第一个变量
@@ -286,7 +353,51 @@ public class IotMainWorkOrderBomServiceImpl implements IotMainWorkOrderBomServic
                 }
             }
         }
-        return closest; // 可能返回 null(当所有输入均为 null 时)
+        return closest; // 可能返回 null(当所有输入均为 null 时) */
+        // 存储所有非空值
+        /* List<BigDecimal> values = new ArrayList<>();
+        if (runningTimeDistance != null) values.add(runningTimeDistance);
+        if (runningKiloDistance != null) values.add(runningKiloDistance);
+        if (naturalDateDistance != null) values.add(naturalDateDistance);
+        // 如果没有有效值,返回null
+        if (values.isEmpty()) {
+            return null;
+        }
+        // 找到实际数值最小的值
+        BigDecimal minValue = values.get(0);
+        for (BigDecimal value : values) {
+            if (value.compareTo(minValue) < 0) {
+                minValue = value;
+            }
+        }
+        return minValue; */
+        // 存储所有非空值及其转换值(小时->天)
+        List<Triple<BigDecimal, String, BigDecimal>> values = new ArrayList<>();
+        if (runningTimeDistance != null) {
+            // 转换为天数用于比较,但保留原始值
+            BigDecimal converted = runningTimeDistance.divide(BigDecimal.valueOf(24), 2, BigDecimal.ROUND_HALF_UP);
+            values.add(new Triple<>(runningTimeDistance, "H", converted));
+        }
+        if (runningKiloDistance != null) {
+            // 公里值不转换
+            values.add(new Triple<>(runningKiloDistance, "KM", runningKiloDistance));
+        }
+        if (naturalDateDistance != null) {
+            // 自然日值不转换
+            values.add(new Triple<>(naturalDateDistance, "D", naturalDateDistance));
+        }
+        // 如果没有有效值,返回null
+        if (values.isEmpty()) {
+            return null;
+        }
+        // 找到转换后值最小的原始值
+        Triple<BigDecimal, String, BigDecimal> minTriple = values.get(0);
+        for (Triple<BigDecimal, String, BigDecimal> triple : values) {
+            if (triple.getThird().compareTo(minTriple.getThird()) < 0) {
+                minTriple = triple;
+            }
+        }
+        return minTriple.getFirst(); // 返回原始值
     }
 
     @Override
@@ -294,4 +405,28 @@ public class IotMainWorkOrderBomServiceImpl implements IotMainWorkOrderBomServic
         iotMainWorkOrderBomMapper.insertBatch(workOrderBOMs);
     }
 
+    private static class Triple<A, B, C> {
+        private final A first;
+        private final B second;
+        private final C third;
+
+        public Triple(A first, B second, C third) {
+            this.first = first;
+            this.second = second;
+            this.third = third;
+        }
+
+        public A getFirst() {
+            return first;
+        }
+
+        public B getSecond() {
+            return second;
+        }
+
+        public C getThird() {
+            return third;
+        }
+    }
+
 }