#
Junjie
昨天 a0d8732c1d698b25850c0949f7b8967333d67d21
src/main/java/com/zy/core/trace/StationTaskTraceRegistry.java
@@ -152,6 +152,21 @@
        persistState(state);
    }
    public void updateLoopHint(Integer taskNo,
                               Boolean loopAlertActive,
                               String loopAlertType,
                               String loopAlertText,
                               Integer loopAlertCount,
                               Map<String, Object> details) {
        ensureCacheLoaded();
        TraceTaskState state = taskStateMap.get(taskNo);
        if (state == null) {
            return;
        }
        state.updateLoopHint(loopAlertActive, loopAlertType, loopAlertText, loopAlertCount, details);
        persistState(state);
    }
    public List<StationTaskTraceVo> listLatestTraces() {
        ensureCacheLoaded();
        cleanupExpired();
@@ -435,6 +450,10 @@
        private List<StationTaskTraceSegmentVo> segmentList = new ArrayList<>();
        private Integer issuedSegmentCount;
        private Integer totalSegmentCount;
        private Boolean loopAlertActive;
        private String loopAlertType;
        private String loopAlertText;
        private Integer loopAlertCount;
        private Long updatedAt;
        private Long terminalExpireAt;
        private List<StationTaskTraceEventVo> events = new ArrayList<>();
@@ -462,6 +481,10 @@
        private List<StationTaskTraceSegmentVo> segmentList = new ArrayList<>();
        private Integer issuedSegmentCount = 0;
        private Integer totalSegmentCount = 0;
        private Boolean loopAlertActive = Boolean.FALSE;
        private String loopAlertType;
        private String loopAlertText;
        private Integer loopAlertCount;
        private final List<StationTaskTraceEventVo> events = new ArrayList<>();
        private Long updatedAt = System.currentTimeMillis();
        private Long terminalExpireAt;
@@ -477,6 +500,7 @@
                                                            List<Integer> localPathStationIds,
                                                            List<StationTaskTraceSegmentVo> localSegmentList) {
            TraceRegistration registration = new TraceRegistration();
            boolean firstPlan = this.traceVersion == null || this.traceVersion <= 0;
            boolean rerouted = !isTerminalStatus(this.status) && this.traceVersion != null && this.traceVersion > 0;
            int nextTraceVersion = rerouted ? this.traceVersion + 1 : 1;
            int pathOffset = rerouted ? this.passedStationIds.size() : 0;
@@ -498,6 +522,9 @@
            this.latestIssuedSegmentPath = new ArrayList<>();
            this.status = rerouted ? STATUS_REROUTED : STATUS_WAITING;
            this.terminalExpireAt = null;
            if (firstPlan) {
                clearLoopHintState();
            }
            rebuildProgress(planCurrentStationId);
            this.updatedAt = System.currentTimeMillis();
@@ -507,6 +534,7 @@
            details.put("segmentCount", this.totalSegmentCount);
            details.put("pathOffset", pathOffset);
            details.put("currentStationId", this.currentStationId);
            appendLoopHintDetails(details);
            appendEvent(rerouted ? "REROUTED" : "PLAN_READY",
                    rerouted ? "输送任务路径已重算并续接轨迹" : "输送任务分段计划已建立",
                    details);
@@ -591,9 +619,6 @@
            }
            this.status = terminalStatus;
            this.blockedStationId = blockedStationId;
            if (shouldClearPathOnTerminal(terminalStatus)) {
                clearPathState();
            }
            this.updatedAt = System.currentTimeMillis();
            this.terminalExpireAt = this.updatedAt + TERMINAL_KEEP_MS;
