使用activiti框架,首先要创建bpmn流程图,这里有两种选择,一种是ide自带的插件(eclipse就不说了,网上各种说好用的;idea是真心难用,而且是好几年没更新的插件了),一种是用activiti官方提供的工具 activiti-app (6.0的版本,我们只用它来画图),把activiti-app.war放在tomcat运行。
先创建一个流程
流程图如下:
关键属性:
因为没有使用activiti的identity(认证)部分,所有我选择动态传入一个参数进去(动态变量名不能重复):
将画好的流程图下载到本地,在processes目录下新建文件(leave.bpmn20.xml),将流程图用文本打开复制进xml中:
<?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/processdef"> <process id="leave" name="请假流程" isExecutable="true"> <startEvent id="startEvent1" name="开始"></startEvent> <userTask id="sid-E9DA7720-703C-4AA1-9233-5B4977C4D7FA" name="填写申请" activiti:assignee="${user}"><!--activiti:assignee 任务指定人--> <extensionElements> <modeler:initiator-can-complete xmlns:modeler="http://activiti.com/modeler"><![CDATA[false]]></modeler:initiator-can-complete> </extensionElements> </userTask> <sequenceFlow id="sid-782B5EB0-C3E4-4EA8-B47C-001493E8CFAA" sourceRef="startEvent1" targetRef="sid-E9DA7720-703C-4AA1-9233-5B4977C4D7FA"></sequenceFlow> <userTask id="sid-F30892E7-AD40-44CE-8FE1-911564290536" name="领导批准" activiti:assignee="${approve}"> <extensionElements> <modeler:initiator-can-complete xmlns:modeler="http://activiti.com/modeler"><![CDATA[false]]></modeler:initiator-can-complete> </extensionElements> </userTask> <sequenceFlow id="sid-9BAB7E50-7E04-4C87-8448-ECDF79CB4405" sourceRef="sid-E9DA7720-703C-4AA1-9233-5B4977C4D7FA" targetRef="sid-F30892E7-AD40-44CE-8FE1-911564290536"></sequenceFlow> <exclusiveGateway id="sid-C7396408-A226-4385-9E87-C63DA1295BEE"></exclusiveGateway> <sequenceFlow id="sid-4570FB2C-F000-4EB9-961B-795DFD3CD037" sourceRef="sid-F30892E7-AD40-44CE-8FE1-911564290536" targetRef="sid-C7396408-A226-4385-9E87-C63DA1295BEE"></sequenceFlow> <endEvent id="sid-E8EA0FBB-EAE1-4316-BAAE-1465DD35D4EA" name="结束"></endEvent> <!--sequenceFlow 流程分支--> <sequenceFlow id="sid-715DC950-61C4-4AA8-9662-A6FD71611EAA" name="审核不通过" sourceRef="sid-C7396408-A226-4385-9E87-C63DA1295BEE" targetRef="sid-E9DA7720-703C-4AA1-9233-5B4977C4D7FA"> <conditionExpression xsi:type="tFormalExpression"><![CDATA[${audit==0}]]></conditionExpression> </sequenceFlow> <sequenceFlow id="sid-EA1D4887-5497-43DE-9D57-A03D3B719681" name="审核通过" sourceRef="sid-C7396408-A226-4385-9E87-C63DA1295BEE" targetRef="sid-E8EA0FBB-EAE1-4316-BAAE-1465DD35D4EA"> <conditionExpression xsi:type="tFormalExpression"><![CDATA[${audit==1}]]></conditionExpression> </sequenceFlow> </process> <bpmndi:BPMNDiagram id="BPMNDiagram_leave"> <bpmndi:BPMNPlane bpmnElement="leave" id="BPMNPlane_leave"> <bpmndi:BPMNShape bpmnElement="startEvent1" id="BPMNShape_startEvent1"> <omgdc:Bounds height="30.0" width="30.0" x="90.0" y="150.0"></omgdc:Bounds> </bpmndi:BPMNShape> <bpmndi:BPMNShape bpmnElement="sid-E9DA7720-703C-4AA1-9233-5B4977C4D7FA" id="BPMNShape_sid-E9DA7720-703C-4AA1-9233-5B4977C4D7FA"> <omgdc:Bounds height="80.0" width="100.0" x="165.0" y="125.0"></omgdc:Bounds> </bpmndi:BPMNShape> <bpmndi:BPMNShape bpmnElement="sid-F30892E7-AD40-44CE-8FE1-911564290536" id="BPMNShape_sid-F30892E7-AD40-44CE-8FE1-911564290536"> <omgdc:Bounds height="80.0" width="100.0" x="300.0" y="125.0"></omgdc:Bounds> </bpmndi:BPMNShape> <bpmndi:BPMNShape bpmnElement="sid-C7396408-A226-4385-9E87-C63DA1295BEE" id="BPMNShape_sid-C7396408-A226-4385-9E87-C63DA1295BEE"> <omgdc:Bounds height="40.0" width="40.0" x="450.0" y="145.0"></omgdc:Bounds> </bpmndi:BPMNShape> <bpmndi:BPMNShape bpmnElement="sid-E8EA0FBB-EAE1-4316-BAAE-1465DD35D4EA" id="BPMNShape_sid-E8EA0FBB-EAE1-4316-BAAE-1465DD35D4EA"> <omgdc:Bounds height="28.0" width="28.0" x="540.0" y="151.0"></omgdc:Bounds> </bpmndi:BPMNShape> <bpmndi:BPMNEdge bpmnElement="sid-782B5EB0-C3E4-4EA8-B47C-001493E8CFAA" id="BPMNEdge_sid-782B5EB0-C3E4-4EA8-B47C-001493E8CFAA"> <omgdi:waypoint x="120.0" y="165.0"></omgdi:waypoint> <omgdi:waypoint x="165.0" y="165.0"></omgdi:waypoint> </bpmndi:BPMNEdge> <bpmndi:BPMNEdge bpmnElement="sid-9BAB7E50-7E04-4C87-8448-ECDF79CB4405" id="BPMNEdge_sid-9BAB7E50-7E04-4C87-8448-ECDF79CB4405"> <omgdi:waypoint x="265.0" y="165.0"></omgdi:waypoint> <omgdi:waypoint x="300.0" y="165.0"></omgdi:waypoint> </bpmndi:BPMNEdge> <bpmndi:BPMNEdge bpmnElement="sid-4570FB2C-F000-4EB9-961B-795DFD3CD037" id="BPMNEdge_sid-4570FB2C-F000-4EB9-961B-795DFD3CD037"> <omgdi:waypoint x="400.0" y="165.20746887966806"></omgdi:waypoint> <omgdi:waypoint x="450.4166666666667" y="165.41666666666666"></omgdi:waypoint> </bpmndi:BPMNEdge> <bpmndi:BPMNEdge bpmnElement="sid-715DC950-61C4-4AA8-9662-A6FD71611EAA" id="BPMNEdge_sid-715DC950-61C4-4AA8-9662-A6FD71611EAA"> <omgdi:waypoint x="470.5" y="145.5"></omgdi:waypoint> <omgdi:waypoint x="470.5" y="26.0"></omgdi:waypoint> <omgdi:waypoint x="215.0" y="26.0"></omgdi:waypoint> <omgdi:waypoint x="215.0" y="125.0"></omgdi:waypoint> </bpmndi:BPMNEdge> <bpmndi:BPMNEdge bpmnElement="sid-EA1D4887-5497-43DE-9D57-A03D3B719681" id="BPMNEdge_sid-EA1D4887-5497-43DE-9D57-A03D3B719681"> <omgdi:waypoint x="489.6144578313253" y="165.3855421686747"></omgdi:waypoint> <omgdi:waypoint x="540.0002509882663" y="165.0838308324056"></omgdi:waypoint> </bpmndi:BPMNEdge> </bpmndi:BPMNPlane> </bpmndi:BPMNDiagram> </definitions>
spring: datasource: #数据源基本配置 username: root password: root url: jdbc:mysql://localhost/activiti?serverTimezone=Asia/Shanghai&characterEncoding=UTF-8&nullCatalogMeansCurrent=true&useSSL=false&useLegacyDatetimeCode=false driver-class-name: com.mysql.cj.jdbc.Driver activiti: # 开启历史库 db-history-used: true history-level: audit
历史信息级别可以配置成以下几种(activiti7感觉默认的是none):
none: 忽略所有历史存档。这是流程执行时性能最好的状态,但没有任何历史信息可用。
activity: 保存所有流程实例信息和活动实例信息。 在流程实例结束时, 最后一个流程实例中的最新的变量值将赋值给历史变量。 不会保存过程中的详细信息。
audit: 它保存所有流程实例信息, 活动信息, 保证所有的变量和提交的表单属性保持同步 这样所有用户交互信息都是可追溯的,可以用来审计。
full: 这个是最高级别的历史信息存档,同样也是最慢的。 这个级别存储发生在审核以及所有其它细节的信息, 主要是更新流程变量。
/** * 开启一个请假流程 * @param user 用户key * @param processDefinitionKey 流程图key 每一个流程有对应的一个key这个是某一个流程内固定的写在bpmn内的 */ void startLeaveProcess(String user,String processDefinitionKey){ System.out.println(user+"开启一个请假流程:"+ processDefinitionKey); HashMap<String, Object> variables=new HashMap<>(); variables.put("user", user);//userKey在上文的流程变量中指定了 ProcessInstance instance = runtimeService .startProcessInstanceByKey(processDefinitionKey,variables); System.out.println("流程实例ID:"+instance.getId()); System.out.println("流程定义ID:"+instance.getProcessDefinitionId()); System.out.println("=================================================================="); }
运行结果:
张三开启一个请假流程:leave 流程实例ID:34f2f038-0d07-11ea-b319-9c5c8e7034f6 流程定义ID:leave:1:32f7bc77-0d07-11ea-b319-9c5c8e7034f6 ==================================================================
注意数据库的变化:
mysql> select ID_,REV_,PROC_INST_ID_,NAME_,ASSIGNEE_ from act_ru_task; +--------------------------------------+------+--------------------------------------+----------+-----------+ | ID_ | REV_ | PROC_INST_ID_ | NAME_ | ASSIGNEE_ | +--------------------------------------+------+--------------------------------------+----------+-----------+ | 34f8958d-0d07-11ea-b319-9c5c8e7034f6 | 1 | 34f2f038-0d07-11ea-b319-9c5c8e7034f6 | 填写申请 | 张三 | +--------------------------------------+------+--------------------------------------+----------+-----------+ 1 row in set
/** * 查询当前任务流程 */ void queryLeaveProcessING(String assignee){ System.out.println(assignee+"查询自己当前的流程:"); List<Task> list = taskService.createTaskQuery()//创建任务查询对象 .taskAssignee(assignee)//指定个人任务查询 .list(); if(list!=null && list.size()>0){ for(Task task:list){ System.out.println("任务ID:"+task.getId()); System.out.println("任务名称:"+task.getName()); System.out.println("任务的创建时间:"+task.getCreateTime()); System.out.println("任务的办理人:"+task.getAssignee()); System.out.println("流程实例ID:"+task.getProcessInstanceId()); System.out.println("执行对象ID:"+task.getExecutionId()); System.out.println("流程定义ID:"+task.getProcessDefinitionId()); Map<String, Object> map = task.getProcessVariables(); for (Map.Entry<String, Object> m : map.entrySet()) { System.out.println("key:" + m.getKey() + " value:" + m.getValue()); } for (Map.Entry<String, Object> m : task.getTaskLocalVariables().entrySet()) { System.out.println("key:" + m.getKey() + " value:" + m.getValue()); } } } System.out.println("=================================================================="); }
运行结果:
张三查询自己当前的流程: 任务ID:34f8958d-0d07-11ea-b319-9c5c8e7034f6 任务名称:填写申请 任务的创建时间:Fri Nov 22 17:05:19 CST 2019 任务的办理人:张三 流程实例ID:34f2f038-0d07-11ea-b319-9c5c8e7034f6 执行对象ID:34f3b38a-0d07-11ea-b319-9c5c8e7034f6 流程定义ID:leave:1:32f7bc77-0d07-11ea-b319-9c5c8e7034f6 ==================================================================
void completeTask(String approve,String taskId){ System.out.println(approve+":提交自己的流程:"+taskId); //任务ID HashMap<String, Object> variables=new HashMap<>(); variables.put("approve", approve);//userKey在上文的流程变量中指定了 taskService.complete(taskId,variables); System.out.println("完成任务:任务ID:"+taskId); System.out.println("=================================================================="); }
运行结果:
领导李四:提交自己的流程:34f8958d-0d07-11ea-b319-9c5c8e7034f6 完成任务:任务ID:34f8958d-0d07-11ea-b319-9c5c8e7034f6 ==================================================================
数据库:
mysql> select ID_,REV_,PROC_INST_ID_,NAME_,ASSIGNEE_ from act_ru_task; +--------------------------------------+------+--------------------------------------+----------+-----------+ | ID_ | REV_ | PROC_INST_ID_ | NAME_ | ASSIGNEE_ | +--------------------------------------+------+--------------------------------------+----------+-----------+ | e60702be-0d08-11ea-8a0a-9c5c8e7034f6 | 1 | 34f2f038-0d07-11ea-b319-9c5c8e7034f6 | 领导批准 | 领导李四 | +--------------------------------------+------+--------------------------------------+----------+-----------+ 1 row in set
void completeTask(String user,String taskId,int audit){ System.out.println(user+":提交自己的流程:"+taskId+" ;是否通过:"+audit); //任务ID HashMap<String, Object> variables=new HashMap<>(); variables.put("audit", audit);//userKey在上文的流程变量中指定了 taskService.complete(taskId,variables); System.out.println("完成任务:任务ID:"+taskId); System.out.println("=================================================================="); }
运行结果:
领导李四:提交自己的流程:e60702be-0d08-11ea-8a0a-9c5c8e7034f6 ;是否通过:1 完成任务:任务ID:e60702be-0d08-11ea-8a0a-9c5c8e7034f6 ==================================================================
数据库:
mysql> select ID_,REV_,PROC_INST_ID_,NAME_,ASSIGNEE_ from act_ru_task; Empty set
根据流程ID查询:
List<HistoricTaskInstance> list=historyService // 历史相关Service .createHistoricTaskInstanceQuery() // 创建历史活动实例查询 .processInstanceId("34f2f038-0d07-11ea-b319-9c5c8e7034f6") // 执行流程实例id .orderByTaskCreateTime() .asc() .list(); for(HistoricTaskInstance hai:list){ System.out.println("活动ID:"+hai.getId()); System.out.println("流程实例ID:"+hai.getProcessInstanceId()); System.out.println("活动名称:"+hai.getName()); System.out.println("办理人:"+hai.getAssignee()); System.out.println("开始时间:"+hai.getStartTime()); System.out.println("结束时间:"+hai.getEndTime()); System.out.println("=================================================================="); }
运行结果:
活动ID:34f8958d-0d07-11ea-b319-9c5c8e7034f6 流程实例ID:34f2f038-0d07-11ea-b319-9c5c8e7034f6 活动名称:填写申请 办理人:张三 开始时间:Fri Nov 22 17:05:19 CST 2019 结束时间:Fri Nov 22 17:17:25 CST 2019 ================================================================== 活动ID:e60702be-0d08-11ea-8a0a-9c5c8e7034f6 流程实例ID:34f2f038-0d07-11ea-b319-9c5c8e7034f6 活动名称:领导批准 办理人:领导李四 开始时间:Fri Nov 22 17:17:25 CST 2019 结束时间:Fri Nov 22 17:31:03 CST 2019 ==================================================================
查询个人历史记录:
List<HistoricTaskInstance> list = historyService.createHistoricTaskInstanceQuery().taskAssignee("张三").orderByTaskCreateTime().asc().list(); for(HistoricTaskInstance hai:list){ System.out.println("活动ID:"+hai.getId()); System.out.println("流程实例ID:"+hai.getProcessInstanceId()); System.out.println("活动名称:"+hai.getName()); System.out.println("办理人:"+hai.getAssignee()); System.out.println("开始时间:"+hai.getStartTime()); System.out.println("结束时间:"+hai.getEndTime()); System.out.println("=================================================================="); }
运行结果:
活动ID:34f8958d-0d07-11ea-b319-9c5c8e7034f6 流程实例ID:34f2f038-0d07-11ea-b319-9c5c8e7034f6 活动名称:填写申请 办理人:张三 开始时间:Fri Nov 22 17:05:19 CST 2019 结束时间:Fri Nov 22 17:17:25 CST 2019 ==================================================================
数据库:
mysql> select ID_,PROC_INST_ID_,NAME_,ASSIGNEE_ from act_hi_taskinst; +--------------------------------------+--------------------------------------+----------+-----------+ | ID_ | PROC_INST_ID_ | NAME_ | ASSIGNEE_ | +--------------------------------------+--------------------------------------+----------+-----------+ | 34f8958d-0d07-11ea-b319-9c5c8e7034f6 | 34f2f038-0d07-11ea-b319-9c5c8e7034f6 | 填写申请 | 张三 | | e60702be-0d08-11ea-8a0a-9c5c8e7034f6 | 34f2f038-0d07-11ea-b319-9c5c8e7034f6 | 领导批准 | 领导李四 | +--------------------------------------+--------------------------------------+----------+-----------+ 2 rows in set
package com.example.activitidemo2; import org.activiti.engine.HistoryService; import org.activiti.engine.RepositoryService; import org.activiti.engine.RuntimeService; import org.activiti.engine.TaskService; import org.activiti.engine.history.HistoricTaskInstance; import org.activiti.engine.runtime.ProcessInstance; import org.activiti.engine.task.Task; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; import javax.annotation.Resource; import java.util.HashMap; import java.util.List; import java.util.Map; @SpringBootTest class ActivitiDemo2ApplicationTests { @Resource RepositoryService repositoryService; @Resource RuntimeService runtimeService; @Resource TaskService taskService; @Resource HistoryService historyService; @Test void contextLoads() { System.out.println("Number of process definitions : " + repositoryService.createProcessDefinitionQuery().count()); System.out.println("Number of tasks : " + taskService.createTaskQuery().count()); runtimeService.startProcessInstanceByKey("oneTaskProcess"); System.out.println("Number of tasks after process start: " + taskService.createTaskQuery().count()); } @Test void testProcess(){ //张三开启一个请假流程 String user = "张三"; String approve = "领导李四"; // startLeaveProcess(user,"leave"); //张三查询自己流程 // queryLeaveProcessING(user); // 提交给领导李四审核 // String taskId = "34f8958d-0d07-11ea-b319-9c5c8e7034f6"; // completeTask(approve,taskId); //领导李四查询自己的流程 // queryLeaveProcessING(approve); //李四提交自己的流程 completeTask(approve,"e60702be-0d08-11ea-8a0a-9c5c8e7034f6",1); //张三查询自己的历史流程 // queryHistoryTask(userKey); } /** * 开启一个请假流程 * @param user 用户key * @param processDefinitionKey 流程图key 每一个流程有对应的一个key这个是某一个流程内固定的写在bpmn内的 */ void startLeaveProcess(String user,String processDefinitionKey){ System.out.println(user+"开启一个请假流程:"+ processDefinitionKey); HashMap<String, Object> variables=new HashMap<>(); variables.put("user", user);//userKey在上文的流程变量中指定了 ProcessInstance instance = runtimeService .startProcessInstanceByKey(processDefinitionKey,variables); System.out.println("流程实例ID:"+instance.getId()); System.out.println("流程定义ID:"+instance.getProcessDefinitionId()); System.out.println("=================================================================="); } /** * 查询当前任务流程 */ void queryLeaveProcessING(String assignee){ System.out.println(assignee+"查询自己当前的流程:"); List<Task> list = taskService.createTaskQuery()//创建任务查询对象 .taskAssignee(assignee)//指定个人任务查询 .list(); if(list!=null && list.size()>0){ for(Task task:list){ System.out.println("任务ID:"+task.getId()); System.out.println("任务名称:"+task.getName()); System.out.println("任务的创建时间:"+task.getCreateTime()); System.out.println("任务的办理人:"+task.getAssignee()); System.out.println("流程实例ID:"+task.getProcessInstanceId()); System.out.println("执行对象ID:"+task.getExecutionId()); System.out.println("流程定义ID:"+task.getProcessDefinitionId()); Map<String, Object> map = task.getProcessVariables(); for (Map.Entry<String, Object> m : map.entrySet()) { System.out.println("key:" + m.getKey() + " value:" + m.getValue()); } for (Map.Entry<String, Object> m : task.getTaskLocalVariables().entrySet()) { System.out.println("key:" + m.getKey() + " value:" + m.getValue()); } } } System.out.println("=================================================================="); } @Test void completeTask(String approve,String taskId){ System.out.println(approve+":提交自己的流程:"+taskId); //任务ID HashMap<String, Object> variables=new HashMap<>(); variables.put("approve", approve);//userKey在上文的流程变量中指定了 taskService.complete(taskId,variables); System.out.println("完成任务:任务ID:"+taskId); System.out.println("=================================================================="); } @Test void completeTask(String user,String taskId,int audit){ System.out.println(user+":提交自己的流程:"+taskId+" ;是否通过:"+audit); //任务ID HashMap<String, Object> variables=new HashMap<>(); variables.put("audit", audit);//userKey在上文的流程变量中指定了 taskService.complete(taskId,variables); System.out.println("完成任务:任务ID:"+taskId); System.out.println("=================================================================="); } @Test void queryHistoryTask(){ List<HistoricTaskInstance> list=historyService // 历史相关Service .createHistoricTaskInstanceQuery() // 创建历史活动实例查询 .processInstanceId("34f2f038-0d07-11ea-b319-9c5c8e7034f6") // 执行流程实例id .orderByTaskCreateTime() .asc() .list(); for(HistoricTaskInstance hai:list){ System.out.println("活动ID:"+hai.getId()); System.out.println("流程实例ID:"+hai.getProcessInstanceId()); System.out.println("活动名称:"+hai.getName()); System.out.println("办理人:"+hai.getAssignee()); System.out.println("开始时间:"+hai.getStartTime()); System.out.println("结束时间:"+hai.getEndTime()); System.out.println("=================================================================="); } // List<HistoricTaskInstance> list = historyService.createHistoricTaskInstanceQuery().taskAssignee("张三").orderByTaskCreateTime().asc().list(); // for(HistoricTaskInstance hai:list){ // System.out.println("活动ID:"+hai.getId()); // System.out.println("流程实例ID:"+hai.getProcessInstanceId()); // System.out.println("活动名称:"+hai.getName()); // System.out.println("办理人:"+hai.getAssignee()); // System.out.println("开始时间:"+hai.getStartTime()); // System.out.println("结束时间:"+hai.getEndTime()); // System.out.println("=================================================================="); // } } }