Solon v3.0.9

节点任务、连接条件和驱动器定制

</> markdown

solon-flow 采用开放式架构

  • 节点“任务”与连接“条件”的描述没有固定格式,是由 ChainDriver 的处理决定
  • 使用哪个 ChainDriver 就采用哪种格式描述

就像 jdbc 的 Driver, mysql 和 pgsql 的语法各不同。

1、默认格式(SimpleChainDriver 方案,框架内置)

示例
任务描述@t(任务组件风格) 或者 context.result=1; (java 脚本风格) 或者 #c12(跨链调用风格)
条件描述user_id > 12(java 脚本表达式风格。要求结果为布尔值)
  • 任务组件风格

@开头,表过调用对应名字的组件。

@Component("t") //任务组件
public class TaskComponentT implements TaskComponent {

    @Override
    public void run(ChainContext context, Node node) throws Throwable {
        
    }
}
  • 跨链调用风格(相当于子流程)

#开头,表示调用引擎实例内的对应id的链。通过定制,也可对接 solon faas 的能力。

  • 脚本风格(默认支持完整的 Java 语法。其它脚本可定制驱动实现)

SimpleChainDriver 的脚本能力,由 Liquor 提供。contextcontext.model() 里的变量在脚本里可直接使用。

//任务脚本(示例1)//完整的 java 代码
context.result=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")

配置示例:

#c1.chain.yml
id: c1
nodes:
  - task: 'System.out.println("Hello world");'
#c2.chain.yml
id: c2
nodes:
  - task: '@t' #执行组件
  - task: '#c1' #执行链(相当于子流程)
  - task: 'System.out.println("Hello world");' #执行脚本

2、定制格式参考(RubberChainDriver 方案,第三方的)

示例
任务描述F,tag/fun1;R,tag/rule1(dsl 风格)
条件描述m.user_id,>,12,A;m,F,$ssss(m),E(dsl 风格)

3、驱动器定制参考

本示例,模拟有状态的审批型流程(一般,计算型的不需要定制)。仅供参考

@Component
public class ApproveChainDriver extends SimpleChainDriver implements ChainDriver{
    @Inject
    FlowService flowService;
    
    @Override
    public void handleTask(ChainContext context, Task task) throws Throwable {
        if (tryIfChainTask(context, task)) {
            //如果跨链调用
            return;
        }

        if (tryIfComponentTask(context, task)) {
            //如果用组件运行
            return;
        }

        String instance_id = context.get("instance_id");
        String user_role = context.get("user_role");
        String chain_id = task.node().chain().id();
        String task_id = task.node().id();

        //通过数据库查找状态(或者,把状态批量加载到上下文参考)
        TaskState taskState = flowService.getState(instance_id, task_id);//查询任务装态

        if (taskState == null) {
            //中断(流,不会再往下驱动),等用户操作出状态
            context.interrupt();

            //如果当前用户匹配这个节点任务
            if(user_role.equals(task.node().meta().get("role"))){
                //则把这个节点,作为结果(用于展示界面)
                context.result = task.node();
            }
        }
    }
}

定制驱动器的应用(驱动器作为上下文的一部分)

@Component
public class DemoCom {
    @Inject
    FlowEngine flowEngine;

    public void test() throws Exception {
        ChainContext context = new ChainContext();
        context.put("instance_id", "1");
        context.put("user_id", "2");
        context.put("user_role", "leader");
        
        flowEngine.eval("c1", context);
        
        //获取结果节点
        Node node = (Node)context.result;
        
        //构建操作节点界面...
    }
}

4、驱动器定制的默认替代与命名

  • 替代内置的默认驱动器
@Component
public class ApproveChainDriver extends SimpleChainDriver implements ChainDriver{
   ....
}
  • 使用命名驱动器(链配置时,指定驱动器名字。可用于多驱动并存)
@Component("approveChainDriver")
public class ApproveChainDriver extends SimpleChainDriver implements ChainDriver{
   ....
}
# demo2.chain.yml

id: "d2"
title: "请假审批"
driver: "approveChainDriver" #指定驱动器名字。可用于多驱动并存
nodes:
  - ...

附:相关定制接口

  • TaskComponent 节点任务组件接口
public interface TaskComponent {
    /**
     * 运行
     */
    void run(ChainContext context, Node node) throws Throwable;
}
  • ChainDriver 链驱动器接口
public interface ChainDriver {
    /**
     * 节点运行开始时
     */
    void onNodeStart(ChainContext context, Node node);

    /**
     * 节点运行结束时
     */
    void onNodeEnd(ChainContext context, Node node);

    /**
     * 处理条件检测
     */
    boolean handleTest(ChainContext context, Condition condition) throws Throwable;

    /**
     * 处理执行任务
     */
    void handleTask(ChainContext context, Task task) throws Throwable;
}