自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

賦能轉(zhuǎn)轉(zhuǎn)回收:LiteFlow可視化編排方案設(shè)計(jì)

開發(fā)
LiteFlow是一款編排式的規(guī)則引擎框架,可以通過表達(dá)式的方式來編排組件或方法的執(zhí)行流程,并且支持一些高級(jí)的流程編排。

1、引言

LiteFlow解決哪些場景的問題呢?通過下面的例子感受一下。

假設(shè)有三個(gè)組件(或方法)stepOne、stepTwo、stepThree,并且你想要按照順序打印"one"、"two"、"three",通常我們編寫代碼的方式可能是這樣的:

@Component
public class PrintService {
    /**
     * 執(zhí)行業(yè)務(wù)代碼
     */
    private void doExecute() {
        stepOne();
        stepTwo();
        stepThree();
    }

    private void stepOne() {
        // 業(yè)務(wù)代碼
        System.out.println("one");
    }

    private void stepTwo() {
        // 業(yè)務(wù)代碼
        System.out.println("two");
    }

    private void stepThree() {
        // 業(yè)務(wù)代碼
        System.out.println("three");
    }
}

這樣寫最簡單粗暴,但是如果之后有調(diào)整打印順序的話,例如你想打印two、one、three,或者直接跳過two直接打印one、three,你一定需要修改代碼并且重新上線。

// 打印two、one、three
    public void doExecute() {
        stepTwo();
        stepOne();
        stepThree();
    }
    // 打印one、three
    public void doExecute() {
        stepOne();
        stepThree();
    }

對(duì)于需要?jiǎng)討B(tài)調(diào)整執(zhí)行流程的業(yè)務(wù)場景,顯然不適合將流程硬編碼在代碼中。

2、 LiteFlow簡介

LiteFlow是一款編排式的規(guī)則引擎框架,可以通過表達(dá)式的方式來編排組件或方法的執(zhí)行流程,并且支持一些高級(jí)的流程編排。

上述案例如何通過更高級(jí)的方式來實(shí)現(xiàn)零代碼修改、無需重新上線即可編排流程了呢?我們基于LiteFlow做一些改造。

2.1 引入jar包

可以去官網(wǎng)根據(jù)需要選擇合適的版本,這里用的是最新版本

<dependency>
    <groupId>com.yomahub</groupId>
    <artifactId>liteflow-spring-boot-starter</artifactId>
    <version>2.12.0</version>
</dependency>

2.2 定義組件

將打印功能分別定義成一個(gè)個(gè)組件,繼承NodeComponent 這個(gè)抽象父類并實(shí)現(xiàn)其中的方法:

@Component
public class PrintOne extends NodeComponent {

    @Override
    public void process() throws Exception {
        // 業(yè)務(wù)代碼
        System.out.println("one");
    }
}
@Component
public class PrintTwo extends NodeComponent {

    @Override
    public void process() throws Exception {
        // 業(yè)務(wù)代碼
        System.out.println("two");
    }
}
@Component
public class PrintThree extends NodeComponent {

    @Override
    public void process() throws Exception {
        // 業(yè)務(wù)代碼
        System.out.println("three");
    }
}

2.3 執(zhí)行流程

定義好組件之后,我們就開始編寫組件執(zhí)行的流程表達(dá)式了,官方名稱叫EL表達(dá)式;上述案例可以這樣編寫表達(dá)式:

THEN(node("printOne"),node("printTwo"),node("pirntThree"));

并給這個(gè)流程起個(gè)名字(流程唯一標(biāo)識(shí)):print_flow

根據(jù)流程名稱執(zhí)行流程:

@Component
public class PrintService {

    @Autowired
    private FlowExecutor flowExecutor;

    /**
     * 執(zhí)行業(yè)務(wù)代碼
     */
    public void doExecute() {
        // 開始執(zhí)行流程
        LiteflowResponse response = flowExecutor.execute2Resp("print_flow");
        // 根據(jù)執(zhí)行結(jié)果進(jìn)行后續(xù)操作
        // ......
    }
}

