From 8782476e9cb4480be5da588888d08022b7942d0c Mon Sep 17 00:00:00 2001
From: xiaoyi <908147524@qq.com>
Date: 星期六, 07 十一月 2026 14:07:59 +0800
Subject: [PATCH] feat: 补齐需求10/15/16/17/18/21/23,改用现有模块替代 ERP 依赖

---
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/collect/MesCollectQueryService.java |   99 +++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 99 insertions(+), 0 deletions(-)

diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/collect/MesCollectQueryService.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/collect/MesCollectQueryService.java
index a99818e..23fe617 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/collect/MesCollectQueryService.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/collect/MesCollectQueryService.java
@@ -17,6 +17,11 @@
 import cn.iocoder.yudao.module.mes.service.md.workstation.MesMdWorkstationService;
 import jakarta.annotation.Resource;
 import org.springframework.stereotype.Service;
+import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
+import cn.iocoder.yudao.module.mes.framework.collect.config.MesCollectProperties;
+import java.time.Duration;
+import java.util.ArrayList;
+import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.invalidParamException;
 
 import java.time.LocalDateTime;
 import java.util.List;
@@ -60,6 +65,100 @@
     private MesCollectTaskResolver taskResolver;
     @Resource
     private MesMdWorkstationService workstationService;
+    @Resource
+    private MesCollectProperties collectProperties;
+
+    /** 鍩轰簬淇濈暀鐨勬暟閲囧巻鍙茶绠楃绾垮尯闂达紝棣栨閲囨牱涔嬪墠涓嶄綔鎺ㄦ柇銆� */
+    public MesCollectOfflineResultRespVO getOfflineIntervals(String deviceCode, LocalDateTime start, LocalDateTime end) {
+        LocalDateTime now = LocalDateTime.now();
+        if (deviceCode == null || deviceCode.isBlank() || start == null || end == null || !start.isBefore(end)
+                || end.isAfter(now) || Duration.between(start, end).compareTo(Duration.ofDays(7)) > 0) {
+            throw invalidParamException("璇烽�夋嫨鏈夋晥鐨勮澶囧拰鏃堕棿鑼冨洿锛屽崟娆℃渶澶氭煡璇竷澶╋紝缁撴潫鏃堕棿涓嶈兘鏅氫簬褰撳墠鏃堕棿");
+        }
+        if (start.isBefore(now.minusDays(collectProperties.getRetention().getRealtimeDays()))) {
+            throw invalidParamException("鏌ヨ鏃堕棿瓒呭嚭鏁伴噰鍘嗗彶淇濈暀鏈燂紝鏃犳硶鍙潬璁$畻鍋滄満璁板綍");
+        }
+        var prior = realtimeHistoryMapper.selectOne(new LambdaQueryWrapperX<MesCollectRealtimeHistoryDO>()
+                .eq(MesCollectRealtimeHistoryDO::getDeviceCode, deviceCode)
+                .ge(MesCollectRealtimeHistoryDO::getCollectTime, now.minusDays(collectProperties.getRetention().getRealtimeDays()))
+                .le(MesCollectRealtimeHistoryDO::getCollectTime, start)
+                .orderByDesc(MesCollectRealtimeHistoryDO::getCollectTime).last("LIMIT 1"));
+        var history = realtimeHistoryMapper.selectList(new LambdaQueryWrapperX<MesCollectRealtimeHistoryDO>()
+                .eq(MesCollectRealtimeHistoryDO::getDeviceCode, deviceCode)
+                .gt(MesCollectRealtimeHistoryDO::getCollectTime, start)
+                .le(MesCollectRealtimeHistoryDO::getCollectTime, end)
+                .orderByAsc(MesCollectRealtimeHistoryDO::getCollectTime).last("LIMIT 100001"));
+        if (history.size() > 100000) throw invalidParamException("閲囨牱鏁版嵁杩囧锛岃缂╃煭鏌ヨ鏃堕棿鑼冨洿");
+        List<LocalDateTime> samples = new ArrayList<>();
+        if (prior != null) samples.add(prior.getCollectTime());
+        history.forEach(item -> samples.add(item.getCollectTime()));
+        var intervals = CollectOfflinePolicy.calculate(samples, start, end, Duration.ofMinutes(ONLINE_TIMEOUT_MINUTES)).stream()
+                .map(item -> new MesCollectOfflineIntervalRespVO(item.start(), item.end(), Duration.between(item.start(), item.end()).getSeconds()))
+                .toList();
+        return new MesCollectOfflineResultRespVO(samples.isEmpty() ? null : samples.getFirst(), intervals);
+    }
+
+    /**
+     * 璁惧鍋滄満鏄庣粏鎶ヨ〃锛氭寜閲囬泦闂撮殧鎺ㄧ畻姣忓彴璁惧鐨勫仠鏈哄尯闂翠笌鍚堣銆�
+     *
+     * <p>涓庡崟璁惧绂荤嚎鏌ヨ淇濇寔鍚屼竴鍙e緞锛堝湪绾胯秴鏃堕槇鍊笺�佷繚鐣欐湡銆佷竷澶╀笂闄愶級锛�
+     * 渚� OEE 鎶ヨ〃瀹氫綅鍋滄満鐭澘浣跨敤銆�
+     */
+    public cn.iocoder.yudao.module.mes.controller.admin.collect.vo.MesCollectDowntimeDetailRespVO
+            getDowntimeDetail(LocalDateTime start, LocalDateTime end) {
+        LocalDateTime now = LocalDateTime.now();
+        if (start == null || end == null || !start.isBefore(end) || end.isAfter(now)
+                || Duration.between(start, end).compareTo(Duration.ofDays(31)) > 0) {
+            throw invalidParamException("璇烽�夋嫨鏈夋晥鐨勬椂闂磋寖鍥达紝鍗曟鏈�澶氭煡璇笁鍗佷竴澶╋紝缁撴潫鏃堕棿涓嶈兘鏅氫簬褰撳墠鏃堕棿");
+        }
+        if (start.isBefore(now.minusDays(collectProperties.getRetention().getRealtimeDays()))) {
+            throw invalidParamException("鏌ヨ鏃堕棿瓒呭嚭鏁伴噰鍘嗗彶淇濈暀鏈燂紝鏃犳硶鍙潬璁$畻鍋滄満璁板綍");
+        }
+        var result = new cn.iocoder.yudao.module.mes.controller.admin.collect.vo.MesCollectDowntimeDetailRespVO();
+        result.setStartTime(start);
+        result.setEndTime(end);
+        result.setTimeoutMinutes((int) ONLINE_TIMEOUT_MINUTES);
+        long totalMinutes = Duration.between(start, end).toMinutes();
+        result.setTotalMinutes(totalMinutes);
+
+        var machineries = machineryMapper.selectListWithCollectDeviceCode();
+        List<cn.iocoder.yudao.module.mes.controller.admin.collect.vo.MesCollectDowntimeDetailRespVO.Item> items = new ArrayList<>();
+        long sumDowntime = 0L;
+        for (var machinery : machineries) {
+            String deviceCode = machinery.getCollectDeviceCode();
+            var prior = realtimeHistoryMapper.selectOne(new LambdaQueryWrapperX<MesCollectRealtimeHistoryDO>()
+                    .eq(MesCollectRealtimeHistoryDO::getDeviceCode, deviceCode)
+                    .ge(MesCollectRealtimeHistoryDO::getCollectTime, now.minusDays(collectProperties.getRetention().getRealtimeDays()))
+                    .le(MesCollectRealtimeHistoryDO::getCollectTime, start)
+                    .orderByDesc(MesCollectRealtimeHistoryDO::getCollectTime).last("LIMIT 1"));
+            var history = realtimeHistoryMapper.selectList(new LambdaQueryWrapperX<MesCollectRealtimeHistoryDO>()
+                    .eq(MesCollectRealtimeHistoryDO::getDeviceCode, deviceCode)
+                    .gt(MesCollectRealtimeHistoryDO::getCollectTime, start)
+                    .le(MesCollectRealtimeHistoryDO::getCollectTime, end)
+                    .orderByAsc(MesCollectRealtimeHistoryDO::getCollectTime).last("LIMIT 100001"));
+            if (history.size() > 100000) throw invalidParamException("閲囨牱鏁版嵁杩囧锛岃缂╃煭鏌ヨ鏃堕棿鑼冨洿");
+            List<LocalDateTime> samples = new ArrayList<>();
+            int count = 0;
+            if (prior != null) { samples.add(prior.getCollectTime()); count++; }
+            history.forEach(item -> samples.add(item.getCollectTime()));
+            count += history.size();
+            var intervals = CollectOfflinePolicy.calculate(samples, start, end, Duration.ofMinutes(ONLINE_TIMEOUT_MINUTES)).stream()
+                    .map(i -> new cn.iocoder.yudao.module.mes.controller.admin.collect.vo.MesCollectDowntimeDetailRespVO.Interval(
+                            i.start(), i.end(), Duration.between(i.start(), i.end()).getSeconds()))
+                    .toList();
+            long minutes = intervals.stream().mapToLong(i -> i.getSeconds() / 60).sum();
+            sumDowntime += minutes;
+            items.add(new cn.iocoder.yudao.module.mes.controller.admin.collect.vo.MesCollectDowntimeDetailRespVO.Item(
+                    machinery.getId(), machinery.getCode(), machinery.getName(), deviceCode,
+                    count, intervals.size(), minutes, intervals));
+        }
+        items.sort((a, b) -> Long.compare(b.getDowntimeMinutes(), a.getDowntimeMinutes()));
+        result.setItems(items);
+        result.setDowntimeMinutes(sumDowntime);
+        result.setDowntimeRate(totalMinutes > 0 && !items.isEmpty()
+                ? Math.round(sumDowntime * 10000D / (totalMinutes * items.size())) / 100D : 0D);
+        return result;
+    }
 
     /**
      * 鑾峰彇宸查厤缃暟閲囩紪鐮佺殑璁惧瀹炴椂鐘舵�佸垪琛�

--
Gitblit v1.9.3