SpringBoot 整合Activiti(二)——流程审批完整示例

前两天做了一个SpringBoot整合Activiti的完整示例,功能包括:退回/通过(节点条件)、指定办理人、生成流程图、高亮显示已办节点、查询任务列表(办理人)等,下面先简单记录(含完整代码),十六上班后再详细补充。

1、画流程图:

SpringBoot 整合Activiti(二)——流程审批完整示例_第1张图片

高亮生成的流程图(流程已至办理):

SpringBoot 整合Activiti(二)——流程审批完整示例_第2张图片

这个流程图比较简单,这里不介绍如何画的了(记得让文件名称与id相同),下面有最终的流程图源码。

2、设置业户提交、资料不全、已签发的事件触发代码:

点击选择事件节点,下方properties里选择如下图:

SpringBoot 整合Activiti(二)——流程审批完整示例_第3张图片

找不到properties的话 要选择Activiti模式就可以:


#{activityDemoServiceImpl.updateBizStatus(execution,"tj")} 表示将使用activityDemoServiceImpl服务的updateBizStatus方法,里面有两个参数,execution是当前流程节点对象,可获取流程信息及业务key等,"tj"字符串参数。(这里设置的服务名是小写开头,实际服务名是大写开头,否则找不到——不知道是不是因为我用的5.22)

3、指定受理/审批 办理人员:

也是选中,然后如下图操作:

SpringBoot 整合Activiti(二)——流程审批完整示例_第4张图片

${activityDemoServiceImpl.findUsersForSL(execution)}跟上面一样,不过注意,事件监听是以#开头,这里是以$符号。

4、增加2、3设置的服务方法(ActivityDemoServiceImpl)

SpringBoot的好处是默认会配置和注入很多服务,所以我们使用起来非常方便,流程图不需要单独部署,直接到服务根据流程名称或id就可以直接获取到想要的内容。

直接上代码了:

package com.example.service.impl;

import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;
import javax.imageio.ImageIO;

import org.activiti.bpmn.model.BpmnModel;
import org.activiti.engine.HistoryService;
import org.activiti.engine.IdentityService;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;  
import org.activiti.engine.TaskService;
import org.activiti.engine.delegate.DelegateExecution;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.impl.cfg.ProcessEngineConfigurationImpl;
import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity;
import org.activiti.engine.impl.pvm.PvmTransition;
import org.activiti.engine.impl.pvm.process.ActivityImpl;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.activiti.image.ProcessDiagramGenerator;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service 
public class ActivityDemoServiceImpl { 
	
	@Autowired  
	private RuntimeService runtimeService;  
	@Autowired  
	private TaskService taskService;  
	@Autowired  
	private HistoryService historyService;  
	@Autowired  
	private RepositoryService repositoryService;  
	@Autowired  
	private ProcessEngineConfigurationImpl processEngineConfiguration;  
	
	/**
	 * 启动流程
	 * @param bizId 业务id
	 */
	public void startProcesses(String bizId) { 
		ProcessInstance pi = runtimeService.startProcessInstanceByKey("demo5", bizId);//流程图id,业务表id
		System.out.println("流程启动成功,流程id:"+pi.getId());
	}  
	
