Junjie
11 小时以前 8027c8e2e0b5c559da612b187031dd6fd82d9bc7
#任务分析异常修复
1个文件已添加
11个文件已修改
669 ■■■■ 已修改文件
src/main/java/com/zy/asrs/entity/WrkAnalysis.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/service/WrkAnalysisService.java 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/service/WrkMastLogService.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/service/impl/WrkAnalysisServiceImpl.java 209 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/service/impl/WrkMastLogServiceImpl.java 9 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/task/WrkMastScheduler.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/WrkAnalysisMapper.xml 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/sql/20260322_add_wrk_analysis_page_and_inbound_status_migration.sql 261 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/sql/20260331_add_station_err_log.sql 67 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/sql/20260404_fix_wrk_analysis_pk_and_log_link.sql 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/webapp/static/js/wrkAnalysis/wrkAnalysis.js 22 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/webapp/views/wrkAnalysis/wrkAnalysis.html 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
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任务号")
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();
src/main/java/com/zy/asrs/service/WrkMastLogService.java
@@ -7,4 +7,6 @@
    boolean save(Integer wrkNo);
    WrkMastLog saveRecord(Integer wrkNo);
}
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;
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;
    }
}
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())) {
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" />
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`),
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()
src/main/resources/sql/20260404_fix_wrk_analysis_pk_and_log_link.sql
New file
@@ -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='任务执行分析汇总表';
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) {
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>