|
@@ -1,19 +1,25 @@
|
|
|
package cn.iocoder.yudao.module.pms.service.iotmainworkorderbom;
|
|
|
|
|
|
+import cn.hutool.core.collection.CollUtil;
|
|
|
+import cn.hutool.core.util.ObjUtil;
|
|
|
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.iotdevicerunlog.vo.IotDeviceRunLogRespVO;
|
|
|
import cn.iocoder.yudao.module.pms.controller.admin.iotmainworkorderbom.vo.IotMainWorkOrderBomPageReqVO;
|
|
|
import cn.iocoder.yudao.module.pms.controller.admin.iotmainworkorderbom.vo.IotMainWorkOrderBomSaveReqVO;
|
|
|
-import cn.iocoder.yudao.module.pms.dal.dataobject.iotmainworkorder.IotMainWorkOrderDO;
|
|
|
import cn.iocoder.yudao.module.pms.dal.dataobject.iotmainworkorderbom.IotMainWorkOrderBomDO;
|
|
|
import cn.iocoder.yudao.module.pms.dal.mysql.iotmainworkorderbom.IotMainWorkOrderBomMapper;
|
|
|
import cn.iocoder.yudao.module.pms.enums.ErrorCodeConstant;
|
|
|
+import cn.iocoder.yudao.module.pms.service.iotdevicerunlog.IotDeviceRunLogService;
|
|
|
+import org.springframework.beans.factory.annotation.Autowired;
|
|
|
import org.springframework.stereotype.Service;
|
|
|
import org.springframework.validation.annotation.Validated;
|
|
|
|
|
|
import javax.annotation.Resource;
|
|
|
-
|
|
|
-import java.util.List;
|
|
|
+import java.math.BigDecimal;
|
|
|
+import java.time.LocalDateTime;
|
|
|
+import java.time.temporal.ChronoUnit;
|
|
|
+import java.util.*;
|
|
|
|
|
|
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
|
|
|
|
@@ -28,6 +34,8 @@ public class IotMainWorkOrderBomServiceImpl implements IotMainWorkOrderBomServic
|
|
|
|
|
|
@Resource
|
|
|
private IotMainWorkOrderBomMapper iotMainWorkOrderBomMapper;
|
|
|
+ @Autowired
|
|
|
+ private IotDeviceRunLogService iotDeviceRunLogService;
|
|
|
|
|
|
@Override
|
|
|
public Long createIotMainWorkOrderBom(IotMainWorkOrderBomSaveReqVO createReqVO) {
|
|
@@ -76,6 +84,211 @@ public class IotMainWorkOrderBomServiceImpl implements IotMainWorkOrderBomServic
|
|
|
return iotMainWorkOrderBomMapper.selectList(pageReqVO);
|
|
|
}
|
|
|
|
|
|
+ @Override
|
|
|
+ public Map<Long, String> mainWorkOrderNearestDistance(IotMainWorkOrderBomPageReqVO pageReqVO) {
|
|
|
+ List<IotMainWorkOrderBomDO> workOrderBomS = getIotMainWorkOrderBomList(pageReqVO);
|
|
|
+ Map<Long, String> resultMap = new HashMap<>();
|
|
|
+ // 遍历工单明细 找出每个工单中 触发保养时间最近的 时间/里程/自然日
|
|
|
+ if (CollUtil.isNotEmpty(workOrderBomS)) {
|
|
|
+ Set<Long> deviceIds = new HashSet<>();
|
|
|
+ workOrderBomS.forEach(bom -> {
|
|
|
+ deviceIds.add(bom.getDeviceId());
|
|
|
+ });
|
|
|
+ // 查询当前所有设备的 累计运行里程 累计运行时间
|
|
|
+ Map<Long, IotDeviceRunLogRespVO> deviceRunLogMap = iotDeviceRunLogService.getDeviceRunLogMap(new ArrayList<>(deviceIds));
|
|
|
+ Map<Long, List<Map<String, Object>>> orderDistancePair = new HashMap<>();
|
|
|
+ workOrderBomS.forEach(bom -> {
|
|
|
+ BigDecimal runningTimeDistance = null;
|
|
|
+ BigDecimal runningKiloDistance = null;
|
|
|
+ BigDecimal naturalDateDistance = null;
|
|
|
+ // 计算每个保养项 每个保养规则下的 距离保养时间
|
|
|
+ if (0 == bom.getRunningTimeRule()) {
|
|
|
+ // 运行时间保养规则
|
|
|
+ if (deviceRunLogMap.containsKey(bom.getDeviceId())) {
|
|
|
+ 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);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (0 == bom.getMileageRule()) {
|
|
|
+ // 运行里程保养规则
|
|
|
+ // 累计运行里程规则 累计运行里程 >= (上次保养运行里程+运行里程周期-提前量)
|
|
|
+ if (deviceRunLogMap.containsKey(bom.getDeviceId())) {
|
|
|
+ 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);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (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.subtract(natualDateLead).longValue(); // 转为长整型天数
|
|
|
+ // 计算目标日期:上次保养日期 + 有效天数(注意:LocalDateTime加天数会自动处理日期进位)
|
|
|
+ LocalDateTime targetDate = lastNaturalDate.plusDays(days);
|
|
|
+ // 获取当前日期时间
|
|
|
+ LocalDateTime now = LocalDateTime.now();
|
|
|
+ // 计算日期差值(以天为单位)
|
|
|
+ naturalDateDistance = new BigDecimal(ChronoUnit.DAYS.between(targetDate, now));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 找出距离0最近的数,作为工单的最近保养距离数据
|
|
|
+ Object closet = chooseNearestDistance(runningTimeDistance, runningKiloDistance, naturalDateDistance);
|
|
|
+ Map<String, Object> tempDistance = new HashMap<>();
|
|
|
+ if (closet == runningTimeDistance) {
|
|
|
+ tempDistance.put("H", closet);
|
|
|
+ } else if (closet == runningKiloDistance) {
|
|
|
+ tempDistance.put("KM", closet);
|
|
|
+ } else if (closet == naturalDateDistance) {
|
|
|
+ tempDistance.put("D", closet);
|
|
|
+ }
|
|
|
+ if (orderDistancePair.containsKey(bom.getWorkOrderId())) {
|
|
|
+ List<Map<String, Object>> tempDistances = orderDistancePair.get(bom.getWorkOrderId());
|
|
|
+ tempDistances.add(tempDistance);
|
|
|
+ orderDistancePair.put(bom.getWorkOrderId(), tempDistances);
|
|
|
+ } else {
|
|
|
+ List<Map<String, Object>> tempDistances = new ArrayList<>();
|
|
|
+ tempDistances.add(tempDistance);
|
|
|
+ orderDistancePair.put(bom.getWorkOrderId(), tempDistances);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ // 以保养工单id 为维度 统计每个保养工单明细中 距离最近的保养数据
|
|
|
+ resultMap = findClosestToZero(orderDistancePair);
|
|
|
+ }
|
|
|
+ return resultMap;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 找出每个工单对应的集合中 距离时间最短的 数据
|
|
|
+ * @param orderDistancePair
|
|
|
+ */
|
|
|
+ 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();
|
|
|
+ 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;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 将有效结果放入最终Map
|
|
|
+ if (closestMap != null) {
|
|
|
+ result.put(key, convertMapToString(closestMap));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 将Map转换为String格式:value1 key1; value2 key2
|
|
|
+ */
|
|
|
+ private String convertMapToString(Map<String, Object> map) {
|
|
|
+ StringBuilder sb = new StringBuilder();
|
|
|
+ for (Map.Entry<String, Object> entry : map.entrySet()) {
|
|
|
+ if (sb.length() > 0) {
|
|
|
+ sb.append(";"); // 添加分隔符
|
|
|
+ }
|
|
|
+ String key = entry.getKey();
|
|
|
+ Object value = entry.getValue();
|
|
|
+
|
|
|
+ // 处理null值
|
|
|
+ String valueStr = (value != null) ? value.toString() : "null";
|
|
|
+
|
|
|
+ // 转义特殊字符(如有需要)
|
|
|
+ sb.append(valueStr).append(" ").append(key);
|
|
|
+ }
|
|
|
+ return sb.toString();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 计算单个Map中所有BigDecimal值的最小绝对值
|
|
|
+ * @param map
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ private BigDecimal calculateMinAbsolute(Map<String, Object> map) {
|
|
|
+ BigDecimal minAbs = null;
|
|
|
+
|
|
|
+ for (Object value : map.values()) {
|
|
|
+ if (!(value instanceof BigDecimal)) continue;
|
|
|
+
|
|
|
+ BigDecimal decimalValue = (BigDecimal) value;
|
|
|
+ BigDecimal absValue = decimalValue.abs();
|
|
|
+
|
|
|
+ if (minAbs == null || absValue.compareTo(minAbs) < 0) {
|
|
|
+ minAbs = absValue;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return minAbs; // 可能为null(无BigDecimal值)
|
|
|
+ }
|
|
|
+
|
|
|
+ private BigDecimal chooseNearestDistance(BigDecimal runningTimeDistance, BigDecimal runningKiloDistance, BigDecimal naturalDateDistance){
|
|
|
+ // 存储当前找到的最小绝对值的变量
|
|
|
+ BigDecimal closest = null;
|
|
|
+ // 存储当前最小绝对值(BigDecimal 类型)
|
|
|
+ BigDecimal minAbs = null;
|
|
|
+ // 比较第一个变量
|
|
|
+ if (runningTimeDistance != null) {
|
|
|
+ closest = runningTimeDistance;
|
|
|
+ minAbs = runningTimeDistance.abs();
|
|
|
+ }
|
|
|
+ // 比较第二个变量
|
|
|
+ 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;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 比较第三个变量
|
|
|
+ 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;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return closest; // 可能返回 null(当所有输入均为 null 时)
|
|
|
+ }
|
|
|
+
|
|
|
@Override
|
|
|
public void batchAddOrderBOMs(List<IotMainWorkOrderBomDO> workOrderBOMs) {
|
|
|
iotMainWorkOrderBomMapper.insertBatch(workOrderBOMs);
|