Solon Flow 在提供 json/ xml 编排之后。还提供了一套极为丝滑的流程图 Fluent API。它让流程定义回归到程序员最熟悉的工具——代码。

通过 Fluent API，你可以像写 Java Stream 一样，通过链式调用快速勾勒出业务流转图。


### 1、环境准备

首先，确保你的 Java 项目中已经引入了 solon-flow 依赖：

```xml
<dependency>
    <groupId>org.noear</groupId>
    <artifactId>solon-flow</artifactId>
</dependency>
```

### 2、核心概念：Graph 与 GraphSpec

在动手写代码前，需要理解两个关键概念：

* Graph (图)：流程的最终实体，包含所有节点和连线的运行逻辑。
* GraphSpec (图规格/定义)：它是构建图的“蓝图”。在 v3.8 之后，它是 Fluent API 操作的核心对象。



### 3、 实战：手动编排一个“订单处理流”

假设我们有一个简单的订单流程：开始 -> 检查库存 -> 支付 -> 结束。

* 第一步：准备业务处理组件（）

Solon-flow 的设计理念是 **“逻辑与实现分离”**。 我们先定义具体的业务动作：

```java
import org.noear.solon.annotation.Component;
import org.noear.solon.flow.FlowContext;
import org.noear.solon.flow.Node;
import org.noear.solon.flow.TaskComponent;

// 容器 Bean 的形式（此处以 Solon 为例）
@Component("checkStock")
public class CheckStockProcessor implements TaskComponent {
    @Override
    public void run(FlowContext ctx, Node node) throws Throwable {
        System.out.println("--- 正在检查库存... ---");
        ctx.put("stock_ok", true); // 在上下文中存入结果
    }
}

//-------------

// 普通 Java 类形式
import org.noear.solon.annotation.Component;
import org.noear.solon.flow.FlowContext;
import org.noear.solon.flow.Node;
import org.noear.solon.flow.TaskComponent;


public class PayProcessor implements TaskComponent {
    @Override
    public void run(FlowContext context, Node node) throws Throwable {
        System.out.println("--- 支付成功！ ---");
    }
}
```


* 第二步：使用 Fluent API 编排流程

下面是本文的核心代码。我们通过 Graph.create 启动编排：

```java
import org.noear.solon.flow.Graph;

public class FlowConfig {

    public Graph buildOrderFlow() {
        // 使用 Fluent API 构建
        return Graph.create("order_flow", "订单处理流程", spec -> {
            // 1. 定义开始节点并连接到下一个
            spec.addStart("n1").title("开始").linkAdd("n2");

            // 2. 定义业务节点，绑定对应的 Bean 标识
            spec.addActivity("n2")
                    .title("库存检查")
                    .task("@checkStock") // 关联上面定义的 Bean（从容器获取）
                    .linkAdd("n3");

            spec.addActivity("n3")
                    .title("支付")
                    .task(new PayProcessor()) //硬编码方式（不用经过容器）
                    .linkAdd("n4");

            // 3. 定义结束节点
            spec.addEnd("n4").title("结束");
        });
    }
}
```


### 4、关键 API 深度解析


* `spec.addStart(id) / addEnd(id)`：定义流程的边界。每一个图必须有且只有一个 Start 节点，可以有多个 End 节点。
* `spec.addActivity(id)`：这是最常用的节点，代表一个具体任务。
* `.task("@beanName")`：这是核心联动点。@ 符号告诉 Solon 去容器中寻找对应的处理器。
* `.linkAdd(targetId)`：最简单的单向连线。它建立了一个从当前节点到目标节点的直接流转。


### 5、如何运行这个图？

有了 Graph 对象后，我们需要通过 FlowEngine 来触发它：

```java
import org.noear.solon.annotation.Component;
import org.noear.solon.annotation.Inject;
import org.noear.solon.flow.FlowContext;
import org.noear.solon.flow.FlowEngine;
import org.noear.solon.flow.Graph;

@Component
public class OrderService {
    @Inject
    FlowEngine flowEngine;

    public void processOrder() {
        // 1. 构建图（实际生产中通常会缓存此对象）
        Graph orderGraph = new FlowConfig().buildOrderFlow();

        // 2. 准备执行上下文（可以携带业务参数）
        FlowContext context = FlowContext.of("ORD-20231024");

        // 3. 执行流程
        flowEngine.eval(orderGraph, context);
    }
}
```


### 总结与预告


通过本文，你已经学会了如何不依赖任何配置文件，纯手工在内存中“画”出一个流程图。这种方式极大地提高了代码的可读性，并且让“流程定义”本身也成为了类型安全的代码。

但是，现实中的流程往往不是一条直线。 如果库存不足怎么办？如果金额巨大需要人工审批怎么办？

在后面的 **《逻辑之魂 —— 节点的“条件流转”与表达式》** 中，我们将引入“分支判断”，让你的 Graph 真正具备处理复杂业务的能力。
