前言
作为开发人员或多或少都会接触过工作流引擎,如 activiti、 jbpm等,这些工作流引擎都会比较重。在小项目中引入就会显得不是很优雅。本文主要简单介绍工作流引擎的原理并讲述如何慢慢打造一款适合自己的工作流引擎。
流程节点
当工程需若干个不同程序(流程)或分若干个阶段来完成时,某一程序或某一阶段结束,另一程序或某一阶段开始时的转接点(类别点或时间点),称流程节点。 常见的流程节点有:开始结束、任务节点、条件节点、分支节点、合并节点、子流程节点、结束节点等。为了简单起见,这里只讲解最简单的流程(开始->任务->结束)。
开始节点
流程的开始,做一些初始化操作的节点。
- 类型 START
- 模型类 StartModel
- 属性说明
属性名 | 类型 | 说明 |
---|---|---|
id | String | 节点id,流程节点的唯一标识 |
name | String | 节点名称,说明是节点做什么的 |
nextNodeId | String | 下一个节点id |
ext | Map | 扩展属性 |
任务节点
该节点会产生任务,任务完成后才能进行下一步
- 类型 TASK
- 模型类 TaskModel
- 属性说明
属性名 | 类型 | 说明 |
---|---|---|
id | String | 节点id,流程节点的唯一标识 |
name | String | 节点名称,说明是节点做什么的 |
nextNodeId | String | 下一个节点id |
performType | String | 参与方式(ANY->任何一个参与者处理完即可执行下一步,ALL->所有参与者都完成,才可执行下一步) |
expireTime | String | 期望完成时间(产生的任务期望啥时间完成) |
reminderTime | String | 提醒时间(如任务未处理,啥时候提醒) |
reminderRepeat | Integer | 提醒间隔(分钟)(如任务未处理,提醒规则是什么) |
autoExecute | Boolean | 是否自动执行(如任务未处理且到期,是否自动执行) |
ext | Map | 扩展属性 |
结束节点
该节点说明流程已走完,在这里触发流程完成事件
- 类型 END
- 模型类 EndModel
- 属性说明
属性名 | 类型 | 说明 |
---|---|---|
id | String | 节点id,流程节点的唯一标识 |
name | String | 节点名称,说明是节点做什么的 |
ext | Map | 扩展属性 |
流程定义样例
- 新闻中心稿件发布流程
1 | 复制代码{ |
表结构设计
- flow_define(流程定义)
字段 | 类型 | 空 | 默认 | 注释 |
---|---|---|---|---|
id | char(36) | False | 主键 | |
flow_define_code | varchar(16) | True | 流程定义编码 | |
flow_name | varchar(100) | True | 流程名称 | |
description | varchar(255) | True | 流程描述 | |
flow_define | mediumtext | True | 流程定义(json字符串) | |
create_time | datetime | True | 创建时间 | |
update_time | datetime | True | 更新时间 | |
is_deleted | tinyint(1) unsigned | True | 0 | 是否删除(1->删除YES,0->未删除NO) |
- flow_work(流程实例)
字段 | 类型 | 空 | 默认 | 注释 |
---|---|---|---|---|
id | char(36) | False | 主键 | |
flow_name | varchar(100) | True | 流程名称 | |
flow_define_code | varchar(32) | True | 流程定义编码 | |
last_operator | char(36) | True | 最后一个操作者id | |
current_node_id | char(36) | True | 当前节点id | |
next_node_id | char(36) | True | 下一步节点id | |
flow_define | mediumtext | True | 流程定义(json字符串) | |
status | int(6) | True | 流程状态(10->开始START,30->结束END,40->取消CANCEL) | |
flow_param | mediumtext | True | 流程参数json | |
create_time | datetime | True | 创建时间 | |
update_time | datetime | True | 更新时间 | |
is_deleted | tinyint(1) unsigned | True | 0 | 是否删除(1->删除YES,0->未删除NO) |
- flow_task(流程任务)
字段 | 类型 | 空 | 默认 | 注释 |
---|---|---|---|---|
id | char(36) | False | 主键 | |
flow_work_id | char(36) | True | 流程id | |
flow_node_id | char(36) | True | 流程节点id | |
task_name | varchar(100) | True | 任务名称 | |
operator | char(36) | True | 操作者用户id | |
actor_user_id | char(36) | True | 执行用户的id | |
status | varchar(6) | True | 10 | 任务状态(10->新建CREATED,20->已完成FINISHED,30->过期EXPIRED,40->取消CANCEL,50->驳回REJECT) |
service_type | varchar(32) | True | 业务类型 | |
service_id | varchar(36) | True | 关联的业务id | |
finish_time | datetime | True | 完成时间 | |
flow_param | mediumtext | True | 流程参数json | |
create_time | datetime | True | 创建时间 | |
update_time | datetime | True | 更新时间 | |
is_deleted | tinyint(1) unsigned | True | 0 | 是否删除(1->删除YES,0->未删除NO) |
处理流程关键字描述
- 关于流程定义文件
流程定义文件即描述流程各节点关系的文件,在这里使用json文件来描述,存储在flow_define表的flow_define字段上。
- 关于流程定义文件解析
流程定义文件解析即将流程定义文件中的各个流程节点解析出来,并将其组装成流程模型。
- 关于流程实例
流程实例即使用流程定义生成的一个流程实例,生成的流程实例会存储在flow_work表。(该操作是由业务系统发起,即发起一个流程。)
- 关于任务
任务会在任务节点上产生,即当执行到任务节点时,会使用该节点的处理器,给参与的人员分派任务,分派的任务存储在flow_task表中。业务系统中可通过查询该表,拿到登录用户的待办任务。
- 关于执行完成任务
当参与人员把待办任务处理后,业务系统会调用完成任务方法,流程引擎会修改任务完成状态,并驱动流程往下一个节点行进。
- 关于驳回任务
当参与人员驳回任务后,业务系统会调用任务驳回方法,流程引擎会将流程进度调回上一个节点。
核心处理流程描述
FlowWorkCtl.java
- 启动流程实例
- 加载流程定义
- 解析流程定义文件
- 节点处理器控制器调用执行节点处理
- 如果是开始节点,只需要发布开始事件
- 如果是任务节点,则根据节点配置,分派任务,发布开始事件并更新流程实例节点状态(非必须,只是简单记录)
- 如果是结束节点,则修改流程实例为完成,并发布结束事件。
- 执行任务
- 获取任务实例
- 获取流程实例
- 解析流程定义文件
- 将任务设置为已完成
- 判断节点任务是否完成
- 如果已完成,则拿到下一个节点,调用节点处理控制器执行
- 如未完成,则无需处理
- 驳回任务
- 获取任务实例
- 获取流程实例
- 解析流程定义文件
- 将任务设置为已驳回
- 修改节点的其他参与人员的任务为已取消
- 拿到上一个节点模型,调用节点处理控制器执行
开始编码
工程目录
1 | 复制代码com.mole.modules.flow |
核心类
模型类
- FlowModel.java
1 | 复制代码package com.mole.modules.flow.model; |
- BaseModel.java
1 | 复制代码package com.mole.modules.flow.model; |
- StartModel.java
1 | 复制代码package com.mole.modules.flow.model; |
- TaskModel.java
1 | 复制代码package com.mole.modules.flow.model; |
- EndModel
1 | 复制代码package com.mole.modules.flow.model; |
流程节点解析类
将流程定义json转成对应的流程模型
- FlowNodeParser.java
1 | 复制代码package com.mole.modules.flow.parser; |
节点处理器类
节点处理器主要是对不同的节点进行不一样调度处理,如开始节点会驱动流程往下一步走,任务节点会产生任务,结束节点会将流程结束等。
- FlowNodeProcessor.java
1 | 复制代码package com.mole.modules.flow.processor; |
- StartNodeProcessorImpl.java
1 | 复制代码package com.mole.modules.flow.processor.impl; |
- TaskNodeProcessorImpl.java
1 | 复制代码package com.mole.modules.flow.processor.impl; |
- EndNodeProcessorImpl.java
1 | 复制代码package com.mole.modules.flow.processor.impl; |
节点处理器控制类
由节点处理器控制类统一调用处理器
- FlowNodeProcesstorCtl.java
1 | 复制代码package com.mole.modules.flow.processor; |
节点事件接口类
节点事件接口,由业务系统实现
- FlowStartNodeEvent
1 | 复制代码package com.mole.modules.flow.event; |
- FlowTaskNodeEvent.java
1 | 复制代码package com.mole.modules.flow.event; |
- FlowTaskCreatedEvent.java
1 | 复制代码package com.mole.modules.flow.event; |
- FlowEndNodeEvent.java
1 | 复制代码package com.mole.modules.flow.event; |
自定义任务节点参与人接口类
1 | 复制代码package com.mole.modules.flow.service; |
流程控制类
提供给业务操作流程的类
- FlowWorkCtl.java
1 | 复制代码package com.mole.modules.flow; |
调用样例
- 启动一个流程实例
1 | 复制代码String flowDefineCode = "yzq_invite_bid"; |
- 完成一个任务
1 | 复制代码String flowTaskId = "flowTaskId"; |
- 驳回任务
1 | 复制代码String flowTaskId = "flowTaskId"; |
本工程使用到的框架
- springboot 2.0
- tk.mybatis
其他
本想开源的,但是该项目和公司的框架耦合的太厉害,不太方便抽离,等哪天有空,会考虑把这部分代码单独抽离开源。还有一点就是,目前考虑的只是最简单的顺序流程,涉及到条件、分支、合并、子流程等复杂的流程暂时还没考虑。不过本意只是想一步步来,慢慢剖析原理。
本文转载自: 掘金