第三部分
1. 架构体系
其实我觉得学东西的时候 最快的方法就是把自己当作设计者。先想要是你的话,你怎么弄,这样学的比较快。
这个是从官网上面拉取的。上面列出了常用的API
那么如果让 我来做一个 流程引擎的话。我会按照下面的想法去做。
- 流程图按照xml文件解析。存在数据库的表里面,按照结点的流程顺序存在一起。 也就是这个节点的下一个结点是啥。用json存放,里面可能还有表达式,也都存在里面。
- 搞一张流程实例的表,存放现在运行的实例。用一个bus_key来存放业务id。用请假流程举例子。请假单的id就是这个这个key。
- 对应上面的搞一张流程实例的节点表,关联上面的流程实例表。
- 一张对应的部署表。存放流程图。
这基本就行了,
开启节点。就在实例的表和运行节点的表中存放数据。
完成结点,从运行节点的表里面删除数据。并且通过xml的那个表得到下一个结点的id。添加进 流程实例的节点表里面就好了。
这就一个大题的思路。至于别的啥的,不要太认真。
开始说认真的了哦
这些常用的api 在官网就都是有介绍的。
按照上图的介绍。在实际中,先要获得ProcessEngineConfiguration ,在获得processEngine,才能获得下面的东西。
官网上说的很明确了 官网说明
我在这里就没啥可说的了。
打这开始,是个分界线,开始说一些activity的东西了。
2. 流程实例
一个人发起一个流程就是流程实例,比如张三要请假,张三的请假就是一个流程实例
按照之前的在来一遍
2.1. 启动流程实例
流程定义部署在 activiti 后,就可以在系统中通过 activiti 去管理该流程的执行
,执行流程表示流程的一次执行
。比如部署系统请假流程后,如果某用户要申请请假这时就需要执行这个流程,如果 另外一个用户也要申请请假则也需要执行该流程,每个执行互不影响,每个执行是单独的流程实例
。
启动流程实例,操作如下数据库表:
下面先看act_ru开头的表
-
SELECT * FROM
activiti
.ACT_RU_EXECUTION
LIMIT 0, 1000 ----- 流程实例执行表,记录当前流程实例的执行情况
说明:
流程实例执行,如果当前只有一个分支时,一个流程实例只有一条记录且执行表的主键 id 和流程实 例 id 相同,如果当前有多个分支正在运行则该执行表中有多条记录,存在执行表的主键和流程实例 id 不相同的记录。不论当前有几个分支总会有一条记录的执行表的主键和流程实例 id 相同 一个流程实例运行完成,此表中与流程实例相关的记录删除。 -
SELECT * FROM
activiti
.ACT_RU_TASK
LIMIT 0, 1000 ---- 任务执行表,记录当前执行的任务
-
SELECT * FROM
activiti
.ACT_RU_IDENTITYLINK
LIMIT 0, 1000 ---- 任务参与者,记录当前参与任务的用户或组
下面在看act_hi开头的表 -
SELECT * FROM
activiti
.ACT_HI_ACTINST
LIMIT 0, 1000 ---- 活动历史表,记录所有活动
没有endTime就是正在运行的节点。 -
SELECT * FROM
activiti
.ACT_HI_IDENTITYLINK
LIMIT 0, 1000 ---- 任务参与者,记录当前参与任务的用户或组
-
SELECT * FROM
activiti
.ACT_HI_PROCINST
LIMIT 0, 1000 ---- 流程实例历史表
流程实例启动,会在此表插入一条记录,流程实例运行完成记录也不会删除
-
SELECT * FROM
activiti
.ACT_HI_TASKINST
LIMIT 0, 1000 ---- 任务历史表,记录所有任务
这个和ACT_HI_ACTINST
表的差别在于,这个只是部分的节点,那个会保存所有的节点,也就是说这个会保存真正需要执行的节点。
活动包括任务,所以此表中不仅记录了任务,还记录了流程执行过程的其它活动,比如开始事件、结束事件。
2.2. 查询流程实例
public static void main(String[] args) {
String key = "hoilday";
String resource = "activiti.cfg.xml";
ProcessEngineConfiguration processEngineConfiguration = ProcessEngineConfiguration.createProcessEngineConfigurationFromResource(resource);
ProcessEngine processEngine = processEngineConfiguration.buildProcessEngine();
RuntimeService runtimeService = processEngine.getRuntimeService();
List<ProcessInstance> processInstanceList = runtimeService.createProcessInstanceQuery()
.processDefinitionKey(key)
.list();
processInstanceList.forEach(t -> {
logger.info("---------------------------------------->");
logger.info("businessKey:{}", t.getBusinessKey());
logger.info("deploymentId:{}", t.getDeploymentId());
logger.info("processDefinitionId:{}", t.getProcessDefinitionId());
logger.info("processDefinitionKey:{}", t.getProcessDefinitionKey());
logger.info("processDefinitionName:{}", t.getProcessDefinitionName());
logger.info("processVariables:{}", JSONUtils.toJSONString(t.getProcessVariables()));
});
}
2.2.1 关联 businessKey
businessKey
就是一个业务标示。比如一个请假流程的请假单号。一个贷款流程的订单号,能够唯一标示这个业务的。在实际的开发中,一般就是一个业务号。
在之后的查询中,就可以通过businessKey来查询。 就拿一个贷款流程来说吧,通过businessKey(订单号)就能查询 这个单子现在走到那一步,贷款的运行历史。相关信息。
在流程实例启动的时候可以设置这个流程的 businessKey
之后的代码里面会设置
2.2 挂起、激活流程实例
某些情况可能由于流程变更需要将当前运行的流程暂停而不是直接删除,流程暂停后将不会继续执行。
2.2.1 1.6.1 全部流程实例挂起
流程定义下边所有的流程实例全部暂停,将不允许启动新的流程实例
public static void main(String[] args) {
String key = "hoilday";
String resource = "activiti.cfg.xml";
ProcessEngineConfiguration processEngineConfiguration = ProcessEngineConfiguration.createProcessEngineConfigurationFromResource(resource);
ProcessEngine processEngine = processEngineConfiguration.buildProcessEngine();
RepositoryService repositoryService = processEngine.getRepositoryService();
// 方式1; 通过key来 挂起 流程。这个流程下面的所有实例将不在使用,并且不能开启该流程的新的实例。
//repositoryService.suspendProcessDefinitionByKey(key);
//方式2 : 通过流程的id来挂起。 首先要查询 流程定义的id。通过id 来挂起
ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
.processDefinitionKey(key).singleResult();// singleResult 表示查一个 ,list表示查询多个
String deploymentId = processDefinition.getId(); //流程部署id
logger.info("{} 流程是否挂起:{}",deploymentId,processDefinition.isSuspended());
if(true == processDefinition.isSuspended()){
//流程已经挂起,全部激活
repositoryService.activateProcessDefinitionById(deploymentId);
logger.info("{} 流程是否挂起:{}",deploymentId,processDefinition.isSuspended());
logger.info("流程 已经激活");
}else{
//流程没有运行,还在激活状态。全部挂起
repositoryService.suspendProcessDefinitionById(deploymentId);
logger.info("{} 流程是否挂起:{}",deploymentId,processDefinition.isSuspended());
logger.info("流程 已经挂起");
}
}
上面的代码就是先判断流程有没有挂起,没有就挂起,有就激活
如果在挂起之后启动在启动这个实例的流程就会报错。
3. 个人任务
3.1 分配任务负责人
分配任务有三种种方式
- 流程图中指定好该节点的负责人
- 在流程图中通过表达式分配
- 在程序中通过监听器分配
3.1.1 固定分配
在流程图里面通过指定 assignee 属性,比如这个这个节点的的办理人就是工号为22222的人。
这种方式看看就好了,基本没有啥作用。
3.1.2 表达式分配
可以通过UEL(Unified Expression Language)表达式分配来做。和SPEL差不多
一般用。因为没把人写死,这个其实就是一个流程中的流程变量。
支持三种写法,
- 上面的是一种
变量名写法
对象.属性
的写法 。eg:${user.userCode]
user 也是 activiti 的一个流程变量,user.userCode表示通过调用 user 的 getter 方法获取值。- UEL-method
可以通过bean.方法名 的使用 官网介绍
下面的是一个列子
使用流程变量来分配任务
在这个任务开始之前,得把流程图自己重新写一下。之前的流程图没有 指定任务的 办理人。最简单的方式就是把库删了,重新弄一个,然后重新部署一遍。
,
还可以删除部署,
// 部署ID, 级联删除
关于级联删除 如果用过hibernate,你肯定知道,级联就是和这个部署id有关的都给删除掉。
repositoryService.deleteDeployment(deploymentId1,true);
也可以重新在部署一遍,activiti会用最新的。在act_re_deployment的表中的 engine_version_字段来表示,activit会自动找最新的弄。
在流程启动的时候指定 办理人,启动之后,在一个节点完成之后,就可以指定下一个节点的 办理人
public static void main(String[] args) {
String key="hoilday";
String resource = "activiti.cfg.xml";
ProcessEngineConfiguration processEngineConfiguration = ProcessEngineConfiguration.createProcessEngineConfigurationFromResource(resource);
ProcessEngine processEngine = processEngineConfiguration.buildProcessEngine();
RuntimeService runtimeService = processEngine.getRuntimeService();
HashMap<String, Object> var = new HashMap<>();
var.put("assigneeUser","zhangsan");
runtimeService.startProcessInstanceByKey(key,var);
}
可以看到这里指定了办理人。这个任务也只能是张三来做了。
3.1.3 监听器分配
官网讲任务监听器
eg:通过监听器来指定办理人
任务监听器是发生对应的任务相关事件时执行自定义 java 逻辑 或表达式
taskListeners 来指定监听,指定监听的那种类型的task。
- event 事件种类
Create:任务创建后触发
Assignment:任务分配后触发
Delete:任务完成后触发
All:所有事件发生都触发 - type 类型,是class还是表达式
-Class 类的全路径名字 - fields。字段
通过任务的名字,来指定不同的办理人.
需要注意的是,如果监听类里面出现了异常,那后面的流程也是不能运行的
3.2 查询任务
查询任务负责人的待办任务:
public static void main(String[] args) {
String key="hoilday";
String resource = "activiti.cfg.xml";
ProcessEngineConfiguration processEngineConfiguration = ProcessEngineConfiguration.createProcessEngineConfigurationFromResource(resource);
ProcessEngine processEngine = processEngineConfiguration.buildProcessEngine();
TaskService taskService = processEngine.getTaskService();
Task zhangsanTask = taskService.createTaskQuery().processDefinitionKey(key)
.includeProcessVariables().taskAssignee("zhangsan").singleResult();
logger.info("流程实例id: {}",zhangsanTask.getProcessInstanceId());
logger.info("任务id:{}",zhangsanTask.getId());
logger.info("任务负责人:{}",zhangsanTask.getAssignee());
logger.info("任务名称:{}",zhangsanTask.getName());
}
3.3 办理任务
主要就是完成任务,然后设置下一步的任务的候选人。
HashMap<String, Object> var = new HashMap<>();
var.put("day",4);
taskService.complete(zhangsanTask.getId(),var);
在完成任务的时候指定下一个变量。
看现在的节点走到了总经理审批。 因为day > 3
就先到这里吧,到这里就能实现一个基本的流程了,网上也有很多关于activiti的视频和博客。以一篇博客为中心,别的为辅助,看看就会了。当然,还有官网。