solon-flow 采用开放式架构

* 支持流程驱动器（FlowDriver）定制
* “节点任务” 与“连接条件” 的描述没有固定格式，是由流程驱动器（FlowDriver）的处理决定
* 使用哪个（或定制的） FlowDriver 就采用哪种格式描述
 
就像 jdbc 的 Driver, mysql 和 pgsql 的语法各不同。

### 1、默认流程驱动器（SimpleFlowDriver 框架内置）的描述格式


|        | 示例                                                          | 
|--------|-------------------------------------------------------------|
| 节点任务描述   | 或者 `@task1`（`@`开头，任务组件风格。从容器查找任务组件）  <br/>或者 `#graph1`（`#`开头，跨图调用风格。从引擎里查找另一个图）<br/>或者 `$script1` （`$`开头，引用图的元数据作为脚本风格。从图的元数据查找）<br/>或者 `order.score=1;` （直接脚本风格。默认为完整的 Java 语法） | 
| 连接条件描述   | 或者 `@condition1`（@开头，条件组件风格。从容器查找条件组件）<br/>或者 `user_id > 12`（直接表达式风格，要求结果为布尔值。默认为 SnEL 表达式语法）                     | 

* 任务组件、条件组件风格

以`@`开头，表过调用对应名字的组件。

```java
@Component("task1") //任务组件
public class TaskComponentImpl implements TaskComponent {

    @Override
    public void run(FlowContext context, Node node) throws Throwable {
        
    }
}


@Component("condition1") //条件组件（v3.7.3 后支持）
public class ConditionComponentImpl implements ConditionComponent {
    @Override
    public boolean test(FlowContext context) throws Throwable {
        return false;
    }
}
```


* 跨图调用风格（相当于子流程图）

以`#`开头，表示调用引擎实例内的对应id的图。



* 引用图的元数据作为脚本风格

以 `$`开头，表示引用图的元数据 key（支持多层次）。可以让 layout 部分更清爽，又不使用“任务组件”（清爽的实现，单文件配置完成所有的处理）。

```yaml
id: "c8"
title: "计算编排"
layout:
  - { type: "start"}
  - { task: 'order.score=1;'} #初始化 result 值，用于后面计算
  - { when: "1=1", task: '$script.script1'} 
  - { task: '$script2'}
  - { type: "end"}
meta:
  script:
    script1: |
      import java.util.ArrayList; //任务脚本，支持导入和注释
      
      order.score=order.score+1;
  script2: |
    order.score=order.score+1;
```


* 直接脚本风格（默认支持完整的 Java 语法。其它脚本可定制驱动实现）


SimpleFlowDriver 的脚本能力，默认由 [Liquor](/article/liquor) 提供。`context` 和 `context.model()` 里的变量在脚本里可直接使用。

```java
//任务脚本（示例1）//完整的 java 代码
order.score=1;

//任务脚本（示例2）//完整的 java 代码
if(order_id > 0) { //order_id 即为模型里的变量： context.get("order_id")
    System.out.println("订单号: " + order_id);
}

//===================

//条件脚本（示例1）//产生一个布尔结果
user_id > 12 //user_id 即为模型里的变量： context.get("user_id")
```



配置示例：

```yaml
#c1.yml
id: chain1
layout:
  - task: 'System.out.println("Hello world");'
```

```yaml
#c2.yml
id: c2
layout:
  - task: '@task1' #执行组件
  - task: '#graph1' #执行图（相当于子流程）
  - task: '$script1' #执行引用脚本
  - task: 'System.out.println("Hello world");' #直接执行脚本
meta:
  script1: |
    order.score = order.score+1;
  script2: |
    import org.noear.solon.net.http.HttpUtils;
    
    let html = HttpUtils.http("http://demo.com/test").get();
    System.out.println(html);'
```

### 2、（默认）条件与任务脚本补充说明

默认支持完整的 Java 语法

* `context` 和 `context.model()` 里的变量在脚本里可直接使用。
* 可以 `import` 类，比如使用 `Math` 类或静态方法（和在 java 类里用一样）
* 在 java 类里可以直接使用的类，在条件或任务脚本里也可以直接用（比如 `System`）

如果要给变量赋值？

```java
context.put("key", val);
//或者（变量的属性） 
order.score = val;
```

注意：如果更换脚本执行器，可能会略有不同。

### 3、流程驱动器定制后的节点任务、连接条件描述参考

以下为第三方的 RubberFlowDriver 方案（对应业务侧其它配置方案）。可作思路拓展参考。


|            | 示例                                        |
|------------|-------------------------------------------|
| 节点任务描述       | `F,tag/fun1;R,tag/rule1`（dsl 风格）          |
| 连接条件描述       | `m.user_id,>,12,A;m,F,$ssss(m),E`（dsl 风格） | 



