From 2b8a06fad6cc62d8d46307626489b68c4f8f065d Mon Sep 17 00:00:00 2001
From: Junjie <fallin.jie@qq.com>
Date: 星期五, 27 三月 2026 16:15:49 +0800
Subject: [PATCH] #

---
 src/main/webapp/static/js/wrkAnalysis/wrkAnalysis.js                   |   11 +++++
 src/main/webapp/views/wrkAnalysis/wrkAnalysis.html                     |    5 ++
 src/main/java/com/zy/asrs/service/impl/WrkAnalysisServiceImpl.java     |   16 ++++++++
 src/test/java/com/zy/asrs/service/impl/WrkAnalysisServiceImplTest.java |   73 ++++++++++++++++++++++++++++++++++++
 4 files changed, 105 insertions(+), 0 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 c000928..f647010 100644
--- a/src/main/java/com/zy/asrs/service/impl/WrkAnalysisServiceImpl.java
+++ b/src/main/java/com/zy/asrs/service/impl/WrkAnalysisServiceImpl.java
@@ -16,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;
@@ -467,6 +469,7 @@
         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));
@@ -842,6 +845,19 @@
         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) {
         if (Cools.isEmpty(rawValue) || !rawValue.contains("~")) {
             return;
diff --git a/src/main/webapp/static/js/wrkAnalysis/wrkAnalysis.js b/src/main/webapp/static/js/wrkAnalysis/wrkAnalysis.js
index 4ea91bf..1844fe8 100644
--- a/src/main/webapp/static/js/wrkAnalysis/wrkAnalysis.js
+++ b/src/main/webapp/static/js/wrkAnalysis/wrkAnalysis.js
@@ -35,6 +35,7 @@
                 taskEndTime$: "",
                 taskDurationMs: null,
                 avgTaskBeatDurationMs: null,
+                avgTaskPerHour: null,
                 avgTotalDurationMs: null,
                 avgStationDurationMs: null,
                 avgCraneDurationMs: null,
@@ -819,6 +820,16 @@
                 }
                 return this.formatDurationBySeconds(ms / 1000);
             },
+            formatTaskPerHour: function (value) {
+                if (value === null || value === undefined || value === "") {
+                    return "--";
+                }
+                var num = Number(value);
+                if (!isFinite(num)) {
+                    return "--";
+                }
+                return this.trimTrailingZeros(num.toFixed(2)) + " 浠诲姟/灏忔椂";
+            },
             formatDurationBySeconds: function (seconds) {
                 var totalSeconds = Number(seconds || 0);
                 if (!isFinite(totalSeconds)) {
diff --git a/src/main/webapp/views/wrkAnalysis/wrkAnalysis.html b/src/main/webapp/views/wrkAnalysis/wrkAnalysis.html
index d18b9fe..1cda19f 100644
--- a/src/main/webapp/views/wrkAnalysis/wrkAnalysis.html
+++ b/src/main/webapp/views/wrkAnalysis/wrkAnalysis.html
@@ -414,6 +414,11 @@
                             <div class="summary-sub">鎬讳换鍔℃�昏�楁椂 / 浠诲姟鏁�</div>
                         </div>
                         <div class="summary-card">
+                            <div class="summary-label">骞冲潎姣忓皬鏃惰妭鎷�</div>
+                            <div class="summary-value">{{ formatTaskPerHour(analysis.summary.avgTaskPerHour) }}</div>
+                            <div class="summary-sub">浠诲姟鏁� / 鎬讳换鍔℃�昏�楁椂锛堝皬鏃讹級</div>
+                        </div>
+                        <div class="summary-card">
                             <div class="summary-label">骞冲潎鎬昏�楁椂</div>
                             <div class="summary-value">{{ formatDuration(analysis.summary.avgTotalDurationMs) }}</div>
                             <div class="summary-sub">鍒涘缓鍒板畬鎴愮殑骞冲潎鑰楁椂</div>
diff --git a/src/test/java/com/zy/asrs/service/impl/WrkAnalysisServiceImplTest.java b/src/test/java/com/zy/asrs/service/impl/WrkAnalysisServiceImplTest.java
new file mode 100644
index 0000000..1460a51
--- /dev/null
+++ b/src/test/java/com/zy/asrs/service/impl/WrkAnalysisServiceImplTest.java
@@ -0,0 +1,73 @@
+package com.zy.asrs.service.impl;
+
+import com.zy.asrs.entity.WrkAnalysis;
+import com.zy.asrs.service.BasCrnpErrLogService;
+import com.zy.asrs.service.BasDualCrnpErrLogService;
+import com.zy.asrs.service.BasRgvErrLogService;
+import com.zy.asrs.service.BasStationService;
+import com.zy.asrs.service.BasWrkStatusService;
+import com.zy.asrs.service.WrkMastLogService;
+import com.zy.asrs.service.WrkMastService;
+import org.junit.jupiter.api.Test;
+import org.springframework.test.util.ReflectionTestUtils;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+class WrkAnalysisServiceImplTest {
+
+    @Test
+    @SuppressWarnings("unchecked")
+    void buildAnalysisResult_calculatesAverageTaskPerHour() {
+        BasWrkStatusService basWrkStatusService = mock(BasWrkStatusService.class);
+        when(basWrkStatusService.getById(org.mockito.ArgumentMatchers.any())).thenReturn(null);
+        WrkAnalysisServiceImpl service = new WrkAnalysisServiceImpl(
+                mock(WrkMastLogService.class),
+                mock(BasCrnpErrLogService.class),
+                mock(BasDualCrnpErrLogService.class),
+                mock(BasRgvErrLogService.class),
+                mock(BasStationService.class),
+                basWrkStatusService,
+                mock(WrkMastService.class)
+        );
+
+        Map<String, Object> result = ReflectionTestUtils.invokeMethod(
+                service,
+                "buildAnalysisResult",
+                Arrays.asList(
+                        analysis(1001, date("2026-03-27 13:00:00"), date("2026-03-27 13:10:00"), 600_000L),
+                        analysis(1002, date("2026-03-27 13:15:00"), date("2026-03-27 13:30:00"), 900_000L)
+                ),
+                "finish_time"
+        );
+
+        Map<String, Object> summary = (Map<String, Object>) result.get("summary");
+        assertEquals(4.0d, ((Number) summary.get("avgTaskPerHour")).doubleValue(), 0.0001d);
+    }
+
+    private WrkAnalysis analysis(int wrkNo, Date appeTime, Date finishTime, long totalDurationMs) {
+        WrkAnalysis item = new WrkAnalysis();
+        item.setWrkNo(wrkNo);
+        item.setAppeTime(appeTime);
+        item.setFinishTime(finishTime);
+        item.setTotalDurationMs(totalDurationMs);
+        item.setFinalWrkSts(80L);
+        item.setMetricCompleteness("COMPLETE");
+        return item;
+    }
+
+    private Date date(String value) {
+        try {
+            return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(value);
+        } catch (ParseException e) {
+            throw new IllegalArgumentException(e);
+        }
+    }
+}

--
Gitblit v1.9.1