refactor: DetailService uses ExecutionStore, tree built from parentProcessorId
This commit is contained in:
@@ -1,104 +1,61 @@
|
|||||||
package com.cameleer3.server.core.detail;
|
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.*;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Provides execution detail with reconstructed processor tree.
|
|
||||||
* <p>
|
|
||||||
* 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 {
|
public class DetailService {
|
||||||
|
|
||||||
private final ExecutionRepository repository;
|
private final ExecutionStore executionStore;
|
||||||
|
|
||||||
public DetailService(ExecutionRepository repository) {
|
public DetailService(ExecutionStore executionStore) {
|
||||||
this.repository = repository;
|
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<ExecutionDetail> getDetail(String executionId) {
|
public Optional<ExecutionDetail> getDetail(String executionId) {
|
||||||
return repository.findRawById(executionId)
|
return executionStore.findById(executionId)
|
||||||
.map(this::toDetail);
|
.map(exec -> {
|
||||||
|
List<ProcessorRecord> processors = executionStore.findProcessors(executionId);
|
||||||
|
List<ProcessorNode> 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<ProcessorNode> buildTree(List<ProcessorRecord> processors) {
|
||||||
List<ProcessorNode> roots = reconstructTree(
|
if (processors.isEmpty()) return List.of();
|
||||||
row.processorIds(),
|
|
||||||
row.processorTypes(),
|
|
||||||
row.processorStatuses(),
|
|
||||||
row.processorStarts(),
|
|
||||||
row.processorEnds(),
|
|
||||||
row.processorDurations(),
|
|
||||||
row.processorDiagramNodeIds(),
|
|
||||||
row.processorErrorMessages(),
|
|
||||||
row.processorErrorStacktraces(),
|
|
||||||
row.processorDepths(),
|
|
||||||
row.processorParentIndexes()
|
|
||||||
);
|
|
||||||
|
|
||||||
return new ExecutionDetail(
|
Map<String, ProcessorNode> nodeMap = new LinkedHashMap<>();
|
||||||
row.executionId(),
|
for (ProcessorRecord p : processors) {
|
||||||
row.routeId(),
|
nodeMap.put(p.processorId(), new ProcessorNode(
|
||||||
row.agentId(),
|
p.processorId(), p.processorType(), p.status(),
|
||||||
row.status(),
|
p.startTime(), p.endTime(),
|
||||||
row.startTime(),
|
p.durationMs() != null ? p.durationMs() : 0L,
|
||||||
row.endTime(),
|
p.diagramNodeId(), p.errorMessage(), p.errorStacktrace()
|
||||||
row.durationMs(),
|
));
|
||||||
row.correlationId(),
|
|
||||||
row.exchangeId(),
|
|
||||||
row.errorMessage(),
|
|
||||||
row.errorStackTrace(),
|
|
||||||
row.diagramContentHash(),
|
|
||||||
roots
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reconstruct the nested processor tree from flat parallel arrays.
|
|
||||||
* <p>
|
|
||||||
* Uses parentIndexes to wire children: parentIndex == -1 means the node is a root.
|
|
||||||
* Otherwise, parentIndex is the array index of the parent node.
|
|
||||||
*/
|
|
||||||
List<ProcessorNode> 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]
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
List<ProcessorNode> roots = new ArrayList<>();
|
List<ProcessorNode> roots = new ArrayList<>();
|
||||||
for (int i = 0; i < len; i++) {
|
for (ProcessorRecord p : processors) {
|
||||||
if (parentIndexes[i] == -1) {
|
ProcessorNode node = nodeMap.get(p.processorId());
|
||||||
roots.add(nodes[i]);
|
if (p.parentProcessorId() == null) {
|
||||||
|
roots.add(node);
|
||||||
} else {
|
} 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;
|
return roots;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user