From 8027c8e2e0b5c559da612b187031dd6fd82d9bc7 Mon Sep 17 00:00:00 2001
From: Junjie <fallin.jie@qq.com>
Date: 星期六, 04 四月 2026 12:03:48 +0800
Subject: [PATCH] #任务分析异常修复

---
 src/main/webapp/static/js/wrkAnalysis/wrkAnalysis.js                                   |   22 
 src/main/resources/sql/20260404_fix_wrk_analysis_pk_and_log_link.sql                   |   47 +++
 src/main/webapp/views/wrkAnalysis/wrkAnalysis.html                                     |    6 
 src/main/java/com/zy/asrs/service/WrkAnalysisService.java                              |    4 
 src/main/java/com/zy/asrs/service/impl/WrkAnalysisServiceImpl.java                     |  209 +++++++++++++++--
 src/main/java/com/zy/asrs/entity/WrkAnalysis.java                                      |   10 
 src/main/java/com/zy/asrs/service/impl/WrkMastLogServiceImpl.java                      |    9 
 src/main/resources/mapper/WrkAnalysisMapper.xml                                        |    6 
 src/main/java/com/zy/asrs/task/WrkMastScheduler.java                                   |   26 +
 src/main/java/com/zy/asrs/service/WrkMastLogService.java                               |    2 
 src/main/resources/sql/20260322_add_wrk_analysis_page_and_inbound_status_migration.sql |  261 +++++++++++++++------
 src/main/resources/sql/20260331_add_station_err_log.sql                                |   67 ++++-
 12 files changed, 522 insertions(+), 147 deletions(-)

diff --git a/src/main/java/com/zy/asrs/entity/WrkAnalysis.java b/src/main/java/com/zy/asrs/entity/WrkAnalysis.java
index df43106..314b5c2 100644
--- a/src/main/java/com/zy/asrs/entity/WrkAnalysis.java
+++ b/src/main/java/com/zy/asrs/entity/WrkAnalysis.java
@@ -17,8 +17,16 @@
 
     private static final long serialVersionUID = 1L;
 
+    @ApiModelProperty(value = "涓婚敭ID")
+    @TableId(value = "id", type = IdType.AUTO)
+    private Long id;
+
+    @ApiModelProperty(value = "宸ヤ綔鍘嗗彶ID")
+    @TableField("wrk_log_id")
+    private Long wrkLogId;
+
     @ApiModelProperty(value = "宸ヤ綔鍙�")
-    @TableId(value = "wrk_no", type = IdType.INPUT)
+    @TableField("wrk_no")
     private Integer wrkNo;
 
     @ApiModelProperty(value = "WMS浠诲姟鍙�")
diff --git a/src/main/java/com/zy/asrs/service/WrkAnalysisService.java b/src/main/java/com/zy/asrs/service/WrkAnalysisService.java
index 219035e..8c5d06c 100644
--- a/src/main/java/com/zy/asrs/service/WrkAnalysisService.java
+++ b/src/main/java/com/zy/asrs/service/WrkAnalysisService.java
@@ -30,7 +30,9 @@
 
     void finishTask(WrkMast wrkMast, Date finishTime);
 
-    Map<Integer, WrkAnalysis> mapByWrkNos(Collection<Integer> wrkNos);
+    void finishTask(WrkMast wrkMast, Date finishTime, Long wrkLogId);
+
+    Map<Long, WrkAnalysis> mapByWrkLogIds(Collection<Long> wrkLogIds);
 
     Map<String, Object> queryOptions();
 
diff --git a/src/main/java/com/zy/asrs/service/WrkMastLogService.java b/src/main/java/com/zy/asrs/service/WrkMastLogService.java
index 36a9bc9..553668d 100644
--- a/src/main/java/com/zy/asrs/service/WrkMastLogService.java
+++ b/src/main/java/com/zy/asrs/service/WrkMastLogService.java
@@ -7,4 +7,6 @@
 
     boolean save(Integer wrkNo);
 
+    WrkMastLog saveRecord(Integer wrkNo);
+
 }
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 da83645..6eab1a5 100644
--- a/src/main/java/com/zy/asrs/service/impl/WrkAnalysisServiceImpl.java
+++ b/src/main/java/com/zy/asrs/service/impl/WrkAnalysisServiceImpl.java
@@ -75,7 +75,7 @@
         if (wrkMast == null || wrkMast.getWrkNo() == null) {
             return;
         }
-        WrkAnalysis entity = this.getById(wrkMast.getWrkNo());
+        WrkAnalysis entity = findActiveRecord(wrkMast.getWrkNo());
         Date now = new Date();
         if (entity == null) {
             entity = new WrkAnalysis();
@@ -96,7 +96,11 @@
         entity.setStationFaultDurationMs(defaultLong(entity.getStationFaultDurationMs()));
         entity.setMetricCompleteness(METRIC_PARTIAL);
         entity.setUpdateTime(now);
-        this.saveOrUpdate(entity);
+        if (entity.getId() == null) {
+            this.save(entity);
+        } else {
+            this.updateById(entity);
+        }
     }
 
     @Override
@@ -220,6 +224,12 @@
     @Override
     @Transactional(rollbackFor = Exception.class)
     public void finishTask(WrkMast wrkMast, Date finishTime) {
+        finishTask(wrkMast, finishTime, null);
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void finishTask(WrkMast wrkMast, Date finishTime, Long wrkLogId) {
         if (wrkMast == null || wrkMast.getWrkNo() == null) {
             return;
         }
@@ -238,7 +248,8 @@
                 && entity.getStationDurationMs() == null) {
             entity.setStationDurationMs(0L);
         }
-        FaultSummary faultSummary = buildFaultSummary(wrkMast.getWrkNo(), time);
+        entity.setWrkLogId(wrkLogId);
+        FaultSummary faultSummary = buildFaultSummary(wrkMast.getWrkNo(), entity.getAppeTime(), time);
         entity.setHasFault(faultSummary.hasFault);
         entity.setFaultCount(faultSummary.totalCount);
         entity.setFaultDurationMs(faultSummary.totalDurationMs);
@@ -252,19 +263,21 @@
         entity.setStationFaultDurationMs(faultSummary.stationDurationMs);
         entity.setMetricCompleteness(resolveMetricCompleteness(wrkMast, entity));
         entity.setUpdateTime(time);
-        this.saveOrUpdate(entity);
+        this.updateById(entity);
     }
 
     @Override