@@ -605,20 +630,42 @@
            appendEvent(eventType, message, nextDetails);
        }
        private void clearPathState() {
            this.fullPathStationIds = new ArrayList<>();
            this.issuedStationIds = new ArrayList<>();
            this.passedStationIds = new ArrayList<>();
            this.pendingStationIds = new ArrayList<>();
            this.latestIssuedSegmentPath = new ArrayList<>();
            this.segmentList = new ArrayList<>();
            this.issuedSegmentCount = 0;
            this.totalSegmentCount = 0;
        }
        private synchronized void updateLoopHint(Boolean loopAlertActive,
                                                 String loopAlertType,
                                                 String loopAlertText,
                                                 Integer loopAlertCount,
                                                 Map<String, Object> details) {
            boolean active = Boolean.TRUE.equals(loopAlertActive)
                    && loopAlertCount != null
                    && loopAlertCount > 2
                    && loopAlertText != null
                    && !loopAlertText.trim().isEmpty();
            String nextType = active ? loopAlertType : null;
            String nextText = active ? loopAlertText.trim() : null;
            Integer nextCount = active ? loopAlertCount : null;
            boolean changed = !Objects.equals(this.loopAlertActive, active)
                    || !Objects.equals(this.loopAlertType, nextType)
                    || !Objects.equals(this.loopAlertText, nextText)
                    || !Objects.equals(this.loopAlertCount, nextCount);
            if (!changed) {
                return;
            }
        private boolean shouldClearPathOnTerminal(String terminalStatus) {
            return STATUS_BLOCKED.equals(terminalStatus)
                    || STATUS_CANCELLED.equals(terminalStatus);
            this.loopAlertActive = active;
            this.loopAlertType = nextType;
            this.loopAlertText = nextText;
            this.loopAlertCount = nextCount;
            this.updatedAt = System.currentTimeMillis();
            if (!active) {
                return;
            }
            Map<String, Object> nextDetails = copyDetails(details);
            nextDetails.put("loopAlertActive", Boolean.TRUE);
            nextDetails.put("loopAlertType", this.loopAlertType);
            nextDetails.put("loopAlertText", this.loopAlertText);
            nextDetails.put("loopAlertCount", this.loopAlertCount);
            appendEvent("LOOP_REPEAT_ALERT", this.loopAlertText, nextDetails);
        }
        private synchronized boolean shouldRemove(long now) {
@@ -647,6 +694,10 @@
            vo.setSegmentList(copySegmentListWithIssued(segmentList, issuedSegmentCount));
            vo.setIssuedSegmentCount(issuedSegmentCount);
            vo.setTotalSegmentCount(totalSegmentCount);
            vo.setLoopAlertActive(loopAlertActive);
            vo.setLoopAlertType(loopAlertType);
            vo.setLoopAlertText(loopAlertText);
            vo.setLoopAlertCount(loopAlertCount);
            vo.setUpdatedAt(updatedAt);
            vo.setEvents(copyEventList(events));
            return vo;
@@ -670,6 +721,10 @@
            snapshot.setSegmentList(copySegmentList(segmentList));
            snapshot.setIssuedSegmentCount(issuedSegmentCount);
            snapshot.setTotalSegmentCount(totalSegmentCount);
            snapshot.setLoopAlertActive(loopAlertActive);
            snapshot.setLoopAlertType(loopAlertType);
            snapshot.setLoopAlertText(loopAlertText);
            snapshot.setLoopAlertCount(loopAlertCount);
            snapshot.setUpdatedAt(updatedAt);
            snapshot.setTerminalExpireAt(terminalExpireAt);
            snapshot.setEvents(copyEventList(events));
@@ -693,6 +748,10 @@
            state.segmentList = copySegmentList(snapshot.getSegmentList());
            state.issuedSegmentCount = snapshot.getIssuedSegmentCount() == null ? 0 : snapshot.getIssuedSegmentCount();
            state.totalSegmentCount = snapshot.getTotalSegmentCount() == null ? state.segmentList.size() : snapshot.getTotalSegmentCount();
            state.loopAlertActive = Boolean.TRUE.equals(snapshot.getLoopAlertActive());
            state.loopAlertType = snapshot.getLoopAlertType();
            state.loopAlertText = snapshot.getLoopAlertText();
            state.loopAlertCount = snapshot.getLoopAlertCount();
            state.updatedAt = snapshot.getUpdatedAt() == null ? System.currentTimeMillis() : snapshot.getUpdatedAt();
            state.terminalExpireAt = snapshot.getTerminalExpireAt();
            state.events.clear();
@@ -743,6 +802,23 @@
            this.pendingStationIds = copyIntegerList(fullPath.subList(currentIndex + 1, fullPath.size()));
        }
        private void clearLoopHintState() {
            this.loopAlertActive = Boolean.FALSE;
            this.loopAlertType = null;
            this.loopAlertText = null;
            this.loopAlertCount = null;
        }
        private void appendLoopHintDetails(Map<String, Object> details) {
            if (details == null || !Boolean.TRUE.equals(this.loopAlertActive) || this.loopAlertCount == null || this.loopAlertCount <= 2) {
                return;
            }
            details.put("loopAlertActive", Boolean.TRUE);
            details.put("loopAlertType", this.loopAlertType);
            details.put("loopAlertText", this.loopAlertText);
            details.put("loopAlertCount", this.loopAlertCount);
        }
        private boolean acceptTraceVersion(Integer incomingTraceVersion) {
            return incomingTraceVersion != null
                    && this.traceVersion != null