	/**
	 * 
	* 

描述: 根据用户id查询待办任务列表

* @author 范相如 * @date 2018年2月25日 */ public List findTasksByUserId(String userId) { List resultTask = taskService.createTaskQuery().processDefinitionKey("demo5").taskCandidateOrAssigned(userId).list(); return resultTask; } /** * *

描述:任务审批 (通过/拒接)

* @author 范相如 * @date 2018年2月25日 * @param taskId 任务id * @param userId 用户id * @param result false OR true */ public void completeTask(String taskId,String userId,String result) { //获取流程实例 taskService.claim(taskId, userId); Map vars = new HashMap(); vars.put("sign", "true"); taskService.complete(taskId, vars); } /** * 更改业务流程状态#{ActivityDemoServiceImpl.updateBizStatus(execution,"tj")} * @param execution * @param status */ public void updateBizStatus(DelegateExecution execution,String status) { String bizId = execution.getProcessBusinessKey(); //根据业务id自行处理业务表 System.out.println("业务表["+bizId+"]状态更改成功,状态更改为:"+status); } //流程节点权限用户列表${ActivityDemoServiceImpl.findUsers(execution,sign)} public List findUsersForSL(DelegateExecution execution){ return Arrays.asList("sly1","sly2"); } //流程节点权限用户列表${ActivityDemoServiceImpl.findUsers(execution,sign)} public List findUsersForSP(DelegateExecution execution){ return Arrays.asList("spy1","uspy2"); } /** * *

描述: 生成流程图 * 首先启动流程,获取processInstanceId,替换即可生成

* @author 范相如 * @date 2018年2月25日 * @param processInstanceId * @throws Exception */ public void queryProImg(String processInstanceId) throws Exception { //获取历史流程实例 HistoricProcessInstance processInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstanceId).singleResult(); //根据流程定义获取输入流 InputStream is = repositoryService.getProcessDiagram(processInstance.getProcessDefinitionId()); BufferedImage bi = ImageIO.read(is); File file = new File("demo2.png"); if(!file.exists()) file.createNewFile(); FileOutputStream fos = new FileOutputStream(file); ImageIO.write(bi, "png", fos); fos.close(); is.close(); System.out.println("图片生成成功"); List tasks = taskService.createTaskQuery().taskCandidateUser("userId").list(); for(Task t : tasks) { System.out.println(t.getName()); } } /** * 流程图高亮显示 * 首先启动流程,获取processInstanceId,替换即可生成 * @throws Exception */ public void queryProHighLighted(String processInstanceId) throws Exception { //获取历史流程实例 HistoricProcessInstance processInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstanceId).singleResult(); //获取流程图 BpmnModel bpmnModel = repositoryService.getBpmnModel(processInstance.getProcessDefinitionId()); ProcessDiagramGenerator diagramGenerator = processEngineConfiguration.getProcessDiagramGenerator(); ProcessDefinitionEntity definitionEntity = (ProcessDefinitionEntity)repositoryService.getProcessDefinition(processInstance.getProcessDefinitionId()); List highLightedActivitList = historyService.createHistoricActivityInstanceQuery().processInstanceId(processInstanceId).list(); //高亮环节id集合 List highLightedActivitis = new ArrayList(); //高亮线路id集合 List highLightedFlows = getHighLightedFlows(definitionEntity,highLightedActivitList); for(HistoricActivityInstance tempActivity : highLightedActivitList){ String activityId = tempActivity.getActivityId(); highLightedActivitis.add(activityId); } //配置字体 InputStream imageStream = diagramGenerator.generateDiagram(bpmnModel, "png", highLightedActivitis, highLightedFlows,"宋体","微软雅黑","黑体",null,2.0); BufferedImage bi = ImageIO.read(imageStream); File file = new File("demo2.png"); if(!file.exists()) file.createNewFile(); FileOutputStream fos = new FileOutputStream(file); ImageIO.write(bi, "png", fos); fos.close(); imageStream.close(); System.out.println("图片生成成功"); } /** * 获取需要高亮的线 * @param processDefinitionEntity * @param historicActivityInstances * @return */ private List getHighLightedFlows( ProcessDefinitionEntity processDefinitionEntity, List historicActivityInstances) { List highFlows = new ArrayList();// 用以保存高亮的线flowId for (int i = 0; i < historicActivityInstances.size() - 1; i++) {// 对历史流程节点进行遍历 ActivityImpl activityImpl = processDefinitionEntity .findActivity(historicActivityInstances.get(i) .getActivityId());// 得到节点定义的详细信息 List sameStartTimeNodes = new ArrayList();// 用以保存后需开始时间相同的节点 ActivityImpl sameActivityImpl1 = processDefinitionEntity .findActivity(historicActivityInstances.get(i + 1) .getActivityId()); // 将后面第一个节点放在时间相同节点的集合里 sameStartTimeNodes.add(sameActivityImpl1); for (int j = i + 1; j < historicActivityInstances.size() - 1; j++) { HistoricActivityInstance activityImpl1 = historicActivityInstances .get(j);// 后续第一个节点 HistoricActivityInstance activityImpl2 = historicActivityInstances .get(j + 1);// 后续第二个节点 if (activityImpl1.getStartTime().equals( activityImpl2.getStartTime())) { // 如果第一个节点和第二个节点开始时间相同保存 ActivityImpl sameActivityImpl2 = processDefinitionEntity .findActivity(activityImpl2.getActivityId()); sameStartTimeNodes.add(sameActivityImpl2); } else { // 有不相同跳出循环 break; } } List pvmTransitions = activityImpl .getOutgoingTransitions();// 取出节点的所有出去的线 for (PvmTransition pvmTransition : pvmTransitions) { // 对所有的线进行遍历 ActivityImpl pvmActivityImpl = (ActivityImpl) pvmTransition .getDestination(); // 如果取出的线的目标节点存在时间相同的节点里,保存该线的id,进行高亮显示 if (sameStartTimeNodes.contains(pvmActivityImpl)) { highFlows.add(pvmTransition.getId()); } } } return highFlows; } }

5、测试:(ActivitiDemoTest)

package com.example.test;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;

import javax.annotation.Resource;

import org.activiti.engine.HistoryService;
import org.activiti.engine.TaskService;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.history.HistoricDetail;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.history.HistoricTaskInstance;
import org.activiti.engine.history.HistoricVariableInstance;
import org.activiti.engine.task.Task;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;

import com.example.Application;
import com.example.entity.Person;
import com.example.service.impl.ActivityDemoServiceImpl;

//这是JUnit的注解,通过这个注解让SpringJUnit4ClassRunner这个类提供Spring测试上下文。  
@RunWith(SpringJUnit4ClassRunner.class)  
//这是Spring Boot注解,为了进行集成测试,需要通过这个注解加载和配置Spring应用上下  
@SpringBootTest(classes = Application.class)  
@WebAppConfiguration 
public class ActivitiDemoTest {
	@Resource
	private ActivityDemoServiceImpl activityDemoServiceImpl;
	@Autowired  
	private TaskService taskService;  
	@Autowired  
	private HistoryService historyService;  

	//启动流程---215001
	@Test
	public void startPro() {
		activityDemoServiceImpl.startProcesses("123456");
	}

	//获取受理员任务列表
	@Test
	public void findTasksForSL() {
		List lists = activityDemoServiceImpl.findTasksByUserId("sly1");
		System.out.println("任务列表:"+lists);//任务列表:[Task[id=210028, name=受理], Task[id=215005, name=受理]]
	}

	//受理员受理数据
	@Test
	public void completeTasksForSL() {
		activityDemoServiceImpl.completeTask("210028", "sly1", "true");//受理后,任务列表数据减少
	}

	//获取审批员任务列表
	@Test
	public void findTasksForSP() {
		List lists = activityDemoServiceImpl.findTasksByUserId("spy1");
		System.out.println("任务列表:"+lists);//任务列表:[Task[id=220004, name=审批]]
	}

	//审批员通过审核
	@Test
	public void completeTasksForSP() {
		activityDemoServiceImpl.completeTask("220004", "spy1", "true");//审批后,任务列表数据减少
	}


	//设置流程变量
	//设置流程变量【基本类型】  
	@Test
	public void setTasksVar() {
		List lists = activityDemoServiceImpl.findTasksByUserId("sly1");
		for(Task task : lists) {//不知为何,变量保存成功,但数据表只有请假天数含有任务id,单获取流程变量时,根据任务id均可获取到(如下一测试)
			taskService.setVariable(task.getId(), "请假人", "sly1");  
			taskService.setVariableLocal(task.getId(), "请假天数",3);  
			taskService.setVariable(task.getId(), "请假日期", new Date());  
		}
	}

	//获取流程变量
	@Test
	public void getTasksVar() {
		List lists = activityDemoServiceImpl.findTasksByUserId("sly1");
		for(Task task : lists) {
			//获取流程变量【基本类型】  
			String person = (String) taskService.getVariable(task.getId(), "请假人");  
			Integer day = (Integer) taskService.getVariableLocal(task.getId(), "请假天数");  
			Date date = (Date) taskService.getVariable(task.getId(), "请假日期");   

			System.out.println("流程变量:"+person+"||"+day+"||"+date+"||");
		}
	}

	//设置流程变量【实体】  
	@Test
	public void setTasksVarEntity() {
		List lists = activityDemoServiceImpl.findTasksByUserId("sly1");
		for(Task task : lists) {
			Person p = new Person();  
			p.setName("翠花");  
			p.setId(20);  
			p.setDate();;  
			p.setNote("回去探亲,一起吃个饭123");  
			taskService.setVariable(task.getId(), "人员信息(添加固定版本)", p);  

			System.out.println("设置流程变量成功!");  
		}
	}

	//获取流程变量【实体】  实体必须序列化
	@Test
	public void getTasksVarEntity() {
		List lists = activityDemoServiceImpl.findTasksByUserId("sly1");
		for(Task task : lists) {
			// 2.获取流程变量,使用javaBean类型  
			Person p = (Person)taskService.getVariable(task.getId(), "人员信息(添加固定版本)");  
			System.out.println(" 请假人:  "+p.getName()+"  请假天数:  "+p.getId()+"   请假时间:"+ p.getDate()+ "   请假原因: "+p.getNote());  
		}
	}


	//生成流程图---232501
	@Test
	public void queryProImg() throws Exception {
		activityDemoServiceImpl.queryProImg("232501");
	}

	//生成流程图(高亮)---232501
	@Test
	public void queryProHighLighted() throws Exception {
		activityDemoServiceImpl.queryProHighLighted("232501");
	}

	/** 
	 * 查询流程变量的历史表,可以根据变量名称查询该变量的所有历史信息  
	 */  
	@Test  
	public void findHistoryProcessVariables(){
		List list = historyService.createHistoricVariableInstanceQuery()//创建一个历史的流程变量查询对象  
				.variableName("请假天数")  
				.list();  
		if (list!=null &&list.size()>0) {  
			for (HistoricVariableInstance hvi : list) {  
				System.out.println(hvi.getId()+"     "+hvi.getProcessInstanceId()+"   "+hvi.getVariableName()  
				+"   "+hvi.getVariableTypeName()+"    "+hvi.getValue());  
				System.out.println("########################################");  
			}  
		}  

	}


	/**
	 *  历史流程实例查询
	 *  http://blog.csdn.net/luckyzhoustar/article/details/48652783
	 */
	@Test
	public void findHistoricProcessInstance() {
		// 查询已完成的流程  
		List datas = historyService  
				.createHistoricProcessInstanceQuery().finished().list();  
		System.out.println("使用finished方法:" + datas.size());  
		// 根据流程定义ID查询  
		datas = historyService.createHistoricProcessInstanceQuery()  
				.processDefinitionId("processDefinitionId").list();  
		System.out.println("使用processDefinitionId方法: " + datas.size());  
		// 根据流程定义key(流程描述文件的process节点id属性)查询  
		datas = historyService.createHistoricProcessInstanceQuery()  
				.processDefinitionKey("processDefinitionKey").list();  
		System.out.println("使用processDefinitionKey方法: " + datas.size());  
		// 根据业务主键查询  
		datas = historyService.createHistoricProcessInstanceQuery()  
				.processInstanceBusinessKey("processInstanceBusinessKey").list();  
		System.out.println("使用processInstanceBusinessKey方法: " + datas.size());  
		// 根据流程实例ID查询  
		datas = historyService.createHistoricProcessInstanceQuery()  
				.processInstanceId("processInstanceId").list();  
		System.out.println("使用processInstanceId方法: " + datas.size());  
		// 查询没有完成的流程实例  
		historyService.createHistoricProcessInstanceQuery().unfinished().list();  
		System.out.println("使用unfinished方法: " + datas.size()); 
	}

	/**
	 *  历史任务查询
	 * @throws ParseException 
	 */
	@Test
	public void findHistoricTasks() throws ParseException {
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");  

		//历史数据查询  
		List datas = historyService.createHistoricTaskInstanceQuery()  
				.finished().list();  
		System.out.println("使用finished方法查询:" + datas.size());
		datas = historyService.createHistoricTaskInstanceQuery()  
				.processDefinitionId("processDefinitionId").list();  
		System.out.println("使用processDefinitionId方法查询:" + datas.size());
		datas = historyService.createHistoricTaskInstanceQuery()  
				.processDefinitionKey("testProcess").list();  
		System.out.println("使用processDefinitionKey方法查询:" + datas.size());
		datas = historyService.createHistoricTaskInstanceQuery()  
				.processDefinitionName("testProcess2").list();  
		System.out.println("使用processDefinitionName方法查询:" + datas.size());
		datas = historyService.createHistoricTaskInstanceQuery()  
				.processFinished().list();  
		System.out.println("使用processFinished方法查询:" + datas.size());
		datas = historyService.createHistoricTaskInstanceQuery()  
				.processInstanceId("processInstanceId").list();  
		System.out.println("使用processInstanceId方法查询:" + datas.size());
		datas = historyService.createHistoricTaskInstanceQuery()  
				.processUnfinished().list();  
		System.out.println("使用processUnfinished方法查询:" + datas.size());
		datas = historyService.createHistoricTaskInstanceQuery()  
				.taskAssignee("crazyit").list();  
		System.out.println("使用taskAssignee方法查询:" + datas.size());
		datas = historyService.createHistoricTaskInstanceQuery()  
				.taskAssigneeLike("%zy%").list();  
		System.out.println("使用taskAssigneeLike方法查询:" + datas.size());
		datas = historyService.createHistoricTaskInstanceQuery()  
				.taskDefinitionKey("usertask1").list();  
		System.out.println("使用taskDefinitionKey方法查询:" + datas.size()); 
		datas = historyService.createHistoricTaskInstanceQuery()  
				.taskDueAfter(sdf.parse("2020-10-11 06:00:00")).list();  
		System.out.println("使用taskDueAfter方法查询:" + datas.size()); 
		datas = historyService.createHistoricTaskInstanceQuery()  
				.taskDueBefore(sdf.parse("2022-10-11 06:00:00")).list();  
		System.out.println("使用taskDueBefore方法查询:" + datas.size());
		datas = historyService.createHistoricTaskInstanceQuery()  
				.taskDueDate(sdf.parse("2020-10-11 06:00:00")).list();  
		System.out.println("使用taskDueDate方法查询:" + datas.size());
		datas = historyService.createHistoricTaskInstanceQuery()  
				.unfinished().list();  
		System.out.println("使用unfinished方法查询:" + datas.size());
	}
	/**
	 *  历史行为查询
	 *  流程在进行过程中,每每走一个节点,都会记录流程节点的信息,包括节点的id,名称、类型、时间等,保存到ACT_HI_ACTINST表中。
	 * @throws ParseException 
	 */
	@Test
	public void findHistoricActivityInstance() {
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");  

		//查询数据  
		List datas = historyService.createHistoricActivityInstanceQuery()  
				.activityId("endevent1").list();  
		System.out.println("使用activityId查询:" + datas.size());
		datas = historyService.createHistoricActivityInstanceQuery()  
				.activityInstanceId(datas.get(0).getId()).list();  
		System.out.println("使用activityInstanceId查询:" + datas.size());
		datas = historyService.createHistoricActivityInstanceQuery()  
				.activityType("intermediateSignalCatch").list();  
		System.out.println("使用activityType查询:" + datas.size());
		datas = historyService.createHistoricActivityInstanceQuery()  
				.executionId("executionId").list();  
		System.out.println("使用executionId查询:" + datas.size());  
		datas = historyService.createHistoricActivityInstanceQuery().finished().list();  
		System.out.println("使用finished查询:" + datas.size());  
		datas = historyService.createHistoricActivityInstanceQuery()  
				.processInstanceId("processInstanceId").list();  
		System.out.println("使用processInstanceId查询:" + datas.size());
		datas = historyService.createHistoricActivityInstanceQuery()  
				.taskAssignee("crazyit").list();  
		System.out.println("使用taskAssignee查询:" + datas.size()); 
		datas = historyService.createHistoricActivityInstanceQuery().unfinished().list();  
		System.out.println("使用unfinished查询:" + datas.size());  
	}

	/**
	 *  历史流程明细查询
	 *  在流程进行的过程中,会产生许多明细数据,只有将History设置为最高级别的时候,才会被记录到ACT_HI_DETAIL表中。
	 * @throws ParseException 
	 */
	@Test
	public void findHistoricDetail() {
		// 查询历史行为  
		HistoricActivityInstance act = historyService.createHistoricActivityInstanceQuery()  
				.activityName("First Task").finished().singleResult();  
		List datas = historyService.createHistoricDetailQuery()  
				.activityInstanceId(act.getId()).list();  
		System.out.println("使用activityInstanceId方法查询:" + datas.size());
		datas = historyService.createHistoricDetailQuery().excludeTaskDetails().list();  
		System.out.println("使用excludeTaskDetails方法查询:" + datas.size());  
		datas = historyService.createHistoricDetailQuery().formProperties().list();  
		System.out.println("使用formProperties方法查询:" + datas.size()); 
		datas = historyService.createHistoricDetailQuery().processInstanceId("processInstanceId").list();  
		System.out.println("使用processInstanceId方法查询:" + datas.size());
		datas = historyService.createHistoricDetailQuery().taskId("taskId").list();  
		System.out.println("使用taskId方法查询:" + datas.size());  
		datas = historyService.createHistoricDetailQuery().variableUpdates().list();  
		System.out.println("使用variableUpdates方法查询:" + datas.size());
	}

}


流程图源码:



  
    
    
    
    
    
    
    
    
    
    
    
    
    
    
      
    
    
    
    
    
    
  
  
    
      
        
      
      
        
      
      
        
      
      
        
      
      
        
      
      
        
      
      
        
      
      
        
      
      
        
      
      
        
        
      
      
        
        
      
      
        
        
      
      
        
        
      
      
        
        
      
      
        
        
        
      
      
        
        
        
      
      
        
        
      
      
        
        
      
      
        
        
      
    
  

你可能感兴趣的:(Activiti)