上一篇文章大概讲了下什么是流程引擎,为什么我们要用流程引擎,他的基本原理是啥,以及怎么进行基本的使用,这篇文章我们再讲下其他的一些使用。

删除流程部署
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类型。

注意:

  1. 如果UEL表达式中流程变量名不存在会报错
  2. 如果UEL表达式中流程变量为空null,流程不按UEL表达式执行,直接流程结束
  3. 如果UEL表达式都不符合条件,流程结束
  4. 如果任务连线不设置条件,会走flow序号最小的那天=条路径
  5. 这种简单的分支下,如果多条分支的条件都成立时,会多条分支一起走,会出现问题。所以分支一般用网关

这种分支方式很简单,但也有一些限制,如果不是很复杂的流程,可以这样使用,但还是建议使用网关。

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这个接口

流程驳回:

我们的实际中的流程经常会被打回,这种情况下我个建议使用排他网关进行分支。当然,网上也有其他的方式进行撤回,但我还是感觉这样子更简单点

activiti学习笔记二的更多相关文章

  1. Activiti 学习笔记记录(二)

    上一篇:Activiti 学习笔记记录 导读:对于工作流引擎的使用,我们都知道,需要一个业务事件,比如请假,它会去走一个流程(提交申请->领导审批---(批,不批)---->结束),Act ...

  2. Activiti 学习笔记记录(2016-8-31)

    上一篇:Activiti 学习笔记记录(二) 导读:上一篇学习了bpmn 画图的常用图形标记.那如何用它们组成一个可用文件呢? 我们知道 bpmn 其实是一个xml 文件

  3. activiti学习笔记一

    activiti学习笔记 在讲activiti之前我们必须先了解一下什么是工作流,什么是工作流引擎. 在我们的日常工作中,我们会碰到很多流程化的东西,什么是流程化呢,其实通俗来讲就是有一系列固定的步骤 ...

  4. WPF的Binding学习笔记(二)

    原文: http://www.cnblogs.com/pasoraku/archive/2012/10/25/2738428.htmlWPF的Binding学习笔记(二) 上次学了点点Binding的 ...

  5. AJax 学习笔记二(onreadystatechange的作用)

    AJax 学习笔记二(onreadystatechange的作用) 当发送一个请求后,客户端无法确定什么时候会完成这个请求,所以需要用事件机制来捕获请求的状态XMLHttpRequest对象提供了on ...

  6. [Firefly引擎][学习笔记二][已完结]卡牌游戏开发模型的设计

    源地址:http://bbs.9miao.com/thread-44603-1-1.html 在此补充一下Socket的验证机制:socket登陆验证.会采用session会话超时的机制做心跳接口验证 ...

  7. JMX学习笔记(二)-Notification

    Notification通知,也可理解为消息,有通知,必然有发送通知的广播,JMX这里采用了一种订阅的方式,类似于观察者模式,注册一个观察者到广播里,当有通知时,广播通过调用观察者,逐一通知. 这里写 ...

  8. java之jvm学习笔记二(类装载器的体系结构)

    java的class只在需要的时候才内转载入内存,并由java虚拟机的执行引擎来执行,而执行引擎从总的来说主要的执行方式分为四种, 第一种,一次性解释代码,也就是当字节码转载到内存后,每次需要都会重新 ...

  9. Java IO学习笔记二

    Java IO学习笔记二 流的概念 在程序中所有的数据都是以流的方式进行传输或保存的,程序需要数据的时候要使用输入流读取数据,而当程序需要将一些数据保存起来的时候,就要使用输出流完成. 程序中的输入输 ...

随机推荐

  1. (linux)Centos 7 xfsdump文件系统的备份和恢复

         XFS提供了 xfsdump 和 xfsrestore 工具协助备份XFS文件系统中的数据.xfsdump 按inode顺序备份一个XFS文件系统. centos7选择xfs格式作为默认文件 ...

  2. Java实现 LeetCode 761 特殊的二进制序列(括号问题)

    761. 特殊的二进制序列 特殊的二进制序列是具有以下两个性质的二进制序列: 0 的数量与 1 的数量相等. 二进制序列的每一个前缀码中 1 的数量要大于等于 0 的数量. 给定一个特殊的二进制序列 ...

  3. (Java实现) 洛谷 P1387 最大正方形

    题目描述 在一个n*m的只包含0和1的矩阵里找出一个不包含0的最大正方形,输出边长. 输入输出格式 输入格式: 输入文件第一行为两个整数n,m(1<=n,m<=100),接下来n行,每行m ...

  4. (Java实现) 均分纸牌

    题目描述 有 N 堆纸牌,编号分别为 1,2,-, N.每堆上有若干张,但纸牌总数必为 N 的倍数.可以在任一堆上取若于张纸牌,然后移动. 移牌规则为:在编号为 1 堆上取的纸牌,只能移到编号为 2 ...

  5. Java实现 蓝桥杯 算法训练 数字三角形

    算法训练 数字三角形 时间限制:1.0s 内存限制:256.0MB 问题描述 (图3.1-1)示出了一个数字三角形. 请编一个程序计算从顶至底的某处的一条路 径,使该路径所经过的数字的总和最大. ●每 ...

  6. Java实现 LeetCode 415 字符串相加

    415. 字符串相加 给定两个字符串形式的非负整数 num1 和num2 ,计算它们的和. 注意: num1 和num2 的长度都小于 5100. num1 和num2 都只包含数字 0-9. num ...

  7. Java实现 蓝桥杯VIP 算法训练求先序排列

    问题描述 给出一棵二叉树的中序与后序排列.求出它的先序排列.(约定树结点用不同的大写字母表示,长度<=8). 输入格式 两行,每行一个字符串,分别表示中序和后序排列 输出格式 一个字符串,表示所 ...

  8. java实现蔬菜价格计算

    ** 蔬菜价格计算** 计算蔬菜总价 为了丰富群众菜篮子,平抑菜价,相关部分组织了蔬菜的调运.今某箱中有多个品种的蔬菜.蔬菜的单价(元/公斤)存放在price数组中,蔬菜的重量(公斤)存放在weigh ...

  9. java实现第六届蓝桥杯生成回文数

    生成回文数 所谓回文数就是左右对称的数字,比如: 585,5885,123321- 当然,单个的数字也可以算作是对称的. 小明发现了一种生成回文数的方法: 比如,取数字19,把它与自己的翻转数相加: ...

  10. WPF 学习(一)

    一.WPF介绍 WPF全称 Windows Presentation Foundation,干啥用的? 主要是用来制作Windows桌面客户端软件的. .Net平台下制作Windows桌面客户端软件主 ...