From aa710969e00e9d7e56a276066a239f74d5c49310 Mon Sep 17 00:00:00 2001
From: Junjie <fallin.jie@qq.com>
Date: 星期二, 31 三月 2026 21:47:07 +0800
Subject: [PATCH] #

---
 src/main/java/com/zy/asrs/service/impl/WrkAnalysisServiceImpl.java |  170 ++++++++++++++++++++++++++++++++++++++++++++++----------
 1 files changed, 138 insertions(+), 32 deletions(-)

diff --git a/src/main/java/com/zy/asrs/service/impl/WrkAnalysisServiceImpl.java b/src/main/java/com/zy/asrs/service/impl/WrkAnalysisServiceImpl.java
index 7b3e214..da83645 100644
--- a/src/main/java/com/zy/asrs/service/impl/WrkAnalysisServiceImpl.java
+++ b/src/main/java/com/zy/asrs/service/impl/WrkAnalysisServiceImpl.java
@@ -3,6 +3,7 @@
 import com.alibaba.fastjson.JSONArray;
 import com.alibaba.fastjson.JSONObject;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.core.common.Cools;
@@ -15,6 +16,8 @@
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
+import java.math.BigDecimal;
+import java.math.RoundingMode;
 import java.text.SimpleDateFormat;
 import java.util.*;
 import java.util.stream.Collectors;
@@ -27,7 +30,8 @@
             WrkStsType.SETTLE_INBOUND.sts,
             WrkStsType.COMPLETE_OUTBOUND.sts,
             WrkStsType.SETTLE_OUTBOUND.sts,
-            WrkStsType.COMPLETE_LOC_MOVE.sts
+            WrkStsType.COMPLETE_LOC_MOVE.sts,
+            WrkStsType.COMPLETE_CRN_MOVE.sts
     );
     private static final String MODE_TASK = "TASK";
     private static final String MODE_TIME = "TIME";
@@ -42,6 +46,7 @@
     private final BasCrnpErrLogService basCrnpErrLogService;
     private final BasDualCrnpErrLogService basDualCrnpErrLogService;
     private final BasRgvErrLogService basRgvErrLogService;
+    private final BasStationErrLogService basStationErrLogService;
     private final BasStationService basStationService;
     private final BasWrkStatusService basWrkStatusService;
     private final WrkMastService wrkMastService;
@@ -50,6 +55,7 @@
                                   BasCrnpErrLogService basCrnpErrLogService,
                                   BasDualCrnpErrLogService basDualCrnpErrLogService,
                                   BasRgvErrLogService basRgvErrLogService,
