package com.zy.core.network.fake; import com.zy.asrs.domain.vo.FakeTaskTraceEventVo; import com.zy.asrs.domain.vo.FakeTaskTraceVo; import org.springframework.stereotype.Component; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @Component public class FakeTaskTraceRegistry { private static final int MAX_EVENT_COUNT = 200; private static final long TERMINAL_KEEP_MS = 3000L; private final Map taskStateMap = new ConcurrentHashMap(); public void record(Integer taskNo, String threadImpl, String status, Integer startStationId, Integer currentStationId, Integer finalTargetStationId, Integer blockedStationId, List stitchedPathStationIds, List passedStationIds, List pendingStationIds, List latestAppendedPath, String eventType, String message, Map details, boolean terminal) { if (taskNo == null || taskNo <= 0) { return; } cleanupExpired(); TraceTaskState taskState = taskStateMap.computeIfAbsent(taskNo, TraceTaskState::new); taskState.apply(threadImpl, status, startStationId, currentStationId, finalTargetStationId, blockedStationId, stitchedPathStationIds, passedStationIds, pendingStationIds, latestAppendedPath, eventType, message, details, terminal); } public List listActiveTraces() { cleanupExpired(); List result = new ArrayList(); for (TraceTaskState taskState : taskStateMap.values()) { result.add(taskState.toVo()); } Collections.sort(result, new Comparator() { @Override public int compare(FakeTaskTraceVo o1, FakeTaskTraceVo o2) { long v1 = o1.getUpdatedAt() == null ? 0L : o1.getUpdatedAt(); long v2 = o2.getUpdatedAt() == null ? 0L : o2.getUpdatedAt(); return Long.compare(v2, v1); } }); return result; } private void cleanupExpired() { long now = System.currentTimeMillis(); for (Map.Entry entry : taskStateMap.entrySet()) { TraceTaskState state = entry.getValue(); if (state != null && state.shouldRemove(now)) { taskStateMap.remove(entry.getKey(), state); } } } private static List copyIntegerList(List source) { List result = new ArrayList(); if (source == null) { return result; } for (Integer item : source) { if (item != null) { result.add(item); } } return result; } private static Map copyDetails(Map source) { Map result = new LinkedHashMap(); if (source == null || source.isEmpty()) { return result; } for (Map.Entry entry : source.entrySet()) { Object value = entry.getValue(); if (value instanceof List) { result.put(entry.getKey(), new ArrayList((List) value)); } else if (value instanceof Map) { result.put(entry.getKey(), new LinkedHashMap((Map) value)); } else { result.put(entry.getKey(), value); } } return result; } private static class TraceTaskState { private final Integer taskNo; private String threadImpl; private String status = "WAITING"; private Integer startStationId; private Integer currentStationId; private Integer finalTargetStationId; private Integer blockedStationId; private List stitchedPathStationIds = new ArrayList(); private List passedStationIds = new ArrayList(); private List pendingStationIds = new ArrayList(); private List latestAppendedPath = new ArrayList(); private final List events = new ArrayList(); private Long updatedAt = System.currentTimeMillis(); private Long terminalExpireAt; private TraceTaskState(Integer taskNo) { this.taskNo = taskNo; } private synchronized void apply(String threadImpl, String status, Integer startStationId, Integer currentStationId, Integer finalTargetStationId, Integer blockedStationId, List stitchedPathStationIds, List passedStationIds, List pendingStationIds, List latestAppendedPath, String eventType, String message, Map details, boolean terminal) { this.threadImpl = threadImpl; this.status = status == null ? "WAITING" : status; this.startStationId = startStationId; this.currentStationId = currentStationId; this.finalTargetStationId = finalTargetStationId; this.blockedStationId = blockedStationId; this.stitchedPathStationIds = copyIntegerList(stitchedPathStationIds); this.passedStationIds = copyIntegerList(passedStationIds); this.pendingStationIds = copyIntegerList(pendingStationIds); this.latestAppendedPath = copyIntegerList(latestAppendedPath); long now = System.currentTimeMillis(); this.updatedAt = now; if (eventType != null) { FakeTaskTraceEventVo event = new FakeTaskTraceEventVo(); event.setTimestamp(now); event.setEventType(eventType); event.setMessage(message); event.setStatus(this.status); event.setCurrentStationId(this.currentStationId); event.setTargetStationId(this.finalTargetStationId); event.setDetails(copyDetails(details)); this.events.add(event); if (this.events.size() > MAX_EVENT_COUNT) { this.events.remove(0); } } this.terminalExpireAt = terminal ? now + TERMINAL_KEEP_MS : null; } private synchronized boolean shouldRemove(long now) { return terminalExpireAt != null && terminalExpireAt <= now; } private synchronized FakeTaskTraceVo toVo() { FakeTaskTraceVo vo = new FakeTaskTraceVo(); vo.setTaskNo(taskNo); vo.setThreadImpl(threadImpl); vo.setStatus(status); vo.setStartStationId(startStationId); vo.setCurrentStationId(currentStationId); vo.setFinalTargetStationId(finalTargetStationId); vo.setBlockedStationId(blockedStationId); vo.setStitchedPathStationIds(copyIntegerList(stitchedPathStationIds)); vo.setPassedStationIds(copyIntegerList(passedStationIds)); vo.setPendingStationIds(copyIntegerList(pendingStationIds)); vo.setLatestAppendedPath(copyIntegerList(latestAppendedPath)); vo.setUpdatedAt(updatedAt); vo.setEvents(new ArrayList(events)); return vo; } } }