#
Junjie
21 小时以前 a17781479bd4acb36069472ac1a987df21c0cc05
src/main/java/com/zy/asrs/service/impl/WrkAnalysisServiceImpl.java
@@ -331,7 +331,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) {
@@ -445,10 +445,25 @@
        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("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));
@@ -457,7 +472,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));
@@ -472,21 +487,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);
@@ -501,10 +508,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) {
@@ -512,7 +519,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) {
@@ -528,9 +536,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);
@@ -538,6 +546,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) {
@@ -550,11 +593,24 @@
    }
    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());
        }
        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) {
@@ -905,4 +961,10 @@
        private long craneDurationCount;
    }
    private enum TrendBucketType {
        MINUTE,
        HOUR,
        DAY
    }
}