package cn.iocoder.yudao.module.mes.service.dv.statistics; import cn.hutool.core.collection.CollUtil; import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; import cn.iocoder.yudao.module.mes.controller.admin.dv.statistics.vo.MesDvMachineryStatisticsRespVO; import cn.iocoder.yudao.module.mes.dal.dataobject.dv.borrow.MesDvBorrowDO; import cn.iocoder.yudao.module.mes.dal.dataobject.dv.checkrecord.MesDvCheckRecordDO; import cn.iocoder.yudao.module.mes.dal.dataobject.dv.machinery.MesDvMachineryDO; import cn.iocoder.yudao.module.mes.dal.dataobject.dv.machinery.MesDvMachineryTypeDO; import cn.iocoder.yudao.module.mes.dal.dataobject.dv.maintenrecord.MesDvMaintenRecordDO; import cn.iocoder.yudao.module.mes.dal.dataobject.dv.repair.MesDvRepairDO; import cn.iocoder.yudao.module.mes.dal.mysql.dv.borrow.MesDvBorrowMapper; import cn.iocoder.yudao.module.mes.dal.mysql.dv.checkrecord.MesDvCheckRecordMapper; import cn.iocoder.yudao.module.mes.dal.mysql.dv.maintenrecord.MesDvMaintenRecordMapper; import cn.iocoder.yudao.module.mes.dal.mysql.dv.repair.MesDvRepairMapper; import cn.iocoder.yudao.module.mes.service.dv.machinery.MesDvMachineryService; import cn.iocoder.yudao.module.mes.service.dv.machinery.MesDvMachineryTypeService; import cn.iocoder.yudao.module.mes.service.md.workstation.MesMdWorkshopService; import cn.iocoder.yudao.module.mes.service.pro.process.MesProProcessService; import jakarta.annotation.Resource; import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; import java.math.BigDecimal; import java.math.RoundingMode; import java.time.Duration; import java.time.LocalDateTime; import java.util.*; import java.util.stream.Collectors; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; /** * MES 设备统计分析 Service 实现类 * * @author 超级管理员 */ @Service @Validated public class MesDvStatisticsServiceImpl implements MesDvStatisticsService { private static final BigDecimal MINUTES_PER_HOUR = BigDecimal.valueOf(60); @Resource private MesDvMachineryService machineryService; @Resource private MesDvMachineryTypeService machineryTypeService; @Resource private MesMdWorkshopService workshopService; @Resource private MesProProcessService processService; @Resource private MesDvRepairMapper repairMapper; @Resource private MesDvCheckRecordMapper checkRecordMapper; @Resource private MesDvMaintenRecordMapper maintenRecordMapper; @Resource private MesDvBorrowMapper borrowMapper; @Override public List getMachineryStatistics(LocalDateTime beginTime, LocalDateTime endTime, Long machineryId) { // 1. 查询设备 List machineryList = machineryService.getMachineryList(); if (machineryId != null) { machineryList = machineryList.stream() .filter(m -> Objects.equals(m.getId(), machineryId)).collect(Collectors.toList()); } if (CollUtil.isEmpty(machineryList)) { return Collections.emptyList(); } Set machineryIds = convertSet(machineryList, MesDvMachineryDO::getId); // 2. 查询区间内各类单据 List repairList = repairMapper.selectList( new LambdaQueryWrapperX() .in(MesDvRepairDO::getMachineryId, machineryIds) .betweenIfPresent(MesDvRepairDO::getRequireDate, beginTime, endTime)); List checkRecordList = checkRecordMapper.selectList( new LambdaQueryWrapperX() .in(MesDvCheckRecordDO::getMachineryId, machineryIds) .betweenIfPresent(MesDvCheckRecordDO::getCheckTime, beginTime, endTime)); List maintenRecordList = maintenRecordMapper.selectList( new LambdaQueryWrapperX() .in(MesDvMaintenRecordDO::getMachineryId, machineryIds) .betweenIfPresent(MesDvMaintenRecordDO::getMaintenTime, beginTime, endTime)); List borrowList = borrowMapper.selectList( new LambdaQueryWrapperX() .in(MesDvBorrowDO::getMachineryId, machineryIds) .betweenIfPresent(MesDvBorrowDO::getBorrowTime, beginTime, endTime)); Map> repairMap = repairList.stream() .collect(Collectors.groupingBy(MesDvRepairDO::getMachineryId)); Map checkCountMap = checkRecordList.stream() .collect(Collectors.groupingBy(MesDvCheckRecordDO::getMachineryId, Collectors.counting())); Map maintenCountMap = maintenRecordList.stream() .collect(Collectors.groupingBy(MesDvMaintenRecordDO::getMachineryId, Collectors.counting())); Map borrowCountMap = borrowList.stream() .collect(Collectors.groupingBy(MesDvBorrowDO::getMachineryId, Collectors.counting())); // 3. 名称解析 Map typeMap = machineryTypeService.getMachineryTypeMap( convertSet(machineryList, MesDvMachineryDO::getMachineryTypeId)); Map workshopMap = workshopService.getWorkshopMap(convertSet(machineryList, MesDvMachineryDO::getWorkshopId)); Map processMap = processService.getProcessMap(convertSet(machineryList, MesDvMachineryDO::getProcessId)); // 4. 逐台设备汇总 long totalMinutes = Math.max(Duration.between(beginTime, endTime).toMinutes(), 1); LocalDateTime now = LocalDateTime.now(); List result = new ArrayList<>(machineryList.size()); for (MesDvMachineryDO machinery : machineryList) { MesDvMachineryStatisticsRespVO vo = new MesDvMachineryStatisticsRespVO(); vo.setMachineryId(machinery.getId()); vo.setMachineryCode(machinery.getCode()); vo.setMachineryName(machinery.getName()); vo.setStatus(machinery.getStatus()); cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen(typeMap, machinery.getMachineryTypeId(), type -> vo.setMachineryTypeName(type.getName())); cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen(workshopMap, machinery.getWorkshopId(), workshop -> vo.setWorkshopName(workshop.getName())); cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen(processMap, machinery.getProcessId(), process -> vo.setProcessName(process.getName())); List repairs = repairMap.getOrDefault(machinery.getId(), Collections.emptyList()); vo.setRepairCount(repairs.size()); vo.setCheckCount(checkCountMap.getOrDefault(machinery.getId(), 0L).intValue()); vo.setMaintenCount(maintenCountMap.getOrDefault(machinery.getId(), 0L).intValue()); vo.setBorrowCount(borrowCountMap.getOrDefault(machinery.getId(), 0L).intValue()); // 累计维修耗时:优先取登记的耗时,未登记时按报修→完成时间估算 BigDecimal repairHours = BigDecimal.ZERO; long downMinutes = 0L; LocalDateTime lastRepairTime = null; for (MesDvRepairDO repair : repairs) { if (lastRepairTime == null || repair.getRequireDate().isAfter(lastRepairTime)) { lastRepairTime = repair.getRequireDate(); } LocalDateTime repairEnd = repair.getFinishDate() != null ? repair.getFinishDate() : now; if (repair.getDurationHours() != null) { repairHours = repairHours.add(repair.getDurationHours()); } else { long minutes = Math.max(Duration.between(repair.getRequireDate(), repairEnd).toMinutes(), 0); repairHours = repairHours.add(BigDecimal.valueOf(minutes) .divide(MINUTES_PER_HOUR, 2, RoundingMode.HALF_UP)); } // 停机时长:仅统计落在区间内的部分 LocalDateTime downStart = repair.getRequireDate().isBefore(beginTime) ? beginTime : repair.getRequireDate(); LocalDateTime downEnd = repairEnd.isAfter(endTime) ? endTime : repairEnd; if (downEnd.isAfter(downStart)) { downMinutes += Duration.between(downStart, downEnd).toMinutes(); } } vo.setRepairHours(repairHours.setScale(2, RoundingMode.HALF_UP)); vo.setLastRepairTime(lastRepairTime); vo.setDownHours(BigDecimal.valueOf(downMinutes) .divide(MINUTES_PER_HOUR, 2, RoundingMode.HALF_UP)); // 使用率 =(统计时长 - 停机时长)/ 统计时长,区间内无停机即 100% long usedMinutes = Math.max(totalMinutes - downMinutes, 0L); vo.setUsageRate(BigDecimal.valueOf(usedMinutes) .multiply(BigDecimal.valueOf(100)) .divide(BigDecimal.valueOf(totalMinutes), 2, RoundingMode.HALF_UP)); result.add(vo); } // 使用率升序,故障多的排前面,便于关注问题设备 result.sort(Comparator.comparing(MesDvMachineryStatisticsRespVO::getUsageRate) .thenComparing(MesDvMachineryStatisticsRespVO::getRepairCount, Comparator.reverseOrder())); return result; } }