一般我們會(huì)將流程放到數(shù)據(jù)庫中,如果想改變打印順序,只需要修改表達(dá)式即可,例如:打印two、one、three。

THEN(node("printTwo"),node("printOne"),node("pirntThree"));

打印two、three。

THEN(node("printTwo"),node("printThree"));

然后LiteFlow真正強(qiáng)大的地方遠(yuǎn)不止如此,它不僅僅支持簡單的串行編排,還支持更加復(fù)雜的邏輯例如(并行編排)WHEN、(條件編排)IF、(選擇編排)SWITCH、(循環(huán)編排)FOR等。

2.4 官網(wǎng)

上述的簡單示例旨在為不熟悉LiteFlow框架的伙伴們提供一個(gè)初步的認(rèn)知。要想真正基于LiteFlow將業(yè)務(wù)流程落地并運(yùn)用到實(shí)際業(yè)務(wù)場景中,還需要通過官方文檔深入了解該框架的運(yùn)作原理和核心機(jī)制。
https://liteflow.cc/

3、可視化編排(形態(tài)進(jìn)階)

3.1 為什么要可視化

官網(wǎng)提供修改表達(dá)式的方式只有一個(gè),那就是手寫!官網(wǎng)并沒有提供配套的可視化工具,手寫可能存在諸多問題和不便,例如:

  • 容易出錯(cuò):表達(dá)式少一個(gè)字母甚至一個(gè)逗號(hào)都不行!
  • 流程不可視:我們只能完全依賴大腦去構(gòu)想這些流程,運(yùn)營或產(chǎn)品團(tuán)隊(duì)想要了解或討論流程,也只能依賴于其他畫圖工具來手動(dòng)繪制和表達(dá)。
  • 節(jié)點(diǎn)不可配置:我們的運(yùn)營會(huì)根據(jù)不同的場景對(duì)節(jié)點(diǎn)進(jìn)行動(dòng)態(tài)配置,沒有可視化界面,運(yùn)營改動(dòng)配置的需求則無從下手。

所以可視化對(duì)于編排流程來說意義重大,對(duì)于研發(fā)能更準(zhǔn)確地理解和設(shè)計(jì)流程,還能讓運(yùn)營能更便捷地監(jiān)控和管理流程。

3.2 方案設(shè)計(jì)

網(wǎng)上有一些網(wǎng)友開源的項(xiàng)目,但基本都是個(gè)人維護(hù),對(duì)于復(fù)雜流程的處理不是很好,質(zhì)量也參差不齊,所以自己進(jìn)行了調(diào)研和設(shè)計(jì);支持普通節(jié)點(diǎn)、判斷節(jié)點(diǎn)、選擇節(jié)點(diǎn)、并行節(jié)點(diǎn);循環(huán)節(jié)點(diǎn)目前業(yè)務(wù)不需要,有需要的可以自己拓展,掌握方案之后拓展節(jié)點(diǎn)類型非常簡單。完成可視化編排需要解決兩個(gè)問題:

  • 一款與用戶交互的前端畫布(推薦logicFlow,有自己熟悉的也行)
  • 將畫布數(shù)據(jù)轉(zhuǎn)化成EL表達(dá)式(手寫算法,基于DFS的遞歸)

這里重點(diǎn)講畫布數(shù)據(jù)轉(zhuǎn)化為EL的過程。

3.2.1 整體流程

創(chuàng)建流程

流程的核心在第5步,下面會(huì)重點(diǎn)講解。

圖片

回顯流程

解析EL成本很高,所以我選擇不解析表達(dá)式,直接將前端傳入的畫布json數(shù)據(jù)返回給前端進(jìn)行回顯。

3.2.2 后端抽象語法樹設(shè)計(jì)

節(jié)點(diǎn)類型枚舉

public enum NodeEnum {
    // 普通節(jié)點(diǎn),對(duì)應(yīng)普通組件
    COMMON,
    // 并行節(jié)點(diǎn),對(duì)應(yīng)并行組件
    WHEN,
    // 判斷節(jié)點(diǎn),對(duì)應(yīng)判斷組件
    IF,
    // 選擇節(jié)點(diǎn),對(duì)應(yīng)選擇組件
    SWITCH,
    // 匯總節(jié)點(diǎn)(自定義)
    SUMMARY,
    // 開始節(jié)點(diǎn)(自定義)
    START,
    // 結(jié)束節(jié)點(diǎn)(自定義)
    END;
}

COMMON

普通節(jié)點(diǎn),入度和出度都為1。

圖片

IF

判斷節(jié)點(diǎn),包含一個(gè)true分支,一個(gè)false分支,入度為1,出度為2。圖片

SWITCH

根據(jù)SWITCH返回的tag,來決定執(zhí)行后續(xù)哪個(gè)流程。入度為1,出度大于1。圖片

WHEN

官網(wǎng)沒有WHEN節(jié)點(diǎn)的概念,我這里自定義WHEN節(jié)點(diǎn)會(huì)避免很多問題。圖片

為什么要定義WHEN節(jié)點(diǎn)?

WHEN作為一個(gè)出度大于1的節(jié)點(diǎn),和IF、SWITCH不同的是WHEN并沒有一個(gè)前置節(jié)點(diǎn)去驅(qū)動(dòng)一個(gè)流程。

假設(shè)這樣一個(gè)流程,如果沒有WHEN節(jié)點(diǎn)的支持,展示到畫布上的效果很差。

THEN(
    IF(node("c"), 
        WHEN(
            node("a"),
            node("b"),
            node("d"),
            node("e")
        ).ignoreError(true)
    ),
    node("f")
)

圖片

SUMMARY

官網(wǎng)沒有這種節(jié)點(diǎn),自定義節(jié)點(diǎn),用于匯總所有分支節(jié)點(diǎn),也就是WHEN、IF、SWITCH節(jié)點(diǎn)。入度大于1,出度為1。圖片

為什么要定義SUMMARY節(jié)點(diǎn)?

構(gòu)建EL算法是基于遞歸實(shí)現(xiàn)的,參考的是深度優(yōu)先遍歷算法(DFS),這種嵌套方式如果沒有一個(gè)結(jié)束標(biāo)志會(huì)一直執(zhí)行下去。

舉個(gè)例子:圖片

基于圖1生成EL表達(dá)式

THEN(
    node("c"),
    WHEN(
        THEN(node("b"),node("e")),
        THEN(node("d"),node("e"))
    )
)

基于圖2生成EL表達(dá)式

THEN(
    node("c"),
    WHEN(node("b"),node("d")),
    node("e")
)

可以看出來圖2的EL表達(dá)式才是我們想要的。

市面上有名的工作流引擎在畫布上處理匯總問題也是這樣設(shè)計(jì)的,比如在activiti中使用并行網(wǎng)關(guān)開啟會(huì)簽,也必須用并行網(wǎng)關(guān)在會(huì)簽結(jié)束時(shí)進(jìn)行匯總,否則就會(huì)出現(xiàn)重復(fù)審批的問題。

START

開始節(jié)點(diǎn),一個(gè)流程必須有一個(gè)開始節(jié)點(diǎn),入度為0,出度為1。
END

結(jié)束節(jié)點(diǎn),一個(gè)流程必須有一個(gè)結(jié)束節(jié)點(diǎn),入度為1,出度為0。
上述節(jié)點(diǎn)類型的類定義

// 抽象父類
@Getter
public abstract class Node {

    // node的唯一id
    private final String id;

    // node名稱,對(duì)應(yīng)LiteFlow的Bean名稱
    private final String name;

    // 入度
    private final List<Node> pre = Lists.newArrayList();

    // 節(jié)點(diǎn)類型
    private final NodeEnum nodeEnum;

    // 出度
    private final List<Node> next = Lists.newArrayList();

    protected Node(String id, String name, NodeEnum nodeEnum) {
        this.id = id;
        this.name = name;
        this.nodeEnum = nodeEnum;
    }

    public void addNextNode(Node node) {
        next.add(node);
    }

    public void addPreNode(Node preNode) {
        pre.add(preNode);
    }
}
// 普通節(jié)點(diǎn)
public class CommonNode extends Node {

    public CommonNode(@NonNull String id, @NonNull String name) {
        super(id, name, NodeEnum.COMMON);
    }
}
// 并行節(jié)點(diǎn)
public class WhenNode extends Node {

    public WhenNode(@NonNull String id, @NonNull String name) {
        super(id, name, NodeEnum.WHEN);
    }
}
// 判斷節(jié)點(diǎn)
@Getter
public class IfNode extends Node {

    private Node trueNode;

    private Node falseNode;

    public IfNode(@NonNull String id, @NonNull String name) {
        super(id, name, NodeEnum.IF);
    }

    public void setTrueNode(Node trueNode) {
        this.trueNode = trueNode;
        super.addNextNode(trueNode);
    }

    public void setFalseNode(Node falseNode) {
        this.falseNode = falseNode;
        super.addNextNode(falseNode);
    }
}
// 選擇節(jié)點(diǎn)
@Getter
public class SwitchNode extends Node {

    private final Map<Node, String> nodeTagMap = Maps.newHashMap();

    public SwitchNode(@NonNull String id, @NonNull String name) {
        super(id, name, NodeEnum.SWITCH);
    }

    public void putNodeTag(Node node, String tag) {
        nodeTagMap.put(node, tag);
        super.addNextNode(node);
    }
}
// 開始節(jié)點(diǎn)
public class StartNode extends Node {

    public StartNode(@NonNull String id, @NonNull String name) {
        super(id, name, NodeEnum.START);
    }
}
// 結(jié)束節(jié)點(diǎn)
public class EndNode extends Node {

    public EndNode(@NonNull String id, @NonNull String name) {
        super(id, name, NodeEnum.END);
    }
}
// 匯總節(jié)點(diǎn)
public class SummaryNode extends Node {

    public SummaryNode(@NonNull String id, @NonNull String name) {
        super(id, name, NodeEnum.SUMMARY);
    }
}

3.2.3 畫布JSON數(shù)據(jù)設(shè)計(jì)

畫布數(shù)據(jù)最終體現(xiàn)在JSON語法樹,數(shù)據(jù)結(jié)構(gòu)如下:

{
    "nodeEntities": [
        {
            "id": "節(jié)點(diǎn)的唯一id,由前端生成。必填",
            "name": "節(jié)點(diǎn)名稱,對(duì)應(yīng)LiteFlow的節(jié)點(diǎn)名稱,spring的beanName。必填",
            "label": "前端節(jié)點(diǎn)展示名稱,到時(shí)候給前端。必填",
            "nodeType": "節(jié)點(diǎn)的類型,有COMMON、IF、SWITCH、WHEN、START、END和SUMMARY。必填",
            "x": "x坐標(biāo)。必填",
            "y": "y坐標(biāo)。必填"
        }
    ],
    "nodeEdges": [
        {
            "source": "源節(jié)點(diǎn)。必填",
            "target": "目標(biāo)節(jié)點(diǎn)。必填",
            "ifNodeFlag": "if類型節(jié)點(diǎn)的true和false,只有ifNode時(shí)必填,其他node隨意",
            "tag": "switch類型的下層節(jié)點(diǎn)的tag,主機(jī)有switchNode時(shí)必填,其他node隨意"
        }
    ]
}

用戶拖動(dòng)畫布節(jié)點(diǎn)和節(jié)點(diǎn)之間連線的過程,其實(shí)就是維護(hù)節(jié)點(diǎn)數(shù)組和邊數(shù)組的過程。

3.2.4 畫布JSON數(shù)據(jù)合法校驗(yàn)

下面是針對(duì)畫布json數(shù)據(jù)的一些簡單合法性校驗(yàn),可以自己根據(jù)需要拓展,實(shí)現(xiàn)很簡單,最后有具體實(shí)現(xiàn)代碼,需要的可以下載。

  • 流程必須有一個(gè)開始節(jié)點(diǎn)和一個(gè)結(jié)束節(jié)點(diǎn)
  • 校驗(yàn)節(jié)點(diǎn)類型,只能是IF、WHEN、COMMON、SWITCH、START、END和SUMMARY
  • IF、WHEN、SWITCH節(jié)點(diǎn)的數(shù)量總和與SUMMARY類型節(jié)點(diǎn)數(shù)量總和校驗(yàn)
  • 校驗(yàn)節(jié)點(diǎn)和邊的source和target是否能對(duì)應(yīng)上
  • 校驗(yàn)SWITCH的出度邊是否有tag,且tag不能為空
  • 校驗(yàn)IF節(jié)點(diǎn)有沒有ifNodeFlag的標(biāo)識(shí),并且總有一條true分支,總有一條false分支

3.2.5 畫布JSON數(shù)據(jù)轉(zhuǎn)化為抽象語法樹

舉個(gè)簡單的例子:圖片對(duì)應(yīng)的JSON語法樹如下;避免篇幅過長,這里只列舉了部分屬性。

{
    "nodeEntities": [
        {
            "id": "a",
            "label": "a",
            "nodeType": "COMMON"
        },
        {
            "id": "e",
            "label": "e",
            "nodeType": "WHEN"
        },
        {
            "id": "b",
            "label": "b",
            "nodeType": "COMMON"
        },
        ......
    ],
    "nodeEdges": [
        {
            "source": "a",
            "target": "e",
        },
        {
            "source": "e",
            "target": "b",
        },
        {
            "source": "e",
            "target": "c",
        },
        ......
    ]
}

JSON轉(zhuǎn)化為抽象語法樹,實(shí)際就是創(chuàng)建節(jié)點(diǎn)對(duì)象,并維護(hù)節(jié)點(diǎn)的屬性,下面是偽代碼。

// 創(chuàng)建節(jié)點(diǎn)對(duì)象
        List<Node> nodes = Lists.newArrayList();
        for (NodeEntity nodeEntity : nodeEntities) {
            Node node = null;
            switch (nodeEntity.getNodeType()) {
                case NodeEnum.COMMON;
                    node = new CommonNode("節(jié)點(diǎn)的id", "節(jié)點(diǎn)的label");
                    break;
                case NodeEnum.WHEN;
                    node = new WhenNode("節(jié)點(diǎn)的id", "節(jié)點(diǎn)的label");
                    break;
                case NodeEnum.SUMMARY;
                    node = new SummaryNode("節(jié)點(diǎn)的id", "節(jié)點(diǎn)的label");
                    break;
                default:
                    throw new RuntimeException("未知的節(jié)點(diǎn)類型!");
            }
            nodes.add(node);
        }
        // 構(gòu)建nodeId和node的map
        Map<String, Node> nodeIdAndNodeMap = nodes.stream()
        .collect(Collectors.toMap(Node::getId, Function.identity()));
        // 維護(hù)節(jié)點(diǎn)間關(guān)系
        for (NodeEdge nodeEdge : nodeEdges) {
            Node sourceNode = nodeIdAndNodeMap.get(nodeEdge.getSource());
            Node targetNode = nodeIdAndNodeMap.get(nodeEdge.getTarget());
            sourceNode.addNextNode(targetNode);
            targetNode.addPreNode(sourceNode);
            ......
        }

疑問:為什么要設(shè)計(jì)JSON和AST(抽象語法樹)兩種數(shù)據(jù)結(jié)構(gòu)?

