前两天做了一个SpringBoot整合Activiti的完整示例,功能包括:退回/通过(节点条件)、指定办理人、生成流程图、高亮显示已办节点、查询任务列表(办理人)等,下面先简单记录(含完整代码),十六上班后再详细补充。
1、画流程图:
高亮生成的流程图(流程已至办理):
这个流程图比较简单,这里不介绍如何画的了(记得让文件名称与id相同),下面有最终的流程图源码。
2、设置业户提交、资料不全、已签发的事件触发代码:
点击选择事件节点,下方properties里选择如下图:
找不到properties的话 要选择Activiti模式就可以:
#{activityDemoServiceImpl.updateBizStatus(execution,"tj")} 表示将使用activityDemoServiceImpl服务的updateBizStatus方法,里面有两个参数,execution是当前流程节点对象,可获取流程信息及业务key等,"tj"字符串参数。(这里设置的服务名是小写开头,实际服务名是大写开头,否则找不到——不知道是不是因为我用的5.22)
3、指定受理/审批 办理人员:
也是选中,然后如下图操作:
${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());
}
}
流程图源码: