软件产品都面临业务需求、流程快速变化的现状,往往之前设计绘制的业务流程在几个月之后就会失效的问题,如果按照一定语法开发的代码可以实时正确反应业务流程就解决了业务流程保鲜的问题。yunqiao-boot-starter-flow
就是解决这一问题的开发框架。
快速开始
一、安装依赖
在 maven pom.xml
中添加以下依赖,如果使用六边形架构,一般是添加到 application
层。
<properties><yunqiao-boot.version>0.1.1</yunqiao-boot.version></properties><dependencies> ... <dependency><groupId>com.aliyun.gts.yunqiao</groupId><artifactId>yunqiao-boot-starter-flow</artifactId><version>${yunqiao-boot.version}</version></dependency> ... </dependencies>
二、编程界面
yunqiao-boot-starter-flow
有主要两个类来进行业务流程编写:
ISequence
对线性流程进行编写。
主要使用ISequence.then(String name, Function<T, R> function)
方法进行流程步骤编排,多个步骤可以链式调用多个then
来完成一个线性没有分支的业务流程。其中name
为业务节点名称,会渲染到流程图中。function
为处理函数。
Example:
importcom.aliyun.gts.yunqiao.arch.flow.rx.sequence.Sequence; importstaticcom.aliyun.gts.yunqiao.arch.flow.rx.Alias.seq; // Sequence 是 ISequence 使用 rxjava3 实现的线性流程。intlength=Sequence.<String, Integer>create("去空格测长度") .start() .then("去空格", String::trim) .then("算长度", String::length) .stop() .run(" 1234 "); // seq 是 Sequence 的简写。Stringresult=seq("算术题").then("+1", n->n+1) .then("+2", n->n+2) .then("秒", n->n+"s") .run(1);
IGraph
对分支流程进行编写。
主要使用:
.sequence
添加一个线性流程,可以直接使用上面的Sequence
.whether
添加一个 是,否 分支,相当于if else
.parallel
添加多个并行执行的流程分支。
Example:
// Graph 是 IGraph 使用 rxjava3 实现的分支流程。Graph.<Optional<Void>, Optional<Void>>create("流程图") .whether("参数检查", it->true, Sequence.create("通过", newPassInputFunction<>()), Sequence.create("不通过", "抛异常", it-> { thrownewRuntimeException(); }).end() ) .sequence(Sequence.create("先行序列") .then(newPrintNamedFunction("先行1")) .then(newPrintNamedFunction("先行2"))) .whether("是否需要排他", __->false, Sequence.create("排他序列1") .then(newPrintNamedFunction("排他1-1")) .then(newPrintNamedFunction("排他1-2")), Sequence.create("排他序列2") .then(newPrintNamedFunction("排他2-1")) .then(newPrintNamedFunction("排他2-2"))) .both( Sequence.create("并行序列1").then(newPrintNamedFunction("并行1-1")).then(newPrintNamedFunction("并行1-2")), Sequence.create("并行序列1").then(newPrintNamedFunction("并行2-1")).then(newPrintNamedFunction("并行2-2")), (__, ___) -> { System.out.println("汇聚"); return___; }).run();
三、图形显示
编写好的业务流程可以进行图形化展示,在需要展示的流程方法上打上Flow
注解。
Example:
publicResponseVocreate(RequestVoreq) { returnGraph.create("").sequence(...).whether(...).run(req); }
然后可以使用以下两种方式进行图形化展示
使用云巧工坊
- 打开 云巧工坊 -> 资产生产 -> 点击 初始化云巧标准应用 链接
- 按照提示步骤创建一个应用,并把代码 push 到应用仓库中
- 进入应用详情,点击刷新评分按钮,则会创建一个代码扫描流水线。
- 可以在大禹查看流水线运行状态和日志:
- 当流水线运行成功之后,刷新云巧工坊的页面就可以查看评分和流程图了
idea 插件实时展示
使用 yunqiao-intellij-plugin 可以在 IDEA 系编辑器中实时查看编写的业务流程图:
使用方法如下:
- 从云巧资产市场下载插件。
- 打开 IDEA 插件管理界面,选择从磁盘上安装。
- 安装成功后可以看到右侧工具栏多了一个架构工作台的界面(目前只有业务流程可视化功能)。
- 编写一个流程,就可以看到流程图啦。
注:如果看不到流程,请手动 clean 工程再 compile 试试。
四、Api 列表
IFlow
ISequence
和 IGraph
的基类,定义类流程的通用方法。
/*** @param <I> 流程入参类型* @param <O> 流程出参类型* @author YanPeng* @date 2021/9/13*/publicinterfaceIFlow<I, O, SextendsIFlow<I, O, S>> { /*** 运行一个流程,使用默认 Context** @param input 流程入参* @return 流程出参*/Orun(Iinput); /*** 开始一个流程,使用自定义 Context** @param input 流程入参* @param context 自定义 Context* @param <C> 自定义 Context类型,需要实现 {@link com.aliyun.gts.x.arch.sketch.flow.Context} 接口* @return 流程出参*/<CextendsContext>Orun(Iinput, Ccontext); /*** 没有业务含义,在流程图中添加一个开始节点** @return 自身,链式调用*/Sstart(); /*** 没有业务含义,在流程图中添加异常结束节点** @return 自身,链式调用*/Send(); /*** 没有业务含义,在流程图中添加正常结束节点** @return 自身,链式调用*/Sstop(); /*** 当流程开始的时候回调** @param consumer 回调函数* @return 自身,链式调用*/SdoOnStart(Consumer<?>consumer); /*** 当流程结束的时候回调(无论正常还是异常结束)** @param onFinally 回调函数* @return 自身,链式调用*/SdoFinally(Consumer<?>onFinally); }
ISequence
/*** @param <I> {@link IFlow}* @param <O> {@link IFlow}* @author yanpeng*/publicinterfaceISequence<I, O>extendsIFlow<I, O, ISequence<I, O>> { /*** 下一步** @param function 下一步执行函数* @param <T> 函数入参类型* @param <R> 函数返回值类型* @return 函数执行结果*/<T, R>ISequence<I, O>then(NamedFunction<T, R>function); /*** 下一步** @param name 函数名称(节点名称)* @param function 下一步执行函数* @param <T> 函数入参类型* @param <R> 函数返回值类型* @return 函数执行结果*/<T, R>ISequence<I, O>then(Stringname, Function<T, R>function); /*** 获取所有已设置的 Function** @return 已设置function列表*/Collection<?extendsNamedFunction<?, ?>>functions(); }
IGraph
publicinterfaceIGraph<I, O>extendsIFlow<I, O, IGraph<I, O>> { /*** 执行一个序列** @param sequence 需要执行的序列* @return 自身,链式调用*/<T, R>IGraph<I, O>sequence(ISequence<T, R>sequence); /*** 执行一个只有一个Function的序列** @param name Function名* @param function 执行函数* @return 自身,链式调用*/<T, R>IGraph<I, O>then(Stringname, Function<T, R>function); /*** 执行排他分支,及只有一个分支符合执行条件** @param conditionName 条件描述* @param branches 分支* @return 自身,链式调用*/IGraph<I, O>exclusive(StringconditionName, IBranch<?, ?>... branches); /*** 执行是否分支,排他分支的一种** @param on 是否执行的条件* @param positive 条件为真时执行* @param negative 条件为假时执行* @param <T> 初始入参类型* @return 自身,链式调用*/<T>IGraph<I, O>whether(NamedPredicate<T>on, ISequence<T, ?>positive, ISequence<T, ?>negative); /*** 执行是否分支,排他分支的一种** @param conditionName 条件名称* @param on 是否执行的条件* @param positive 条件为真时执行* @param negative 条件为假时执行* @param <T> 初始入参类型* @return 自身,链式调用*/<T>IGraph<I, O>whether(StringconditionName, Predicate<T>on, ISequence<T, ?>positive, ISequence<T, ?>negative); /*** 执行是否分支,排他分支的一种** @param conditionName 条件名称* @param on 是否执行的条件* @param positiveName 为真的节点名称* @param positive 条件为真时执行* @param negativeName 为假的节点名称* @param negative 条件为假时执行* @param <T> 初始入参类型* @return 自身,链式调用*/<T>IGraph<I, O>whether(StringconditionName, Predicate<T>on, StringpositiveName, Function<T, ?>positive, StringnegativeName, Function<T, ?>negative); /*** 执行是否分支,排他分支的一种** @param conditionName 条件名称* @param on 是否执行的条件* @param positiveName 为真的节点名称* @param positive 条件为真时执行* @param negative 条件为假时执行* @param <T> 初始入参类型* @return 自身,链式调用*/<T>IGraph<I, O>whether(StringconditionName, Predicate<T>on, StringpositiveName, Function<T, ?>positive, NamedFunction<T, ?>negative); /*** 两个分支都执行** @param seq1 分支1* @param seq2 分支2* @param reducer 分支聚合处理函数* @param <T> 初始入参类型* @param <R> 聚合结果类型* @return 自身,链式调用*/<T, R>IGraph<I, O>both(ISequence<T, ?>seq1, ISequence<T, ?>seq2, BiFunction<?, ?, R>reducer); /*** 执行并行流程,及所有分支都执行** @param sequences 分支列表* @param reducer 分支聚合处理函数* @param <T> 初始入参类型* @param <R> 聚合结果类型* @return 自身,链式调用*/<T, R>IGraph<I, O>parallel(List<ISequence<T, ?>>sequences, BiFunction<?, ?, R>reducer); }