根據(jù)上述JSON數(shù)據(jù)可以發(fā)現(xiàn),用戶編輯畫布時(shí),前端只需要維護(hù)節(jié)點(diǎn)和邊兩個(gè)數(shù)組即可;而生成EL表達(dá)式的操作在后端,生成方法是利用遞歸實(shí)現(xiàn)的深度優(yōu)先遍歷算法(DFS),顯然JSON是不滿足遞歸需求的,所以JSON轉(zhuǎn)換為AST。

總之設(shè)計(jì)JSON和AST就是為了方便前后端去各自維護(hù)數(shù)據(jù)。

3.2.6 抽象語法樹生成EL表達(dá)式

整個(gè)流程的核心就在這里,AST生成EL表達(dá)式

同樣用上面的例子來模擬生成EL表達(dá)式過程,該流程只涉及THEN和WHEN,我們約定把THEN和WHEN當(dāng)成數(shù)組來處理,例如THEN(node("a"),node("b"))對(duì)應(yīng)數(shù)組[node("a"),node("b")],同理WHEN。

  1. 流程必須以一個(gè)數(shù)組開始。圖片
[
    node("a")
]

  1. 遇見WHEN分支節(jié)點(diǎn)e,創(chuàng)建一個(gè)新數(shù)組,并加入上一層數(shù)組。

圖片

[
    node("a"),
    [
    ]
]

  1. 分支節(jié)點(diǎn)之后的每一個(gè)分支都要?jiǎng)?chuàng)建一個(gè)數(shù)組,并且加入到分支節(jié)點(diǎn)的數(shù)組中。圖片
[
    node("a"),
    [
        [
            node("b")
        ]
    ]
]

  1. 正常的串行,節(jié)點(diǎn)直接加入最內(nèi)層數(shù)組。

圖片

[
    node("a"),
    [
        [
            node("b"),
            node("d")
        ]
    ]
]

  1. 遇見匯總節(jié)點(diǎn),什么也不處理。圖片
[
    node("a"),
    [
        [
            node("b"),
            node("d")
        ]
    ]
]

  1. 繼續(xù)向下,將f節(jié)點(diǎn)加入WHEN節(jié)點(diǎn)所在的數(shù)組,到達(dá)遞歸的出口。

圖片

[
    node("a"),
    [
        [
            node("b"),
            node("d")
        ]
    ],
    node("f")
]

這可能有疑問,程序是如何定位到WHEN所在的數(shù)組在哪呢?

利用棧,遇到WHEN節(jié)點(diǎn)的時(shí)候會(huì)將WHEN節(jié)點(diǎn)所在的數(shù)組壓棧,等遇到匯總節(jié)點(diǎn)時(shí)將數(shù)組出棧,那么可以確定f節(jié)點(diǎn)應(yīng)該加入出棧時(shí)的數(shù)組了。


  1. 因?yàn)槭菑膃節(jié)點(diǎn)開始有分支流程的,以b節(jié)點(diǎn)開頭的分支已經(jīng)執(zhí)行完,回溯到另一條分支;同樣c節(jié)點(diǎn)屬于e的一條分支,分支節(jié)點(diǎn)之后的每一個(gè)分支都要?jiǎng)?chuàng)建一個(gè)數(shù)組,并且加入到分支節(jié)點(diǎn)的數(shù)組中。圖片
[
    node("a"),
    [
        [
            node("b"),
            node("d")
        ],
        [
            node("c")
        ]
    ],
    node("f")
]

  1. 到了匯總節(jié)點(diǎn),因?yàn)楸闅v以b節(jié)點(diǎn)開頭的分支時(shí)已經(jīng)訪問了該匯總節(jié)點(diǎn),這次不處理,到達(dá)遞歸的出口。
    圖片
[
    node("a"),
    [
        [
            node("b"),
            node("d")
        ],
        [
            node("c")
        ]
    ],
    node("f")
]

如何判斷匯總節(jié)點(diǎn)是否訪問過?