-    public Map<Integer, WrkAnalysis> mapByWrkNos(Collection<Integer> wrkNos) {
-        Map<Integer, WrkAnalysis> result = new LinkedHashMap<>();
-        if (wrkNos == null || wrkNos.isEmpty()) {
+    public Map<Long, WrkAnalysis> mapByWrkLogIds(Collection<Long> wrkLogIds) {
+        Map<Long, WrkAnalysis> result = new LinkedHashMap<>();
+        if (wrkLogIds == null || wrkLogIds.isEmpty()) {
             return result;
         }
-        List<WrkAnalysis> list = this.listByIds(new LinkedHashSet<>(wrkNos));
+        List<WrkAnalysis> list = this.list(new QueryWrapper<WrkAnalysis>()
+                .in("wrk_log_id", new LinkedHashSet<>(wrkLogIds))
+                .orderByAsc("wrk_log_id", "id"));
         for (WrkAnalysis item : list) {
-            if (item != null && item.getWrkNo() != null) {
-                result.put(item.getWrkNo(), item);
+            if (item != null && item.getWrkLogId() != null) {
+                result.put(item.getWrkLogId(), item);
             }
         }
         return result;
@@ -316,11 +329,11 @@
         QueryWrapper<WrkMastLog> wrapper = buildHistoryLogWrapper(param);
         Page<WrkMastLog> page = wrkMastLogService.page(new Page<>(pageNo, pageSize), wrapper);
         List<WrkMastLog> logList = page.getRecords();
-        List<Integer> wrkNos = logList.stream().map(WrkMastLog::getWrkNo).filter(Objects::nonNull).collect(Collectors.toList());
-        Map<Integer, WrkAnalysis> analysisMap = mapByWrkNos(wrkNos);
+        List<Long> logIds = logList.stream().map(WrkMastLog::getId).filter(Objects::nonNull).collect(Collectors.toList());
+        Map<Long, WrkAnalysis> analysisMap = mapByWrkLogIds(logIds);
         List<Map<String, Object>> records = new ArrayList<>();
         for (WrkMastLog log : logList) {
-            records.add(toListItem(log, analysisMap.get(log.getWrkNo())));
+            records.add(toListItem(log, analysisMap.get(log.getId())));
         }
         Map<String, Object> data = new LinkedHashMap<>();
         data.put("records", records);
@@ -340,7 +353,8 @@
         QueryWrapper<WrkAnalysis> wrapper = buildAnalysisWrapper(request, mode);
         List<WrkAnalysis> list = this.list(wrapper);
         list.sort(Comparator.comparing(WrkAnalysis::getFinishTime, Comparator.nullsLast(Date::compareTo))
-                .thenComparing(WrkAnalysis::getWrkNo, Comparator.nullsLast(Integer::compareTo)));
+                .thenComparing(WrkAnalysis::getWrkLogId, Comparator.nullsLast(Long::compareTo))
+                .thenComparing(WrkAnalysis::getId, Comparator.nullsLast(Long::compareTo)));
         Collections.reverse(list);
         String timeField = TIME_FIELD_FINISH;
         if (TIME_FIELD_APPE.equalsIgnoreCase(request.getString("timeField"))) {
@@ -380,7 +394,7 @@
         applyDeviceTypeFilter(wrapper, upperTrim(stringValue(param.get("deviceType"))));
         applyRange(wrapper, "appe_time", stringValue(param.get("appeTimeRange")));
         applyRange(wrapper, "modi_time", stringValue(param.get("finishTimeRange")));
-        wrapper.orderByDesc("modi_time", "wrk_no");
+        wrapper.orderByDesc("modi_time", "id");
         return wrapper;
     }
 
@@ -413,6 +427,11 @@
             wrapper.isNotNull("rgv_no");
         }
         if (MODE_TASK.equals(mode)) {
+            List<Long> wrkLogIds = parseLongIds(request.get("wrkLogIds"));
+            if (!wrkLogIds.isEmpty()) {
+                wrapper.in("wrk_log_id", wrkLogIds);
+                return wrapper;
+            }
             List<Integer> wrkNos = parseWrkNos(request.get("wrkNos"));
             if (wrkNos.isEmpty()) {
                 wrapper.eq("wrk_no", -1);
@@ -439,7 +458,7 @@
             return;
         }
         QueryWrapper<WrkAnalysis> analysisWrapper = new QueryWrapper<>();
-        analysisWrapper.select("wrk_no");
+        analysisWrapper.select("wrk_log_id");
         if (DEVICE_TYPE_CRN.equals(deviceType)) {
             analysisWrapper.isNotNull("crn_no");
         } else if (DEVICE_TYPE_DUAL_CRN.equals(deviceType)) {
@@ -449,15 +468,15 @@
         } else {
             return;
         }
-        List<Integer> wrkNos = this.list(analysisWrapper).stream()
-                .map(WrkAnalysis::getWrkNo)
+        List<Long> wrkLogIds = this.list(analysisWrapper).stream()
+                .map(WrkAnalysis::getWrkLogId)
                 .filter(Objects::nonNull)
                 .collect(Collectors.toList());
-        if (wrkNos.isEmpty()) {
-            wrapper.eq("wrk_no", -1);
+        if (wrkLogIds.isEmpty()) {
+            wrapper.eq("id", -1);
             return;
         }
-        wrapper.in("wrk_no", wrkNos);
+        wrapper.in("id", wrkLogIds);
     }
 
     private Map<String, Object> buildAnalysisResult(List<WrkAnalysis> list, String timeField) {
@@ -639,6 +658,7 @@
 
     private Map<String, Object> toListItem(WrkMastLog log, WrkAnalysis analysis) {
         Map<String, Object> item = new LinkedHashMap<>();
+        item.put("logId", log.getId());
         item.put("wrkNo", log.getWrkNo());
         item.put("wmsWrkNo", log.getWmsWrkNo());
         item.put("wrkSts", log.getWrkSts());
@@ -660,6 +680,9 @@
 
     private Map<String, Object> toDetailItem(WrkAnalysis item) {
         Map<String, Object> result = new LinkedHashMap<>();
+        result.put("id", item.getId());
+        result.put("wrkLogId", item.getWrkLogId());
+        result.put("rowKey", buildRowKey(item));
         result.put("wrkNo", item.getWrkNo());
         result.put("wmsWrkNo", item.getWmsWrkNo());
         result.put("ioType", item.getIoType());
@@ -692,15 +715,31 @@
         target.put("metricCompleteness", analysis == null ? METRIC_PARTIAL : analysis.getMetricCompleteness());
     }
 
-    private FaultSummary buildFaultSummary(Integer wrkNo, Date finishTime) {
+    private FaultSummary buildFaultSummary(Integer wrkNo, Date taskStartTime, Date finishTime) {
         FaultSummary summary = new FaultSummary();
         if (wrkNo == null) {
             return summary;
         }
-        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));
+        List<BasCrnpErrLog> crnList = filterTaskFaultLogs(
+                basCrnpErrLogService.list(new QueryWrapper<BasCrnpErrLog>().eq("wrk_no", wrkNo)),
+                taskStartTime,
+                finishTime
+        );
+        List<BasDualCrnpErrLog> dualList = filterTaskFaultLogs(
+                basDualCrnpErrLogService.list(new QueryWrapper<BasDualCrnpErrLog>().eq("wrk_no", wrkNo)),
+                taskStartTime,
+                finishTime
+        );
+        List<BasRgvErrLog> rgvList = filterTaskFaultLogs(
+                basRgvErrLogService.list(new QueryWrapper<BasRgvErrLog>().eq("task_no", wrkNo)),
+                taskStartTime,
+                finishTime
+        );
+        List<BasStationErrLog> stationList = filterTaskFaultLogs(
+                basStationErrLogService.list(new QueryWrapper<BasStationErrLog>().eq("wrk_no", wrkNo)),
+                taskStartTime,
+                finishTime
+        );
         summary.crnCount = crnList.size();
         summary.crnDurationMs = durationMs(crnList, finishTime);
         summary.dualCount = dualList.size();
@@ -758,13 +797,13 @@
         if (wrkMast == null || wrkMast.getWrkNo() == null) {
             return null;
         }
-        WrkAnalysis entity = this.getById(wrkMast.getWrkNo());
+        WrkAnalysis entity = findActiveRecord(wrkMast.getWrkNo());
         if (entity != null) {
             syncBaseFields(entity, wrkMast);
             return entity;
         }
         initForTask(wrkMast);
-        return this.getById(wrkMast.getWrkNo());
+        return findActiveRecord(wrkMast.getWrkNo());
     }
 
     private void syncBaseFields(WrkAnalysis entity, WrkMast wrkMast) {
@@ -787,6 +826,28 @@
             return item.getAppeTime();
         }
         return item.getFinishTime();
+    }
+
+    private WrkAnalysis findActiveRecord(Integer wrkNo) {
+        if (wrkNo == null) {
+            return null;
+        }
+        return this.getOne(new QueryWrapper<WrkAnalysis>()
+                .eq("wrk_no", wrkNo)
+                .isNull("wrk_log_id")
+                .isNull("finish_time")
+                .orderByDesc("id")
+                .last("limit 1"), false);
+    }
+
+    private String buildRowKey(WrkAnalysis item) {
+        if (item.getWrkLogId() != null) {
+            return "log-" + item.getWrkLogId();
+        }
+        if (item.getId() != null) {
+            return "analysis-" + item.getId();
+        }
+        return "wrk-" + defaultInt(item.getWrkNo()) + "-" + defaultLong(item.getAppeTime() == null ? null : item.getAppeTime().getTime());
     }
 
     private Map<String, Object> option(String key, String code, String label, Object value) {
@@ -894,6 +955,59 @@
         wrapper.le(column, DateUtils.convert(parts[1].trim()));
     }
 
+    private <T> List<T> filterTaskFaultLogs(List<T> source, Date taskStartTime, Date taskFinishTime) {
+        if (source == null || source.isEmpty()) {
+            return Collections.emptyList();
+        }
+        return source.stream()
+                .filter(item -> overlapsTaskWindow(resolveFaultStartTime(item), resolveFaultEndTime(item), taskStartTime, taskFinishTime))
+                .collect(Collectors.toList());
+    }
+
+    private Date resolveFaultStartTime(Object item) {
+        if (item instanceof BasCrnpErrLog) {
+            return ((BasCrnpErrLog) item).getStartTime();
+        }
+        if (item instanceof BasDualCrnpErrLog) {
+            return ((BasDualCrnpErrLog) item).getStartTime();
+        }
+        if (item instanceof BasRgvErrLog) {
+            return ((BasRgvErrLog) item).getStartTime();
+        }
+        if (item instanceof BasStationErrLog) {
+            return ((BasStationErrLog) item).getStartTime();
+        }
+        return null;
+    }
+
+    private Date resolveFaultEndTime(Object item) {
+        if (item instanceof BasCrnpErrLog) {
+            return ((BasCrnpErrLog) item).getEndTime();
+        }
+        if (item instanceof BasDualCrnpErrLog) {
+            return ((BasDualCrnpErrLog) item).getEndTime();
+        }
+        if (item instanceof BasRgvErrLog) {
+            return ((BasRgvErrLog) item).getEndTime();
+        }
+        if (item instanceof BasStationErrLog) {
+            return ((BasStationErrLog) item).getEndTime();
+        }
+        return null;
+    }
+
+    private boolean overlapsTaskWindow(Date eventStartTime, Date eventEndTime, Date taskStartTime, Date taskFinishTime) {
+        long taskStart = taskStartTime == null ? Long.MIN_VALUE : taskStartTime.getTime();
+        long taskEnd = taskFinishTime == null ? Long.MAX_VALUE : taskFinishTime.getTime();
+        long eventStart = eventStartTime == null
+                ? (eventEndTime == null ? Long.MIN_VALUE : eventEndTime.getTime())
+                : eventStartTime.getTime();
+        long eventEnd = eventEndTime == null
+                ? (eventStartTime == null ? Long.MAX_VALUE : eventStartTime.getTime())
+                : eventEndTime.getTime();
+        return eventStart <= taskEnd && eventEnd >= taskStart;
+    }
+
     private List<Integer> parseWrkNos(Object value) {
         List<Integer> result = new ArrayList<>();
         if (value == null) {
@@ -933,6 +1047,45 @@
         return result;
     }
 
+    private List<Long> parseLongIds(Object value) {
+        List<Long> result = new ArrayList<>();
+        if (value == null) {
+            return result;
+        }
+        if (value instanceof JSONArray) {
+            JSONArray array = (JSONArray) value;
+            for (int i = 0; i < array.size(); i++) {
+                Long item = parseLong(array.get(i));
+                if (item != null) {
+                    result.add(item);
+                }
+            }
+            return result;
+        }
+        if (value instanceof Collection) {
+            for (Object item : (Collection<?>) value) {
+                Long parsed = parseLong(item);
+                if (parsed != null) {
+                    result.add(parsed);
+                }
+            }
+            return result;
+        }
+        String text = String.valueOf(value).trim();
+        if (text.startsWith("[") && text.endsWith("]")) {
+            return parseLongIds(JSONArray.parseArray(text));
+        }
+        if (!Cools.isEmpty(text)) {
+            for (String part : text.split(",")) {
+                Long parsed = parseLong(part);
+                if (parsed != null) {
+                    result.add(parsed);
+                }
+            }
+        }
+        return result;
+    }
+
     private Integer parseInteger(Object value) {
         if (value == null || Cools.isEmpty(value)) {
             return null;
diff --git a/src/main/java/com/zy/asrs/service/impl/WrkMastLogServiceImpl.java b/src/main/java/com/zy/asrs/service/impl/WrkMastLogServiceImpl.java
index 1092423..b11e947 100644
--- a/src/main/java/com/zy/asrs/service/impl/WrkMastLogServiceImpl.java
+++ b/src/main/java/com/zy/asrs/service/impl/WrkMastLogServiceImpl.java
@@ -20,13 +20,18 @@
 
     @Override
     public boolean save(Integer wrkNo) {
+        return saveRecord(wrkNo) != null;
+    }
+
+    @Override
+    public WrkMastLog saveRecord(Integer wrkNo) {
         WrkMast wrkMast = wrkMastService.selectByWorkNo(wrkNo);
         if (wrkMast == null) {
-            return false;
+            return null;
         }
 
         WrkMastLog wrkMastLog = new WrkMastLog();
         wrkMastLog.sync(wrkMast);
-        return this.baseMapper.insert(wrkMastLog) > 0;
+        return this.baseMapper.insert(wrkMastLog) > 0 ? wrkMastLog : null;
     }
 }
diff --git a/src/main/java/com/zy/asrs/task/WrkMastScheduler.java b/src/main/java/com/zy/asrs/task/WrkMastScheduler.java
index 81946e9..c7258ce 100644
--- a/src/main/java/com/zy/asrs/task/WrkMastScheduler.java
+++ b/src/main/java/com/zy/asrs/task/WrkMastScheduler.java
@@ -5,6 +5,7 @@
 import com.zy.asrs.domain.enums.NotifyMsgType;
 import com.zy.asrs.entity.BasStation;
 import com.zy.asrs.entity.LocMast;
+import com.zy.asrs.entity.WrkMastLog;
 import com.zy.asrs.entity.WrkMast;
 import com.zy.asrs.service.*;
 import com.zy.asrs.utils.NotifyUtils;
@@ -88,10 +89,11 @@
             }
 
             // 淇濆瓨宸ヤ綔涓绘。鍘嗗彶妗�
-            if (!wrkMastLogService.save(wrkMast.getWrkNo())) {
+            WrkMastLog wrkMastLog = wrkMastLogService.saveRecord(wrkMast.getWrkNo());
+            if (wrkMastLog == null) {
                 log.info("淇濆瓨宸ヤ綔鍘嗗彶妗workNo={}]澶辫触", wrkMast.getWrkNo());
             } else {
-                wrkAnalysisService.finishTask(wrkMast, resolveFinishTime(wrkMast));
+                wrkAnalysisService.finishTask(wrkMast, resolveFinishTime(wrkMast), wrkMastLog.getId());
             }
             // 鍒犻櫎宸ヤ綔涓绘。
             if (!wrkMastService.removeById(wrkMast.getWrkNo())) {
@@ -149,10 +151,11 @@
             }
 
             // 淇濆瓨宸ヤ綔涓绘。鍘嗗彶妗�
-            if (!wrkMastLogService.save(wrkMast.getWrkNo())) {
+            WrkMastLog wrkMastLog = wrkMastLogService.saveRecord(wrkMast.getWrkNo());
+            if (wrkMastLog == null) {
                 log.info("淇濆瓨宸ヤ綔鍘嗗彶妗workNo={}]澶辫触", wrkMast.getWrkNo());
             } else {
-                wrkAnalysisService.finishTask(wrkMast, resolveFinishTime(wrkMast));
+                wrkAnalysisService.finishTask(wrkMast, resolveFinishTime(wrkMast), wrkMastLog.getId());
             }
             // 鍒犻櫎宸ヤ綔涓绘。
             if (!wrkMastService.removeById(wrkMast.getWrkNo())) {
@@ -227,10 +230,11 @@
             }
 
             // 淇濆瓨宸ヤ綔涓绘。鍘嗗彶妗�
-            if (!wrkMastLogService.save(wrkMast.getWrkNo())) {
+            WrkMastLog wrkMastLog = wrkMastLogService.saveRecord(wrkMast.getWrkNo());
+            if (wrkMastLog == null) {
                 log.info("淇濆瓨宸ヤ綔鍘嗗彶妗workNo={}]澶辫触", wrkMast.getWrkNo());
             } else {
-                wrkAnalysisService.finishTask(wrkMast, resolveFinishTime(wrkMast));
+                wrkAnalysisService.finishTask(wrkMast, resolveFinishTime(wrkMast), wrkMastLog.getId());
             }
             // 鍒犻櫎宸ヤ綔涓绘。
             if (!wrkMastService.removeById(wrkMast.getWrkNo())) {
@@ -251,10 +255,11 @@
         }
 
         for (WrkMast wrkMast : wrkMasts) {
-            if (!wrkMastLogService.save(wrkMast.getWrkNo())) {
+            WrkMastLog wrkMastLog = wrkMastLogService.saveRecord(wrkMast.getWrkNo());
+            if (wrkMastLog == null) {
                 log.info("淇濆瓨宸ヤ綔鍘嗗彶妗workNo={}]澶辫触", wrkMast.getWrkNo());
             } else {
-                wrkAnalysisService.finishTask(wrkMast, resolveFinishTime(wrkMast));
+                wrkAnalysisService.finishTask(wrkMast, resolveFinishTime(wrkMast), wrkMastLog.getId());
             }
 
             if (!wrkMastService.removeById(wrkMast.getWrkNo())) {
@@ -273,10 +278,11 @@
 
         for (WrkMast wrkMast : wrkMasts) {
             // 淇濆瓨宸ヤ綔涓绘。鍘嗗彶妗�
-            if (!wrkMastLogService.save(wrkMast.getWrkNo())) {
+            WrkMastLog wrkMastLog = wrkMastLogService.saveRecord(wrkMast.getWrkNo());
+            if (wrkMastLog == null) {
                 log.info("淇濆瓨宸ヤ綔鍘嗗彶妗workNo={}]澶辫触", wrkMast.getWrkNo());
             } else {
-                wrkAnalysisService.finishTask(wrkMast, resolveFinishTime(wrkMast));
+                wrkAnalysisService.finishTask(wrkMast, resolveFinishTime(wrkMast), wrkMastLog.getId());
             }
             // 鍒犻櫎宸ヤ綔涓绘。
             if (!wrkMastService.removeById(wrkMast.getWrkNo())) {
diff --git a/src/main/resources/mapper/WrkAnalysisMapper.xml b/src/main/resources/mapper/WrkAnalysisMapper.xml
index 92bf472..998b550 100644
--- a/src/main/resources/mapper/WrkAnalysisMapper.xml
+++ b/src/main/resources/mapper/WrkAnalysisMapper.xml
@@ -3,7 +3,9 @@
 <mapper namespace="com.zy.asrs.mapper.WrkAnalysisMapper">
 
     <resultMap id="BaseResultMap" type="com.zy.asrs.entity.WrkAnalysis">
-        <id column="wrk_no" property="wrkNo" />
+        <id column="id" property="id" />
+        <result column="wrk_log_id" property="wrkLogId" />
+        <result column="wrk_no" property="wrkNo" />
         <result column="wms_wrk_no" property="wmsWrkNo" />
         <result column="io_type" property="ioType" />
         <result column="final_wrk_sts" property="finalWrkSts" />
@@ -32,6 +34,8 @@
         <result column="dual_crn_fault_duration_ms" property="dualCrnFaultDurationMs" />
         <result column="rgv_fault_count" property="rgvFaultCount" />
         <result column="rgv_fault_duration_ms" property="rgvFaultDurationMs" />
+        <result column="station_fault_count" property="stationFaultCount" />
+        <result column="station_fault_duration_ms" property="stationFaultDurationMs" />
         <result column="metric_completeness" property="metricCompleteness" />
         <result column="create_time" property="createTime" />
         <result column="update_time" property="updateTime" />
diff --git a/src/main/resources/sql/20260322_add_wrk_analysis_page_and_inbound_status_migration.sql b/src/main/resources/sql/20260322_add_wrk_analysis_page_and_inbound_status_migration.sql
index 8d8350f..91137c3 100644
--- a/src/main/resources/sql/20260322_add_wrk_analysis_page_and_inbound_status_migration.sql
+++ b/src/main/resources/sql/20260322_add_wrk_analysis_page_and_inbound_status_migration.sql
@@ -1,4 +1,6 @@
 CREATE TABLE IF NOT EXISTS `asr_wrk_analysis` (
+  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '涓婚敭ID',
+  `wrk_log_id` bigint DEFAULT NULL COMMENT '宸ヤ綔鍘嗗彶ID',
   `wrk_no` int NOT NULL COMMENT '宸ヤ綔鍙�',
   `wms_wrk_no` varchar(64) DEFAULT NULL COMMENT 'WMS浠诲姟鍙�',
   `io_type` int DEFAULT NULL COMMENT '鍏ュ嚭搴撶被鍨�',
@@ -31,7 +33,9 @@
   `metric_completeness` varchar(16) DEFAULT 'PARTIAL' COMMENT '鏁版嵁瀹屾暣鎬�',
   `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '鍒涘缓鏃堕棿',
   `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '鏇存柊鏃堕棿',
-  PRIMARY KEY (`wrk_no`),
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `uk_wrk_analysis_wrk_log_id` (`wrk_log_id`),
+  KEY `idx_wrk_analysis_wrk_no` (`wrk_no`),
   KEY `idx_wrk_analysis_finish_time` (`finish_time`),
   KEY `idx_wrk_analysis_appe_time` (`appe_time`),
   KEY `idx_wrk_analysis_io_type` (`io_type`),
@@ -117,6 +121,7 @@
 WHERE `wrk_sts` = 2;
 
 INSERT INTO `asr_wrk_analysis` (
+    `wrk_log_id`,
     `wrk_no`,
     `wms_wrk_no`,
     `io_type`,
@@ -147,6 +152,7 @@
     `update_time`
 )
 SELECT
+    l.`id`,
     l.`wrk_no`,
     l.`wms_wrk_no`,
     l.`io_type`,
@@ -166,83 +172,192 @@
     END AS `total_duration_ms`,
     CASE WHEN l.`io_type` = 201 THEN 0 ELSE NULL END AS `station_duration_ms`,
     NULL AS `crane_duration_ms`,
-    CASE WHEN COALESCE(c.`fault_count`, 0) + COALESCE(d.`fault_count`, 0) + COALESCE(r.`fault_count`, 0) > 0 THEN 1 ELSE 0 END AS `has_fault`,
-    COALESCE(c.`fault_count`, 0) + COALESCE(d.`fault_count`, 0) + COALESCE(r.`fault_count`, 0) AS `fault_count`,
-    COALESCE(c.`fault_duration_ms`, 0) + COALESCE(d.`fault_duration_ms`, 0) + COALESCE(r.`fault_duration_ms`, 0) AS `fault_duration_ms`,
-    COALESCE(c.`fault_count`, 0) AS `crn_fault_count`,
-    COALESCE(c.`fault_duration_ms`, 0) AS `crn_fault_duration_ms`,
-    COALESCE(d.`fault_count`, 0) AS `dual_crn_fault_count`,
-    COALESCE(d.`fault_duration_ms`, 0) AS `dual_crn_fault_duration_ms`,
-    COALESCE(r.`fault_count`, 0) AS `rgv_fault_count`,
-    COALESCE(r.`fault_duration_ms`, 0) AS `rgv_fault_duration_ms`,
+    CASE WHEN (
+        COALESCE((
+            SELECT COUNT(*)
+            FROM `asr_bas_crnp_err_log` e
+            WHERE e.`wrk_no` = l.`wrk_no`
+              AND (e.`start_time` IS NULL OR e.`start_time` <= COALESCE(l.`modi_time`, l.`io_time`, l.`appe_time`))
+              AND COALESCE(e.`end_time`, e.`start_time`, COALESCE(l.`modi_time`, l.`io_time`, l.`appe_time`)) >= COALESCE(l.`appe_time`, l.`io_time`, l.`modi_time`)
+        ), 0)
+        + COALESCE((
+            SELECT COUNT(*)
+            FROM `asr_bas_dual_crnp_err_log` e
+            WHERE e.`wrk_no` = l.`wrk_no`
+              AND (e.`start_time` IS NULL OR e.`start_time` <= COALESCE(l.`modi_time`, l.`io_time`, l.`appe_time`))
+              AND COALESCE(e.`end_time`, e.`start_time`, COALESCE(l.`modi_time`, l.`io_time`, l.`appe_time`)) >= COALESCE(l.`appe_time`, l.`io_time`, l.`modi_time`)
+        ), 0)
+        + COALESCE((
+            SELECT COUNT(*)
+            FROM `asr_bas_rgv_err_log` e
+            WHERE e.`task_no` = l.`wrk_no`
+              AND (e.`start_time` IS NULL OR e.`start_time` <= COALESCE(l.`modi_time`, l.`io_time`, l.`appe_time`))
+              AND COALESCE(e.`end_time`, e.`start_time`, COALESCE(l.`modi_time`, l.`io_time`, l.`appe_time`)) >= COALESCE(l.`appe_time`, l.`io_time`, l.`modi_time`)
+        ), 0)
+    ) > 0 THEN 1 ELSE 0 END AS `has_fault`,
+    COALESCE((
+        SELECT COUNT(*)
+        FROM `asr_bas_crnp_err_log` e
+        WHERE e.`wrk_no` = l.`wrk_no`
+          AND (e.`start_time` IS NULL OR e.`start_time` <= COALESCE(l.`modi_time`, l.`io_time`, l.`appe_time`))
+          AND COALESCE(e.`end_time`, e.`start_time`, COALESCE(l.`modi_time`, l.`io_time`, l.`appe_time`)) >= COALESCE(l.`appe_time`, l.`io_time`, l.`modi_time`)
+    ), 0)
+    + COALESCE((
+        SELECT COUNT(*)
+        FROM `asr_bas_dual_crnp_err_log` e
+        WHERE e.`wrk_no` = l.`wrk_no`
+          AND (e.`start_time` IS NULL OR e.`start_time` <= COALESCE(l.`modi_time`, l.`io_time`, l.`appe_time`))
+          AND COALESCE(e.`end_time`, e.`start_time`, COALESCE(l.`modi_time`, l.`io_time`, l.`appe_time`)) >= COALESCE(l.`appe_time`, l.`io_time`, l.`modi_time`)
+    ), 0)
+    + COALESCE((
+        SELECT COUNT(*)
+        FROM `asr_bas_rgv_err_log` e
+        WHERE e.`task_no` = l.`wrk_no`
+          AND (e.`start_time` IS NULL OR e.`start_time` <= COALESCE(l.`modi_time`, l.`io_time`, l.`appe_time`))
+          AND COALESCE(e.`end_time`, e.`start_time`, COALESCE(l.`modi_time`, l.`io_time`, l.`appe_time`)) >= COALESCE(l.`appe_time`, l.`io_time`, l.`modi_time`)
+    ), 0) AS `fault_count`,
+    COALESCE((
+        SELECT SUM(
+            CASE
+                WHEN e.`start_time` IS NULL THEN 0
+                ELSE GREATEST(
+                    TIMESTAMPDIFF(
+                        MICROSECOND,
+                        GREATEST(e.`start_time`, COALESCE(l.`appe_time`, l.`io_time`, l.`modi_time`)),
+                        LEAST(COALESCE(e.`end_time`, COALESCE(l.`modi_time`, l.`io_time`, l.`appe_time`)), COALESCE(l.`modi_time`, l.`io_time`, l.`appe_time`))
+                    ),
+                    0
+                ) DIV 1000
+            END
+        )
+        FROM `asr_bas_crnp_err_log` e
+        WHERE e.`wrk_no` = l.`wrk_no`
+          AND (e.`start_time` IS NULL OR e.`start_time` <= COALESCE(l.`modi_time`, l.`io_time`, l.`appe_time`))
+          AND COALESCE(e.`end_time`, e.`start_time`, COALESCE(l.`modi_time`, l.`io_time`, l.`appe_time`)) >= COALESCE(l.`appe_time`, l.`io_time`, l.`modi_time`)
+    ), 0)
+    + COALESCE((
+        SELECT SUM(
+            CASE
+                WHEN e.`start_time` IS NULL THEN 0
+                ELSE GREATEST(
+                    TIMESTAMPDIFF(
+                        MICROSECOND,
+                        GREATEST(e.`start_time`, COALESCE(l.`appe_time`, l.`io_time`, l.`modi_time`)),
+                        LEAST(COALESCE(e.`end_time`, COALESCE(l.`modi_time`, l.`io_time`, l.`appe_time`)), COALESCE(l.`modi_time`, l.`io_time`, l.`appe_time`))
+                    ),
+                    0
+                ) DIV 1000
+            END
+        )
+        FROM `asr_bas_dual_crnp_err_log` e
+        WHERE e.`wrk_no` = l.`wrk_no`
+          AND (e.`start_time` IS NULL OR e.`start_time` <= COALESCE(l.`modi_time`, l.`io_time`, l.`appe_time`))
+          AND COALESCE(e.`end_time`, e.`start_time`, COALESCE(l.`modi_time`, l.`io_time`, l.`appe_time`)) >= COALESCE(l.`appe_time`, l.`io_time`, l.`modi_time`)
+    ), 0)
+    + COALESCE((
+        SELECT SUM(
+            CASE
+                WHEN e.`start_time` IS NULL THEN 0
+                ELSE GREATEST(
+                    TIMESTAMPDIFF(
+                        MICROSECOND,
+                        GREATEST(e.`start_time`, COALESCE(l.`appe_time`, l.`io_time`, l.`modi_time`)),
+                        LEAST(COALESCE(e.`end_time`, COALESCE(l.`modi_time`, l.`io_time`, l.`appe_time`)), COALESCE(l.`modi_time`, l.`io_time`, l.`appe_time`))
+                    ),
+                    0
+                ) DIV 1000
+            END
+        )
+        FROM `asr_bas_rgv_err_log` e
+        WHERE e.`task_no` = l.`wrk_no`
+          AND (e.`start_time` IS NULL OR e.`start_time` <= COALESCE(l.`modi_time`, l.`io_time`, l.`appe_time`))
+          AND COALESCE(e.`end_time`, e.`start_time`, COALESCE(l.`modi_time`, l.`io_time`, l.`appe_time`)) >= COALESCE(l.`appe_time`, l.`io_time`, l.`modi_time`)
+    ), 0) AS `fault_duration_ms`,
+    COALESCE((
+        SELECT COUNT(*)
+        FROM `asr_bas_crnp_err_log` e
+        WHERE e.`wrk_no` = l.`wrk_no`
+          AND (e.`start_time` IS NULL OR e.`start_time` <= COALESCE(l.`modi_time`, l.`io_time`, l.`appe_time`))
+          AND COALESCE(e.`end_time`, e.`start_time`, COALESCE(l.`modi_time`, l.`io_time`, l.`appe_time`)) >= COALESCE(l.`appe_time`, l.`io_time`, l.`modi_time`)
+    ), 0) AS `crn_fault_count`,
+    COALESCE((
+        SELECT SUM(
+            CASE
+                WHEN e.`start_time` IS NULL THEN 0
+                ELSE GREATEST(
+                    TIMESTAMPDIFF(
+                        MICROSECOND,
+                        GREATEST(e.`start_time`, COALESCE(l.`appe_time`, l.`io_time`, l.`modi_time`)),
+                        LEAST(COALESCE(e.`end_time`, COALESCE(l.`modi_time`, l.`io_time`, l.`appe_time`)), COALESCE(l.`modi_time`, l.`io_time`, l.`appe_time`))
+                    ),
+                    0
+                ) DIV 1000
+            END
+        )
+        FROM `asr_bas_crnp_err_log` e
+        WHERE e.`wrk_no` = l.`wrk_no`
+          AND (e.`start_time` IS NULL OR e.`start_time` <= COALESCE(l.`modi_time`, l.`io_time`, l.`appe_time`))
+          AND COALESCE(e.`end_time`, e.`start_time`, COALESCE(l.`modi_time`, l.`io_time`, l.`appe_time`)) >= COALESCE(l.`appe_time`, l.`io_time`, l.`modi_time`)
+    ), 0) AS `crn_fault_duration_ms`,
+    COALESCE((
+        SELECT COUNT(*)
+        FROM `asr_bas_dual_crnp_err_log` e
+        WHERE e.`wrk_no` = l.`wrk_no`
+          AND (e.`start_time` IS NULL OR e.`start_time` <= COALESCE(l.`modi_time`, l.`io_time`, l.`appe_time`))
+          AND COALESCE(e.`end_time`, e.`start_time`, COALESCE(l.`modi_time`, l.`io_time`, l.`appe_time`)) >= COALESCE(l.`appe_time`, l.`io_time`, l.`modi_time`)
+    ), 0) AS `dual_crn_fault_count`,
+    COALESCE((
+        SELECT SUM(
+            CASE
+                WHEN e.`start_time` IS NULL THEN 0
+                ELSE GREATEST(
+                    TIMESTAMPDIFF(
+                        MICROSECOND,
+                        GREATEST(e.`start_time`, COALESCE(l.`appe_time`, l.`io_time`, l.`modi_time`)),
+                        LEAST(COALESCE(e.`end_time`, COALESCE(l.`modi_time`, l.`io_time`, l.`appe_time`)), COALESCE(l.`modi_time`, l.`io_time`, l.`appe_time`))
+                    ),
+                    0
+                ) DIV 1000
+            END
+        )
+        FROM `asr_bas_dual_crnp_err_log` e
+        WHERE e.`wrk_no` = l.`wrk_no`
+          AND (e.`start_time` IS NULL OR e.`start_time` <= COALESCE(l.`modi_time`, l.`io_time`, l.`appe_time`))
+          AND COALESCE(e.`end_time`, e.`start_time`, COALESCE(l.`modi_time`, l.`io_time`, l.`appe_time`)) >= COALESCE(l.`appe_time`, l.`io_time`, l.`modi_time`)
+    ), 0) AS `dual_crn_fault_duration_ms`,
+    COALESCE((
+        SELECT COUNT(*)
+        FROM `asr_bas_rgv_err_log` e
+        WHERE e.`task_no` = l.`wrk_no`
+          AND (e.`start_time` IS NULL OR e.`start_time` <= COALESCE(l.`modi_time`, l.`io_time`, l.`appe_time`))
+          AND COALESCE(e.`end_time`, e.`start_time`, COALESCE(l.`modi_time`, l.`io_time`, l.`appe_time`)) >= COALESCE(l.`appe_time`, l.`io_time`, l.`modi_time`)
+    ), 0) AS `rgv_fault_count`,
+    COALESCE((
+        SELECT SUM(
+            CASE
+                WHEN e.`start_time` IS NULL THEN 0
+                ELSE GREATEST(
+                    TIMESTAMPDIFF(
+                        MICROSECOND,
+                        GREATEST(e.`start_time`, COALESCE(l.`appe_time`, l.`io_time`, l.`modi_time`)),
+                        LEAST(COALESCE(e.`end_time`, COALESCE(l.`modi_time`, l.`io_time`, l.`appe_time`)), COALESCE(l.`modi_time`, l.`io_time`, l.`appe_time`))
+                    ),
+                    0
+                ) DIV 1000
+            END
+        )
+        FROM `asr_bas_rgv_err_log` e
+        WHERE e.`task_no` = l.`wrk_no`
+          AND (e.`start_time` IS NULL OR e.`start_time` <= COALESCE(l.`modi_time`, l.`io_time`, l.`appe_time`))
+          AND COALESCE(e.`end_time`, e.`start_time`, COALESCE(l.`modi_time`, l.`io_time`, l.`appe_time`)) >= COALESCE(l.`appe_time`, l.`io_time`, l.`modi_time`)
+    ), 0) AS `rgv_fault_duration_ms`,
     'PARTIAL' AS `metric_completeness`,
     NOW(),
     NOW()
 FROM `asr_wrk_mast_log` l
-LEFT JOIN (
-    SELECT
-        e.`wrk_no`,
-        COUNT(*) AS `fault_count`,
-        SUM(
-            CASE
-                WHEN e.`start_time` IS NULL THEN 0
-                ELSE GREATEST(
-                    TIMESTAMPDIFF(
-                        MICROSECOND,
-                        e.`start_time`,
-                        COALESCE(e.`end_time`, wl.`modi_time`, wl.`io_time`, wl.`appe_time`, e.`start_time`)
-                    ),
-                    0
-                ) DIV 1000
-            END
-        ) AS `fault_duration_ms`
-    FROM `asr_bas_crnp_err_log` e
-    LEFT JOIN `asr_wrk_mast_log` wl ON wl.`wrk_no` = e.`wrk_no`
-    GROUP BY e.`wrk_no`
-) c ON c.`wrk_no` = l.`wrk_no`
-LEFT JOIN (
-    SELECT
-        e.`wrk_no`,
-        COUNT(*) AS `fault_count`,
-        SUM(
-            CASE
-                WHEN e.`start_time` IS NULL THEN 0
-                ELSE GREATEST(
-                    TIMESTAMPDIFF(
-                        MICROSECOND,
-                        e.`start_time`,
-                        COALESCE(e.`end_time`, wl.`modi_time`, wl.`io_time`, wl.`appe_time`, e.`start_time`)
-                    ),
-                    0
-                ) DIV 1000
-            END
-        ) AS `fault_duration_ms`
-    FROM `asr_bas_dual_crnp_err_log` e
-    LEFT JOIN `asr_wrk_mast_log` wl ON wl.`wrk_no` = e.`wrk_no`
-    GROUP BY e.`wrk_no`
-) d ON d.`wrk_no` = l.`wrk_no`
-LEFT JOIN (
-    SELECT
-        e.`task_no` AS `wrk_no`,
-        COUNT(*) AS `fault_count`,
-        SUM(
-            CASE
-                WHEN e.`start_time` IS NULL THEN 0
-                ELSE GREATEST(
-                    TIMESTAMPDIFF(
-                        MICROSECOND,
-                        e.`start_time`,
-                        COALESCE(e.`end_time`, wl.`modi_time`, wl.`io_time`, wl.`appe_time`, e.`start_time`)
-                    ),
-                    0
-                ) DIV 1000
-            END
-        ) AS `fault_duration_ms`
-    FROM `asr_bas_rgv_err_log` e
-    LEFT JOIN `asr_wrk_mast_log` wl ON wl.`wrk_no` = e.`task_no`
-    GROUP BY e.`task_no`
-) r ON r.`wrk_no` = l.`wrk_no`
 ON DUPLICATE KEY UPDATE
+    `wrk_log_id` = VALUES(`wrk_log_id`),
+    `wrk_no` = VALUES(`wrk_no`),
     `wms_wrk_no` = VALUES(`wms_wrk_no`),
     `io_type` = VALUES(`io_type`),
     `final_wrk_sts` = VALUES(`final_wrk_sts`),
diff --git a/src/main/resources/sql/20260331_add_station_err_log.sql b/src/main/resources/sql/20260331_add_station_err_log.sql
index be5f1fd..90d19cc 100644
--- a/src/main/resources/sql/20260331_add_station_err_log.sql
+++ b/src/main/resources/sql/20260331_add_station_err_log.sql
@@ -69,42 +69,75 @@
 DEALLOCATE PREPARE stmt_station_fault_duration;
 
 UPDATE `asr_wrk_analysis` a
-LEFT JOIN (
-    SELECT
-        e.`wrk_no`,
-        COUNT(*) AS `fault_count`,
-        SUM(
+SET a.`station_fault_count` = COALESCE((
+        SELECT COUNT(*)
+        FROM `asr_bas_station_err_log` e
+        WHERE e.`wrk_no` = a.`wrk_no`
+          AND (e.`start_time` IS NULL OR e.`start_time` <= COALESCE(a.`finish_time`, a.`update_time`, a.`appe_time`))
+          AND COALESCE(e.`end_time`, e.`start_time`, COALESCE(a.`finish_time`, a.`update_time`, a.`appe_time`)) >= COALESCE(a.`appe_time`, a.`finish_time`, a.`update_time`)
+    ), 0),
+    a.`station_fault_duration_ms` = COALESCE((
+        SELECT SUM(
             CASE
                 WHEN e.`start_time` IS NULL THEN 0
                 ELSE GREATEST(
                     TIMESTAMPDIFF(
                         MICROSECOND,
-                        e.`start_time`,
-                        COALESCE(e.`end_time`, wl.`modi_time`, wl.`io_time`, wl.`appe_time`, e.`start_time`)
+                        GREATEST(e.`start_time`, COALESCE(a.`appe_time`, a.`finish_time`, a.`update_time`)),
+                        LEAST(COALESCE(e.`end_time`, COALESCE(a.`finish_time`, a.`update_time`, a.`appe_time`)), COALESCE(a.`finish_time`, a.`update_time`, a.`appe_time`))
                     ),
                     0
                 ) DIV 1000
             END
-        ) AS `fault_duration_ms`
-    FROM `asr_bas_station_err_log` e
-    LEFT JOIN `asr_wrk_mast_log` wl ON wl.`wrk_no` = e.`wrk_no`
-    GROUP BY e.`wrk_no`
-) s ON s.`wrk_no` = a.`wrk_no`
-SET a.`station_fault_count` = COALESCE(s.`fault_count`, 0),
-    a.`station_fault_duration_ms` = COALESCE(s.`fault_duration_ms`, 0),
+        )
+        FROM `asr_bas_station_err_log` e
+        WHERE e.`wrk_no` = a.`wrk_no`
+          AND (e.`start_time` IS NULL OR e.`start_time` <= COALESCE(a.`finish_time`, a.`update_time`, a.`appe_time`))
+          AND COALESCE(e.`end_time`, e.`start_time`, COALESCE(a.`finish_time`, a.`update_time`, a.`appe_time`)) >= COALESCE(a.`appe_time`, a.`finish_time`, a.`update_time`)
+    ), 0),
     a.`fault_count` = COALESCE(a.`crn_fault_count`, 0)
         + COALESCE(a.`dual_crn_fault_count`, 0)
         + COALESCE(a.`rgv_fault_count`, 0)
-        + COALESCE(s.`fault_count`, 0),
+        + COALESCE((
+            SELECT COUNT(*)
+            FROM `asr_bas_station_err_log` e
+            WHERE e.`wrk_no` = a.`wrk_no`
+              AND (e.`start_time` IS NULL OR e.`start_time` <= COALESCE(a.`finish_time`, a.`update_time`, a.`appe_time`))
+              AND COALESCE(e.`end_time`, e.`start_time`, COALESCE(a.`finish_time`, a.`update_time`, a.`appe_time`)) >= COALESCE(a.`appe_time`, a.`finish_time`, a.`update_time`)
+        ), 0),
     a.`fault_duration_ms` = COALESCE(a.`crn_fault_duration_ms`, 0)
         + COALESCE(a.`dual_crn_fault_duration_ms`, 0)
         + COALESCE(a.`rgv_fault_duration_ms`, 0)
-        + COALESCE(s.`fault_duration_ms`, 0),
+        + COALESCE((
+            SELECT SUM(
+                CASE
+                    WHEN e.`start_time` IS NULL THEN 0
+                    ELSE GREATEST(
+                        TIMESTAMPDIFF(
+                            MICROSECOND,
+                            GREATEST(e.`start_time`, COALESCE(a.`appe_time`, a.`finish_time`, a.`update_time`)),
+                            LEAST(COALESCE(e.`end_time`, COALESCE(a.`finish_time`, a.`update_time`, a.`appe_time`)), COALESCE(a.`finish_time`, a.`update_time`, a.`appe_time`))
+                        ),
+                        0
+                    ) DIV 1000
+                END
+            )
+            FROM `asr_bas_station_err_log` e
+            WHERE e.`wrk_no` = a.`wrk_no`
+              AND (e.`start_time` IS NULL OR e.`start_time` <= COALESCE(a.`finish_time`, a.`update_time`, a.`appe_time`))
+              AND COALESCE(e.`end_time`, e.`start_time`, COALESCE(a.`finish_time`, a.`update_time`, a.`appe_time`)) >= COALESCE(a.`appe_time`, a.`finish_time`, a.`update_time`)
+        ), 0),
     a.`has_fault` = CASE
         WHEN COALESCE(a.`crn_fault_count`, 0)
             + COALESCE(a.`dual_crn_fault_count`, 0)
             + COALESCE(a.`rgv_fault_count`, 0)
-            + COALESCE(s.`fault_count`, 0) > 0 THEN 1
+            + COALESCE((
+                SELECT COUNT(*)
+                FROM `asr_bas_station_err_log` e
+                WHERE e.`wrk_no` = a.`wrk_no`
+                  AND (e.`start_time` IS NULL OR e.`start_time` <= COALESCE(a.`finish_time`, a.`update_time`, a.`appe_time`))
+                  AND COALESCE(e.`end_time`, e.`start_time`, COALESCE(a.`finish_time`, a.`update_time`, a.`appe_time`)) >= COALESCE(a.`appe_time`, a.`finish_time`, a.`update_time`)
+            ), 0) > 0 THEN 1
         ELSE 0
     END,
     a.`update_time` = NOW()
diff --git a/src/main/resources/sql/20260404_fix_wrk_analysis_pk_and_log_link.sql b/src/main/resources/sql/20260404_fix_wrk_analysis_pk_and_log_link.sql
new file mode 100644
index 0000000..157247c
--- /dev/null
+++ b/src/main/resources/sql/20260404_fix_wrk_analysis_pk_and_log_link.sql
@@ -0,0 +1,47 @@
+DROP TABLE IF EXISTS `asr_wrk_analysis`;
+
+CREATE TABLE `asr_wrk_analysis` (
+  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '涓婚敭ID',
+  `wrk_log_id` bigint DEFAULT NULL COMMENT '宸ヤ綔鍘嗗彶ID',
+  `wrk_no` int NOT NULL COMMENT '宸ヤ綔鍙�',
+  `wms_wrk_no` varchar(64) DEFAULT NULL COMMENT 'WMS浠诲姟鍙�',
+  `io_type` int DEFAULT NULL COMMENT '鍏ュ嚭搴撶被鍨�',
+  `final_wrk_sts` bigint DEFAULT NULL COMMENT '鏈�缁堝伐浣滅姸鎬�',
+  `source_sta_no` int DEFAULT NULL COMMENT '婧愮珯',
+  `sta_no` int DEFAULT NULL COMMENT '鐩爣绔�',
+  `source_loc_no` varchar(64) DEFAULT NULL COMMENT '婧愬簱浣�',
+  `loc_no` varchar(64) DEFAULT NULL COMMENT '鐩爣搴撲綅',
+  `crn_no` int DEFAULT NULL COMMENT '鍫嗗灈鏈哄彿',
+  `dual_crn_no` int DEFAULT NULL COMMENT '鍙屽伐浣嶅爢鍨涙満鍙�',
+  `rgv_no` int DEFAULT NULL COMMENT 'RGV鍙�',
+  `appe_time` datetime DEFAULT NULL COMMENT '鍒涘缓鏃堕棿',
+  `finish_time` datetime DEFAULT NULL COMMENT '瀹屾垚鏃堕棿',
+  `total_duration_ms` bigint DEFAULT NULL COMMENT '鎬昏�楁椂姣',
+  `station_start_time` datetime DEFAULT NULL COMMENT '绔欑偣寮�濮嬫椂闂�',
+  `station_end_time` datetime DEFAULT NULL COMMENT '绔欑偣缁撴潫鏃堕棿',
+  `station_duration_ms` bigint DEFAULT NULL COMMENT '绔欑偣鑰楁椂姣',
+  `crane_start_time` datetime DEFAULT NULL COMMENT '鍫嗗灈鏈哄紑濮嬫椂闂�',
+  `crane_end_time` datetime DEFAULT NULL COMMENT '鍫嗗灈鏈虹粨鏉熸椂闂�',
+  `crane_duration_ms` bigint DEFAULT NULL COMMENT '鍫嗗灈鏈鸿�楁椂姣',
+  `has_fault` int DEFAULT '0' COMMENT '鏄惁鏁呴殰',
+  `fault_count` int DEFAULT '0' COMMENT '鏁呴殰娆℃暟',
+  `fault_duration_ms` bigint DEFAULT '0' COMMENT '鏁呴殰鑰楁椂姣',
+  `crn_fault_count` int DEFAULT '0' COMMENT '鍗曞爢鍨涙満鏁呴殰娆℃暟',
+  `crn_fault_duration_ms` bigint DEFAULT '0' COMMENT '鍗曞爢鍨涙満鏁呴殰鑰楁椂姣',
+  `dual_crn_fault_count` int DEFAULT '0' COMMENT '鍙屽伐浣嶅爢鍨涙満鏁呴殰娆℃暟',
+  `dual_crn_fault_duration_ms` bigint DEFAULT '0' COMMENT '鍙屽伐浣嶅爢鍨涙満鏁呴殰鑰楁椂姣',
+  `rgv_fault_count` int DEFAULT '0' COMMENT 'RGV鏁呴殰娆℃暟',
+  `rgv_fault_duration_ms` bigint DEFAULT '0' COMMENT 'RGV鏁呴殰鑰楁椂姣',
+  `station_fault_count` int DEFAULT '0' COMMENT '杈撻�佺珯鐐规晠闅滄鏁�',
+  `station_fault_duration_ms` bigint DEFAULT '0' COMMENT '杈撻�佺珯鐐规晠闅滆�楁椂姣',
+  `metric_completeness` varchar(16) DEFAULT 'PARTIAL' COMMENT '鏁版嵁瀹屾暣鎬�',
+  `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '鍒涘缓鏃堕棿',
+  `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '鏇存柊鏃堕棿',
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `uk_wrk_analysis_wrk_log_id` (`wrk_log_id`),
+  KEY `idx_wrk_analysis_wrk_no` (`wrk_no`),
+  KEY `idx_wrk_analysis_finish_time` (`finish_time`),
+  KEY `idx_wrk_analysis_appe_time` (`appe_time`),
+  KEY `idx_wrk_analysis_io_type` (`io_type`),
+  KEY `idx_wrk_analysis_final_wrk_sts` (`final_wrk_sts`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='浠诲姟鎵ц鍒嗘瀽姹囨�昏〃';
diff --git a/src/main/webapp/static/js/wrkAnalysis/wrkAnalysis.js b/src/main/webapp/static/js/wrkAnalysis/wrkAnalysis.js
index 1844fe8..ee5fe57 100644
--- a/src/main/webapp/static/js/wrkAnalysis/wrkAnalysis.js
+++ b/src/main/webapp/static/js/wrkAnalysis/wrkAnalysis.js
@@ -70,7 +70,7 @@
                 listLoading: false,
                 analyzeLoading: false,
                 exportingPdf: false,
-                selectedWrkNoMap: {},
+                selectedLogIdMap: {},
                 analysis: createEmptyAnalysis(),
                 analysisReady: false,
                 charts: {
@@ -83,8 +83,8 @@
             };
         },
         computed: {
-            selectedWrkNos: function () {
-                return Object.keys(this.selectedWrkNoMap).map(function (key) {
+            selectedLogIds: function () {
+                return Object.keys(this.selectedLogIdMap).map(function (key) {
                     return Number(key);
                 }).filter(function (value) {
                     return !!value;
@@ -181,7 +181,7 @@
                 this.filters = createDefaultFilters();
                 this.currentPage = 1;
                 this.pageSize = 20;
-                this.selectedWrkNoMap = {};
+                this.selectedLogIdMap = {};
                 this.analysis = createEmptyAnalysis();
                 this.analysisReady = false;
                 this.disposeCharts();
@@ -204,24 +204,24 @@
                 }
                 table.clearSelection();
                 (this.tableData || []).forEach(function (row) {
-                    if (self.selectedWrkNoMap[row.wrkNo]) {
+                    if (self.selectedLogIdMap[row.logId]) {
                         table.toggleRowSelection(row, true);
                     }
                 });
             },
             syncCurrentPageSelection: function (selection) {
-                var nextMap = Object.assign({}, this.selectedWrkNoMap);
+                var nextMap = Object.assign({}, this.selectedLogIdMap);
                 var selectedMap = {};
                 (selection || []).forEach(function (row) {
-                    selectedMap[row.wrkNo] = true;
+                    selectedMap[row.logId] = true;
                 });
                 (this.tableData || []).forEach(function (row) {
-                    delete nextMap[row.wrkNo];
+                    delete nextMap[row.logId];
                 });
                 Object.keys(selectedMap).forEach(function (key) {
                     nextMap[key] = true;
                 });
-                this.selectedWrkNoMap = nextMap;
+                this.selectedLogIdMap = nextMap;
             },
             runAnalysis: function () {
                 var self = this;
@@ -234,11 +234,11 @@
                     deviceType: this.filters.deviceType
                 };
                 if (this.filters.mode === "TASK") {
-                    if (!this.selectedWrkNos.length) {
+                    if (!this.selectedLogIds.length) {
                         this.$message.warning("璇峰厛鍕鹃�夎鍒嗘瀽鐨勪换鍔�");
                         return;
                     }
-                    request.wrkNos = this.selectedWrkNos;
+                    request.wrkLogIds = this.selectedLogIds;
                     request.timeField = this.filters.timeField;
                 } else {
                     if (!this.filters.timeRange || this.filters.timeRange.length !== 2) {
diff --git a/src/main/webapp/views/wrkAnalysis/wrkAnalysis.html b/src/main/webapp/views/wrkAnalysis/wrkAnalysis.html
index 1cda19f..aead7e1 100644
--- a/src/main/webapp/views/wrkAnalysis/wrkAnalysis.html
+++ b/src/main/webapp/views/wrkAnalysis/wrkAnalysis.html
@@ -308,7 +308,7 @@
     <section class="panel">
         <div class="panel-head">
             <div class="panel-title">鍘嗗彶浠诲姟鍒楄〃</div>
-            <div class="selection-meta">宸查�変换鍔� {{ selectedWrkNos.length }} 鏉�</div>
+            <div class="selection-meta">宸查�変换鍔� {{ selectedLogIds.length }} 鏉�</div>
         </div>
         <div class="panel-body">
             <el-table
@@ -317,7 +317,7 @@
                     border
                     stripe
                     size="mini"
-                    row-key="wrkNo"
+                    row-key="logId"
                     @selection-change="syncCurrentPageSelection"
                     v-loading="listLoading"
                     style="width: 100%;">
@@ -488,7 +488,7 @@
                             </tr>
                             </thead>
                             <tbody>
-                            <tr v-for="row in analysis.detail" :key="'pdf-' + row.wrkNo">
+                            <tr v-for="row in analysis.detail" :key="'pdf-' + row.rowKey">
                                 <td>{{ row.wrkNo }}</td>
                                 <td>{{ row.wmsWrkNo }}</td>
                                 <td>{{ row.ioType$ }}</td>

--
Gitblit v1.9.1