activiti 一个流程的运转步骤 以请假流程为例
---为了加深对activiti的理解记忆,对自己做的一个流程进行自述。加强记忆
请假实例
一.设计请假的流程图以及流程文件,完善对应数据项,比如用户信息,请假单信息
--请假单
--流程图
--流程文件leaveBill.bpmn
<?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="LeaveBill" name="LeaveBillProcess" isExecutable="true">
<startEvent id="startevent1" name="Start"></startEvent>
<userTask id="usertask1" name="提交申请" activiti:assignee="${inputUser}" activiti:formKey="workflowAction_audit.action"></userTask>
<sequenceFlow id="flow1" sourceRef="startevent1" targetRef="usertask1"></sequenceFlow>
<userTask id="usertask2" name="经纪人审批" activiti:formKey="workflowAction_audit.action">
<extensionElements>
<activiti:taskListener event="create" class="cn.itcast.ssh.utils.MangerTaskHandlerCandidateUsers"></activiti:taskListener>
</extensionElements>
</userTask>
<sequenceFlow id="flow2" sourceRef="usertask1" targetRef="usertask2"></sequenceFlow>
<userTask id="usertask3" name="老总审批" activiti:assignee="王中军" activiti:formKey="workflowAction_audit.action"></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_LeaveBill">
<bpmndi:BPMNPlane bpmnElement="LeaveBill" id="BPMNPlane_LeaveBill">
<bpmndi:BPMNShape bpmnElement="startevent1" id="BPMNShape_startevent1">
<omgdc:Bounds height="35.0" width="35.0" x="290.0" y="60.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="usertask1" id="BPMNShape_usertask1">
<omgdc:Bounds height="55.0" width="105.0" x="255.0" y="140.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="usertask2" id="BPMNShape_usertask2">
<omgdc:Bounds height="55.0" width="105.0" x="255.0" y="230.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="usertask3" id="BPMNShape_usertask3">
<omgdc:Bounds height="55.0" width="105.0" x="255.0" y="310.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="endevent1" id="BPMNShape_endevent1">
<omgdc:Bounds height="35.0" width="35.0" x="290.0" y="400.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge bpmnElement="flow1" id="BPMNEdge_flow1">
<omgdi:waypoint x="307.0" y="95.0"></omgdi:waypoint>
<omgdi:waypoint x="307.0" y="140.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="flow2" id="BPMNEdge_flow2">
<omgdi:waypoint x="307.0" y="195.0"></omgdi:waypoint>
<omgdi:waypoint x="307.0" y="230.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="flow3" id="BPMNEdge_flow3">
<omgdi:waypoint x="307.0" y="285.0"></omgdi:waypoint>
<omgdi:waypoint x="307.0" y="310.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="flow4" id="BPMNEdge_flow4">
<omgdi:waypoint x="307.0" y="365.0"></omgdi:waypoint>
<omgdi:waypoint x="307.0" y="400.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</definitions>
二.准备请假单的增删改查页面,用来处理请假单的生成业务逻辑
填写好请假单后,就要开始申请请假了。这里主要是更新请假状态,启动请假流程实例。runtimeService.startProcessInstanceByKey(key,objId,variables);key时请假实体类的类名,其实就是一个标识。这里还可以通过流程id来启动startProcessInstanceById
/**更新请假状态,启动流程实例,让启动的流程实例关联业务*/
@Override
public void saveStartProcess(WorkflowBean workflowBean) {
//1:获取请假单ID,使用请假单ID,查询请假单的对象LeaveBill
Long id = workflowBean.getId();
LeaveBill leaveBill = leaveBillDao.findLeaveBillById(id);
//2:更新请假单的请假状态从0变成1(初始录入-->审核中)
leaveBill.setState(1);
//3:使用当前对象获取到流程定义的key(对象的名称就是流程定义的key)
String key = leaveBill.getClass().getSimpleName();
/**
* 4:从Session中获取当前任务的办理人,使用流程变量设置下一个任务的办理人
* inputUser是流程变量的名称,
* 获取的办理人是流程变量的值
*/
Map<String, Object> variables = new HashMap<String,Object>();
variables.put("inputUser", SessionContext.get().getName());//表示惟一用户
/**
* 5: (1)使用流程变量设置字符串(格式:LeaveBill.id的形式),通过设置,让启动的流程(流程实例)关联业务
(2)使用正在执行对象表中的一个字段BUSINESS_KEY(Activiti提供的一个字段),让启动的流程(流程实例)关联业务
*/
//格式:LeaveBill.id的形式(使用流程变量)
String objId = key+"."+id;
variables.put("objId", objId);
//6:使用流程定义的key,启动流程实例,同时设置流程变量,同时向正在执行的执行对象表中的字段BUSINESS_KEY添加业务数据,同时让流程关联业务
runtimeService.startProcessInstanceByKey(key,objId,variables);
}
三.启动以后,流程框架会按照你画好的流程图,走到第一个节点,这里说的是除了start节点。也就是申请请假节点。这里设置的申请请假处理人,是当前登录人。随意,当我们启动请假流程后,我们也就有了一个任务,提交申请的任务。查看我们的任务:
/**2:使用当前用户名查询正在执行的任务表,获取当前任务的集合List<Task>*/
@Override
public List<Task> findTaskListByName(String name) {
List<Task> list = taskService.createTaskQuery()//
.taskAssignee(name)//指定个人任务查询
.orderByTaskCreateTime().asc()//
.list();
if(null==list||list.size()<1){//如果指定个人任务为空,则查询多人任务
list = taskService.createTaskQuery().
taskCandidateUser(name).
orderByTaskCreateTime().
asc().
list();
}
if(null==list||list.size()<1){//如果指定多人任务为空在获取组的任务
list = taskService.createTaskQuery().taskCandidateGroup(name).
orderByTaskCreateTime().
asc().
list();
}
return list;
}
---处理我们的任务,使其想下一个节点进行
public String audit(){
//获取任务ID
String taskId = workflowBean.getTaskId();
/**一:使用任务ID,查找请假单ID,从而获取请假单信息*/
LeaveBill leaveBill = workflowService.findLeaveBillByTaskId(taskId);
ValueContext.putValueStack(leaveBill);
/**二:已知任务ID,查询ProcessDefinitionEntiy对象,从而获取当前任务完成之后的连线名称,并放置到List<String>集合中*/
List<String> outcomeList = workflowService.findOutComeListByTaskId(taskId);
ValueContext.putValueContext("outcomeList", outcomeList);
/**三:查询所有历史审核人的审核信息,帮助当前人完成审核,返回List<Comment>*/
List<Comment> commentList = workflowService.findCommentByTaskId(taskId);
ValueContext.putValueContext("commentList", commentList);
return "taskForm";
}
------获取后续流程节点的信息
/**二:已知任务ID,查询ProcessDefinitionEntiy对象,从而获取当前任务完成之后的连线名称,并放置到List<String>集合中*/
@Override
public List<String> findOutComeListByTaskId(String taskId) {
//返回存放连线的名称集合
List<String> list = new ArrayList<String>();
//1:使用任务ID,查询任务对象
Task task = taskService.createTaskQuery()//
.taskId(taskId)//使用任务ID查询
.singleResult();
//2:获取流程定义ID
String processDefinitionId = task.getProcessDefinitionId();
//3:查询ProcessDefinitionEntiy对象
ProcessDefinitionEntity processDefinitionEntity = (ProcessDefinitionEntity) repositoryService.getProcessDefinition(processDefinitionId);
//使用任务对象Task获取流程实例ID
String processInstanceId = task.getProcessInstanceId();
//使用流程实例ID,查询正在执行的执行对象表,返回流程实例对象
ProcessInstance pi = runtimeService.createProcessInstanceQuery()//
.processInstanceId(processInstanceId)//使用流程实例ID查询
.singleResult();
//获取当前活动的id
String activityId = pi.getActivityId();
//4:获取当前的活动
ActivityImpl activityImpl = processDefinitionEntity.findActivity(activityId);
//5:获取当前活动完成之后连线的名称
List<PvmTransition> pvmList = activityImpl.getOutgoingTransitions();
if(pvmList!=null && pvmList.size()>0){
for(PvmTransition pvm:pvmList){
String name = (String) pvm.getProperty("name");
if(StringUtils.isNotBlank(name)){
list.add(name);
}
else{
list.add("默认提交");
}
}
}
return list;
}
-------获取完后通过任务id获取历史批注信息
/**获取批注信息,传递的是当前任务ID,获取历史任务ID对应的批注*/
@Override
public List<Comment> findCommentByTaskId(String taskId) {
List<Comment> list = new ArrayList<Comment>();
//使用当前的任务ID,查询当前流程对应的历史任务ID
//使用当前任务ID,获取当前任务对象
Task task = taskService.createTaskQuery()//
.taskId(taskId)//使用任务ID查询
.singleResult();
//获取流程实例ID
String processInstanceId = task.getProcessInstanceId();
//使用流程实例ID,查询历史任务,获取历史任务对应的每个任务ID
List<HistoricTaskInstance> htiList = historyService.createHistoricTaskInstanceQuery()//历史任务表查询
.processInstanceId(processInstanceId)//使用流程实例ID查询
.list();
//遍历集合,获取每个任务ID
if(htiList!=null && htiList.size()>0){
for(HistoricTaskInstance hti:htiList){
//任务ID
String htaskId = hti.getId();
//获取批注信息
List<Comment> taskList = taskService.getTaskComments(htaskId);//对用历史完成后的任务ID
list.addAll(taskList);
}
}
list = taskService.getProcessInstanceComments(processInstanceId);
return list;
}
----以上信息用来展现将来任务进行的状态,流程图的查看
四.提交申请,进入下一环节
/**指定连线的名称完成任务*/
@Override
public void saveSubmitTask(WorkflowBean workflowBean) {
//获取任务ID
String taskId = workflowBean.getTaskId();
//获取连线的名称
String outcome = workflowBean.getOutcome();
//批注信息
String message = workflowBean.getComment();
//获取请假单ID
Long id = workflowBean.getId();
/**
* 1:在完成之前,添加一个批注信息,向act_hi_comment表中添加数据,用于记录对当前申请人的一些审核信息
*/
//使用任务ID,查询任务对象,获取流程流程实例ID
Task task = taskService.createTaskQuery()//
.taskId(taskId)//使用任务ID查询
.singleResult();
//获取流程实例ID
String processInstanceId = task.getProcessInstanceId();
/**
* 注意:添加批注的时候,由于Activiti底层代码是使用:
* String userId = Authentication.getAuthenticatedUserId();
CommentEntity comment = new CommentEntity();
comment.setUserId(userId);
所有需要从Session中获取当前登录人,作为该任务的办理人(审核人),对应act_hi_comment表中的User_ID的字段,不过不添加审核人,该字段为null
所以要求,添加配置执行使用Authentication.setAuthenticatedUserId();添加当前任务的审核人
* */
Authentication.setAuthenticatedUserId(SessionContext.get().getName());
taskService.addComment(taskId, processInstanceId, message);
/**
* 2:如果连线的名称是“默认提交”,那么就不需要设置,如果不是,就需要设置流程变量
* 在完成任务之前,设置流程变量,按照连线的名称,去完成任务
流程变量的名称:outcome
流程变量的值:连线的名称
*/
Map<String, Object> variables = new HashMap<String,Object>();
if(outcome!=null && !outcome.equals("默认提交")){
variables.put("outcome", outcome);
}
//3:使用任务ID,完成当前人的个人任务,同时流程变量
taskService.complete(taskId, variables);
//4:当任务完成之后,需要指定下一个任务的办理人(使用类)-----已经开发完成
/**
* 5:在完成任务之后,判断流程是否结束
如果流程结束了,更新请假单表的状态从1变成2(审核中-->审核完成)
*/
ProcessInstance pi = runtimeService.createProcessInstanceQuery()//
.processInstanceId(processInstanceId)//使用流程实例ID查询
.singleResult();
//流程结束了
if(pi==null){
//更新请假单表的状态从1变成2(审核中-->审核完成)
LeaveBill bill = leaveBillDao.findLeaveBillById(id);
bill.setState(2);
}
}
五.接下来的老总审批和4是完全相同的重复步骤了。不做解释了
走到最后节点时,流程实例ProcessInstance会为空,则进行业务逻辑处理。完成请假单的所有流程。到这里就结束了。
工作流的运作轨迹,都在代码中,注释的很清楚。