用Set,訪問過的匯總節(jié)點(diǎn)加入Set中,下次再訪問先判斷Set中有沒有該匯總節(jié)點(diǎn),有就不往下執(zhí)行,到達(dá)遞歸出口。

結(jié)束!


根據(jù)上面簡單示例,下面是用遞歸實(shí)現(xiàn)DFS的偽代碼;文末有全量源碼,感興趣的可以下載參考一下。

public static String ast2El(Node head) {
        if (head == null) {
            return null;
        }
        // 用于存放when節(jié)點(diǎn)List
        Deque<List> stack = new ArrayDeque<>();
        // 用于標(biāo)記是否處理過summary節(jié)點(diǎn)了
        Set<String> doneSummary = Sets.newHashSet();
        List list = tree2El(head, new ArrayList(), stack, doneSummary);
        // 將list生成EL,你可以認(rèn)為框架有對(duì)應(yīng)的方法
        return toEL(list);
    }

    private static List tree2El(Node currentNode,
                                List currentThenList,
                                Deque<List> stack,
                                Set<String> doneSummary) {
        switch (currentNode.getNodeEnum()) {
            case COMMON:
                currentThenList.add(currentNode.getId());
                for (Node nextNode : currentNode.getNext()) {
                    tree2El(nextNode, currentThenList, stack, doneSummary);
                }
            case WHEN:
                stack.push(currentThenList);
                List whenELList = new ArrayList<>();
                currentThenList.add(whenELList);
                for (Node nextNode : currentNode.getNext()) {
                    List thenELList = new ArrayList<>();
                    whenELList.add(thenELList);
                    tree2El(nextNode, thenELList, stack, doneSummary);
                }
            case SUMMARY:
                if (!doneSummary.contains(currentNode.getId())) {
                    doneSummary.add(currentNode.getId());
                    // 這種節(jié)點(diǎn)只有0個(gè)或者1個(gè)nextNode
                    for (Node nextNode : currentNode.getNext()) {
                        tree2El(nextNode, stack.pop(), stack, doneSummary);
                    }
                }
            default:
                throw new RuntimeException("未知的節(jié)點(diǎn)類型!");
        }
        return currentThenList;
    }


3.2.7 校驗(yàn)EL表達(dá)式的合法性

這是生成EL表達(dá)式的最后一步;框架有本身有支持校驗(yàn)EL合法性的方法,在生成EL之后進(jìn)行校驗(yàn)。

// 校驗(yàn)是否符合EL語法
Boolean isValid = LiteFlowChainELBuilder.validate(el);

進(jìn)行完最后一步,EL表達(dá)式就可以入庫了。

3.3 推拉結(jié)合刷新流程

流程入庫之后并不是立即生效,進(jìn)行以下操作后生效。

3.3.1 拉

框架會(huì)定期從數(shù)據(jù)庫(或通過配置指定的任何數(shù)據(jù)源)中同步最新流程,并將這些流程緩存在內(nèi)存中;新流程同步和緩存的過程是平滑進(jìn)行的,不會(huì)干擾或打斷現(xiàn)有流程的執(zhí)行;該框架還允許用戶根據(jù)實(shí)際需求配置數(shù)據(jù)刷新的時(shí)間間隔(默認(rèn)1分鐘),具體配置方法可參照官方文檔進(jìn)行詳細(xì)了解。

3.3.2 推

如果我們希望改動(dòng)的EL表達(dá)式立即生效而不是等待框架被動(dòng)刷新,我們可以通過官方提供的api進(jìn)行主動(dòng)刷新:

flowExecutor.reloadRule();

需要注意的的是,官方提供的方法只是刷新單個(gè)實(shí)例節(jié)點(diǎn)的流程;如果是集群環(huán)境,我們需要借助消息隊(duì)列以達(dá)到通知整個(gè)集群的效果。

3.4 源碼

目前這套設(shè)計(jì)方案已在實(shí)際業(yè)務(wù)場景落地并使用;自己進(jìn)行過很多復(fù)雜流程的驗(yàn)證,基于這種規(guī)則能百分百保證生成EL表達(dá)式的正確性。圖片

