使用 Spring WebFlux 构建递归数据结构

Building a Recursive Data Structure with Spring WebFlux

我有一个使用 Spring WebFlux 框架构建的 REST API,我有一个 returns 和 Flux<ChannelResponse> 的端点,其中 ChannelResponse是一个树状结构的对象,如下图:

public record ChannelResponse(
        long id,
        List<ChannelResponse> children
) {}

现在,我对反应式编程范式没有太多经验,但这就是我用同步逻辑实现这样一个端点的方式,这样每个顶级通道(那些没有父通道的通道)都会被转换进入 ChannelResponse 个对象的树中:

public Flux<ChannelResponse> getAll() {
    return channelRepository.findAllByParentChannelIdOrderByOrdinality(null)
        .map(channel -> getChannelDataRecursive(channel));
}

private FullChannelResponse getChannelDataRecursive(Channel channel) {
    var children = channelRepository.findAllByParentChannelIdOrderByOrdinality(channel.getId())
            .collectList().block();
    List<ChannelResponse> childData = new ArrayList<>();
    for (var child : children) {
        childData.add(getChannelDataRecursive(child));
    }
    return new ChannelResponse(channel.getId(), childData);
}

显然这在 WebFlux 中不起作用,因为我正在尝试进行阻塞存储库调用。

有没有办法以异步方式生成这种递归数据结构?或者,如果不是,我有哪些混合同步和异步代码来实现此结果的选项?

我能够以一种有点奇怪的方式解决它,即使用 expandDeep 运算符生成所有频道的平面列表,排序使得每个 parent 紧随其后他们的一套children。然后,我使用一种简单的同步递归方法将这些数据转换为所需的格式:

public Flux<ChannelResponse> getAll() {
    return channelRepository.findAllByParentChannelIdOrderByOrdinality(null)
            .expandDeep(channel -> channelRepository.findAllByParentChannelIdOrderByOrdinality(channel.getId()))
            .collectList()
            .flatMapMany(channels -> Flux.fromIterable(buildRecursiveChannelResponse(null, channels)));
}

public List<ChannelResponse> buildRecursiveChannelResponse(Long parent, List<Channel> channels) {
    List<ChannelResponse> responses = new ArrayList<>();
    while (!channels.isEmpty()) {
        Channel c = channels.get(0);
        if (!Objects.equals(c.getParentChannelId(), parent)) return responses;
        channels.remove(0);
        var children = buildRecursiveChannelResponse(c.getId(), channels);
        responses.add(new ChannelResponse(c.getId(), children));
    }
    return responses;
}

不过我觉得这个解决方案不是最优的,因为它需要非常具体地了解频道列表的排序方式才能生成树结构。如果有更简洁的方法,请告诉我。