+                                  BasStationErrLogService basStationErrLogService,
                                   BasStationService basStationService,
                                   BasWrkStatusService basWrkStatusService,
                                   WrkMastService wrkMastService) {
@@ -57,6 +63,7 @@
         this.basCrnpErrLogService = basCrnpErrLogService;
         this.basDualCrnpErrLogService = basDualCrnpErrLogService;
         this.basRgvErrLogService = basRgvErrLogService;
+        this.basStationErrLogService = basStationErrLogService;
         this.basStationService = basStationService;
         this.basWrkStatusService = basWrkStatusService;
         this.wrkMastService = wrkMastService;
@@ -85,6 +92,8 @@
         entity.setDualCrnFaultDurationMs(defaultLong(entity.getDualCrnFaultDurationMs()));
         entity.setRgvFaultCount(defaultInt(entity.getRgvFaultCount()));
         entity.setRgvFaultDurationMs(defaultLong(entity.getRgvFaultDurationMs()));
+        entity.setStationFaultCount(defaultInt(entity.getStationFaultCount()));
+        entity.setStationFaultDurationMs(defaultLong(entity.getStationFaultDurationMs()));
         entity.setMetricCompleteness(METRIC_PARTIAL);
         entity.setUpdateTime(now);
         this.saveOrUpdate(entity);
@@ -126,13 +135,11 @@
         if (wrkMast == null || wrkMast.getWrkNo() == null || !Objects.equals(wrkMast.getWrkSts(), WrkStsType.INBOUND_STATION_RUN.sts)) {
             return false;
         }
-        WrkMast updateEntity = new WrkMast();
-        updateEntity.setWrkNo(wrkMast.getWrkNo());
-        updateEntity.setWrkSts(WrkStsType.INBOUND_STATION_RUN_COMPLETE.sts);
         Date now = safeDate(operateTime);
-        updateEntity.setIoTime(now);
-        updateEntity.setModiTime(now);
-        boolean updated = wrkMast.getWrkNo() != null && wrkMastService.update(updateEntity, new QueryWrapper<WrkMast>()
+        boolean updated = wrkMast.getWrkNo() != null && wrkMastService.update(null, new UpdateWrapper<WrkMast>()
+                .set("wrk_sts", WrkStsType.INBOUND_STATION_RUN_COMPLETE.sts)
+                .set("io_time", now)
+                .set("modi_time", now)
                 .eq("wrk_no", wrkMast.getWrkNo())
                 .eq("wrk_sts", WrkStsType.INBOUND_STATION_RUN.sts));
         if (!updated) {
@@ -185,7 +192,9 @@
         entity.setRgvNo(wrkMast.getRgvNo());
         entity.setFinalWrkSts(wrkMast.getWrkSts());
         entity.setUpdateTime(time);
-        if (Objects.equals(wrkMast.getIoType(), WrkIoType.LOC_MOVE.id) && entity.getStationDurationMs() == null) {
+        if ((Objects.equals(wrkMast.getIoType(), WrkIoType.LOC_MOVE.id)
+                || Objects.equals(wrkMast.getIoType(), WrkIoType.CRN_MOVE.id))
+                && entity.getStationDurationMs() == null) {
             entity.setStationDurationMs(0L);
         }
         this.updateById(entity);
@@ -224,7 +233,9 @@
         if (entity.getAppeTime() != null) {
             entity.setTotalDurationMs(durationMs(entity.getAppeTime(), time));
         }
-        if (Objects.equals(wrkMast.getIoType(), WrkIoType.LOC_MOVE.id) && entity.getStationDurationMs() == null) {
+        if ((Objects.equals(wrkMast.getIoType(), WrkIoType.LOC_MOVE.id)
+                || Objects.equals(wrkMast.getIoType(), WrkIoType.CRN_MOVE.id))
+                && entity.getStationDurationMs() == null) {
             entity.setStationDurationMs(0L);
         }
         FaultSummary faultSummary = buildFaultSummary(wrkMast.getWrkNo(), time);
@@ -237,6 +248,8 @@
         entity.setDualCrnFaultDurationMs(faultSummary.dualDurationMs);
         entity.setRgvFaultCount(faultSummary.rgvCount);
         entity.setRgvFaultDurationMs(faultSummary.rgvDurationMs);
+        entity.setStationFaultCount(faultSummary.stationCount);
+        entity.setStationFaultDurationMs(faultSummary.stationDurationMs);
         entity.setMetricCompleteness(resolveMetricCompleteness(wrkMast, entity));
         entity.setUpdateTime(time);
         this.saveOrUpdate(entity);
@@ -264,6 +277,7 @@
         ioTypes.add(option("1", "IN", "鍏ュ簱", WrkIoType.IN.id));
         ioTypes.add(option("2", "OUT", "鍑哄簱", WrkIoType.OUT.id));
         ioTypes.add(option("3", "LOC_MOVE", "绉诲簱", WrkIoType.LOC_MOVE.id));
+        ioTypes.add(option("4", "CRN_MOVE", "鍫嗗灈鏈虹Щ鍔�", WrkIoType.CRN_MOVE.id));
         List<Map<String, Object>> timeFields = new ArrayList<>();
         timeFields.add(option(TIME_FIELD_FINISH, TIME_FIELD_FINISH, "瀹屾垚鏃堕棿", TIME_FIELD_FINISH));
         timeFields.add(option(TIME_FIELD_APPE, TIME_FIELD_APPE, "鍒涘缓鏃堕棿", TIME_FIELD_APPE));
@@ -332,7 +346,7 @@
         if (TIME_FIELD_APPE.equalsIgnoreCase(request.getString("timeField"))) {
             timeField = TIME_FIELD_APPE;
         }
-        return buildAnalysisResult(list, mode, timeField, request);
+        return buildAnalysisResult(list, timeField);
     }
 
     private QueryWrapper<WrkMastLog> buildHistoryLogWrapper(Map<String, Object> param) {
@@ -446,10 +460,29 @@
         wrapper.in("wrk_no", wrkNos);
     }
 
-    private Map<String, Object> buildAnalysisResult(List<WrkAnalysis> list, String mode, String timeField, JSONObject request) {
+    private Map<String, Object> buildAnalysisResult(List<WrkAnalysis> list, String timeField) {
         Map<String, Object> result = new LinkedHashMap<>();
         Map<String, Object> summary = new LinkedHashMap<>();
+        Date taskStartTime = list.stream()
+                .map(WrkAnalysis::getAppeTime)
+                .filter(Objects::nonNull)
+                .min(Date::compareTo)
+                .orElse(null);
+        Date taskEndTime = list.stream()
+                .map(WrkAnalysis::getFinishTime)
+                .filter(Objects::nonNull)
+                .max(Date::compareTo)
+                .orElse(null);
         summary.put("taskCount", list.size());
+        summary.put("taskStartTime", taskStartTime);
+        summary.put("taskStartTime$", formatDate(taskStartTime));
+        summary.put("taskEndTime", taskEndTime);
+        summary.put("taskEndTime$", formatDate(taskEndTime));
+        summary.put("taskDurationMs", taskStartTime == null || taskEndTime == null ? null : durationMs(taskStartTime, taskEndTime));
+        summary.put("avgTaskBeatDurationMs", list.isEmpty() || taskStartTime == null || taskEndTime == null
+                ? null
+                : durationMs(taskStartTime, taskEndTime) / list.size());
+        summary.put("avgTaskPerHour", calculateAvgTaskPerHour(list.size(), summary.get("taskDurationMs")));
         summary.put("avgTotalDurationMs", average(list, item -> item.getTotalDurationMs() != null, WrkAnalysis::getTotalDurationMs));
         summary.put("avgStationDurationMs", average(list, item -> !METRIC_PARTIAL.equals(item.getMetricCompleteness()) && item.getStationDurationMs() != null, WrkAnalysis::getStationDurationMs));
         summary.put("avgCraneDurationMs", average(list, item -> !METRIC_PARTIAL.equals(item.getMetricCompleteness()) && item.getCraneDurationMs() != null, WrkAnalysis::getCraneDurationMs));
@@ -458,7 +491,7 @@
         summary.put("partialTaskCount", list.stream().filter(item -> METRIC_PARTIAL.equals(item.getMetricCompleteness())).count());
         result.put("summary", summary);
         result.put("durationCompare", buildDurationCompare(list));
-        result.put("trend", buildTrend(list, mode, timeField, request));
+        result.put("trend", buildTrend(list, timeField));
         result.put("faultPie", buildFaultPie(list));
         result.put("faultDuration", buildFaultDuration(list));
         result.put("detail", buildDetailRows(list));
@@ -473,21 +506,13 @@
                 .collect(Collectors.toList());
     }
 
-    private List<Map<String, Object>> buildTrend(List<WrkAnalysis> list, String mode, String timeField, JSONObject request) {
+    private List<Map<String, Object>> buildTrend(List<WrkAnalysis> list, String timeField) {
         List<Map<String, Object>> trend = new ArrayList<>();
         if (list.isEmpty()) {
             return trend;
         }
         long startMs = Long.MAX_VALUE;
         long endMs = Long.MIN_VALUE;
-        if (MODE_TIME.equals(mode)) {
-            Long reqStart = parseLong(request.get("startTime"));
-            Long reqEnd = parseLong(request.get("endTime"));
-            if (reqStart != null && reqEnd != null) {
-                startMs = reqStart;
-                endMs = reqEnd;
-            }
-        }
         if (startMs == Long.MAX_VALUE || endMs == Long.MIN_VALUE) {
             for (WrkAnalysis item : list) {
                 Date date = resolveBucketTime(item, timeField);
@@ -502,10 +527,10 @@
         if (startMs == Long.MAX_VALUE || endMs == Long.MIN_VALUE) {
             return trend;
         }
-        boolean hourly = endMs - startMs <= 72L * 60L * 60L * 1000L;
-        SimpleDateFormat keyFormat = new SimpleDateFormat(hourly ? "yyyy-MM-dd HH:00" : "yyyy-MM-dd");
+        TrendBucketType bucketType = resolveTrendBucketType(startMs, endMs);
+        SimpleDateFormat keyFormat = new SimpleDateFormat(resolveTrendBucketPattern(bucketType));
         keyFormat.setTimeZone(TimeZone.getDefault());
-        Map<String, BucketAccumulator> bucketMap = new LinkedHashMap<>();
+        Map<Long, BucketAccumulator> bucketMap = new LinkedHashMap<>();
         List<WrkAnalysis> sorted = new ArrayList<>(list);
         sorted.sort(Comparator.comparing(item -> resolveBucketTime(item, timeField), Comparator.nullsLast(Date::compareTo)));
         for (WrkAnalysis item : sorted) {
@@ -513,7 +538,8 @@
             if (bucketTime == null) {
                 continue;
             }
-            String key = keyFormat.format(bucketTime);
+            Date bucketStart = truncateBucketTime(bucketTime, bucketType);
+            long key = bucketStart.getTime();
             BucketAccumulator accumulator = bucketMap.computeIfAbsent(key, k -> new BucketAccumulator());
             accumulator.taskCount++;
             if (item.getTotalDurationMs() != null) {
@@ -529,9 +555,9 @@
                 accumulator.craneDurationCount++;
             }
         }
-        for (Map.Entry<String, BucketAccumulator> entry : bucketMap.entrySet()) {
+        for (Map.Entry<Long, BucketAccumulator> entry : bucketMap.entrySet()) {
             Map<String, Object> item = new LinkedHashMap<>();
-            item.put("bucketLabel", entry.getKey());
+            item.put("bucketLabel", keyFormat.format(new Date(entry.getKey())));
             item.put("taskCount", entry.getValue().taskCount);
             item.put("avgTotalDurationMs", entry.getValue().totalDurationCount == 0 ? null : entry.getValue().totalDurationMs / entry.getValue().totalDurationCount);
             item.put("avgStationDurationMs", entry.getValue().stationDurationCount == 0 ? null : entry.getValue().stationDurationMs / entry.getValue().stationDurationCount);
@@ -539,6 +565,41 @@
             trend.add(item);
         }
         return trend;
+    }
+
+    private TrendBucketType resolveTrendBucketType(long startMs, long endMs) {
+        long spanMs = Math.max(0L, endMs - startMs);
+        if (spanMs <= 6L * 60L * 60L * 1000L) {
+            return TrendBucketType.MINUTE;
+        }
+        if (spanMs <= 72L * 60L * 60L * 1000L) {
+            return TrendBucketType.HOUR;
+        }
+        return TrendBucketType.DAY;
+    }
+
+    private String resolveTrendBucketPattern(TrendBucketType bucketType) {
+        if (bucketType == TrendBucketType.MINUTE) {
+            return "yyyy-MM-dd HH:mm";
+        }
+        if (bucketType == TrendBucketType.HOUR) {
+            return "yyyy-MM-dd HH:00";
+        }
+        return "yyyy-MM-dd";
+    }
+
+    private Date truncateBucketTime(Date time, TrendBucketType bucketType) {
+        Calendar calendar = Calendar.getInstance();
+        calendar.setTime(time);
+        if (bucketType == TrendBucketType.DAY) {
+            calendar.set(Calendar.HOUR_OF_DAY, 0);
+        }
+        if (bucketType == TrendBucketType.DAY || bucketType == TrendBucketType.HOUR) {
+            calendar.set(Calendar.MINUTE, 0);
+        }
+        calendar.set(Calendar.SECOND, 0);
+        calendar.set(Calendar.MILLISECOND, 0);
+        return calendar.getTime();
     }
 
     private List<Map<String, Object>> buildFaultPie(List<WrkAnalysis> list) {
@@ -551,11 +612,25 @@
     }
 
     private List<Map<String, Object>> buildFaultDuration(List<WrkAnalysis> list) {
+        Map<String, Long> durationMap = new LinkedHashMap<>();
+        for (WrkAnalysis item : list) {
+            addDeviceFaultDuration(durationMap, "鍗曞爢鍨涙満", item.getCrnNo(), item.getCrnFaultDurationMs());
+            addDeviceFaultDuration(durationMap, "鍙屽伐浣嶅爢鍨涙満", item.getDualCrnNo(), item.getDualCrnFaultDurationMs());
+            addDeviceFaultDuration(durationMap, "RGV", item.getRgvNo(), item.getRgvFaultDurationMs());
+            addDeviceFaultDuration(durationMap, "杈撻�佺珯鐐�", null, item.getStationFaultDurationMs());
+        }
         List<Map<String, Object>> result = new ArrayList<>();
-        result.add(slice("鍗曞爢鍨涙満", list.stream().map(WrkAnalysis::getCrnFaultDurationMs).filter(Objects::nonNull).reduce(0L, Long::sum)));
-        result.add(slice("鍙屽伐浣嶅爢鍨涙満", list.stream().map(WrkAnalysis::getDualCrnFaultDurationMs).filter(Objects::nonNull).reduce(0L, Long::sum)));
-        result.add(slice("RGV", list.stream().map(WrkAnalysis::getRgvFaultDurationMs).filter(Objects::nonNull).reduce(0L, Long::sum)));
+        durationMap.forEach((name, durationMs) -> result.add(slice(name, durationMs)));
         return result;
+    }
+
+    private void addDeviceFaultDuration(Map<String, Long> durationMap, String deviceLabel, Integer deviceNo, Long durationMs) {
+        long value = defaultLong(durationMs);
+        if (value <= 0L) {
+            return;
+        }
+        String key = deviceNo == null ? deviceLabel : deviceLabel + deviceNo;
+        durationMap.merge(key, value, Long::sum);
     }
 
     private List<Map<String, Object>> buildDetailRows(List<WrkAnalysis> list) {
@@ -613,6 +688,7 @@
         target.put("crnFaultDurationMs", analysis == null ? 0L : defaultLong(analysis.getCrnFaultDurationMs()));
         target.put("dualCrnFaultDurationMs", analysis == null ? 0L : defaultLong(analysis.getDualCrnFaultDurationMs()));
         target.put("rgvFaultDurationMs", analysis == null ? 0L : defaultLong(analysis.getRgvFaultDurationMs()));
+        target.put("stationFaultDurationMs", analysis == null ? 0L : defaultLong(analysis.getStationFaultDurationMs()));
         target.put("metricCompleteness", analysis == null ? METRIC_PARTIAL : analysis.getMetricCompleteness());
     }
 
@@ -624,14 +700,17 @@
         List<BasCrnpErrLog> crnList = basCrnpErrLogService.list(new QueryWrapper<BasCrnpErrLog>().eq("wrk_no", wrkNo));
         List<BasDualCrnpErrLog> dualList = basDualCrnpErrLogService.list(new QueryWrapper<BasDualCrnpErrLog>().eq("wrk_no", wrkNo));
         List<BasRgvErrLog> rgvList = basRgvErrLogService.list(new QueryWrapper<BasRgvErrLog>().eq("task_no", wrkNo));
+        List<BasStationErrLog> stationList = basStationErrLogService.list(new QueryWrapper<BasStationErrLog>().eq("wrk_no", wrkNo));
         summary.crnCount = crnList.size();
         summary.crnDurationMs = durationMs(crnList, finishTime);
         summary.dualCount = dualList.size();
         summary.dualDurationMs = durationMs(dualList, finishTime);
         summary.rgvCount = rgvList.size();
         summary.rgvDurationMs = durationMs(rgvList, finishTime);
-        summary.totalCount = summary.crnCount + summary.dualCount + summary.rgvCount;
-        summary.totalDurationMs = summary.crnDurationMs + summary.dualDurationMs + summary.rgvDurationMs;
+        summary.stationCount = stationList.size();
+        summary.stationDurationMs = durationMs(stationList, finishTime);
+        summary.totalCount = summary.crnCount + summary.dualCount + summary.rgvCount + summary.stationCount;
+        summary.totalDurationMs = summary.crnDurationMs + summary.dualDurationMs + summary.rgvDurationMs + summary.stationDurationMs;
         summary.hasFault = summary.totalCount > 0 ? 1 : 0;
         return summary;
     }
@@ -650,6 +729,9 @@
             } else if (item instanceof BasRgvErrLog) {
                 startTime = ((BasRgvErrLog) item).getStartTime();
                 endTime = ((BasRgvErrLog) item).getEndTime();
+            } else if (item instanceof BasStationErrLog) {
+                startTime = ((BasStationErrLog) item).getStartTime();
+                endTime = ((BasStationErrLog) item).getEndTime();
             }
             if (startTime == null) {
                 continue;
@@ -758,6 +840,9 @@
         if (Objects.equals(ioType, WrkIoType.LOC_MOVE.id)) {
             return "绉诲簱";
         }
+        if (Objects.equals(ioType, WrkIoType.CRN_MOVE.id)) {
+            return "鍫嗗灈鏈虹Щ鍔�";
+        }
         return String.valueOf(ioType);
     }
 
@@ -782,6 +867,19 @@
             count++;
         }
         return count == 0 ? null : total / count;
+    }
+
+    private Double calculateAvgTaskPerHour(int taskCount, Object taskDurationMsValue) {
+        if (taskCount <= 0 || !(taskDurationMsValue instanceof Number)) {
+            return null;
+        }
+        long taskDurationMs = ((Number) taskDurationMsValue).longValue();
+        if (taskDurationMs <= 0L) {
+            return null;
+        }
+        return BigDecimal.valueOf(taskCount * 3600000D / taskDurationMs)
+                .setScale(2, RoundingMode.HALF_UP)
+                .doubleValue();
     }
 
     private void applyRange(QueryWrapper<WrkMastLog> wrapper, String column, String rawValue) {
@@ -894,6 +992,8 @@
         private long dualDurationMs;
         private int rgvCount;
         private long rgvDurationMs;
+        private int stationCount;
+        private long stationDurationMs;
     }
 
     private static class BucketAccumulator {
@@ -906,4 +1006,10 @@
         private long craneDurationCount;
     }
 
+    private enum TrendBucketType {
+        MINUTE,
+        HOUR,
+        DAY
+    }
+
 }

--
Gitblit v1.9.1