1. 获取工作流引擎
// 方式一:
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 方式二:可以自定义配置文件的名字和ProcessEngineConfiguration对应的bean id值
ProcessEngineConfiguration processEngineConfiguration = ProcessEngineConfiguration.createProcessEngineConfigurationFromResource("activiti.cfg.xml", processEngineConfiguration);
ProcessEngine processEngine = processEngineConfiguration.buildProcessEngine();
2. 部署
@Test
public void testAddBpmnModel() {
BpmnModel bpmnModel = repositoryService.getBpmnModel("helloworld:1:3");
UserTask userTask = (UserTask)bpmnModel.getFlowElement("_4");
userTask.setAssignee("hrbp");
userTask.setName("HRBP审批");
// insert ACT_RE_DEPLOYMENT
// insert ACT_RE_PROCDEF
// insert ACT_GE_BYTEARRAY
repositoryService.createDeployment()
.addBpmnModel("bpmn/helloworld.bpmn", bpmnModel)
.deploy();
}
压缩包方式部署
压缩包方式部署可以将多个.bpmn文件和.png文件打成一个.zip文件,然后一次性部署压缩包中的所有资源,个人感觉用处不大,打成压缩包不便于浏览.bpmn文件。
@Test
public void deployProcessByZip() {
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 定义zip输入流
InputStream inputStream = this.getClass().getClassLoader()
.getResourceAsStream("bpmn/process.zip");
ZipInputStream zipInputStream = new ZipInputStream(inputStream);
RepositoryService repositoryService = processEngine.getRepositoryService();
// 流程部署
Deployment deployment = repositoryService.createDeployment()
.addZipInputStream(zipInputStream)
.deploy();
System.out.println("流程部署id:" + deployment.getId());
System.out.println("流程部署名称:" + deployment.getName());
}
3. 删除部署
删除部署会删除和此次部署相关的所有数据,包括资源表ACT_RE_PROCDEF和ACT_GE_BYTEARRAY。一般情况下很少删除。
/**
* 如果有未完成的流程实例删除不了会抛异常
* ACT_RE_PROCDEF
* ACT_GE_BYTEARRAY
* ACT_RE_DEPLOYMENT
* ACT_RU_IDENTITYLINK
*/
@Test
public void deleteDeployment() {
String deploymentId = "1";
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
processEngine.getRepositoryService()
.deleteDeployment(deploymentId);
}
/**
* 级联删除:删除所有和此次相关的数据,包括未完成的流程实例
* ACT_RE_DEPLOYMENT
* ACT_RE_PROCDEF
* ACT_GE_BYTEARRAY
* ACT_RU_EXECUTION
* ACT_RU_TASK
* ACT_RU_IDENTITYLINK
* ACT_RU_VARIABLE
* ACT_HI_COMMENT
* ACT_HI_PROCINST
* ACT_HI_TASKINST
* ACT_HI_IDENTITYLINK
* ACT_HI_ACTINST
* ACT_HI_VARINST
*/
@Test
public void deleteDeployment() {
String deploymentId = "1";
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// true:表示级联删除
processEngine.getRepositoryService()
.deleteDeployment(deploymentId, true);
}
4. 查询流程定义
@Test
public void testQueryProcessDefinition() {
/**
*select
* distinct RES.*
* FROM ACT_RE_PROCDEF RES
* WHERE RES.KEY_ = 'helloworld-var'
* order by RES.ID_ asc
* LIMIT 2147483647 OFFSET 0;
*/
List<ProcessDefinition> list = ProcessEngines.getDefaultProcessEngine()
.getRepositoryService()
.createProcessDefinitionQuery()
.processDefinitionKey("helloworld-var")
.list();
}
5. 获取.bpmn、.png文件
public void saveBpmnFile() throws IOException {
// 1、得到引擎
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 2、获取repositoryService
RepositoryService repositoryService = processEngine.getRepositoryService();
// 3、得到查询器:ProcessDefinitionQuery,设置查询条件,得到想要的流程定义
ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
.processDefinitionKey("helloworld-var")
.singleResult();
// 4、通过流程定义信息,得到部署ID
String deploymentId = processDefinition.getDeploymentId();
// 5、通过repositoryService的方法,实现读取图片信息和bpmn信息
// png图片的流
InputStream pngInput = repositoryService.getResourceAsStream(deploymentId, processDefinition.getDiagramResourceName());
// bpmn文件的流
InputStream bpmnInput = repositoryService.getResourceAsStream(deploymentId, processDefinition.getResourceName());
// 6、构造OutputStream流
File file_png = new File("d:/helloworld-var.png");
File file_bpmn = new File("d:/helloworld-var.bpmn");
FileOutputStream bpmnOut = new FileOutputStream(file_bpmn);
FileOutputStream pngOut = new FileOutputStream(file_png);
// 7、输入流,输出流的转换
IOUtils.copy(pngInput,pngOut);
IOUtils.copy(bpmnInput,bpmnOut);
// 8、关闭流
pngOut.close();
bpmnOut.close();
pngInput.close();
bpmnInput.close();
}
6. 查询流程元素FlowElement
根据流程定义Id查询ACT_RE_PROCDEF.DEPLOYMENT_ID_,进而查询ACT_GE_BYTEARRAY.BYTES_,然后解析.bpmn文件,封装成Model对象。
@Test
public void testQueryFlowElement(){
BpmnModel bpmnModel = repositoryService.getBpmnModel("helloworld:1:3");
// Process就是.bpmn文件中的<process>节点
Process process = bpmnModel.getProcesses().get(0);
Collection<FlowElement> flowElements = process.getFlowElements();
for (FlowElement flowElement : flowElements) {
if (flowElement instanceof UserTask) {
UserTask userTask = (UserTask)flowElement;
System.out.println(userTask);
}
}
// 直接根据Id属性获取UserTask
UserTask userTask = (UserTask)bpmnModel.getFlowElement("_4");
System.out.println(userTask);
}
7. 获取指定节点的下一个节点
/**
* 获取指定节点的下一个节点
* 排它网关:获取所有分支
*/
@Test
public void getNextNode() {
BpmnModel bpmnModel = repositoryService.getBpmnModel("leaveProcess:1:3");
UserTask userTask = (UserTask) bpmnModel.getFlowElement("pmApprove");
// 获取该节点出去的SequenceFlow
List<SequenceFlow> outgoingFlows = userTask.getOutgoingFlows();
for (SequenceFlow sequenceFlow : outgoingFlows) {
FlowElement flowElement = bpmnModel.getFlowElement(sequenceFlow.getTargetRef());
if (flowElement instanceof ExclusiveGateway) {
ExclusiveGateway exclusiveGateway = (ExclusiveGateway) flowElement;
List<SequenceFlow> gatewayOutgoingFlows = exclusiveGateway.getOutgoingFlows();
for (SequenceFlow gatewaySequenceFlow : gatewayOutgoingFlows) {
FlowElement flowElementItem = bpmnModel.getFlowElement(gatewaySequenceFlow.getTargetRef());
System.out.println(flowElementItem.getId() + "-" + flowElementItem.getName());
}
} else {
System.out.println(flowElement.getId() + "-" + flowElement.getName());
}
}
}
8. 查询正在进行的任务
@Test
public void testQueryRuTask() {
// 查询哪个工作流下的哪个负责人的待办任务
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
List<Task> list = processEngine.getTaskService()
.createTaskQuery()
.processDefinitionKey("helloworld-var")
.taskAssignee("zhangsan")
.list();
// [Task[id=2508, name=请假申请]]
System.out.println(list);
}
select
distinct RES.*
FROM ACT_RU_TASK RES
INNER JOIN ACT_RE_PROCDEF D ON RES.PROC_DEF_ID_ = D.ID_
WHERE RES.ASSIGNEE_ = 'zhangsan' and D.KEY_ = 'helloworld-var'
order by RES.ID_ asc
LIMIT 2147483647 OFFSET 0;
9. 查询历史流程实例
@Test
public void testQueryHistoryProcessInstance() {
ProcessEngines.getDefaultProcessEngine()
.getHistoryService()
.createHistoricProcessInstanceQuery()
.processInstanceId("2501")
.list();
}
select
distinct RES.* ,
DEF.KEY_ as PROC_DEF_KEY_,
DEF.NAME_ as PROC_DEF_NAME_,
DEF.VERSION_ as PROC_DEF_VERSION_,
DEF.DEPLOYMENT_ID_ as DEPLOYMENT_ID_
FROM ACT_HI_PROCINST RES
left outer join ACT_RE_PROCDEF DEF ON RES.PROC_DEF_ID_ = DEF.ID_
WHERE RES.PROC_INST_ID_ = '2501'
order by RES.ID_ asc
LIMIT 2147483647
OFFSET 0;
10. 查询历史活动
@Test
public void queryHistoryActivity() {
// select
// RES.*
// FROM ACT_HI_ACTINST RES
// WHERE RES.PROC_DEF_ID_ = 'helloworld-var:1:4'
// order by RES.ID_ asc
// LIMIT 2147483647 OFFSET 0;
List<HistoricActivityInstance> activityInstanceList = ProcessEngines.getDefaultProcessEngine()
.getHistoryService()
.createHistoricActivityInstanceQuery()
.processDefinitionId("helloworld-var:1:4")
.list();
}
- 挂起和激活流程实例
有时候在特殊的时间内会需要暂停流程的发起,比如月底不允许发起新的请假审批,因为财务要计算工资了等情况就需要将发起申请请求暂停掉,此时可以将流程定义挂起。
有时候我们只会暂停某一个流程实例,比如张三是否要出差现在还不能确定,先暂停他的出差申请,等过几天确定了再继续走流程,这就是挂起单个流程实例。
流程实例挂起相当于暂停了,不允许被继续执行会抛异常(ActivitiException: Cannot complete supended task),也不允许启动新的实例。主要是设置数据库字段挂起状态字段 SUSPENSION_STATE_ 挂起状态 2:挂起,1:激活。
挂起流程定义就是挂起该流程下的所有流程实例。
@Test
public void testSuspendProcessDefinition() {
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
RepositoryService repositoryService = processEngine.getRepositoryService();
ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
.processDefinitionKey("helloworld-var")
.singleResult();
if (processDefinition.isSuspended()) {
repositoryService.activateProcessDefinitionById(processDefinition.getId());
} else {
repositoryService.suspendProcessDefinitionById(processDefinition.getId());
}
}
update ACT_RE_PROCDEF
set
REV_ = 2,
SUSPENSION_STATE_ = 2
WHERE ID_ = 'helloworld-var:1:4' and REV_ = 1;
挂起单个流程实例:分别设置ACT_RU_TASK和ACT_RU_EXECUTION的SUSPENSION_STATE_ = 2
@Test
public void testSuspendedProcessInstance() {
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
RuntimeService runtimeService = processEngine.getRuntimeService();
ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
.processInstanceId("2501")
.singleResult();
boolean suspended = processInstance.isSuspended();
if (suspended) {
runtimeService.activateProcessInstanceById(processInstance.getId());
} else {
runtimeService.suspendProcessInstanceById(processInstance.getId());
}
}
-- ACT_RU_TASK.SUSPENSION_STATE_ = 2
update ACT_RU_TASK
SET
SUSPENSION_STATE_ = 2,
WHERE ID_= '2508' and REV_ = 1;
-- ACT_RU_EXECUTION.SUSPENSION_STATE_ = 2
-- 流程实例
update ACT_RU_EXECUTION
set
SUSPENSION_STATE_ = 2
WHERE ID_ = '2501' and REV_ = 1;
-- 该流程实例下的其它执行流
update ACT_RU_EXECUTION
set
SUSPENSION_STATE_ = 2
WHERE ID_ = '2505' and REV_ = 1;
12. 候选人CandidateUsers
候选人就是不指定任务节点的负责人,而是在实际审批的过程中由候选人自己将任务的负责人设置为自己或者候选人中的其他人,然后再由负责人去完成审批。
经理审批不指定Assignee复制人了,而是指定候选人列表。
@Test
public void startProcessInstance() {
String businessKey = "666";
Map<String, Object> variables = new HashMap<>();
variables.put("creator", "zhangsan");
// 指定候选人列表
variables.put("pmCandidateUsers", "狗经理,猪经理");
ProcessInstance processInstance = ProcessEngines.getDefaultProcessEngine()
.getRuntimeService()
.startProcessInstanceByKey("candidateUsers", businessKey, variables);
}
@Test
public void completeTask() {
TaskService taskService = ProcessEngines.getDefaultProcessEngine().getTaskService();
Task task = taskService
.createTaskQuery()
.processDefinitionKey("candidateUsers")
.taskAssignee("zhangsan")
.singleResult();
taskService.complete(task.getId());
}
当发起人zhangsan完成自己的审批后到底下一个任务节点经理审批时,此时的ASSIGNEE_字段是空的。
/**
* 猪经理拾取任务,即将任务负责人设置为猪经理
*/
@Test
public void testTaskClaim() {
String currentUserId = "猪经理";
TaskService taskService = ProcessEngines.getDefaultProcessEngine().getTaskService();
Task task = taskService.createTaskQuery()
.processDefinitionKey("candidateUsers")
.taskCandidateUser(currentUserId)
.singleResult();
// update ACT_RU_TASK SET ASSIGNEE_ = '猪经理' WHERE ID_= '5002' and REV_ = 1;
// update ACT_HI_ACTINST set ASSIGNEE_ = '猪经理' WHERE ID_ = '5001';
// update ACT_HI_TASKINST set ASSIGNEE_ = '猪经理' WHERE ID_= '5002' and REV_ = 1;;
taskService.claim(task.getId(), currentUserId);
}
// 如果当前任务的负责人是自己,而自己却不想审批,可以直接将任务负责人设置为null或者交接给其他候选人
@Test
public void testSetAssignee() {
TaskService taskService = ProcessEngines.getDefaultProcessEngine().getTaskService();
Task task = taskService
.createTaskQuery()
.processDefinitionKey("candidateUsers")
.taskAssignee("猪经理")
.singleResult();
taskService.setAssignee(task.getId(), null);
taskService.setAssignee(task.getId(), "狗经理");
}
注意:Activiti7中对于setAssignee()方法中的参数并没有做什么校验,可以传任意负责人,即使负责人不属于CandidateUsers也可以设置成功。如果有需要的话需要自己在代码中检查当前设置的负责人是否属于候选人列表中。
update ACT_RU_TASK SET ASSIGNEE_ = null WHERE ID_= '5002' and REV_ = 2;
-- -----------------------------------------------------------------------------------------------
update ACT_HI_ACTINST set ASSIGNEE_ = null WHERE ID_ = '5001';
-- -----------------------------------------------------------------------------------------------
update ACT_HI_TASKINST set ASSIGNEE_ = null WHERE ID_ = '5002';
如果想要同时查询某个人的候选任务和待办任务可以使用 taskCandidateOrAssigned(String userIdForCandidateAndAssignee)
13. 添加审批意见
// 设置当前用户id,最终会保存到act_hi_comment.user_id_字段中
Authentication.setAuthenticatedUserId("666");
// 添加审批意见
taskService.addComment(task.getId(), task.getProcessInstanceId(), "MyCustomComment", JSONObject.toJSONString(jsonObject));
14. 设置任务办理人
实际工作中有的时候我们在画.bpmn文件时不会设置每个UserTask对应的Assignee(即不写死也不设置变量)而是等到即将完成任务的时候再设置任务负责人。这种情况常用于下个任务的负责人由上个任务完成时指定。
taskService.setAssignee(String taskId, String userId);
taskService.complete(taskId);