diff --git a/cameleer3-server-core/src/main/java/com/cameleer3/server/core/detail/DetailService.java b/cameleer3-server-core/src/main/java/com/cameleer3/server/core/detail/DetailService.java
index 27dc39a8..7f6b31ce 100644
--- a/cameleer3-server-core/src/main/java/com/cameleer3/server/core/detail/DetailService.java
+++ b/cameleer3-server-core/src/main/java/com/cameleer3/server/core/detail/DetailService.java
@@ -1,104 +1,61 @@
package com.cameleer3.server.core.detail;
-import com.cameleer3.server.core.storage.ExecutionRepository;
+import com.cameleer3.server.core.storage.ExecutionStore;
+import com.cameleer3.server.core.storage.ExecutionStore.ProcessorRecord;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Optional;
+import java.util.*;
-/**
- * Provides execution detail with reconstructed processor tree.
- *
- * This is a plain class (no Spring annotations) -- it lives in the core module
- * and is wired as a bean by the app module configuration.
- */
public class DetailService {
- private final ExecutionRepository repository;
+ private final ExecutionStore executionStore;
- public DetailService(ExecutionRepository repository) {
- this.repository = repository;
+ public DetailService(ExecutionStore executionStore) {
+ this.executionStore = executionStore;
}
- /**
- * Get the full detail of a route execution, including the nested processor tree.
- *
- * @param executionId the execution ID to look up
- * @return the execution detail, or empty if not found
- */
public Optional getDetail(String executionId) {
- return repository.findRawById(executionId)
- .map(this::toDetail);
+ return executionStore.findById(executionId)
+ .map(exec -> {
+ List processors = executionStore.findProcessors(executionId);
+ List roots = buildTree(processors);
+ return new ExecutionDetail(
+ exec.executionId(), exec.routeId(), exec.agentId(),
+ exec.status(), exec.startTime(), exec.endTime(),
+ exec.durationMs() != null ? exec.durationMs() : 0L,
+ exec.correlationId(), exec.exchangeId(),
+ exec.errorMessage(), exec.errorStacktrace(),
+ exec.diagramContentHash(), roots
+ );
+ });
}
- private ExecutionDetail toDetail(RawExecutionRow row) {
- List roots = reconstructTree(
- row.processorIds(),
- row.processorTypes(),
- row.processorStatuses(),
- row.processorStarts(),
- row.processorEnds(),
- row.processorDurations(),
- row.processorDiagramNodeIds(),
- row.processorErrorMessages(),
- row.processorErrorStacktraces(),
- row.processorDepths(),
- row.processorParentIndexes()
- );
+ List buildTree(List processors) {
+ if (processors.isEmpty()) return List.of();
- return new ExecutionDetail(
- row.executionId(),
- row.routeId(),
- row.agentId(),
- row.status(),
- row.startTime(),
- row.endTime(),
- row.durationMs(),
- row.correlationId(),
- row.exchangeId(),
- row.errorMessage(),
- row.errorStackTrace(),
- row.diagramContentHash(),
- roots
- );
- }
-
- /**
- * Reconstruct the nested processor tree from flat parallel arrays.
- *
- * Uses parentIndexes to wire children: parentIndex == -1 means the node is a root.
- * Otherwise, parentIndex is the array index of the parent node.
- */
- List reconstructTree(
- String[] ids, String[] types, String[] statuses,
- java.time.Instant[] starts, java.time.Instant[] ends, long[] durations,
- String[] diagramNodeIds, String[] errorMessages, String[] errorStacktraces,
- int[] depths, int[] parentIndexes) {
-
- if (ids == null || ids.length == 0) {
- return List.of();
- }
-
- int len = ids.length;
- ProcessorNode[] nodes = new ProcessorNode[len];
-
- for (int i = 0; i < len; i++) {
- nodes[i] = new ProcessorNode(
- ids[i], types[i], statuses[i],
- starts[i], ends[i], durations[i],
- diagramNodeIds[i], errorMessages[i], errorStacktraces[i]
- );
+ Map nodeMap = new LinkedHashMap<>();
+ for (ProcessorRecord p : processors) {
+ nodeMap.put(p.processorId(), new ProcessorNode(
+ p.processorId(), p.processorType(), p.status(),
+ p.startTime(), p.endTime(),
+ p.durationMs() != null ? p.durationMs() : 0L,
+ p.diagramNodeId(), p.errorMessage(), p.errorStacktrace()
+ ));
}
List roots = new ArrayList<>();
- for (int i = 0; i < len; i++) {
- if (parentIndexes[i] == -1) {
- roots.add(nodes[i]);
+ for (ProcessorRecord p : processors) {
+ ProcessorNode node = nodeMap.get(p.processorId());
+ if (p.parentProcessorId() == null) {
+ roots.add(node);
} else {
- nodes[parentIndexes[i]].addChild(nodes[i]);
+ ProcessorNode parent = nodeMap.get(p.parentProcessorId());
+ if (parent != null) {
+ parent.addChild(node);
+ } else {
+ roots.add(node); // orphan safety
+ }
}
}
-
return roots;
}
}