自己的寫的demo,可以借鑒一下思路;里面有一個(gè)構(gòu)造好的復(fù)雜流程案例,通過調(diào)接口的方式自己感受。

https://dl.zhuanstatic.com/fecommon/liteFlow-el.zip

4 效果收益&未來規(guī)劃

通過引入流程的可視化編排,結(jié)合LiteFlow框架的支持,顯著提升了流程設(shè)計(jì)的直觀性和開發(fā)效率,為項(xiàng)目帶來了更為順暢和高效的開發(fā)體驗(yàn)。

4.1 效果收益:

  1. 開發(fā)人員只需要專注于核心的業(yè)務(wù)流程設(shè)計(jì),而無需在語法規(guī)則上耗費(fèi)過多精力。
  2. 通過直觀的可視化流程界面,產(chǎn)品和研發(fā)團(tuán)隊(duì)之間的溝通變得更為高效,復(fù)雜的業(yè)務(wù)邏輯能夠清晰展現(xiàn),避免了不必要的溝通。
  3. 運(yùn)營能夠?qū)崟r(shí)編輯流程節(jié)點(diǎn),并快速了解節(jié)點(diǎn)的屬性配置;例如“黑名單校驗(yàn)”節(jié)點(diǎn)中配置了哪些用戶,從而更加靈活地管理業(yè)務(wù)流程。

4.2 未來規(guī)劃

痛點(diǎn)

  1. 流程編排只是針對(duì)現(xiàn)有節(jié)點(diǎn),對(duì)于新的業(yè)務(wù)節(jié)點(diǎn),依然需要開發(fā)。
  2. 對(duì)外提供服務(wù)可能需要調(diào)用方提供較為詳盡的參數(shù)信息。

規(guī)劃

  1. 希望未來借助動(dòng)態(tài)腳本,實(shí)現(xiàn)全新業(yè)務(wù)流程的快速搭建,無需進(jìn)行任何開發(fā)工作。
  2. 引入數(shù)據(jù)字典的概念,將常用的參數(shù)整合為數(shù)據(jù)字典,例如只需要一個(gè)訂單號(hào),便能根據(jù)數(shù)據(jù)字典獲取該流程想要的參數(shù),從而降低調(diào)用方的開發(fā)成本。
責(zé)任編輯:龐桂玉 來源: 轉(zhuǎn)轉(zhuǎn)技術(shù)
相關(guān)推薦

2024-06-13 07:51:08

2024-06-06 08:18:42

回收業(yè)務(wù)

2023-07-12 08:33:34

引擎LiteFlow編排

2021-01-12 09:38:02

微服務(wù)服務(wù)組合編排

2021-03-25 07:30:24

代碼開發(fā)數(shù)據(jù)

2020-03-11 14:39:26

數(shù)據(jù)可視化地圖可視化地理信息

2020-08-21 16:08:18

NVIDIA

2022-06-29 08:28:58

數(shù)據(jù)可視化數(shù)據(jù)可視化平臺(tái)

2017-10-14 13:54:26

數(shù)據(jù)可視化數(shù)據(jù)信息可視化

2022-08-26 09:15:58

Python可視化plotly

2009-04-21 14:26:41

可視化監(jiān)控IT管理摩卡

2021-06-09 18:52:05

方案設(shè)計(jì)庫存數(shù)

2021-01-09 09:48:10

可視化自然流布局 LowCode

2022-01-14 07:56:38

流布局設(shè)計(jì)拖拽

2015-08-20 10:06:36

可視化

2019-07-17 13:57:06

智慧城市數(shù)據(jù)可視化場景

2021-07-12 17:23:47

零設(shè)計(jì)可視化引擎

2013-07-10 09:56:02

軟件定義網(wǎng)絡(luò)SDN

2010-08-25 17:18:10

DHCP服務(wù)器
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)