activiti学习笔记二
上一篇文章大概讲了下什么是流程引擎,为什么我们要用流程引擎,他的基本原理是啥,以及怎么进行基本的使用,这篇文章我们再讲下其他的一些使用。
删除流程部署
package activiti02; import org.activiti.engine.ProcessEngine; import org.activiti.engine.ProcessEngines; import org.activiti.engine.RepositoryService; import org.activiti.engine.repository.ProcessDefinition; /** * 删除流程部署 * 影响了三张表: * act_ge_bytearray * act_re_deployment * act_re_procdef * 历史表信息会被保留,如果级联删除,会把全部记录删除 * */ public class ActivitiDeleteProcessDefinition { public static void main(String[] args) { // 1、创建ProcessEngine流程引擎对象 ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); // 2、得到Repositoryervice实例 RepositoryService repositoryService = processEngine.getRepositoryService(); // 3、查询流程部署 ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionKey("holiday") .singleResult(); // 4.1、删除流程定义 // repositoryService.deleteDeployment(processDefinition.getDeploymentId()); // 4.2、如果还有未结束的流程节点,可以使用级联删除(true) // deleteDeployment(String deploymentId, boolean cascade) repositoryService.deleteDeployment(processDefinition.getDeploymentId(),true); } }
删除流程实例
package activiti02; import org.activiti.engine.ProcessEngine; import org.activiti.engine.ProcessEngines; import org.activiti.engine.RuntimeService; import org.activiti.engine.TaskService; import org.activiti.engine.runtime.ProcessInstance; import org.activiti.engine.task.Task; import java.util.List; /** * 删除流程实例 * 历史表信息会被保留 * */ public class ActivitiDeleteProcessInstance { public static void main(String[] args) { // 1、创建ProcessEngine流程引擎对象 ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); RuntimeService runtimeService = processEngine.getRuntimeService(); TaskService taskService = processEngine.getTaskService(); Task task = taskService.createTaskQuery().taskAssignee("zhangsan").singleResult(); List<ProcessInstance> processInstanceList = runtimeService.createProcessInstanceQuery() .processInstanceId(task.getProcessInstanceId()).list(); for (ProcessInstance processInstance : processInstanceList) { runtimeService.deleteProcessInstance(processInstance.getId(),"删除流程实例"); } System.err.println("ok"); } }
如果流程实例删除了,那这次的流程就不用再继续执行了。act_ru_* 表的相关记录都删除
挂起/激活流程定义:
package activiti03; import org.activiti.engine.ProcessEngine; import org.activiti.engine.ProcessEngines; import org.activiti.engine.RepositoryService; import org.activiti.engine.repository.ProcessDefinition; /** * 挂起全部流程实例,使其无法启动新的流程实例,未执行完的流程实例也不允许继续执行 * * */ public class ActivitiSuspendAllProcessInstance { public static void main(String[] args) { // 1、得到processEngine对象 ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); // 2、得到repositoryService对象 RepositoryService repositoryService = processEngine.getRepositoryService(); // 3、查询流程定义的对象 ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionKey("holiday") .singleResult(); // 4、得到当前流程定义的实例是否都为暂停状态 boolean suspended = processDefinition.isSuspended(); // 5、判断 if (suspended) { // 5.1、如果是暂停,那就全部激活 repositoryService.activateProcessDefinitionById(processDefinition.getId(),true,null); System.err.println("流程定义:" + processDefinition.getId() + "激活"); }else { // 5.2、如果是活动状态,那就全部挂起 repositoryService.suspendProcessDefinitionById(processDefinition.getId(),true,null); System.err.println("流程定义:" + processDefinition.getId() + "挂起"); } } }
挂起流程定义后,就不能再创建流程实例了,同时未完成的流程实例、流程任务也无法继续完成。
挂起/激活单个流程实例:
package activiti03; import org.activiti.engine.ProcessEngine; import org.activiti.engine.ProcessEngines; import org.activiti.engine.RuntimeService; import org.activiti.engine.runtime.ProcessInstance; /** * 挂起指定一个流程实例,使其无法续执行。 * 给流程定义依旧可以发起新的流程实例,其他流程实例不受影响 * * 和挂起全部区别: * 挂起全部是针对流程定义来的,当流程定义被挂起后,该流程定义下的所有流程实例都自动被挂起 * 挂起一个是针对具体的流程实例来的,只挂起一个流程实例,并不会影响整个流程定义,也不会影响其他流程实例 * */ public class ActivitiSuspendSingleProcessInstance { public static void main(String[] args) { // 1、得到processEngine对象 ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); // 2、得到runtimeService对象 RuntimeService runtimeService = processEngine.getRuntimeService(); // 3、查询流程实例的对象 ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processDefinitionKey("holiday") .singleResult(); // 4、得到当前流程实例的实例是否都为暂停状态 boolean suspended = processInstance.isSuspended(); // 5、判断 if (suspended) { // 5.1、如果是暂停,那就全部激活 runtimeService.activateProcessInstanceById(processInstance.getId()); System.err.println("流程实例定义id:" + processInstance.getId() + "激活"); }else { // 5.2、runtimeService,那就全部挂起 runtimeService.suspendProcessInstanceById(processInstance.getId()); System.err.println("流程实例定义id:" + processInstance.getId() + "挂起"); } } }
activiti流程绑定业务记录(BusinessKey):
package activiti03; import org.activiti.engine.ProcessEngine; import org.activiti.engine.ProcessEngines; import org.activiti.engine.RuntimeService; import org.activiti.engine.runtime.ProcessInstance; /** * activiti与自定义业务结合。 * 原理就是在启动activiti流程实例的时候,传一个业务流程数据的唯一id进来。 * 也就是说,activiti只是负责管理流程的推进,业务数据则是由我们自己来进行处理。 * 两者通过act_ru_execution表的business_key来进行关联 * */ public class ActivitiBusinessKey { public static void main(String[] args) { // 1、得到processEngine对象 ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); // 2、得到RuntimeService对象 RuntimeService runtimeService = processEngine.getRuntimeService(); // 3、得到流程实例,需要直到流程定义的key,也就是流程process文件的Id,可以在bpmn里面查看,也可以在数据库act_re_procdef找到该流程的key // startProcessInstanceByKey(String processDefinitionKey, String businessKey) // 假设我们的业务请假id为1001 ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("holiday","1001"); // 4、输出相关信息 System.out.println("流程部署id ===> "+processInstance.getDeploymentId()); System.out.println("流程实例id ===> "+processInstance.getProcessInstanceId()); System.out.println("活动id ===> "+processInstance.getActivityId()); System.out.println("business_key ===> "+processInstance.getBusinessKey()); } }
其实也就是在启动流程实例的时候,就绑定一个我们自己的业务key,这样以后我们就可以根据这个businessKey来我们自己的表查询相关业务
查询历史节点记录:
package activiti02; import org.activiti.engine.HistoryService; import org.activiti.engine.ProcessEngine; import org.activiti.engine.ProcessEngines; import org.activiti.engine.history.HistoricActivityInstance; import org.activiti.engine.history.HistoricActivityInstanceQuery; import java.util.List; /** * 历史数据查询 * */ public class ActivitiHistoryQuery { public static void main(String[] args) { // 1、创建ProcessEngine流程引擎对象 ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); // 2、得到HistoryService实例 HistoryService historyService = processEngine.getHistoryService(); // 3、获得HistoricActivityInstanceQuery对象,一个查询器 HistoricActivityInstanceQuery historicActivityInstanceQuery = historyService.createHistoricActivityInstanceQuery(); // 4、设置条件,并查询 HistoricActivityInstanceQuery historicActivity = historicActivityInstanceQuery.processInstanceId("12501"); List<HistoricActivityInstance> historicActivityInstanceList = historicActivity.orderByHistoricActivityInstanceStartTime() .asc().list(); // 5、输出流程定义 for (HistoricActivityInstance processDefinition : historicActivityInstanceList) { System.out.println("节点id==》"+processDefinition.getActivityId()); System.out.println("节点名==》"+processDefinition.getActivityName()); System.out.println("流程定义id==》"+processDefinition.getProcessDefinitionId()); System.out.println("流程实例id==》"+processDefinition.getProcessInstanceId()); System.out.println(); } } }
查询流程定义:
package activiti02; import org.activiti.engine.ProcessEngine; import org.activiti.engine.ProcessEngines; import org.activiti.engine.RepositoryService; import org.activiti.engine.repository.ProcessDefinition; import org.activiti.engine.repository.ProcessDefinitionQuery; import java.util.List; public class ActivitiQueryProcessDefinition { public static void main(String[] args) { // 1、创建ProcessEngine流程引擎对象 ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); // 2、得到Repositoryervice实例 RepositoryService repositoryService = processEngine.getRepositoryService(); // 3、获得ProcessDefinitionQuery对象,一个查询器 ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery(); // 4、设置条件,并查询出当前的所有流程定义 List<ProcessDefinition> processDefinitionList = processDefinitionQuery.processDefinitionKey("holiday") .orderByProcessDefinitionVersion() // 设置排序 .desc().list(); // 5、输出流程定义 for (ProcessDefinition processDefinition : processDefinitionList) { System.out.println("流程定义id==》"+processDefinition.getId()); System.out.println("流程定义key==》"+processDefinition.getKey()); System.out.println("流程定义名==》"+processDefinition.getName()); System.out.println("流程定义version==》"+processDefinition.getVersion()); System.out.println("流程部署id==》"+processDefinition.getDeploymentId()); } } }
导出流程文件bpmn和图片:
package activiti02; import org.activiti.engine.ProcessEngine; import org.activiti.engine.ProcessEngines; import org.activiti.engine.RepositoryService; import org.activiti.engine.repository.ProcessDefinition; import org.activiti.engine.repository.ProcessDefinitionQuery; import org.apache.commons.io.IOUtils; import java.io.*; /** * 需求: * 1、从activiti的act_ge_bytearray读取两个资源文件 * 2、将这两个资源文件放到指定目录 * * 实现方案: * 1、使用activiti自带的api,调用IO流转换,输出到磁盘 * 2、使用jdbc的对blob类型、clob类型数据的读取,并 调用IO流转换,输出到磁盘 * * */ public class DownloadBPMNFile { public static void main(String[] args) throws IOException { // 1、创建ProcessEngine流程引擎对象 ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); // 2、得到Repositoryervice实例 RepositoryService repositoryService = processEngine.getRepositoryService(); // 3、得到查询器ProcessDefinitionQuery对象 ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery(); // 4、设置查询条件 processDefinitionQuery.processDefinitionKey("holiday"); // 5、执行查询操作,得到想要的流程定义 ProcessDefinition processDefinition = processDefinitionQuery.singleResult(); // 6、通过流程定义信息,得到部署id String deploymentId = processDefinition.getDeploymentId(); // 7、通过 RepositoryService 的方法,实现读取图片信息以及bpmn文件信息(输入流) // getResourceAsStream(String deploymentId, String resourceName) // processDefinition.getDiagramResourceName():获取流程图片名字 InputStream pngStream = repositoryService.getResourceAsStream(deploymentId, processDefinition.getDiagramResourceName()); // processDefinition.getResourceName():获取流程bpmn文件名字 InputStream bpmnStream = repositoryService.getResourceAsStream(deploymentId,processDefinition.getResourceName()); // 8、构建输出流 OutputStream pngOutputStream = new FileOutputStream("C:\\Users\\HongCheng\\Desktop\\holiday\\" + processDefinition.getDiagramResourceName()); OutputStream bpmnOutputStream = new FileOutputStream("C:\\Users\\HongCheng\\Desktop\\holiday\\" + processDefinition.getResourceName()); // 9、转换输入流并保存文件 IOUtils.copy(pngStream,pngOutputStream); IOUtils.copy(bpmnStream,bpmnOutputStream); // 10、关闭流 pngOutputStream.close(); bpmnOutputStream.close(); pngStream.close(); pngStream.close(); } }
流程变量
package activiti04; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.Setter; import org.activiti.engine.ProcessEngine; import org.activiti.engine.ProcessEngines; import org.activiti.engine.RuntimeService; import org.activiti.engine.runtime.ProcessInstance; import java.io.Serializable; import java.util.HashMap; /** * 启动流程实例,流程节点参与人使用UEL表达式 ${变量名} ${变量名.属性名} ${变量名.方法名()} * */ public class ActivitiStartInstanceUEL { public static void main(String[] args) { // 1、得到processEngine对象 ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); // 2、得到RuntimeService对象 RuntimeService runtimeService = processEngine.getRuntimeService(); // 3、设置assignee的值, 流程节点的参与人可以动态选择 HashMap<String, Object> map = new HashMap<>(); map.put("assignee","zhaoliu"); map.put("user",new User("苏七","666","suqi")); // 4、启动流程实例,同时传入流程定义的参数值 ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("holiday3", map); // 5、输出相关信息 System.out.println(processInstance.getProcessDefinitionName()); } } @Getter @Setter @AllArgsConstructor class User implements Serializable { private String name; private String id; private String assignee; public String getUserAssignee(){ return id + name + assignee; } }
流程变量是指在整个流程实例中使用的变量,activiti支持UEL表达式作为流程变量。
UEL表达式有三种写法: ${变量名} ${变量名.属性名} ${变量名.方法名()}
另外,流程变量分成全局流程变量和局部流程变量。
流程变量分为:
- 全局global变量,所有节点都可以用。
- 任务节点local变量,仅当前节点可用
全局的流程变量可以通过流程实例来添加,也可以通过任务节点来添加
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("holiday4", map);
runtimeService.setVariable(String processInstanceId, String variableName, Object value);
runtimeService.setVariables( String processInstanceId, Map<String, ? extends Object> variables);
taskService.complete(String taskId, Map<String, Object> variables)
taskService.setVariables(String taskId, Map<String, ? extends Object> variables)
taskService.setVariable(String taskId, String variableName, Object value)
局部流程变量:
只能在当前任务节点使用,一旦当前任务节点结束,这个流程变量就会被删除。
但是历史表里面依旧还是会保存我们的局部流程变量,依旧还是可以获取到。只是当前正在执行的任务不能直接获取局部流程变量了而已,曲线获取还是可以的。
局部流程变量的设置:
runtimeService.setVariableLocal(String processInstanceId, String variableName, Object value);
runtimeService.setVariablesLocal( String processInstanceId, Map<String, ? extends Object> variables);
taskService.setVariablesLocal(String taskId, Map<String, ? extends Object> variables)
taskService.setVariableLocal(String taskId, String variableName, Object value)
注意:如果局部变量是加在流程实例上,那依旧还是把所有任务节点共享
下面给一个使用流程变量来确定动态指定任务责任人的例子:
流程变量的使用大致就是这样子。
我们可以在数据库act_ru_variable、act_hi_varinst、act_ge_bytearray找到相关信息
任务监听器:
任务监听器是在发生对应的任务相关事件时执行的自定义java逻辑或表达式。
有四种类型的任务监听器,好像只是用在task 节点上才有用:
- create:任务创建后触发
- assignment:任务分配后触发
- complete:任务完成后触发
- all:所有任务事件都会触发
监听器的执行者有四种:
- java类:必须实现org.activiti.engine.delegate.TaskListener这个接口
- Expression 表达式:
- Delegate Expression 表达式:
- script脚本
我们可以在监听器里面改变任务处理人,也可以进行其他操作
动态选择任务节点的处理人:
参考前面流程变量时最后举得那个例子
简单的任务分支:
需要用到流程变量,流程变量可以是UEL表达式,但条件必须是boolean类型。
注意:
- 如果UEL表达式中流程变量名不存在会报错
- 如果UEL表达式中流程变量为空null,流程不按UEL表达式执行,直接流程结束
- 如果UEL表达式都不符合条件,流程结束
- 如果任务连线不设置条件,会走flow序号最小的那天=条路径
- 这种简单的分支下,如果多条分支的条件都成立时,会多条分支一起走,会出现问题。所以分支一般用网关
这种分支方式很简单,但也有一些限制,如果不是很复杂的流程,可以这样使用,但还是建议使用网关。
flow序号其实就是线的id
任务候选人/任务候选组:
我们之前指定任务都是使用assignee来指定具体哪一个人来执行任务,但是如果这个任务可以有多个执行人,也就是说张三、李四、王五、赵六四个人都可以执行同一个任务,那么就不能通过assignee来指定了。
此时就可以用candidate user(候选人)来指定一堆执行人,将来这一堆执行人都可以看到这个任务,但是只能有一个人将这个任务领取并执行。多个候选人之间通过逗号隔开
另外也有一个candidate groups(候选组)来指定一个人员组,在这个组里的人都是候选人,就不用一个个人来指定了。候选组其实也就是一个角色
组任务的办理流程:
1、指定候选人,或指定候选人组部署流程
2、创建流程实例
3、候选人查询当前待办任务
4、候选人领取任务,将组任务变成个人任务
***如果该候选人领取任务后又不想处理了,可以将任务归还,个人任务变成组任务
5、根据assignee来查询自己的任务
6、处理任务
taskService.claim(task.getId(),任务负责人);领取任务
taskService.setAssignee(task.getId(),null);放弃任务
taskService.setAssignee(task.getId(),任务负责人);委派任务
在我们正常的业务中,
assignee应该是我们的系统用户标识,可以是用户id,也可以是用户名,反正能唯一区分就行。其意思就是这个任务指定某个人完成,一般都是使用流程变量,动态决定谁可以做。例如我们在用钉钉发起审批时,有时就可以指定给哪个领导审。
candidate user(候选人)应该是多个系统用户标识,其意思就是这个任务可以是这堆人中的某一个来完成。
candidate groups(候选组)应该是用户的角色,在正常的系统中,使用角色将用户进行分组。而我们的实际生活中也是这样,有领导角色身份的人,才能进行最后的审批决定。
package activiti04.group; import activiti01.ActivitiDeployment; import org.activiti.engine.*; import org.activiti.engine.repository.Deployment; import org.activiti.engine.runtime.ProcessInstance; import org.activiti.engine.task.Task; import org.junit.Test; import java.io.InputStream; import java.util.HashMap; import java.util.List; import java.util.zip.ZipInputStream; /** * 组任务 * */ public class ActivitiTaskGroup { public static void main(String[] args) { ActivitiTaskGroup activitiTaskGroup = new ActivitiTaskGroup(); activitiTaskGroup.giveUpTask(); } /** * 放弃任务。 * 原理: * 直接将task的assignee设置成null,让候选人重新抢任务。 * 也可以将任务指定给某个具体的用户,相当于委派给别人执行 * 注意: * 如果这个任务不是指定候选组的,那么当这个人放弃后,将没有人可以再领取这个任务 */ @Test public void giveUpTask() { // 1、得到processEngine对象 ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); // 2、得到TaskService对象 TaskService taskService = processEngine.getTaskService(); // 3、查询用户的组任务 String userId = "hongcheng"; List<Task> taskList = taskService.createTaskQuery().processDefinitionKey("holidayGroup") .taskAssignee(userId).list(); // 4、放弃任务 for (Task task : taskList) { System.err.println("流程实例id ==> " + task.getProcessInstanceId()); System.err.println("任务定义key ==> " + task.getTaskDefinitionKey()); System.err.println("任务id ==> " + task.getId()); System.err.println("任务处理人 ==> " + task.getAssignee()); System.err.println("任务名 ==> " + task.getName()); if (userId.equals(task.getAssignee())) { // 放弃任务 taskService.setAssignee(task.getId(), null); // 任务委派 // taskService.setAssignee(task.getId(),"hongcheng"); } System.err.println("任务放弃成功 ==> " + userId); } } /** * 认领组任务,claim也可以说是对外宣称任务 * */ @Test public void claimGroupTaskCandidateUserGroup() { // 1、得到processEngine对象 ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); // 2、得到TaskService对象 TaskService taskService = processEngine.getTaskService(); // 3、查询用户的组任务 String userId = "hongcheng"; List<Task> taskList = taskService.createTaskQuery().processDefinitionKey("holidayGroup") .taskCandidateGroup("leader").list(); // 4、认领任务 for (Task task : taskList) { System.err.println("流程实例id ==> "+task.getProcessInstanceId()); System.err.println("任务定义key ==> "+task.getTaskDefinitionKey()); System.err.println("任务id ==> "+task.getId()); System.err.println("任务处理人 ==> "+task.getAssignee()); System.err.println("任务名 ==> "+task.getName()); // 认领任务 taskService.claim(task.getId(),userId); System.err.println("任务认领成功 ==> "+userId); } } /** * 查询组任务,也就是候选人有自己的任务,此时该组任务并没有真正确定谁是任务的最终负责人assignee * 按候选人组进行查询 * */ @Test public void queryGroupTaskCandidateUserGroup() { // 1、得到processEngine对象 ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); // 2、得到TaskService对象 TaskService taskService = processEngine.getTaskService(); // 3、查询用户的组任务 List<Task> taskList = taskService.createTaskQuery().processDefinitionKey("holidayGroup") .taskCandidateGroup("leader").list(); // 5、输出 for (Task task : taskList) { System.err.println("流程实例id ==> "+task.getProcessInstanceId()); System.err.println("任务定义key ==> "+task.getTaskDefinitionKey()); System.err.println("任务id ==> "+task.getId()); System.err.println("任务处理人 ==> "+task.getAssignee()); System.err.println("任务名 ==> "+task.getName()); } } /** * 认领组任务,claim也可以说是对外宣称任务 * */ @Test public void claimGroupTaskCandidateUser() { // 1、得到processEngine对象 ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); // 2、得到TaskService对象 TaskService taskService = processEngine.getTaskService(); // 3、查询用户的组任务 String userId = "wangwu"; List<Task> taskList = taskService.createTaskQuery().processDefinitionKey("holidayGroup") .taskCandidateUser(userId).list(); // 4、认领任务 for (Task task : taskList) { System.err.println("流程实例id ==> "+task.getProcessInstanceId()); System.err.println("任务定义key ==> "+task.getTaskDefinitionKey()); System.err.println("任务id ==> "+task.getId()); System.err.println("任务处理人 ==> "+task.getAssignee()); System.err.println("任务名 ==> "+task.getName()); // 认领任务 taskService.claim(task.getId(),userId); System.err.println("任务认领成功 ==> "+userId); } } /** * 查询组任务,也就是候选人有自己的任务,此时该组任务并没有真正确定谁是任务的最终负责人assignee * 按候选人列表进行查询 * */ @Test public void queryGroupTaskCandidateUser() { // 1、得到processEngine对象 ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); // 2、得到TaskService对象 TaskService taskService = processEngine.getTaskService(); // 3、查询用户的组任务 List<Task> taskList = taskService.createTaskQuery().processDefinitionKey("holidayGroup") .taskCandidateUser("lisi").list(); // 5、输出 for (Task task : taskList) { System.err.println("流程实例id ==> "+task.getProcessInstanceId()); System.err.println("任务定义key ==> "+task.getTaskDefinitionKey()); System.err.println("任务id ==> "+task.getId()); System.err.println("任务处理人 ==> "+task.getAssignee()); System.err.println("任务名 ==> "+task.getName()); } } /** * 完成任务 * */ @Test public void complete() { // 1、得到processEngine对象 ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); // 2、得到TaskService对象 TaskService taskService = processEngine.getTaskService(); // 3、结合任务查询,将查询到的任务进行处理 List<Task> taskList = taskService.createTaskQuery() .processDefinitionKey("holidayGroup") .taskAssignee("zhangsan") .list(); // 4、完成任务 for (Task task : taskList) { taskService.complete(task.getId()); System.err.println(task.getName()); } } /** * 查询自己的个人任务 * */ @Test public void taskQuery() { // 1、得到processEngine对象 ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); // 2、得到TaskService对象 TaskService taskService = processEngine.getTaskService(); // 3、用流程定义的key和负责人assignee来实现当前用户的任务列表查询 List<Task> taskList = taskService.createTaskQuery() .processDefinitionKey("holidayGroup").taskAssignee("zhangsan") .list(); // 4、任务列表查询 for (Task task : taskList) { System.err.println("流程实例id ==> "+task.getProcessInstanceId()); System.err.println("任务定义key ==> "+task.getTaskDefinitionKey()); System.err.println("任务id ==> "+task.getId()); System.err.println("任务处理人 ==> "+task.getAssignee()); System.err.println("任务名 ==> "+task.getName()); } } /** * 启动流程 * */ @Test public void startInstance() { // 1、得到processEngine对象 ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); // 2、得到RuntimeService对象 RuntimeService runtimeService = processEngine.getRuntimeService(); // 3、设置assignee的值, 流程节点的参与人可以动态选择 HashMap<String, Object> map = new HashMap<>(); // 4、启动流程实例,同时传入流程定义的参数值 ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("holidayGroup", map); // 5、输出相关信息 System.out.println(processInstance.getProcessDefinitionName()); System.out.println(processInstance.getId()); } /** * 部署流程 * */ @Test public void deployment() { // 1、创建ProcessEngine流程引擎对象 ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); // 2、得到Repositoryervice实例 RepositoryService repositoryService = processEngine.getRepositoryService(); // 3、获取压缩文件 InputStream inputStream = ActivitiDeployment.class.getClassLoader().getResourceAsStream("bpmn/group/holidayGroup.zip"); // 4、创建一个ZipInputStream流 ZipInputStream zipInputStream = new ZipInputStream(inputStream); // 3、进行部署,bpmn文件是一定要的,图片文件可以没有,流程key相同的话,会使用最新部署的流程定义 Deployment deployment = repositoryService.createDeployment() .addZipInputStream(zipInputStream) .name("请假申请流程候选人") .deploy(); // 4、输出部署的信息 System.out.println(deployment.getName()); System.out.println(deployment.getId()); } }
<?xml version="1.0" encoding="UTF-8"?> <definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:activiti="http://activiti.org/bpmn"
xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI"
xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC"
xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI"
typeLanguage="http://www.w3.org/2001/XMLSchema"
expressionLanguage="http://www.w3.org/1999/XPath"
targetNamespace="http://www.activiti.org/test"> <process id="holidayGroup" name="候选人流程" isExecutable="true"> <startEvent id="startevent1" name="Start"></startEvent> <userTask id="usertask1" name="填写请假申请" activiti:assignee="zhangsan"></userTask> <sequenceFlow id="flow1" sourceRef="startevent1" targetRef="usertask1"></sequenceFlow> <userTask id="usertask2" name="部门经理审核" activiti:candidateUsers="lisi,wangwu"></userTask> <sequenceFlow id="flow2" sourceRef="usertask1" targetRef="usertask2"></sequenceFlow> <userTask id="usertask3" name="总经理审核" activiti:candidateGroups="leader"></userTask> <sequenceFlow id="flow3" sourceRef="usertask2" targetRef="usertask3"></sequenceFlow> <endEvent id="endevent1" name="End"></endEvent> <sequenceFlow id="flow4" sourceRef="usertask3" targetRef="endevent1"></sequenceFlow> </process> <bpmndi:BPMNDiagram id="BPMNDiagram_holidayGroup"> <bpmndi:BPMNPlane bpmnElement="holidayGroup" id="BPMNPlane_holidayGroup"> <bpmndi:BPMNShape bpmnElement="startevent1" id="BPMNShape_startevent1"> <omgdc:Bounds height="35.0" width="35.0" x="110.0" y="230.0"></omgdc:Bounds> </bpmndi:BPMNShape> <bpmndi:BPMNShape bpmnElement="usertask1" id="BPMNShape_usertask1"> <omgdc:Bounds height="55.0" width="105.0" x="190.0" y="220.0"></omgdc:Bounds> </bpmndi:BPMNShape> <bpmndi:BPMNShape bpmnElement="usertask2" id="BPMNShape_usertask2"> <omgdc:Bounds height="55.0" width="105.0" x="340.0" y="220.0"></omgdc:Bounds> </bpmndi:BPMNShape> <bpmndi:BPMNShape bpmnElement="usertask3" id="BPMNShape_usertask3"> <omgdc:Bounds height="55.0" width="105.0" x="490.0" y="220.0"></omgdc:Bounds> </bpmndi:BPMNShape> <bpmndi:BPMNShape bpmnElement="endevent1" id="BPMNShape_endevent1"> <omgdc:Bounds height="35.0" width="35.0" x="640.0" y="230.0"></omgdc:Bounds> </bpmndi:BPMNShape> <bpmndi:BPMNEdge bpmnElement="flow1" id="BPMNEdge_flow1"> <omgdi:waypoint x="145.0" y="247.0"></omgdi:waypoint> <omgdi:waypoint x="190.0" y="247.0"></omgdi:waypoint> </bpmndi:BPMNEdge> <bpmndi:BPMNEdge bpmnElement="flow2" id="BPMNEdge_flow2"> <omgdi:waypoint x="295.0" y="247.0"></omgdi:waypoint> <omgdi:waypoint x="340.0" y="247.0"></omgdi:waypoint> </bpmndi:BPMNEdge> <bpmndi:BPMNEdge bpmnElement="flow3" id="BPMNEdge_flow3"> <omgdi:waypoint x="445.0" y="247.0"></omgdi:waypoint> <omgdi:waypoint x="490.0" y="247.0"></omgdi:waypoint> </bpmndi:BPMNEdge> <bpmndi:BPMNEdge bpmnElement="flow4" id="BPMNEdge_flow4"> <omgdi:waypoint x="595.0" y="247.0"></omgdi:waypoint> <omgdi:waypoint x="640.0" y="247.0"></omgdi:waypoint> </bpmndi:BPMNEdge> </bpmndi:BPMNPlane> </bpmndi:BPMNDiagram> </definitions>
网关:
网关其实就是控制流程进行分支、汇聚的一个节点
activiti里面网关有4种:
- 排他网关
- 并行网关
- 包含网关
- 事件网关
和简单任务分支相比就是多个了网关节点,其他东西不变。
排他网关:
- 只会执行一条路径,如果有多条路径可以走,那么默认会走下一个节点的id比较小的那条路径。可以防止多条分支同时成立,同时走了多条分支。
- 如果没有一个分支可以走,那就会报错。
- 必须保证有一条路径可以走
并行网关:
- 可以作为多条分支汇聚(只有所有汇聚分支都完成,才会进去下一步)。
- 也可以作为多条分支分散,分散分支时没有条件的,即便你设置了条件,也会被忽略掉,他肯定是全部分支都要执行。
包含网关:
- 同时拥有排他网关和并行网关的特性。也就是说根据条件分散执行条件成立的一个或多个分支,也可以等待一个或多个分支汇聚。
针对上图,当userType==1时,会同时走常规项目和抽血化验这两条路径,如果userType==2,会走三条路径
任务定时提醒:
我们实际的流程中,经常会有人一直不审批,所以就需要有一个提醒机制
这个时候我们就需要用到边界事件。有3篇博客介绍的挺不错的,有兴趣的自己去看:
https://blog.csdn.net/qq_33333654/article/details/101374202
https://blog.csdn.net/qq_33333654/article/details/101373157
https://www.cnblogs.com/dengjiahai/p/6942310.html
iso 8601格式解析
R2/2015-06-04T19:25:16.828696-07:00/P1DT10S
上面的字符串通过"/"分为了三部分即:
重复次数/开始时间/运行间隔
重复次数
- R - 将永远重复
- R1 - 将重复一次
- R231 - 将重复231次。
开始时间
任务第一次运行的时间。如果开始日期时间已经过去,将返回一个错误。
其中"T"用来分割日期和时间,时间后面跟着的"-07:00"表示西七区,注意"-"是连字符,不是减号。
时区默认是0时区,可以用"Z"表示,也可以不写。
对于我国,要使用"+08:00",表示东八区。
上面的字符串表示 2015年6月4日,19点25分16秒828696纳秒,西七区。
运行间隔
运行间隔以"P"开始,和上面一样也是用"T"分割日期和时间,如P1Y2M10DT2H30M15S
- P 开始标记
- 1Y - 一年
- 2M - 两个月
- 10D - 十天
- T - 时间和日期分的割标记
- 2H - 两个小时
- 30M - 三十分钟
- 15S 十五秒钟
例子,注意如果没有年月日,"T"也不能省略
- P1DT1M - 一天一分钟执行一次
- P1W - 一周执行一次
- PT1H - 一小时执行一次
- PT10S - 十秒执行一次
Time duration:延迟时间,使用“运行间隔”的格式,例如:PT1M30S,一分30秒后执行。
Time date:可以是java.util.Date的变量,也可以是符合ISO 8601 的一个时间点字符串
Time cycle:格式:“循环次数/执行间隔”,例如:R3/P1DT3H,循环执行3次,每次间隔1天3小时。这里也可以使用cron表达式。
cancel activiti:是否取消当前节点任务,true表示只要超时了,就取消这个当前节点任务。
原理:activiti会在act_ru_timer_job表增加一条记录,到指定的时间时,会读取这条记录,同时判断这个任务完成没有,如果没有完成,就会执行TimerBoundaryEvent连着的下一个任务。如果制定了要取消当前任务,就会把当前任务取消。
注意:想要启动定时器,加上这段配置
Service Task服务任务:
之前我们都是使用user task,那这两者有啥区别呢?
user task是我们的用户自定义任务,需要我们自己来手动完成
但是service task是服务任务,只要进入了这个任务,系统就会自动执行,不需要我们手动执行。
我们上个例子就用了service task来做循环提醒
需要注意的是,你得实现
org.activiti.engine.delegate.JavaDelegate这个接口
流程驳回:
我们的实际中的流程经常会被打回,这种情况下我个建议使用排他网关进行分支。当然,网上也有其他的方式进行撤回,但我还是感觉这样子更简单点